1use crate::Headers;
2use HttpParsingError::*;
3use memchr::memchr;
4use std::{error::Error, fmt::Display, io};
5
6mod request;
7mod response;
8pub mod simd;
9pub use request::Request;
10pub use response::Response;
11
12#[inline]
13fn parse_headers(buf: &[u8]) -> Result<(Headers<'_>, &[u8]), HttpParsingError> {
14 let mut headers = Headers::new();
15 let mut buf = buf;
16
17 loop {
18 if let Some(rest) = buf.strip_prefix(b"\r\n") {
19 return Ok((headers, rest));
20 }
21
22 let nl = match memchr(b'\n', buf) {
24 Some(p) => p,
25 None => return Err(UnexpectedEof),
26 };
27
28 if nl == 0 || buf[nl - 1] != b'\r' {
30 buf = &buf[nl + 1..];
31 continue;
32 }
33
34 let line = &buf[..nl - 1];
35 let (name, value) = parse_header_line(line)?;
36 headers.add(name, value);
37
38 buf = &buf[nl + 1..];
39 }
40}
41#[inline(always)]
42fn parse_header_line(line: &[u8]) -> Result<(&str, &[u8]), HttpParsingError> {
43 let colon = memchr(b':', line).ok_or(MalformedHeader)?;
44 if !line[..colon]
45 .iter()
46 .copied()
47 .all(is_valid_header_field_byte)
48 {
49 return Err(MalformedHeader);
50 }
51
52 let name_str = unsafe { std::str::from_utf8_unchecked(&line[..colon]) };
53 let value = &line[colon + 1..].trim_ascii_start();
54 Ok((name_str, value))
55}
56
57#[inline]
58fn parse_version(buf: &[u8]) -> Result<(u8, &[u8]), HttpParsingError> {
59 if let Some(rest) = buf.strip_prefix(b"HTTP/1.") {
60 let (&minor, rest) = rest.split_first().ok_or(UnexpectedEof)?;
61 return match minor {
62 b'1' => Ok((1, rest)),
63 b'0' => Ok((0, rest)),
64 _ => Err(UnsupportedHttpVersion),
65 };
66 }
67
68 if b"HTTP/1.".starts_with(&buf[..buf.len().min(7)]) {
69 return Err(UnexpectedEof);
70 }
71 Err(UnsupportedHttpVersion)
72}
73
74const fn make_header_field_byte_mask() -> [bool; 256] {
75 let mut mask = [false; 256];
76 let valid = b"!#$%&'*+-.^_`|~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
77 let mut i = 0;
78 while i < valid.len() {
79 mask[valid[i] as usize] = true;
80 i += 1;
81 }
82 mask
83}
84
85static HEADER_FIELD_BYTE_MASK: [bool; 256] = make_header_field_byte_mask();
86
87#[inline(always)]
88fn is_valid_header_field_byte(b: u8) -> bool {
89 HEADER_FIELD_BYTE_MASK[b as usize]
90}
91
92#[derive(Debug)]
93#[non_exhaustive]
94pub enum HttpParsingError {
95 UnsupportedHttpVersion,
96 MalformedStatusLine,
97 MalformedHeader,
98 UnexpectedEof,
99 StatusLineTooLong,
100 HeaderLineTooLong,
101 TooManyHeaders,
102 IOError(io::Error),
103}
104
105impl PartialEq for HttpParsingError {
106 fn eq(&self, other: &Self) -> bool {
107 match (self, other) {
108 (Self::IOError(_), Self::IOError(_)) => true,
109 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
110 }
111 }
112}
113
114impl Error for HttpParsingError {}
115
116impl Display for HttpParsingError {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 use HttpParsingError::*;
119 match self {
120 MalformedStatusLine => write!(f, "malformed status line"),
121 UnsupportedHttpVersion => write!(f, "invalid http version"),
122 MalformedHeader => write!(f, "malformed header"),
123 UnexpectedEof => write!(f, "unexpected eof"),
124 StatusLineTooLong => write!(f, "status line too long"),
125 HeaderLineTooLong => write!(f, "header line too long"),
126 TooManyHeaders => write!(f, "too many headers"),
127 IOError(e) => write!(f, "io error: {}", e),
128 }
129 }
130}
131
132impl From<std::io::Error> for HttpParsingError {
133 fn from(e: std::io::Error) -> Self {
134 HttpParsingError::IOError(e)
135 }
136}