khttp/parser/
mod.rs

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        // find '\n'
23        let nl = match memchr(b'\n', buf) {
24            Some(p) => p,
25            None => return Err(UnexpectedEof),
26        };
27
28        // require CRLF
29        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}