shared/http/
request.rs

1use crate::{get_body, parse_header, split_lines};
2
3#[derive(Debug)]
4pub struct HttpRequestBuilder<'a> {
5    buffer: &'a mut [u8],
6    position: usize,
7}
8
9impl<'a> HttpRequestBuilder<'a> {
10    pub const LINE_ENDING: &'static [u8] = b"\r\n";
11    pub const HTTP_VERSION_1_1: &'static str = "HTTP/1.1";
12
13    pub fn from_buffer(buffer: &'a mut [u8]) -> Self {
14        buffer.fill(0);
15
16        HttpRequestBuilder {
17            buffer,
18            position: 0,
19        }
20    }
21
22    pub fn into_buffer(self) -> &'a mut [u8] {
23        self.buffer
24    }
25
26    fn add_line_iterator<'b, I>(&mut self, iter: I) -> Option<&mut Self>
27    where
28        I: Iterator<Item = &'b [u8]>,
29    {
30        self.position = crate::add_line_iterator(self.buffer, self.position, iter)?;
31        Some(self)
32    }
33
34    fn add_line(&mut self, line: &[u8]) -> Option<&mut Self> {
35        self.position = crate::add_line(self.buffer, self.position, line)?;
36        Some(self)
37    }
38
39    pub fn add_request(
40        &mut self,
41        method: &str,
42        path: &str,
43        http_version: &str,
44    ) -> Option<&mut Self> {
45        let request_line = [
46            method.as_bytes(),
47            b" ",
48            path.as_bytes(),
49            b" ",
50            http_version.as_bytes(),
51        ];
52        self.add_line_iterator(request_line.into_iter())?;
53        Some(self)
54    }
55
56    pub fn add_header(&mut self, name: &str, value: &[u8]) -> Option<&mut Self> {
57        let header_line = [name.as_bytes(), b": ", value].concat();
58        self.add_line(&header_line)
59    }
60
61    pub fn add_body(&mut self, body: &[u8]) -> Option<&mut Self> {
62        self.add_line(b"")?;
63
64        let destination_buffer = self
65            .buffer
66            .get_mut(self.position..self.position + body.len())?;
67
68        destination_buffer.copy_from_slice(body);
69
70        self.position += body.len();
71
72        Some(self)
73    }
74
75    pub fn get_position(&self) -> usize {
76        self.position
77    }
78}
79
80pub struct HttpRequestParser<'a>(&'a [u8]);
81
82impl<'a> HttpRequestParser<'a> {
83    pub const HOST_HEADER: &'static str = "Host";
84    pub const CONTENT_LENGTH_HEADER: &'static str = "Content-Length";
85    pub const CONTENT_TYPE_HEADER: &'static str = "Content-Type";
86    pub const USER_AGENT_HEADER: &'static str = "User-Agent";
87    pub const CONNECTION_HEADER: &'static str = "Connection";
88    pub const ACCEPT_HEADER: &'static str = "Accept";
89    pub const ACCEPT_ENCODING_HEADER: &'static str = "Accept-Encoding";
90    pub const ACCEPT_LANGUAGE_HEADER: &'static str = "Accept-Language";
91
92    pub fn split_lines(&self) -> impl Iterator<Item = &'a [u8]> + Clone + 'a {
93        split_lines(self.0)
94    }
95
96    pub fn from_buffer(buffer: &'a [u8]) -> Self {
97        HttpRequestParser(buffer)
98    }
99
100    pub fn get_request(&self) -> Option<(&'a str, &'a str)> {
101        let mut lines = self.split_lines();
102
103        let request_line = lines.next()?;
104
105        let mut parts = request_line.splitn(3, |&b| b == b' ');
106
107        let method = str::from_utf8(parts.next()?).ok()?;
108        let path = str::from_utf8(parts.next()?).ok()?;
109
110        Some((method, path))
111    }
112
113    pub fn get_headers(&self) -> impl Iterator<Item = (&'a str, &'a str)> + 'a {
114        let mut lines = self.split_lines();
115
116        // Skip request line
117        lines.next();
118
119        lines
120            .take_while(|line| !line.is_empty())
121            .filter_map(parse_header)
122    }
123
124    pub fn get_body(&self) -> Option<&'a [u8]> {
125        get_body(self.0)
126    }
127}