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 for part in iter {
32 let part_length = part.len();
33
34 let destination_buffer = buffer.get_mut(position..position + part_length)?;
35 destination_buffer.copy_from_slice(part);
36 position += part_length;
37 }
38
39 let line_ending = LINE_ENDING.as_bytes();
41 let destination_buffer = buffer.get_mut(position..position + line_ending.len())?;
42 destination_buffer.copy_from_slice(line_ending);
43 position += line_ending.len();
44
45 Some(position)
46}
47
48pub fn parse_header(buffer: &[u8]) -> Option<(&str, &str)> {
49 let mut parts = buffer.splitn(2, |&b| b == b':');
50
51 let name = parts.next()?;
52 let value = parts.next()?;
53
54 let name = core::str::from_utf8(name).ok()?.trim();
55 let value = core::str::from_utf8(value).ok()?.trim();
56
57 Some((name, value))
58}
59
60pub fn split_lines(buffer: &[u8]) -> impl Iterator<Item = &[u8]> + '_ + Clone {
61 buffer.split(|&b| b == b'\n').map(|line| {
62 if line.ends_with(b"\r") {
63 &line[..line.len() - 1]
64 } else {
65 line
66 }
67 })
68}
69
70pub fn get_body(buffer: &[u8]) -> Option<&[u8]> {
71 let headers_end = buffer
73 .windows(4)
74 .position(|window| window == b"\r\n\r\n")
75 .map(|pos| pos + 4)?; buffer.get(headers_end..)
78}
79
80pub fn format_url<'a>(
81 scheme: &str,
82 host: &str,
83 query_parameters: impl Iterator<Item = (&'a str, &'a str)> + Clone,
84) -> String {
85 let total_query_length: usize = query_parameters
86 .clone()
87 .map(|(key, value)| key.len() + value.len() + 2) .sum();
89
90 let mut url = String::with_capacity(
91 scheme.len() + 3 + host.len() + 1 + total_query_length.saturating_sub(1),
92 ); url.push_str(scheme);
95 url.push_str("://");
96 url.push_str(host);
97 if total_query_length > 0 {
98 url.push('?');
99
100 for (i, (key, value)) in query_parameters.enumerate() {
101 url.push_str(key);
102 url.push('=');
103 url.push_str(value);
104
105 if i < total_query_length - 1 {
106 url.push('&');
107 }
108 }
109 }
110
111 url
112}