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}
229