hyper/
ext.rs

1//! HTTP extensions.
2
3use bytes::Bytes;
4use http::header::HeaderName;
5#[cfg(feature = "http1")]
6use http::header::{IntoHeaderName, ValueIter};
7use http::HeaderMap;
8#[cfg(feature = "ffi")]
9use std::collections::HashMap;
10#[cfg(feature = "http2")]
11use std::fmt;
12
13#[cfg(feature = "http2")]
14/// Represents the `:protocol` pseudo-header used by
15/// the [Extended CONNECT Protocol].
16///
17/// [Extended CONNECT Protocol]: https://datatracker.ietf.org/doc/html/rfc8441#section-4
18#[derive(Clone, Eq, PartialEq)]
19pub struct Protocol {
20    inner: h2::ext::Protocol,
21}
22
23#[cfg(feature = "http2")]
24impl Protocol {
25    /// Converts a static string to a protocol name.
26    pub const fn from_static(value: &'static str) -> Self {
27        Self {
28            inner: h2::ext::Protocol::from_static(value),
29        }
30    }
31
32    /// Returns a str representation of the header.
33    pub fn as_str(&self) -> &str {
34        self.inner.as_str()
35    }
36
37    pub(crate) fn from_inner(inner: h2::ext::Protocol) -> Self {
38        Self { inner }
39    }
40
41    pub(crate) fn into_inner(self) -> h2::ext::Protocol {
42        self.inner
43    }
44}
45
46#[cfg(feature = "http2")]
47impl<'a> From<&'a str> for Protocol {
48    fn from(value: &'a str) -> Self {
49        Self {
50            inner: h2::ext::Protocol::from(value),
51        }
52    }
53}
54
55#[cfg(feature = "http2")]
56impl AsRef<[u8]> for Protocol {
57    fn as_ref(&self) -> &[u8] {
58        self.inner.as_ref()
59    }
60}
61
62#[cfg(feature = "http2")]
63impl fmt::Debug for Protocol {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        self.inner.fmt(f)
66    }
67}
68
69/// A map from header names to their original casing as received in an HTTP message.
70///
71/// If an HTTP/1 response `res` is parsed on a connection whose option
72/// [`http1_preserve_header_case`] was set to true and the response included
73/// the following headers:
74///
75/// ```ignore
76/// x-Bread: Baguette
77/// X-BREAD: Pain
78/// x-bread: Ficelle
79/// ```
80///
81/// Then `res.extensions().get::<HeaderCaseMap>()` will return a map with:
82///
83/// ```ignore
84/// HeaderCaseMap({
85///     "x-bread": ["x-Bread", "X-BREAD", "x-bread"],
86/// })
87/// ```
88///
89/// [`http1_preserve_header_case`]: /client/struct.Client.html#method.http1_preserve_header_case
90#[derive(Clone, Debug)]
91pub(crate) struct HeaderCaseMap(HeaderMap<Bytes>);
92
93#[cfg(feature = "http1")]
94impl HeaderCaseMap {
95    /// Returns a view of all spellings associated with that header name,
96    /// in the order they were found.
97    pub(crate) fn get_all<'a>(
98        &'a self,
99        name: &HeaderName,
100    ) -> impl Iterator<Item = impl AsRef<[u8]> + 'a> + 'a {
101        self.get_all_internal(name).into_iter()
102    }
103
104    /// Returns a view of all spellings associated with that header name,
105    /// in the order they were found.
106    pub(crate) fn get_all_internal<'a>(&'a self, name: &HeaderName) -> ValueIter<'_, Bytes> {
107        self.0.get_all(name).into_iter()
108    }
109
110    pub(crate) fn default() -> Self {
111        Self(Default::default())
112    }
113
114    #[cfg(any(test, feature = "ffi"))]
115    pub(crate) fn insert(&mut self, name: HeaderName, orig: Bytes) {
116        self.0.insert(name, orig);
117    }
118
119    pub(crate) fn append<N>(&mut self, name: N, orig: Bytes)
120    where
121        N: IntoHeaderName,
122    {
123        self.0.append(name, orig);
124    }
125}
126
127#[cfg(feature = "ffi")]
128#[derive(Clone, Debug)]
129/// Hashmap<Headername, numheaders with that name>
130pub(crate) struct OriginalHeaderOrder {
131    /// Stores how many entries a Headername maps to. This is used
132    /// for accounting.
133    num_entries: HashMap<HeaderName, usize>,
134    /// Stores the ordering of the headers. ex: `vec[i] = (headerName, idx)`,
135    /// The vector is ordered such that the ith element
136    /// represents the ith header that came in off the line.
137    /// The `HeaderName` and `idx` are then used elsewhere to index into
138    /// the multi map that stores the header values.
139    entry_order: Vec<(HeaderName, usize)>,
140}
141
142#[cfg(all(feature = "http1", feature = "ffi"))]
143impl OriginalHeaderOrder {
144    pub(crate) fn default() -> Self {
145        OriginalHeaderOrder {
146            num_entries: HashMap::new(),
147            entry_order: Vec::new(),
148        }
149    }
150
151    pub(crate) fn insert(&mut self, name: HeaderName) {
152        if !self.num_entries.contains_key(&name) {
153            let idx = 0;
154            self.num_entries.insert(name.clone(), 1);
155            self.entry_order.push((name, idx));
156        }
157        // Replacing an already existing element does not
158        // change ordering, so we only care if its the first
159        // header name encountered
160    }
161
162    pub(crate) fn append<N>(&mut self, name: N)
163    where
164        N: IntoHeaderName + Into<HeaderName> + Clone,
165    {
166        let name: HeaderName = name.into();
167        let idx;
168        if self.num_entries.contains_key(&name) {
169            idx = self.num_entries[&name];
170            *self.num_entries.get_mut(&name).unwrap() += 1;
171        } else {
172            idx = 0;
173            self.num_entries.insert(name.clone(), 1);
174        }
175        self.entry_order.push((name, idx));
176    }
177
178    // No doc test is run here because `RUSTFLAGS='--cfg hyper_unstable_ffi'`
179    // is needed to compile. Once ffi is stablized `no_run` should be removed
180    // here.
181    /// This returns an iterator that provides header names and indexes
182    /// in the original order received.
183    ///
184    /// # Examples
185    /// ```no_run
186    /// use hyper::ext::OriginalHeaderOrder;
187    /// use hyper::header::{HeaderName, HeaderValue, HeaderMap};
188    ///
189    /// let mut h_order = OriginalHeaderOrder::default();
190    /// let mut h_map = Headermap::new();
191    ///
192    /// let name1 = b"Set-CookiE";
193    /// let value1 = b"a=b";
194    /// h_map.append(name1);
195    /// h_order.append(name1);
196    ///
197    /// let name2 = b"Content-Encoding";
198    /// let value2 = b"gzip";
199    /// h_map.append(name2, value2);
200    /// h_order.append(name2);
201    ///
202    /// let name3 = b"SET-COOKIE";
203    /// let value3 = b"c=d";
204    /// h_map.append(name3, value3);
205    /// h_order.append(name3)
206    ///
207    /// let mut iter = h_order.get_in_order()
208    ///
209    /// let (name, idx) = iter.next();
210    /// assert_eq!(b"a=b", h_map.get_all(name).nth(idx).unwrap());
211    ///
212    /// let (name, idx) = iter.next();
213    /// assert_eq!(b"gzip", h_map.get_all(name).nth(idx).unwrap());
214    ///
215    /// let (name, idx) = iter.next();
216    /// assert_eq!(b"c=d", h_map.get_all(name).nth(idx).unwrap());
217    /// ```
218    pub(crate) fn get_in_order(&self) -> impl Iterator<Item = &(HeaderName, usize)> {
219        self.entry_order.iter()
220    }
221}