ntex_h2/hpack/
header.rs

1#![allow(clippy::len_without_is_empty)]
2
3use ntex_bytes::{ByteString, Bytes};
4use ntex_http::{HeaderName, HeaderValue, Method, StatusCode};
5
6use super::{DecoderError, NeedMore};
7
8/// HTTP/2 Header
9#[derive(Debug, Clone, Eq, PartialEq)]
10pub enum Header<T = HeaderName> {
11    Field { name: T, value: HeaderValue },
12    // TODO: Change these types to `http::uri` types.
13    Authority(ByteString),
14    Method(Method),
15    Scheme(ByteString),
16    Path(ByteString),
17    Protocol(ByteString),
18    Status(StatusCode),
19}
20
21/// The header field name
22#[derive(Debug, Clone, Eq, PartialEq, Hash)]
23pub enum Name<'a> {
24    Field(&'a HeaderName),
25    Authority,
26    Method,
27    Scheme,
28    Path,
29    Protocol,
30    Status,
31}
32
33pub fn len(name: &HeaderName, value: &HeaderValue) -> usize {
34    let n: &str = name.as_ref();
35    32 + n.len() + value.len()
36}
37
38impl Header<Option<HeaderName>> {
39    pub fn reify(self) -> Result<Header, HeaderValue> {
40        use self::Header::*;
41
42        Ok(match self {
43            Field {
44                name: Some(n),
45                value,
46            } => Field { name: n, value },
47            Field { name: None, value } => return Err(value),
48            Authority(v) => Authority(v),
49            Method(v) => Method(v),
50            Scheme(v) => Scheme(v),
51            Path(v) => Path(v),
52            Protocol(v) => Protocol(v),
53            Status(v) => Status(v),
54        })
55    }
56}
57
58impl Header {
59    pub fn new(name: Bytes, value: Bytes) -> Result<Header, DecoderError> {
60        if name.is_empty() {
61            return Err(DecoderError::NeedMore(NeedMore::UnexpectedEndOfStream));
62        }
63        if name[0] == b':' {
64            match &name[1..] {
65                b"authority" => {
66                    let value = ByteString::try_from(value)?;
67                    Ok(Header::Authority(value))
68                }
69                b"method" => {
70                    let method = Method::from_bytes(&value)?;
71                    Ok(Header::Method(method))
72                }
73                b"scheme" => {
74                    let value = ByteString::try_from(value)?;
75                    Ok(Header::Scheme(value))
76                }
77                b"path" => {
78                    let value = ByteString::try_from(value)?;
79                    Ok(Header::Path(value))
80                }
81                b"protocol" => {
82                    let value = ByteString::try_from(value)?;
83                    Ok(Header::Protocol(value))
84                }
85                b"status" => {
86                    let status = StatusCode::from_bytes(&value)?;
87                    Ok(Header::Status(status))
88                }
89                _ => Err(DecoderError::InvalidPseudoheader),
90            }
91        } else {
92            // HTTP/2 requires lower case header names
93            let name = HeaderName::from_lowercase(&name)?;
94            let value = HeaderValue::from_bytes(&value)?;
95
96            Ok(Header::Field { name, value })
97        }
98    }
99
100    pub fn len(&self) -> usize {
101        match *self {
102            Header::Field {
103                ref name,
104                ref value,
105            } => len(name, value),
106            Header::Authority(ref v) => 32 + 10 + v.len(),
107            Header::Method(ref v) => 32 + 7 + v.as_ref().len(),
108            Header::Scheme(ref v) => 32 + 7 + v.len(),
109            Header::Path(ref v) => 32 + 5 + v.len(),
110            Header::Protocol(ref v) => 32 + 9 + v.len(),
111            Header::Status(_) => 32 + 7 + 3,
112        }
113    }
114
115    /// Returns the header name
116    pub fn name(&self) -> Name<'_> {
117        match *self {
118            Header::Field { ref name, .. } => Name::Field(name),
119            Header::Authority(..) => Name::Authority,
120            Header::Method(..) => Name::Method,
121            Header::Scheme(..) => Name::Scheme,
122            Header::Path(..) => Name::Path,
123            Header::Protocol(..) => Name::Protocol,
124            Header::Status(..) => Name::Status,
125        }
126    }
127
128    pub fn value_slice(&self) -> &[u8] {
129        match *self {
130            Header::Field { ref value, .. } => value.as_ref(),
131            Header::Authority(ref v) => v.as_bytes(),
132            Header::Method(ref v) => v.as_ref().as_ref(),
133            Header::Scheme(ref v) => v.as_bytes(),
134            Header::Path(ref v) => v.as_bytes(),
135            Header::Protocol(ref v) => v.as_bytes(),
136            Header::Status(ref v) => v.as_str().as_ref(),
137        }
138    }
139
140    pub fn value_eq(&self, other: &Header) -> bool {
141        match *self {
142            Header::Field { ref value, .. } => {
143                let a = value;
144                match *other {
145                    Header::Field { ref value, .. } => a == value,
146                    _ => false,
147                }
148            }
149            Header::Authority(ref a) => match *other {
150                Header::Authority(ref b) => a == b,
151                _ => false,
152            },
153            Header::Method(ref a) => match *other {
154                Header::Method(ref b) => a == b,
155                _ => false,
156            },
157            Header::Scheme(ref a) => match *other {
158                Header::Scheme(ref b) => a == b,
159                _ => false,
160            },
161            Header::Path(ref a) => match *other {
162                Header::Path(ref b) => a == b,
163                _ => false,
164            },
165            Header::Protocol(ref a) => match *other {
166                Header::Protocol(ref b) => a == b,
167                _ => false,
168            },
169            Header::Status(ref a) => match *other {
170                Header::Status(ref b) => a == b,
171                _ => false,
172            },
173        }
174    }
175
176    pub fn is_sensitive(&self) -> bool {
177        match *self {
178            Header::Field { ref value, .. } => value.is_sensitive(),
179            // TODO: Technically these other header values can be sensitive too.
180            _ => false,
181        }
182    }
183
184    pub fn skip_value_index(&self) -> bool {
185        use ntex_http::header;
186
187        match *self {
188            Header::Field { ref name, .. } => matches!(
189                *name,
190                header::AGE
191                    | header::AUTHORIZATION
192                    | header::CONTENT_LENGTH
193                    | header::ETAG
194                    | header::IF_MODIFIED_SINCE
195                    | header::IF_NONE_MATCH
196                    | header::LOCATION
197                    | header::COOKIE
198                    | header::SET_COOKIE
199            ),
200            Header::Path(..) => true,
201            _ => false,
202        }
203    }
204}
205
206// Mostly for tests
207impl From<Header> for Header<Option<HeaderName>> {
208    fn from(src: Header) -> Self {
209        match src {
210            Header::Field { name, value } => Header::Field {
211                name: Some(name),
212                value,
213            },
214            Header::Authority(v) => Header::Authority(v),
215            Header::Method(v) => Header::Method(v),
216            Header::Scheme(v) => Header::Scheme(v),
217            Header::Path(v) => Header::Path(v),
218            Header::Protocol(v) => Header::Protocol(v),
219            Header::Status(v) => Header::Status(v),
220        }
221    }
222}
223
224impl Name<'_> {
225    pub fn into_entry(self, value: Bytes) -> Result<Header, DecoderError> {
226        match self {
227            Name::Field(name) => Ok(Header::Field {
228                name: name.clone(),
229                value: HeaderValue::from_bytes(&value)?,
230            }),
231            Name::Authority => Ok(Header::Authority(ByteString::try_from(value)?)),
232            Name::Method => Ok(Header::Method(Method::from_bytes(&value)?)),
233            Name::Scheme => Ok(Header::Scheme(ByteString::try_from(value)?)),
234            Name::Path => Ok(Header::Path(ByteString::try_from(value)?)),
235            Name::Protocol => Ok(Header::Protocol(ByteString::try_from(value)?)),
236            Name::Status => {
237                match StatusCode::from_bytes(&value) {
238                    Ok(status) => Ok(Header::Status(status)),
239                    // TODO: better error handling
240                    Err(_) => Err(DecoderError::InvalidStatusCode),
241                }
242            }
243        }
244    }
245
246    pub fn as_slice(&self) -> &[u8] {
247        match *self {
248            Name::Field(ref name) => name.as_ref(),
249            Name::Authority => b":authority",
250            Name::Method => b":method",
251            Name::Scheme => b":scheme",
252            Name::Path => b":path",
253            Name::Protocol => b":protocol",
254            Name::Status => b":status",
255        }
256    }
257}