1use crate::Error;
7use simoom_sys::scan::{scan, FIELD_VALUE, TCHAR};
8
9pub const CONTINUE: &[u8] = b"HTTP/1.1 100 Continue\r\n\r\n";
11pub const CRLF: &[u8] = b"\r\n";
13pub const LAST_CHUNK: &[u8] = b"0\r\n\r\n";
15
16const DAYS: [&[u8; 3]; 7] = [b"Thu", b"Fri", b"Sat", b"Sun", b"Mon", b"Tue", b"Wed"];
17const MONTHS: [&[u8; 3]; 12] =
18 [b"Jan", b"Feb", b"Mar", b"Apr", b"May", b"Jun", b"Jul", b"Aug", b"Sep", b"Oct", b"Nov", b"Dec"];
19
20fn two(out: &mut [u8], n: u64) {
21 out[0] = b'0' + (n / 10) as u8;
22 out[1] = b'0' + (n % 10) as u8;
23}
24
25pub fn http_date(unix_secs: u64) -> [u8; 29] {
27 let days = unix_secs / 86_400;
28 let rem = unix_secs % 86_400;
29 let z = days as i64 + 719_468;
31 let era = z.div_euclid(146_097);
32 let doe = z.rem_euclid(146_097);
33 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
34 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
35 let mp = (5 * doy + 2) / 153;
36 let day = (doy - (153 * mp + 2) / 5 + 1) as u64;
37 let month = if mp < 10 { mp + 3 } else { mp - 9 } as usize;
38 let year = (yoe + era * 400 + i64::from(month <= 2)) as u64;
39 let mut b = *b"Thu, 01 Jan 1970 00:00:00 GMT";
40 b[..3].copy_from_slice(DAYS[(days % 7) as usize]);
41 two(&mut b[5..7], day);
42 b[8..11].copy_from_slice(MONTHS[month - 1]);
43 two(&mut b[12..14], (year / 100) % 100);
44 two(&mut b[14..16], year % 100);
45 two(&mut b[17..19], rem / 3600);
46 two(&mut b[20..22], rem / 60 % 60);
47 two(&mut b[23..25], rem % 60);
48 b
49}
50
51#[derive(Clone, Copy, Debug)]
54pub struct DateCache {
55 secs: u64,
56 buf: [u8; 29],
57}
58
59impl DateCache {
60 pub fn new(unix_secs: u64) -> Self {
62 DateCache { secs: unix_secs, buf: http_date(unix_secs) }
63 }
64
65 pub fn update(&mut self, unix_secs: u64) -> bool {
67 if unix_secs == self.secs {
68 return false;
69 }
70 *self = DateCache::new(unix_secs);
71 true
72 }
73
74 pub fn bytes(&self) -> &[u8; 29] {
76 &self.buf
77 }
78
79 pub fn secs(&self) -> u64 {
81 self.secs
82 }
83}
84
85pub fn reason(status: u16) -> &'static str {
87 match status {
88 100 => "Continue",
89 101 => "Switching Protocols",
90 200 => "OK",
91 201 => "Created",
92 202 => "Accepted",
93 204 => "No Content",
94 206 => "Partial Content",
95 301 => "Moved Permanently",
96 302 => "Found",
97 303 => "See Other",
98 304 => "Not Modified",
99 307 => "Temporary Redirect",
100 308 => "Permanent Redirect",
101 400 => "Bad Request",
102 401 => "Unauthorized",
103 403 => "Forbidden",
104 404 => "Not Found",
105 405 => "Method Not Allowed",
106 406 => "Not Acceptable",
107 408 => "Request Timeout",
108 409 => "Conflict",
109 411 => "Length Required",
110 413 => "Content Too Large",
111 414 => "URI Too Long",
112 415 => "Unsupported Media Type",
113 417 => "Expectation Failed",
114 426 => "Upgrade Required",
115 429 => "Too Many Requests",
116 431 => "Request Header Fields Too Large",
117 500 => "Internal Server Error",
118 501 => "Not Implemented",
119 502 => "Bad Gateway",
120 503 => "Service Unavailable",
121 504 => "Gateway Timeout",
122 505 => "HTTP Version Not Supported",
123 _ => "",
124 }
125}
126
127fn decimal(n: u64, buf: &mut [u8; 20]) -> &[u8] {
129 let mut i = 20;
130 let mut n = n;
131 loop {
132 i -= 1;
133 buf[i] = b'0' + (n % 10) as u8;
134 n /= 10;
135 if n == 0 {
136 return &buf[i..];
137 }
138 }
139}
140
141fn check_field(name: &[u8], value: &[u8]) -> Result<(), Error> {
142 if name.is_empty() || scan(name, &TCHAR) != name.len() {
143 return Err(Error::InvalidHeaderName);
144 }
145 if name.eq_ignore_ascii_case(b"content-length") || name.eq_ignore_ascii_case(b"transfer-encoding") {
146 return Err(Error::FramingHeader);
147 }
148 if scan(value, &FIELD_VALUE) != value.len() {
149 return Err(Error::InvalidHeaderValue);
150 }
151 Ok(())
152}
153
154#[derive(Debug)]
158pub struct HeadWriter<'v> {
159 out: &'v mut Vec<u8>,
160}
161
162impl<'v> HeadWriter<'v> {
163 pub fn response(out: &'v mut Vec<u8>, status: u16) -> Self {
165 let status = status.clamp(100, 999);
166 let mut d = [0u8; 20];
167 out.extend_from_slice(b"HTTP/1.1 ");
168 out.extend_from_slice(decimal(u64::from(status), &mut d));
169 out.push(b' ');
170 out.extend_from_slice(reason(status).as_bytes());
171 out.extend_from_slice(CRLF);
172 HeadWriter { out }
173 }
174
175 pub fn request(out: &'v mut Vec<u8>, method: &[u8], target: &[u8]) -> Result<Self, Error> {
177 if method.is_empty() || scan(method, &TCHAR) != method.len() {
178 return Err(Error::InvalidMethod);
179 }
180 if target.is_empty() || target.iter().any(|&b| b <= b' ' || b >= 0x7f) {
181 return Err(Error::InvalidTarget);
182 }
183 out.extend_from_slice(method);
184 out.push(b' ');
185 out.extend_from_slice(target);
186 out.extend_from_slice(b" HTTP/1.1\r\n");
187 Ok(HeadWriter { out })
188 }
189
190 pub fn header(&mut self, name: &[u8], value: &[u8]) -> Result<&mut Self, Error> {
192 check_field(name, value)?;
193 self.out.extend_from_slice(name);
194 self.out.extend_from_slice(b": ");
195 self.out.extend_from_slice(value);
196 self.out.extend_from_slice(CRLF);
197 Ok(self)
198 }
199
200 pub fn date(&mut self, date: &DateCache) -> &mut Self {
202 self.out.extend_from_slice(b"Date: ");
203 self.out.extend_from_slice(date.bytes());
204 self.out.extend_from_slice(CRLF);
205 self
206 }
207
208 pub fn content_length(&mut self, n: u64) -> &mut Self {
210 let mut d = [0u8; 20];
211 self.out.extend_from_slice(b"Content-Length: ");
212 self.out.extend_from_slice(decimal(n, &mut d));
213 self.out.extend_from_slice(CRLF);
214 self
215 }
216
217 pub fn chunked(&mut self) -> &mut Self {
219 self.out.extend_from_slice(b"Transfer-Encoding: chunked\r\n");
220 self
221 }
222
223 pub fn close(&mut self) -> &mut Self {
225 self.out.extend_from_slice(b"Connection: close\r\n");
226 self
227 }
228
229 pub fn finish(self) {
231 self.out.extend_from_slice(CRLF);
232 }
233}
234
235#[derive(Clone, Debug)]
239pub struct StaticHead {
240 bytes: Vec<u8>,
241 date_at: usize,
242 head_len: usize,
243 secs: u64,
244}
245
246impl StaticHead {
247 pub fn new(status: u16, headers: &[(&[u8], &[u8])], body: &[u8]) -> Result<Self, Error> {
249 let mut bytes = Vec::with_capacity(128 + body.len());
250 let mut w = HeadWriter::response(&mut bytes, status);
251 for &(n, v) in headers {
252 w.header(n, v)?;
253 }
254 w.content_length(body.len() as u64);
255 w.out.extend_from_slice(b"Date: ");
256 let date_at = w.out.len();
257 w.date_slot();
258 w.finish();
259 let head_len = bytes.len();
260 bytes.extend_from_slice(body);
261 Ok(StaticHead { bytes, date_at, head_len, secs: u64::MAX })
262 }
263
264 fn patch(&mut self, date: &DateCache) {
265 if self.secs != date.secs() {
266 self.bytes[self.date_at..self.date_at + 29].copy_from_slice(date.bytes());
267 self.secs = date.secs();
268 }
269 }
270
271 pub fn full(&mut self, date: &DateCache) -> &[u8] {
273 self.patch(date);
274 &self.bytes
275 }
276
277 pub fn head(&mut self, date: &DateCache) -> &[u8] {
279 self.patch(date);
280 &self.bytes[..self.head_len]
281 }
282}
283
284impl HeadWriter<'_> {
285 fn date_slot(&mut self) {
286 self.out.extend_from_slice(&[b' '; 29]);
287 self.out.extend_from_slice(CRLF);
288 }
289}
290
291#[derive(Clone, Copy, Debug)]
293pub struct ChunkHeader {
294 buf: [u8; 18],
295 start: u8,
296}
297
298impl ChunkHeader {
299 pub fn new(len: usize) -> Self {
301 let mut buf = [0u8; 18];
302 buf[16] = b'\r';
303 buf[17] = b'\n';
304 let mut i = 16;
305 let mut n = len as u64;
306 loop {
307 i -= 1;
308 buf[i] = b"0123456789abcdef"[(n & 15) as usize];
309 n >>= 4;
310 if n == 0 {
311 break;
312 }
313 }
314 ChunkHeader { buf, start: i as u8 }
315 }
316
317 pub fn as_bytes(&self) -> &[u8] {
319 &self.buf[self.start as usize..]
320 }
321}
322
323pub fn write_chunk(out: &mut Vec<u8>, data: &[u8]) {
326 if data.is_empty() {
327 return;
328 }
329 out.extend_from_slice(ChunkHeader::new(data.len()).as_bytes());
330 out.extend_from_slice(data);
331 out.extend_from_slice(CRLF);
332}
333
334pub fn write_last_chunk(out: &mut Vec<u8>, trailers: &[(&[u8], &[u8])]) -> Result<(), Error> {
336 for &(n, v) in trailers {
337 check_field(n, v)?;
338 if n.eq_ignore_ascii_case(b"host") {
339 return Err(Error::ForbiddenTrailer);
340 }
341 }
342 out.extend_from_slice(b"0\r\n");
343 for &(n, v) in trailers {
344 out.extend_from_slice(n);
345 out.extend_from_slice(b": ");
346 out.extend_from_slice(v);
347 out.extend_from_slice(CRLF);
348 }
349 out.extend_from_slice(CRLF);
350 Ok(())
351}