Skip to main content

wireforge_app/http/
request.rs

1//! HTTP/1.1 request line parser.
2
3use super::headers::HeaderIter;
4
5/// Parsed HTTP request (method, URI, version, headers, body).
6#[derive(Debug, Clone)]
7pub struct HttpRequest<'a> {
8    buf: &'a [u8],
9    method_end: usize,
10    uri_end: usize,
11    version_end: usize,
12    headers_start: usize,
13    body_start: usize,
14}
15
16impl<'a> HttpRequest<'a> {
17    pub fn new(buf: &'a [u8]) -> Option<Self> {
18        // Find "METHOD SP URI SP VERSION\r\n"
19        let request_line_end = buf.windows(2).position(|w| w == b"\r\n")?;
20        let line = &buf[..request_line_end];
21
22        let sp1 = line.iter().position(|&b| b == b' ')?;
23        let sp2 = line[sp1 + 1..].iter().position(|&b| b == b' ')?;
24        let sp2 = sp1 + 1 + sp2;
25
26        let headers_start = request_line_end + 2;
27        let body_start = find_body_start(&buf[headers_start..]).map(|p| headers_start + p);
28
29        Some(Self {
30            buf,
31            method_end: sp1,
32            uri_end: sp2,
33            version_end: request_line_end,
34            headers_start,
35            body_start: body_start.unwrap_or(buf.len()),
36        })
37    }
38
39    pub fn method(&self) -> &str { core::str::from_utf8(&self.buf[..self.method_end]).unwrap_or("") }
40    pub fn uri(&self) -> &str { core::str::from_utf8(&self.buf[self.method_end + 1..self.uri_end]).unwrap_or("") }
41    pub fn version(&self) -> &str { core::str::from_utf8(&self.buf[self.uri_end + 1..self.version_end]).unwrap_or("") }
42    pub fn headers(&self) -> HeaderIter<'a> { HeaderIter::new(&self.buf[self.headers_start..self.body_start]) }
43    pub fn body(&self) -> &[u8] { &self.buf[self.body_start..] }
44}
45
46fn find_body_start(data: &[u8]) -> Option<usize> {
47    data.windows(4).position(|w| w == b"\r\n\r\n").map(|p| p + 4)
48}