1 | //! HTTP extensions. |
2 | |
3 | use bytes::Bytes; |
4 | #[cfg (any(feature = "http1" , feature = "ffi" ))] |
5 | use http::header::HeaderName; |
6 | #[cfg (feature = "http1" )] |
7 | use http::header::{IntoHeaderName, ValueIter}; |
8 | use http::HeaderMap; |
9 | #[cfg (feature = "ffi" )] |
10 | use std::collections::HashMap; |
11 | #[cfg (feature = "http2" )] |
12 | use std::fmt; |
13 | |
14 | #[cfg (any(feature = "http1" , feature = "ffi" ))] |
15 | mod h1_reason_phrase; |
16 | #[cfg (any(feature = "http1" , feature = "ffi" ))] |
17 | pub 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)] |
25 | pub struct Protocol { |
26 | inner: h2::ext::Protocol, |
27 | } |
28 | |
29 | #[cfg (feature = "http2" )] |
30 | impl 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" )] |
54 | impl<'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" )] |
63 | impl AsRef<[u8]> for Protocol { |
64 | fn as_ref(&self) -> &[u8] { |
65 | self.inner.as_ref() |
66 | } |
67 | } |
68 | |
69 | #[cfg (feature = "http2" )] |
70 | impl 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)] |
98 | pub(crate) struct HeaderCaseMap(HeaderMap<Bytes>); |
99 | |
100 | #[cfg (feature = "http1" )] |
101 | impl 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> |
137 | pub(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" ))] |
150 | impl 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 | } |
229 | |