hyper/
ext.rs

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