Skip to main content

net_wire/http1/
builder.rs

1//! HTTP/1 head builders.
2
3use super::error::Http1BuildError;
4use super::fields::Http1FieldRef;
5use super::fields::{is_token, validate_field_ref};
6use super::head::{Http1RequestHead, Http1ResponseHead, Http1Version};
7use super::head::{valid_reason, valid_request_target};
8
9/// Builds an HTTP/1 request head in caller-owned storage.
10pub struct Http1RequestHeadBuilder<'output, 'input, 'fields> {
11    buffer: &'output mut [u8],
12    method: &'input [u8],
13    target: &'input [u8],
14    version: Http1Version,
15    fields: &'fields [Http1FieldRef<'input>],
16}
17
18impl<'output, 'input, 'fields> Http1RequestHeadBuilder<'output, 'input, 'fields> {
19    /// Creates a request-head builder.
20    pub const fn new(
21        buffer: &'output mut [u8],
22        method: &'input [u8],
23        target: &'input [u8],
24        version: Http1Version,
25        fields: &'fields [Http1FieldRef<'input>],
26    ) -> Self {
27        Self {
28            buffer,
29            method,
30            target,
31            version,
32            fields,
33        }
34    }
35
36    /// Validates and writes the complete request head atomically.
37    pub fn build(self) -> Result<Http1RequestHead<'output>, Http1BuildError> {
38        if self.method.is_empty()
39            || !self.method.iter().copied().all(is_token)
40            || !valid_request_target(self.target)
41            || !self.fields.iter().copied().all(validate_field_ref)
42        {
43            return Err(Http1BuildError::InvalidValue);
44        }
45        let start_line_len = checked_sum(&[
46            self.method.len(),
47            1,
48            self.target.len(),
49            1,
50            self.version.as_bytes().len(),
51            2,
52        ])?;
53        let fields_len = encoded_fields_len(self.fields)?;
54        let required = checked_sum(&[start_line_len, fields_len, 2])?;
55        if self.buffer.len() < required {
56            return Err(Http1BuildError::BufferTooShort {
57                required,
58                available: self.buffer.len(),
59            });
60        }
61
62        let method_end = self.method.len();
63        let target_end = method_end + 1 + self.target.len();
64        let fields_start = start_line_len;
65        let mut offset = 0;
66        write_part(self.buffer, &mut offset, self.method);
67        write_part(self.buffer, &mut offset, b" ");
68        write_part(self.buffer, &mut offset, self.target);
69        write_part(self.buffer, &mut offset, b" ");
70        write_part(self.buffer, &mut offset, self.version.as_bytes());
71        write_part(self.buffer, &mut offset, b"\r\n");
72        write_fields(self.buffer, &mut offset, self.fields);
73        write_part(self.buffer, &mut offset, b"\r\n");
74        Ok(Http1RequestHead::from_validated(
75            &self.buffer[..required],
76            method_end,
77            target_end,
78            fields_start,
79            self.version,
80        ))
81    }
82}
83
84/// Builds an HTTP/1 response head in caller-owned storage.
85pub struct Http1ResponseHeadBuilder<'output, 'input, 'fields> {
86    buffer: &'output mut [u8],
87    version: Http1Version,
88    status: u16,
89    reason: &'input [u8],
90    fields: &'fields [Http1FieldRef<'input>],
91}
92
93impl<'output, 'input, 'fields> Http1ResponseHeadBuilder<'output, 'input, 'fields> {
94    /// Creates a response-head builder.
95    pub const fn new(
96        buffer: &'output mut [u8],
97        version: Http1Version,
98        status: u16,
99        reason: &'input [u8],
100        fields: &'fields [Http1FieldRef<'input>],
101    ) -> Self {
102        Self {
103            buffer,
104            version,
105            status,
106            reason,
107            fields,
108        }
109    }
110
111    /// Validates and writes the complete response head atomically.
112    pub fn build(self) -> Result<Http1ResponseHead<'output>, Http1BuildError> {
113        if self.status > 999
114            || !valid_reason(self.reason)
115            || !self.fields.iter().copied().all(validate_field_ref)
116        {
117            return Err(Http1BuildError::InvalidValue);
118        }
119        let start_line_len =
120            checked_sum(&[self.version.as_bytes().len(), 1, 3, 1, self.reason.len(), 2])?;
121        let fields_len = encoded_fields_len(self.fields)?;
122        let required = checked_sum(&[start_line_len, fields_len, 2])?;
123        if self.buffer.len() < required {
124            return Err(Http1BuildError::BufferTooShort {
125                required,
126                available: self.buffer.len(),
127            });
128        }
129
130        let reason_start = self.version.as_bytes().len() + 1 + 3 + 1;
131        let fields_start = start_line_len;
132        let status = [
133            b'0' + ((self.status / 100) as u8),
134            b'0' + (((self.status / 10) % 10) as u8),
135            b'0' + ((self.status % 10) as u8),
136        ];
137        let mut offset = 0;
138        write_part(self.buffer, &mut offset, self.version.as_bytes());
139        write_part(self.buffer, &mut offset, b" ");
140        write_part(self.buffer, &mut offset, &status);
141        write_part(self.buffer, &mut offset, b" ");
142        write_part(self.buffer, &mut offset, self.reason);
143        write_part(self.buffer, &mut offset, b"\r\n");
144        write_fields(self.buffer, &mut offset, self.fields);
145        write_part(self.buffer, &mut offset, b"\r\n");
146        Ok(Http1ResponseHead::from_validated(
147            &self.buffer[..required],
148            self.status,
149            reason_start,
150            fields_start,
151            self.version,
152        ))
153    }
154}
155
156fn encoded_fields_len(fields: &[Http1FieldRef<'_>]) -> Result<usize, Http1BuildError> {
157    let mut length = 0usize;
158    for field in fields {
159        length = length
160            .checked_add(field.name().len())
161            .and_then(|length| length.checked_add(1))
162            .and_then(|length| length.checked_add(field.value().len()))
163            .and_then(|length| length.checked_add(2))
164            .ok_or(Http1BuildError::LengthTooLarge)?;
165    }
166    Ok(length)
167}
168
169fn checked_sum(parts: &[usize]) -> Result<usize, Http1BuildError> {
170    parts.iter().try_fold(0usize, |total, part| {
171        total
172            .checked_add(*part)
173            .ok_or(Http1BuildError::LengthTooLarge)
174    })
175}
176
177fn write_fields(buffer: &mut [u8], offset: &mut usize, fields: &[Http1FieldRef<'_>]) {
178    for field in fields {
179        write_part(buffer, offset, field.name());
180        write_part(buffer, offset, b":");
181        write_part(buffer, offset, field.value());
182        write_part(buffer, offset, b"\r\n");
183    }
184}
185
186fn write_part(buffer: &mut [u8], offset: &mut usize, part: &[u8]) {
187    let end = *offset + part.len();
188    buffer[*offset..end].copy_from_slice(part);
189    *offset = end;
190}