1mod request;
2mod response;
3mod url;
4
5use alloc::string::String;
6pub use request::*;
7pub use response::*;
8pub use url::*;
9
10pub const LINE_ENDING: &str = "\r\n";
11pub const HEADER_SEPARATOR: &str = ": ";
12
13pub fn add_line(buffer: &mut [u8], position: usize, line: &[u8]) -> Option<usize> {
14 let destination_buffer = buffer.get_mut(position..position + line.len())?;
15 destination_buffer.copy_from_slice(line);
16 let mut new_position = position + line.len();
17
18 let line_ending = LINE_ENDING.as_bytes();
20 let destination_buffer = buffer.get_mut(new_position..new_position + line_ending.len())?;
21 destination_buffer.copy_from_slice(line_ending);
22 new_position += line_ending.len();
23
24 Some(new_position)
25}
26
27pub fn add_line_iterator<'a, I>(buffer: &mut [u8], mut position: usize, iter: I) -> Option<usize>
28where
29 I: Iterator<Item = &'a [u8]>,
30{
31 log::information!(
32 "Adding line iterator to buffer at position {} to buffer of length {}",
33 position,
34 buffer.len()
35 );
36 for part in iter {
37 log::information!("Adding part: {:?}", core::str::from_utf8(part).ok());
38 let part_length = part.len();
39
40 log::information!("Part length: {}", part_length);
41
42 let destination_buffer = buffer.get_mut(position..position + part_length)?;
43
44 log::information!("Copying part to buffer at position {}", position);
45 destination_buffer.copy_from_slice(part);
46 position += part_length;
47 }
48
49 log::information!(
50 "All parts added, final position before line ending: {}",
51 position
52 );
53 let line_ending = LINE_ENDING.as_bytes();
55 let destination_buffer = buffer.get_mut(position..position + line_ending.len())?;
56 destination_buffer.copy_from_slice(line_ending);
57 position += line_ending.len();
58
59 Some(position)
60}
61
62pub fn parse_header(buffer: &[u8]) -> Option<(&str, &str)> {
63 let mut parts = buffer.splitn(2, |&b| b == b':');
64
65 let name = parts.next()?;
66 let value = parts.next()?;
67
68 let name = core::str::from_utf8(name).ok()?.trim();
69 let value = core::str::from_utf8(value).ok()?.trim();
70
71 Some((name, value))
72}
73
74pub fn split_lines(buffer: &[u8]) -> impl Iterator<Item = &[u8]> + '_ + Clone {
75 buffer.split(|&b| b == b'\n').map(|line| {
76 if line.ends_with(b"\r") {
77 &line[..line.len() - 1]
78 } else {
79 line
80 }
81 })
82}
83
84pub fn get_body(buffer: &[u8]) -> Option<&[u8]> {
85 let headers_end = buffer
87 .windows(4)
88 .position(|window| window == b"\r\n\r\n")
89 .map(|pos| pos + 4)?; buffer.get(headers_end..)
92}
93
94pub fn format_url<'a>(
95 scheme: &str,
96 host: &str,
97 query_parameters: impl Iterator<Item = (&'a str, &'a str)> + Clone,
98) -> String {
99 let total_query_length: usize = query_parameters
100 .clone()
101 .map(|(key, value)| key.len() + value.len() + 2) .sum();
103
104 let mut url = String::with_capacity(
105 scheme.len() + 3 + host.len() + 1 + total_query_length.saturating_sub(1),
106 ); url.push_str(scheme);
109 url.push_str("://");
110 url.push_str(host);
111 if total_query_length > 0 {
112 url.push('?');
113
114 for (i, (key, value)) in query_parameters.enumerate() {
115 url.push_str(key);
116 url.push('=');
117 url.push_str(value);
118
119 if i < total_query_length - 1 {
120 url.push('&');
121 }
122 }
123 }
124
125 url
126}