1use std::fmt;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub(crate) enum Kind {
8 Request,
9 Response,
10 Trailers,
11}
12
13const METHOD: usize = 0;
14const SCHEME: usize = 1;
15const AUTHORITY: usize = 2;
16const PATH: usize = 3;
17const STATUS: usize = 4;
18const PROTOCOL: usize = 5;
19
20const fn name_table() -> [bool; 256] {
22 let mut t = [false; 256];
23 let extra = b"!#$%&'*+-.^_`|~";
24 let mut i = 0;
25 while i < extra.len() {
26 t[extra[i] as usize] = true;
27 i += 1;
28 }
29 let mut c = b'0';
30 while c <= b'9' {
31 t[c as usize] = true;
32 c += 1;
33 }
34 let mut c = b'a';
35 while c <= b'z' {
36 t[c as usize] = true;
37 c += 1;
38 }
39 t
40}
41static NAME_CHARS: [bool; 256] = name_table();
42
43#[inline]
46pub(crate) fn valid_value(v: &[u8]) -> bool {
47 if let (Some(&f), Some(&l)) = (v.first(), v.last()) {
48 if f == b' ' || f == b'\t' || l == b' ' || l == b'\t' {
49 return false;
50 }
51 }
52 !v.iter().any(|&b| b == 0 || b == b'\r' || b == b'\n')
53}
54
55#[inline]
57pub(crate) fn valid_name(n: &[u8]) -> bool {
58 !n.is_empty() && n.iter().all(|&b| NAME_CHARS[b as usize])
59}
60
61#[inline]
63pub(crate) fn connection_specific(name: &[u8]) -> bool {
64 matches!(name, b"connection" | b"keep-alive" | b"proxy-connection" | b"transfer-encoding" | b"upgrade")
65}
66
67fn parse_u64(v: &[u8]) -> Option<u64> {
68 if v.is_empty() || v.len() > 19 {
69 return None;
70 }
71 let mut n = 0u64;
72 for &b in v {
73 if !b.is_ascii_digit() {
74 return None;
75 }
76 n = n * 10 + (b - b'0') as u64;
77 }
78 Some(n)
79}
80
81#[derive(Clone)]
86pub struct Headers {
87 buf: Vec<u8>,
88 fields: Vec<(u32, u32, u32)>,
90 pseudo: [u16; 6],
92 npseudo: usize,
93 host: u16,
94 content_length: Option<u64>,
95 malformed: Option<&'static str>,
96 kind: Kind,
97 pub(crate) allow_protocol: bool,
99}
100
101impl Default for Headers {
102 fn default() -> Self {
103 Headers {
104 buf: Vec::new(),
105 fields: Vec::new(),
106 pseudo: [0; 6],
107 npseudo: 0,
108 host: 0,
109 content_length: None,
110 malformed: None,
111 kind: Kind::Request,
112 allow_protocol: false,
113 }
114 }
115}
116
117impl Headers {
118 pub(crate) fn reset(&mut self, kind: Kind) {
119 self.buf.clear();
120 self.fields.clear();
121 self.pseudo = [0; 6];
122 self.npseudo = 0;
123 self.host = 0;
124 self.content_length = None;
125 self.malformed = None;
126 self.kind = kind;
127 }
128
129 #[inline]
130 fn fail(&mut self, why: &'static str) {
131 if self.malformed.is_none() {
132 self.malformed = Some(why);
133 }
134 }
135
136 fn store(&mut self, name: &[u8], value: &[u8]) -> usize {
137 let start = self.buf.len() as u32;
138 self.buf.extend_from_slice(name);
139 self.buf.extend_from_slice(value);
140 self.fields.push((start, name.len() as u32, value.len() as u32));
141 self.fields.len()
142 }
143
144 pub(crate) fn push(&mut self, name: &[u8], value: &[u8]) {
146 if self.malformed.is_some() {
147 return;
148 }
149 if !valid_value(value) {
150 return self.fail("invalid field value");
151 }
152 if name.first() == Some(&b':') {
153 if self.kind == Kind::Trailers {
154 return self.fail("pseudo-header in trailers");
155 }
156 if self.fields.len() > self.npseudo {
157 return self.fail("pseudo-header after a regular field");
158 }
159 let slot = match (self.kind, name) {
160 (Kind::Request, b":method") => METHOD,
161 (Kind::Request, b":scheme") => SCHEME,
162 (Kind::Request, b":authority") => AUTHORITY,
163 (Kind::Request, b":path") => PATH,
164 (Kind::Request, b":protocol") if cfg!(feature = "connect-protocol") && self.allow_protocol => PROTOCOL,
165 (Kind::Response, b":status") => STATUS,
166 _ => return self.fail("unknown or misplaced pseudo-header"),
167 };
168 if self.pseudo[slot] != 0 {
169 return self.fail("duplicate pseudo-header");
170 }
171 self.pseudo[slot] = self.store(name, value) as u16;
172 self.npseudo += 1;
173 return;
174 }
175 if !valid_name(name) {
176 return self.fail("invalid field name");
177 }
178 match name {
179 _ if connection_specific(name) => return self.fail("connection-specific field"),
180 b"te" if value != b"trailers" => return self.fail("te other than trailers"),
181 b"content-length" => match parse_u64(value) {
182 Some(n) if self.content_length.is_none_or(|c| c == n) => self.content_length = Some(n),
183 _ => return self.fail("invalid content-length"),
184 },
185 _ => {}
186 }
187 let i = self.store(name, value);
188 if name == b"host" && self.host == 0 {
189 self.host = i as u16;
190 }
191 }
192
193 pub(crate) fn finish(&mut self) {
195 if self.malformed.is_some() {
196 return;
197 }
198 match self.kind {
199 Kind::Request => {
200 let Some(method) = self.method() else { return self.fail("missing :method") };
201 if cfg!(feature = "connect-protocol") && self.protocol().is_some() {
202 if method != b"CONNECT" {
205 return self.fail(":protocol without CONNECT");
206 }
207 let (Some(scheme), Some(path), Some(_)) = (self.scheme(), self.path(), self.authority()) else {
208 return self.fail("extended CONNECT without :scheme, :path or :authority");
209 };
210 if (scheme == b"http" || scheme == b"https") && path.first() != Some(&b'/') {
211 return self.fail("invalid :path");
212 }
213 } else if method == b"CONNECT" {
214 if self.authority().is_none() || self.scheme().is_some() || self.path().is_some() {
215 return self.fail("malformed CONNECT");
216 }
217 } else {
218 let (Some(scheme), Some(path)) = (self.scheme(), self.path()) else {
219 return self.fail("missing :scheme or :path");
220 };
221 if path.is_empty() {
222 return self.fail("empty :path");
223 }
224 let http = scheme == b"http" || scheme == b"https";
225 if http && path[0] != b'/' && !(path == b"*" && method == b"OPTIONS") {
226 return self.fail("invalid :path");
227 }
228 }
229 if let (Some(a), Some(h)) = (self.authority(), self.get(b"host")) {
230 if a != h {
231 return self.fail(":authority and host differ");
232 }
233 }
234 }
235 Kind::Response => match self.status() {
236 Some(s) if (100..1000).contains(&s) && s != 101 => {}
237 _ => self.fail("missing or invalid :status"),
238 },
239 Kind::Trailers => {}
240 }
241 }
242
243 pub(crate) fn malformed(&self) -> Option<&'static str> {
244 self.malformed
245 }
246
247 #[inline]
248 fn field(&self, i: usize) -> (&[u8], &[u8]) {
249 let (s, n, v) = self.fields[i];
250 let (s, n, v) = (s as usize, n as usize, v as usize);
251 (&self.buf[s..s + n], &self.buf[s + n..s + n + v])
252 }
253
254 #[inline]
255 fn pseudo(&self, slot: usize) -> Option<&[u8]> {
256 match self.pseudo[slot] {
257 0 => None,
258 i => Some(self.field(i as usize - 1).1),
259 }
260 }
261
262 pub fn method(&self) -> Option<&[u8]> {
264 self.pseudo(METHOD)
265 }
266 pub fn scheme(&self) -> Option<&[u8]> {
268 self.pseudo(SCHEME)
269 }
270 pub fn authority(&self) -> Option<&[u8]> {
272 self.pseudo(AUTHORITY)
273 }
274 pub fn path(&self) -> Option<&[u8]> {
276 self.pseudo(PATH)
277 }
278 pub fn protocol(&self) -> Option<&[u8]> {
281 self.pseudo(PROTOCOL)
282 }
283 pub fn status(&self) -> Option<u16> {
285 let s = self.pseudo(STATUS)?;
286 if s.len() != 3 {
287 return None;
288 }
289 parse_u64(s).map(|n| n as u16)
290 }
291 pub fn content_length(&self) -> Option<u64> {
293 self.content_length
294 }
295 pub fn len(&self) -> usize {
297 self.fields.len() - self.npseudo
298 }
299 pub fn is_empty(&self) -> bool {
301 self.len() == 0
302 }
303 pub fn iter(&self) -> impl Iterator<Item = (&[u8], &[u8])> + '_ {
305 (self.npseudo..self.fields.len()).map(move |i| self.field(i))
306 }
307 pub fn get(&self, name: &[u8]) -> Option<&[u8]> {
309 self.iter().find(|(n, _)| *n == name).map(|(_, v)| v)
310 }
311}
312
313impl fmt::Debug for Headers {
314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 let mut l = f.debug_list();
316 for i in 0..self.fields.len() {
317 let (n, v) = self.field(i);
318 l.entry(&(String::from_utf8_lossy(n), String::from_utf8_lossy(v)));
319 }
320 l.finish()
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 fn req(fields: &[(&str, &str)]) -> Headers {
329 let mut h = Headers::default();
330 h.reset(Kind::Request);
331 for (n, v) in fields {
332 h.push(n.as_bytes(), v.as_bytes());
333 }
334 h.finish();
335 h
336 }
337
338 const BASE: [(&str, &str); 4] = [(":method", "GET"), (":scheme", "https"), (":path", "/x"), (":authority", "a")];
339
340 #[test]
341 fn valid_request() {
342 let h = req(&[BASE[0], BASE[1], BASE[2], BASE[3], ("accept", "*/*"), ("te", "trailers"), ("content-length", "0")]);
343 assert_eq!(h.malformed(), None);
344 assert_eq!(h.method(), Some(&b"GET"[..]));
345 assert_eq!(h.path(), Some(&b"/x"[..]));
346 assert_eq!(h.len(), 3);
347 assert_eq!(h.get(b"accept"), Some(&b"*/*"[..]));
348 assert_eq!(h.content_length(), Some(0));
349 assert!(format!("{h:?}").contains("accept"));
350 let h = req(&[(":method", "CONNECT"), (":authority", "a:443")]);
351 assert_eq!(h.malformed(), None);
352 let h = req(&[(":method", "OPTIONS"), (":scheme", "http"), (":path", "*")]);
353 assert_eq!(h.malformed(), None);
354 }
355
356 #[test]
357 fn malformed_requests() {
358 let cases: Vec<Vec<(&str, &str)>> = vec![
359 vec![BASE[1], BASE[2]], vec![BASE[0], BASE[2]], vec![BASE[0], BASE[1]], vec![BASE[0], BASE[1], (":path", "")], vec![BASE[0], BASE[1], (":path", "x")], vec![BASE[0], BASE[0], BASE[1], BASE[2]], vec![BASE[0], BASE[1], BASE[2], (":status", "200")], vec![BASE[0], BASE[1], BASE[2], (":protocol", "ws")], vec![BASE[0], BASE[1], ("a", "b"), BASE[2]], vec![BASE[0], BASE[1], BASE[2], ("Accept", "x")], vec![BASE[0], BASE[1], BASE[2], ("", "x")], vec![BASE[0], BASE[1], BASE[2], ("a b", "x")], vec![BASE[0], BASE[1], BASE[2], ("connection", "close")],
372 vec![BASE[0], BASE[1], BASE[2], ("keep-alive", "1")],
373 vec![BASE[0], BASE[1], BASE[2], ("transfer-encoding", "chunked")],
374 vec![BASE[0], BASE[1], BASE[2], ("upgrade", "h2c")],
375 vec![BASE[0], BASE[1], BASE[2], ("proxy-connection", "x")],
376 vec![BASE[0], BASE[1], BASE[2], ("te", "gzip")],
377 vec![BASE[0], BASE[1], BASE[2], ("content-length", "1x")],
378 vec![BASE[0], BASE[1], BASE[2], ("content-length", "1"), ("content-length", "2")],
379 vec![BASE[0], BASE[1], BASE[2], ("x", "a\r\nb")],
380 vec![BASE[0], BASE[1], BASE[2], ("x", " a")],
381 vec![BASE[0], BASE[1], BASE[2], ("x", "a\t")],
382 vec![BASE[0], BASE[1], BASE[2], ("x", "a\0")],
383 vec![BASE[0], BASE[1], BASE[2], BASE[3], ("host", "b")], vec![(":method", "CONNECT"), (":authority", "a"), (":path", "/")],
385 ];
386 for (i, c) in cases.iter().enumerate() {
387 assert!(req(c).malformed().is_some(), "case {i}: {c:?}");
388 }
389 }
390
391 #[cfg(feature = "connect-protocol")]
392 #[test]
393 fn extended_connect() {
394 let ext = |fields: &[(&str, &str)]| {
395 let mut h = Headers { allow_protocol: true, ..Headers::default() };
396 h.reset(Kind::Request);
397 for (n, v) in fields {
398 h.push(n.as_bytes(), v.as_bytes());
399 }
400 h.finish();
401 h
402 };
403 let ws = [(":method", "CONNECT"), (":protocol", "websocket"), (":scheme", "https"), (":path", "/chat"), (":authority", "a")];
404 let h = ext(&ws);
405 assert_eq!(h.malformed(), None);
406 assert_eq!(h.protocol(), Some(&b"websocket"[..]));
407 assert_eq!(h.path(), Some(&b"/chat"[..]));
408 let h = ext(&[(":method", "CONNECT"), (":authority", "a:443")]);
410 assert_eq!((h.malformed(), h.protocol()), (None, None));
411 let bad: Vec<Vec<(&str, &str)>> = vec![
412 vec![ws[0], ws[1], ws[2], ws[4]], vec![ws[0], ws[1], ws[3], ws[4]], vec![ws[0], ws[1], ws[2], ws[3]], vec![(":method", "GET"), ws[1], ws[2], ws[3], ws[4]], vec![ws[0], ws[1], ws[1], ws[2], ws[3], ws[4]], vec![ws[0], ws[1], ws[2], (":path", "chat"), ws[4]], ];
419 for (i, c) in bad.iter().enumerate() {
420 assert!(ext(c).malformed().is_some(), "case {i}");
421 }
422 assert!(req(&ws).malformed().is_some());
424 }
425
426 #[test]
427 fn responses_and_trailers() {
428 let mut h = Headers::default();
429 h.reset(Kind::Response);
430 h.push(b":status", b"204");
431 h.finish();
432 assert_eq!((h.malformed(), h.status()), (None, Some(204)));
433 for bad in [&b"101"[..], b"20", b"abc", b"2000"] {
434 h.reset(Kind::Response);
435 h.push(b":status", bad);
436 h.finish();
437 assert!(h.malformed().is_some());
438 }
439 h.reset(Kind::Response);
440 h.finish();
441 assert!(h.malformed().is_some());
442 h.reset(Kind::Trailers);
443 h.push(b"grpc-status", b"0");
444 h.finish();
445 assert_eq!(h.malformed(), None);
446 h.reset(Kind::Trailers);
447 h.push(b":status", b"200");
448 assert!(h.malformed().is_some());
449 }
450}