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 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}