Skip to main content

leyline/core/response/
relay.rs

1use http::HeaderMap;
2use http::header::{
3    CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, HeaderName, PROXY_AUTHENTICATE,
4    PROXY_AUTHORIZATION, TE, TRAILER, TRANSFER_ENCODING, UPGRADE,
5};
6
7use super::Response;
8use crate::core::session::decompress::{Decoder, content_codings};
9
10const KEEP_ALIVE: &str = "keep-alive";
11const PROXY_CONNECTION: &str = "proxy-connection";
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14#[non_exhaustive]
15pub enum RelayBody {
16    AsReceived,
17    Decoded,
18}
19
20impl Response {
21    pub fn relay_headers(&self, body: RelayBody) -> HeaderMap {
22        let body = match body {
23            RelayBody::Decoded if !self.decodes_body() => RelayBody::AsReceived,
24            other => other,
25        };
26        relay_headers(&self.headers, body)
27    }
28
29    fn decodes_body(&self) -> bool {
30        let encoding = content_codings(self.headers.get_all(CONTENT_ENCODING));
31        Decoder::new(encoding.as_deref(), &self.compression).is_ok_and(|decoder| decoder.is_some())
32    }
33}
34
35fn hop_by_hop() -> [HeaderName; 9] {
36    [
37        CONNECTION,
38        HeaderName::from_static(KEEP_ALIVE),
39        HeaderName::from_static(PROXY_CONNECTION),
40        PROXY_AUTHENTICATE,
41        PROXY_AUTHORIZATION,
42        TE,
43        TRAILER,
44        TRANSFER_ENCODING,
45        UPGRADE,
46    ]
47}
48
49pub fn relay_headers(headers: &HeaderMap, body: RelayBody) -> HeaderMap {
50    let mut out = headers.clone();
51    if body == RelayBody::Decoded && out.contains_key(CONTENT_ENCODING) {
52        out.remove(CONTENT_ENCODING);
53        out.remove(CONTENT_LENGTH);
54    }
55    let listed: Vec<String> = headers
56        .get_all(CONNECTION)
57        .iter()
58        .filter_map(|value| value.to_str().ok())
59        .flat_map(|value| value.split(','))
60        .map(|token| token.trim().to_ascii_lowercase())
61        .filter(|token| !token.is_empty())
62        .collect();
63    for name in hop_by_hop() {
64        out.remove(name);
65    }
66    for name in &listed {
67        out.remove(name.as_str());
68    }
69    out
70}