Skip to main content

ic_bn_lib/http/
headers.rs

1// Clippy complains that these are interior-mutable.
2// We don't mutate them, so silence it.
3// https://rust-lang.github.io/rust-clippy/master/index.html#/declare_interior_mutable_const
4#![allow(clippy::declare_interior_mutable_const)]
5#![allow(clippy::borrow_interior_mutable_const)]
6
7use http::header::{
8    CONNECTION, HeaderMap, HeaderName, HeaderValue, TE, TRANSFER_ENCODING, UPGRADE,
9};
10
11#[macro_export]
12macro_rules! hname {
13    ($id:expr) => {{ http::header::HeaderName::from_static($id) }};
14}
15
16#[macro_export]
17macro_rules! hval {
18    ($id:expr) => {{ http::header::HeaderValue::from_static($id) }};
19}
20
21// Header names
22pub const X_IC_CACHE_STATUS: HeaderName = hname!("x-ic-cache-status");
23pub const X_IC_CACHE_BYPASS_REASON: HeaderName = hname!("x-ic-cache-bypass-reason");
24pub const X_IC_SUBNET_ID: HeaderName = hname!("x-ic-subnet-id");
25pub const X_IC_NODE_ID: HeaderName = hname!("x-ic-node-id");
26pub const X_IC_SUBNET_TYPE: HeaderName = hname!("x-ic-subnet-type");
27pub const X_IC_CANISTER_ID_CBOR: HeaderName = hname!("x-ic-canister-id-cbor");
28pub const X_IC_METHOD_NAME: HeaderName = hname!("x-ic-method-name");
29pub const X_IC_SENDER: HeaderName = hname!("x-ic-sender");
30pub const X_IC_RETRIES: HeaderName = hname!("x-ic-retries");
31pub const X_IC_ERROR_CAUSE: HeaderName = hname!("x-ic-error-cause");
32pub const X_IC_REQUEST_TYPE: HeaderName = hname!("x-ic-request-type");
33pub const X_IC_CANISTER_ID: HeaderName = hname!("x-ic-canister-id");
34pub const X_IC_COUNTRY_CODE: HeaderName = hname!("x-ic-country-code");
35pub const X_CACHE_TTL: HeaderName = hname!("x-cache-ttl");
36pub const X_FORWARDED_FOR: HeaderName = hname!("x-forwarded-for");
37pub const X_FORWARDED_HOST: HeaderName = hname!("x-forwarded-host");
38pub const X_REQUEST_ID: HeaderName = hname!("x-request-id");
39pub const X_REQUESTED_WITH: HeaderName = hname!("x-requested-with");
40pub const X_REAL_IP: HeaderName = hname!("x-real-ip");
41
42// Header values
43pub const CONTENT_TYPE_CBOR: HeaderValue = hval!("application/cbor");
44pub const CONTENT_TYPE_OCTET_STREAM: HeaderValue = hval!("application/octet-stream");
45pub const CONTENT_TYPE_HTML: HeaderValue = hval!("text/html; charset=utf-8");
46pub const HSTS_1YEAR: HeaderValue = hval!("max-age=31536000; includeSubDomains");
47pub const X_CONTENT_TYPE_OPTIONS_NO_SNIFF: HeaderValue = hval!("nosniff");
48pub const X_FRAME_OPTIONS_DENY: HeaderValue = hval!("DENY");
49
50static CONNECTION_HEADERS: [HeaderName; 5] = [
51    hname!("keep-alive"),
52    hname!("proxy-connection"),
53    hname!("http2-settings"),
54    TRANSFER_ENCODING,
55    UPGRADE,
56];
57
58/// Strip connection-related headers from an HTTP/1.1
59/// request so that it becomes a valid HTTP/2 request
60pub fn strip_connection_headers(headers: &mut HeaderMap) {
61    for header in &CONNECTION_HEADERS {
62        headers.remove(header);
63    }
64
65    // TE is forbidden unless it's "trailers"
66    if headers
67        .get(TE)
68        .is_some_and(|te_header| te_header != "trailers")
69    {
70        headers.remove(TE);
71    }
72
73    if let Some(header) = headers.remove(CONNECTION) {
74        let header_contents = header.to_str().unwrap();
75
76        // A `Connection` header may have a comma-separated list of names of other headers that
77        // are meant for only this specific connection.
78        // Iterate these names and remove them as headers.
79        for name in header_contents.split(',') {
80            let name = name.trim();
81            headers.remove(name);
82        }
83    }
84}