shared/http/
response.rs

1use crate::{get_body, parse_header, split_lines};
2use alloc::string::ToString;
3
4pub struct HttpResponseParser<'a>(&'a [u8]);
5
6impl<'a> HttpResponseParser<'a> {
7    pub fn from_buffer(buffer: &'a [u8]) -> Self {
8        HttpResponseParser(buffer)
9    }
10
11    pub fn split_lines(&self) -> impl Iterator<Item = &'a [u8]> + Clone + 'a {
12        split_lines(self.0)
13    }
14
15    pub fn get_status_code(&self) -> Option<u16> {
16        let mut lines = self.split_lines();
17
18        let status_line = lines.next()?;
19
20        let mut parts = status_line.splitn(3, |&b| b == b' ');
21
22        parts.next()?; // Skip HTTP version
23        let status_code_str = parts.next()?;
24
25        let status_code_str = core::str::from_utf8(status_code_str).ok()?;
26        let status_code = status_code_str.parse::<u16>().ok()?;
27
28        Some(status_code)
29    }
30
31    pub fn get_headers(&self) -> impl Iterator<Item = (&'a str, &'a str)> + 'a {
32        let mut lines = self.split_lines();
33
34        // Skip request line
35        lines.next();
36
37        lines
38            .take_while(|line| !line.is_empty())
39            .filter_map(parse_header)
40    }
41
42    pub fn get_body(&self) -> Option<&'a [u8]> {
43        get_body(self.0)
44    }
45}
46
47pub struct HttpResponseBuilder<'a> {
48    buffer: &'a mut [u8],
49    position: usize,
50}
51
52impl<'a> HttpResponseBuilder<'a> {
53    pub const LINE_ENDING: &'static [u8] = b"\r\n";
54
55    pub fn from_buffer(buffer: &'a mut [u8]) -> Self {
56        buffer.fill(0);
57
58        HttpResponseBuilder {
59            buffer,
60            position: 0,
61        }
62    }
63
64    pub fn add_line(&mut self, buffer: &[u8]) -> Option<()> {
65        let total_length = buffer.len() + Self::LINE_ENDING.len();
66
67        let destination_buffer = self
68            .buffer
69            .get_mut(self.position..self.position + total_length)?;
70
71        destination_buffer[..buffer.len()].copy_from_slice(buffer);
72        destination_buffer[buffer.len()..buffer.len() + Self::LINE_ENDING.len()]
73            .copy_from_slice(Self::LINE_ENDING);
74
75        self.position += total_length;
76
77        Some(())
78    }
79
80    pub fn add_line_iterator<'b, I>(&mut self, iter: I) -> Option<()>
81    where
82        I: Iterator<Item = &'b [u8]>,
83    {
84        // add in the same line all the parts from the iterator
85        let mut position = self.position; // temporary position, will be commited if all parts fit
86
87        for part in iter {
88            let part_length = part.len();
89
90            let destination_buffer = self.buffer.get_mut(position..position + part_length)?;
91
92            destination_buffer.copy_from_slice(part);
93            position += part_length;
94        }
95
96        let destination_buffer = self
97            .buffer
98            .get_mut(position..position + Self::LINE_ENDING.len())?;
99        destination_buffer.copy_from_slice(Self::LINE_ENDING);
100        position += Self::LINE_ENDING.len();
101
102        self.position = position;
103
104        Some(())
105    }
106
107    pub fn add_status_code(&mut self, status_code: u16) -> Option<()> {
108        let status_line = &["HTTP/1.1 ", &status_code.to_string(), " \r\n"].concat();
109        self.add_line(status_line.as_bytes())
110    }
111
112    pub fn add_header(&mut self, name: &str, value: &[u8]) -> Option<()> {
113        let header_line = &[name.as_bytes(), b": ", value].concat();
114        self.add_line(header_line)
115    }
116
117    pub fn add_body(&mut self, body: &[u8]) -> Option<()> {
118        let destination_buffer = self
119            .buffer
120            .get_mut(self.position..self.position + body.len())?;
121
122        destination_buffer.copy_from_slice(body);
123        self.position += body.len();
124
125        Some(())
126    }
127
128    pub fn add_chunk(&mut self, chunk: &[u8]) -> Option<()> {
129        self.add_line(chunk.len().to_string().as_bytes())?;
130        self.add_line(chunk)?;
131        Some(())
132    }
133
134    pub fn get_position(&self) -> usize {
135        self.position
136    }
137}