http_net/write.rs
1//! The allocation-free response writer (Tier 3): where serialized bytes go
2//! ([`Output`]), a borrowed description of a response head
3//! ([`ResponseHeadRef`]), and the [`ServerConn`] methods that write into any
4//! `Output`.
5//!
6//! [`ServerConn::respond`] and this writer share one code path, so a head
7//! written here is byte for byte the head `respond` would write for the same
8//! response, with the same validation and framing rules.
9
10use alloc::vec::Vec;
11use core::fmt;
12
13use crate::{
14 date::HttpDate,
15 error::HttpError,
16 message::Response,
17 redact::{FieldName, FieldValue},
18 server::ServerConn,
19};
20
21/// Where serialized bytes go.
22///
23/// `Vec<u8>` is the default; a driver can write straight into its own buffer
24/// instead (a zeroizing buffer that wipes on drop, a fixed slab in a passport,
25/// a socket's send queue). [`put`](Self::put) cannot fail: an `Output` with a
26/// fixed capacity must grow, or mark itself poisoned and have its owner close
27/// the connection, because a half-written response cannot be recovered
28/// anyway. [`reserve`](Self::reserve) is a hint and may be ignored.
29///
30/// # Examples
31///
32/// ```
33/// use http_net::Output;
34///
35/// /// Counts bytes without keeping them.
36/// struct Meter(usize);
37///
38/// impl Output for Meter {
39/// fn put(&mut self, bytes: &[u8]) {
40/// self.0 += bytes.len();
41/// }
42/// }
43///
44/// let mut m = Meter(0);
45/// m.put(b"HTTP/1.1 ");
46/// m.put(b"204 No Content\r\n");
47/// assert_eq!(m.0, 25);
48/// ```
49pub trait Output {
50 /// Appends `bytes`.
51 fn put(&mut self, bytes: &[u8]);
52
53 /// Says that about `additional` more bytes are coming; an
54 /// implementation may grow once instead of several times. Optional.
55 fn reserve(&mut self, additional: usize) {
56 let _ = additional;
57 }
58}
59
60impl Output for Vec<u8> {
61 #[inline]
62 fn put(&mut self, bytes: &[u8]) {
63 self.extend_from_slice(bytes);
64 }
65
66 #[inline]
67 fn reserve(&mut self, additional: usize) {
68 Vec::reserve(self, additional);
69 }
70}
71
72impl<O: Output + ?Sized> Output for &mut O {
73 #[inline]
74 fn put(&mut self, bytes: &[u8]) {
75 (**self).put(bytes);
76 }
77
78 #[inline]
79 fn reserve(&mut self, additional: usize) {
80 (**self).reserve(additional);
81 }
82}
83
84/// How the body of a response written with
85/// [`ServerConn::write_head`] is supplied.
86///
87/// # Examples
88///
89/// ```
90/// use http_net::OutgoingBody;
91///
92/// let body = b"hello";
93/// let full = OutgoingBody::Full(body.len() as u64);
94/// assert_ne!(full, OutgoingBody::Chunked);
95/// ```
96#[non_exhaustive]
97#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
98pub enum OutgoingBody {
99 /// No body: `content-length: 0` (or nothing, for a status that never
100 /// carries one).
101 Empty,
102 /// A body of exactly this many bytes that the caller writes itself,
103 /// right after the head (for example with one vectored write of head and
104 /// body); sent with `content-length`. The response is complete once the
105 /// caller has written them.
106 Full(u64),
107 /// A streamed body of exactly this many bytes, sent with
108 /// `content-length` through `send_body` and `end_body`.
109 Length(u64),
110 /// A streamed body with chunked encoding (close-delimited to an
111 /// HTTP/1.0 client), through `send_body` and `end_body`.
112 Chunked,
113}
114
115/// What the caller must do after [`ServerConn::write_head`].
116///
117/// # Examples
118///
119/// ```
120/// use http_net::BodyPlan;
121///
122/// let plan = BodyPlan::Raw;
123/// assert!(matches!(plan, BodyPlan::Raw));
124/// ```
125#[non_exhaustive]
126#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
127pub enum BodyPlan {
128 /// The response is complete: there is no body to send (an empty body, a
129 /// `HEAD` request, a status without a body, or an upgrade).
130 None,
131 /// Write the [`OutgoingBody::Full`] bytes as they are, right after the
132 /// head; the connection already counts the response as complete.
133 Raw,
134 /// Stream the body with [`ServerConn::send_body`] (or
135 /// [`send_body_into`](ServerConn::send_body_into)) and end it with
136 /// [`ServerConn::end_body`] (or [`end_body_into`](ServerConn::end_body_into)).
137 Stream,
138}
139
140/// A response head described with borrowed data, for
141/// [`ServerConn::write_head`]: nothing is copied into an owned builder and
142/// nothing is allocated.
143///
144/// The fields are validated when the head is written, with the same rules as
145/// [`Response::header`]: token names, no framing or connection fields (the
146/// connection writes those), no control bytes or surrounding whitespace in
147/// values. A `date` field among them stops a date passed to `write_head`
148/// from being added. `upgrade` names the protocol of a `101 Switching
149/// Protocols`, as [`Response::switching_protocols`] does.
150///
151/// The struct is built with a literal, so its fields are public and it is
152/// not `#[non_exhaustive]`; use [`ResponseHeadRef::new`] to start from the
153/// defaults.
154///
155/// # Examples
156///
157/// ```
158/// use http_net::{OutgoingBody, ResponseHeadRef};
159///
160/// let fields: &[(&str, &[u8])] = &[("content-type", b"text/plain")];
161/// let head = ResponseHeadRef {
162/// status: 200,
163/// fields,
164/// body: OutgoingBody::Full(5),
165/// close: false,
166/// upgrade: None,
167/// };
168/// assert_eq!(head, ResponseHeadRef::new(200, fields, OutgoingBody::Full(5)));
169/// ```
170#[derive(Clone, Copy, PartialEq, Eq, Hash)]
171pub struct ResponseHeadRef<'a> {
172 /// The status code (`100..=599`; among the 1xx codes only `101`).
173 pub status: u16,
174 /// The fields, in order, as `(name, value)`.
175 pub fields: &'a [(&'a str, &'a [u8])],
176 /// How the body is supplied.
177 pub body: OutgoingBody,
178 /// Close the connection after this response.
179 pub close: bool,
180 /// The protocol a `101` switches to.
181 pub upgrade: Option<&'a str>,
182}
183
184/// Credential values (`authorization`, `proxy-authorization`, `cookie`,
185/// `set-cookie`) print as `<redacted N bytes>`, as in every other `Debug`
186/// of the crate.
187impl fmt::Debug for ResponseHeadRef<'_> {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 struct Fields<'a>(&'a [(&'a str, &'a [u8])]);
190 impl fmt::Debug for Fields<'_> {
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 f.debug_list()
193 .entries(self.0.iter().map(|&(name, value)| {
194 (
195 FieldName(name.as_bytes()),
196 FieldValue {
197 name: name.as_bytes(),
198 value,
199 },
200 )
201 }))
202 .finish()
203 }
204 }
205 f.debug_struct("ResponseHeadRef")
206 .field("status", &self.status)
207 .field("fields", &Fields(self.fields))
208 .field("body", &self.body)
209 .field("close", &self.close)
210 .field("upgrade", &self.upgrade)
211 .finish()
212 }
213}
214
215impl<'a> ResponseHeadRef<'a> {
216 /// A head with `status`, `fields`, and `body`; no close, no upgrade.
217 ///
218 /// # Examples
219 ///
220 /// ```
221 /// use http_net::{OutgoingBody, ResponseHeadRef};
222 ///
223 /// let head = ResponseHeadRef::new(204, &[], OutgoingBody::Empty);
224 /// assert!(!head.close);
225 /// ```
226 #[must_use]
227 pub const fn new(status: u16, fields: &'a [(&'a str, &'a [u8])], body: OutgoingBody) -> Self {
228 Self {
229 status,
230 fields,
231 body,
232 close: false,
233 upgrade: None,
234 }
235 }
236}
237
238impl ServerConn {
239 /// Writes a response head described by borrowed data into any
240 /// [`Output`] and says how to send the body. Nothing is allocated; the
241 /// validation, framing fields, `connection` handling, and state changes
242 /// are those of [`respond`](Self::respond), and the bytes are the same
243 /// as `respond` would write for the equivalent [`Response`]. `date`, when
244 /// given and the fields set no `date`, is written as `date: <date>`.
245 ///
246 /// After [`BodyPlan::Raw`] write the [`OutgoingBody::Full`] bytes
247 /// yourself (for example with one vectored write of head and body); the
248 /// response then counts as complete. After [`BodyPlan::Stream`] use
249 /// [`send_body_into`](Self::send_body_into) and
250 /// [`end_body_into`](Self::end_body_into) (or the `Vec` versions).
251 ///
252 /// # Errors
253 ///
254 /// As [`respond`](Self::respond), with the field errors of
255 /// [`Response::header`] for `fields` and those of
256 /// [`Response::switching_protocols`] for `upgrade`
257 /// ([`HttpError::InvalidHeaderValue`] when it is not a protocol token,
258 /// [`HttpError::UpgradeNotOffered`] for `h2c` or a protocol the request
259 /// did not offer). An `upgrade` on a status other than `101`, or a
260 /// `101` without one, is [`HttpError::InvalidState`]. Nothing is written
261 /// on error.
262 ///
263 /// # Examples
264 ///
265 /// ```
266 /// use http_net::{BodyPlan, OutgoingBody, ResponseHeadRef, ServerConn, ServerEvent};
267 ///
268 /// let mut conn = ServerConn::new();
269 /// let _ = conn.feed(b"GET /app.js HTTP/1.1\r\nHost: a\r\n\r\n");
270 /// while !matches!(conn.poll().unwrap(), ServerEvent::RequestEnd) {}
271 ///
272 /// let body: &[u8] = b"console.log(1)";
273 /// let fields: &[(&str, &[u8])] = &[("content-type", b"text/javascript")];
274 /// let head = ResponseHeadRef::new(200, fields, OutgoingBody::Full(body.len() as u64));
275 /// let mut out = Vec::with_capacity(256);
276 /// let plan = conn.write_head(&head, None, &mut out)?;
277 /// assert_eq!(plan, BodyPlan::Raw);
278 /// assert_eq!(out, b"HTTP/1.1 200 OK\r\ncontent-length: 14\r\ncontent-type: text/javascript\r\n\r\n");
279 /// out.extend_from_slice(body); // or one vectored write of both
280 /// # Ok::<(), http_net::HttpError>(())
281 /// ```
282 pub fn write_head<O: Output + ?Sized>(
283 &mut self,
284 head: &ResponseHeadRef<'_>,
285 date: Option<&HttpDate>,
286 out: &mut O,
287 ) -> Result<BodyPlan, HttpError> {
288 self.start_response(head, date.map(HttpDate::as_bytes).map(|d| &d[..]), out)
289 }
290
291 /// [`respond`](Self::respond) into any [`Output`], with an optional
292 /// `date` as in [`respond_with_date`](Self::respond_with_date). A full
293 /// body is written after the head; a streamed one continues with
294 /// [`send_body_into`](Self::send_body_into) and
295 /// [`end_body_into`](Self::end_body_into).
296 ///
297 /// # Errors
298 ///
299 /// As [`respond`](Self::respond).
300 ///
301 /// # Examples
302 ///
303 /// ```
304 /// use http_net::{Output, Response, ServerConn, ServerEvent};
305 ///
306 /// /// A bounded output that refuses to grow past its capacity.
307 /// struct Slab { buf: [u8; 512], len: usize, overflow: bool }
308 ///
309 /// impl Output for Slab {
310 /// fn put(&mut self, bytes: &[u8]) {
311 /// match self.buf.get_mut(self.len..self.len + bytes.len()) {
312 /// Some(dst) => {
313 /// dst.copy_from_slice(bytes);
314 /// self.len += bytes.len();
315 /// }
316 /// None => self.overflow = true, // the owner closes the connection
317 /// }
318 /// }
319 /// }
320 ///
321 /// let mut conn = ServerConn::new();
322 /// let _ = conn.feed(b"GET / HTTP/1.1\r\nHost: a\r\n\r\n");
323 /// while !matches!(conn.poll().unwrap(), ServerEvent::RequestEnd) {}
324 /// let mut slab = Slab { buf: [0; 512], len: 0, overflow: false };
325 /// conn.respond_into(&Response::text(200, "hi"), None, &mut slab)?;
326 /// assert!(!slab.overflow);
327 /// assert!(slab.buf[..slab.len].ends_with(b"\r\n\r\nhi"));
328 /// # Ok::<(), http_net::HttpError>(())
329 /// ```
330 pub fn respond_into<O: Output + ?Sized>(
331 &mut self,
332 response: &Response,
333 date: Option<&HttpDate>,
334 out: &mut O,
335 ) -> Result<(), HttpError> {
336 self.respond_dated(response, date.map(|d| &d.as_bytes()[..]), out)
337 }
338
339 /// [`send_body`](Self::send_body) into any [`Output`].
340 ///
341 /// # Errors
342 ///
343 /// As [`send_body`](Self::send_body).
344 ///
345 /// # Examples
346 ///
347 /// ```
348 /// use http_net::{BodyPlan, OutgoingBody, ResponseHeadRef, ServerConn, ServerEvent};
349 ///
350 /// let mut conn = ServerConn::new();
351 /// let _ = conn.feed(b"GET /log HTTP/1.1\r\nHost: a\r\n\r\n");
352 /// while !matches!(conn.poll().unwrap(), ServerEvent::RequestEnd) {}
353 /// let mut out = Vec::new();
354 /// let head = ResponseHeadRef::new(200, &[], OutgoingBody::Chunked);
355 /// assert_eq!(conn.write_head(&head, None, &mut out)?, BodyPlan::Stream);
356 /// conn.send_body_into(b"line\n", &mut out)?;
357 /// conn.end_body_into(&[], &mut out)?;
358 /// assert!(out.ends_with(b"5\r\nline\n\r\n0\r\n\r\n"));
359 /// # Ok::<(), http_net::HttpError>(())
360 /// ```
361 pub fn send_body_into<O: Output + ?Sized>(
362 &mut self,
363 data: &[u8],
364 out: &mut O,
365 ) -> Result<(), HttpError> {
366 self.stream_body(data, out)
367 }
368
369 /// Ends a streamed body into any [`Output`], with `trailers` (empty for
370 /// none): [`end_body`](Self::end_body) and
371 /// [`end_body_with_trailers`](Self::end_body_with_trailers) in one.
372 ///
373 /// # Errors
374 ///
375 /// As [`end_body_with_trailers`](Self::end_body_with_trailers).
376 ///
377 /// # Examples
378 ///
379 /// See [`send_body_into`](Self::send_body_into).
380 pub fn end_body_into<O: Output + ?Sized>(
381 &mut self,
382 trailers: &[(&str, &[u8])],
383 out: &mut O,
384 ) -> Result<(), HttpError> {
385 self.finish_body(trailers, out)
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 use alloc::vec;
392
393 use super::*;
394 use crate::{HttpError, ServerEvent};
395
396 fn conn_after(request: &[u8]) -> ServerConn {
397 let mut conn = ServerConn::new();
398 let _ = conn.feed(request);
399 loop {
400 match conn.poll() {
401 Ok(ServerEvent::RequestEnd) | Err(_) => return conn,
402 Ok(_) => {}
403 }
404 }
405 }
406
407 const GET: &[u8] = b"GET / HTTP/1.1\r\nHost: a\r\n\r\n";
408
409 #[test]
410 fn test_write_head_full_body_plans_raw_and_completes() {
411 let mut conn = conn_after(GET);
412 let mut out = Vec::new();
413 let head = ResponseHeadRef::new(200, &[("x", b"1")], OutgoingBody::Full(3));
414 assert_eq!(conn.write_head(&head, None, &mut out), Ok(BodyPlan::Raw));
415 assert_eq!(out, b"HTTP/1.1 200 OK\r\ncontent-length: 3\r\nx: 1\r\n\r\n");
416 assert_eq!(conn.requests_served(), 1);
417 assert!(matches!(conn.poll(), Ok(ServerEvent::NeedInput)));
418 }
419
420 #[test]
421 fn test_write_head_head_request_and_bodiless_plan_none() {
422 let mut conn = conn_after(b"HEAD / HTTP/1.1\r\nHost: a\r\n\r\n");
423 let mut out = Vec::new();
424 let head = ResponseHeadRef::new(200, &[], OutgoingBody::Full(9));
425 assert_eq!(conn.write_head(&head, None, &mut out), Ok(BodyPlan::None));
426 assert!(out.ends_with(b"content-length: 9\r\n\r\n"));
427
428 let mut conn = conn_after(GET);
429 let mut out = Vec::new();
430 let head = ResponseHeadRef::new(204, &[], OutgoingBody::Empty);
431 assert_eq!(conn.write_head(&head, None, &mut out), Ok(BodyPlan::None));
432 assert_eq!(out, b"HTTP/1.1 204 No Content\r\n\r\n");
433 let mut conn = conn_after(GET);
434 let head = ResponseHeadRef::new(304, &[], OutgoingBody::Full(1));
435 assert_eq!(
436 conn.write_head(&head, None, &mut Vec::new()),
437 Err(HttpError::BodyNotAllowed)
438 );
439 }
440
441 #[test]
442 fn test_write_head_refuses_bad_fields_and_writes_nothing() {
443 for (fields, err) in [
444 (
445 &[("content-length", &b"5"[..])][..],
446 HttpError::ReservedHeader,
447 ),
448 (&[("bad name", &b"x"[..])][..], HttpError::InvalidHeaderName),
449 (&[("x", &b"a\r\nb"[..])][..], HttpError::BareCr),
450 (&[("x", &b" pad"[..])][..], HttpError::InvalidHeaderValue),
451 (&[("te", &b"trailers"[..])][..], HttpError::ReservedHeader),
452 ] {
453 let mut conn = conn_after(GET);
454 let mut out = Vec::new();
455 let head = ResponseHeadRef::new(200, fields, OutgoingBody::Empty);
456 assert_eq!(
457 conn.write_head(&head, None, &mut out),
458 Err(err),
459 "{fields:?}"
460 );
461 assert!(out.is_empty());
462 // Nothing changed: a normal response still goes out.
463 conn.respond(&Response::new(204), &mut out).unwrap();
464 }
465 }
466
467 #[test]
468 fn test_write_head_upgrade_rules() {
469 let upgrade_req =
470 b"GET / HTTP/1.1\r\nHost: a\r\nConnection: upgrade\r\nUpgrade: websocket\r\n\r\n";
471 let mut conn = conn_after(upgrade_req);
472 let mut out = Vec::new();
473 let mut head = ResponseHeadRef::new(101, &[], OutgoingBody::Empty);
474 assert!(matches!(
475 conn.write_head(&head, None, &mut out),
476 Err(HttpError::InvalidState(_))
477 ));
478 head.upgrade = Some("irc");
479 assert_eq!(
480 conn.write_head(&head, None, &mut out),
481 Err(HttpError::UpgradeNotOffered)
482 );
483 head.upgrade = Some("h2c");
484 assert_eq!(
485 conn.write_head(&head, None, &mut out),
486 Err(HttpError::UpgradeNotOffered)
487 );
488 head.upgrade = Some("a b");
489 assert_eq!(
490 conn.write_head(&head, None, &mut out),
491 Err(HttpError::InvalidHeaderValue)
492 );
493 let mut on_200 = ResponseHeadRef::new(200, &[], OutgoingBody::Empty);
494 on_200.upgrade = Some("websocket");
495 assert!(matches!(
496 conn.write_head(&on_200, None, &mut out),
497 Err(HttpError::InvalidState(_))
498 ));
499 assert!(out.is_empty());
500 head.upgrade = Some("WebSocket");
501 assert_eq!(conn.write_head(&head, None, &mut out), Ok(BodyPlan::None));
502 assert_eq!(
503 out,
504 b"HTTP/1.1 101 Switching Protocols\r\nconnection: upgrade\r\nupgrade: WebSocket\r\n\r\n"
505 );
506 assert!(conn.is_upgraded());
507 }
508
509 #[test]
510 fn test_write_head_date_unless_set() {
511 let date = HttpDate::from_unix_seconds(0);
512 let mut conn = conn_after(GET);
513 let mut out = Vec::new();
514 let head = ResponseHeadRef::new(204, &[], OutgoingBody::Empty);
515 assert_eq!(
516 conn.write_head(&head, Some(&date), &mut out),
517 Ok(BodyPlan::None)
518 );
519 assert!(
520 out.windows(35)
521 .any(|w| w == b"date: Thu, 01 Jan 1970 00:00:00 GMT")
522 );
523
524 let mut conn = conn_after(GET);
525 let mut out = Vec::new();
526 let fields: &[(&str, &[u8])] = &[("Date", b"x")];
527 let head = ResponseHeadRef::new(204, fields, OutgoingBody::Empty);
528 assert_eq!(
529 conn.write_head(&head, Some(&date), &mut out),
530 Ok(BodyPlan::None)
531 );
532 assert!(!out.windows(4).any(|w| w == b"1970"));
533 }
534
535 #[test]
536 fn test_streaming_into_output_and_state_errors() {
537 let mut conn = conn_after(GET);
538 let mut out = Vec::new();
539 assert!(matches!(
540 conn.send_body_into(b"x", &mut out),
541 Err(HttpError::InvalidState(_))
542 ));
543 let head = ResponseHeadRef::new(200, &[], OutgoingBody::Length(3));
544 assert_eq!(conn.write_head(&head, None, &mut out), Ok(BodyPlan::Stream));
545 assert!(matches!(
546 conn.write_head(&head, None, &mut out),
547 Err(HttpError::InvalidState(_))
548 ));
549 conn.send_body_into(b"ab", &mut out).unwrap();
550 assert_eq!(
551 conn.end_body_into(&[], &mut out),
552 Err(HttpError::BodyLengthMismatch)
553 );
554 conn.send_body_into(b"c", &mut out).unwrap();
555 assert_eq!(
556 conn.end_body_into(&[("x", b"1")], &mut out),
557 Err(HttpError::TrailerNotAllowed)
558 );
559 conn.end_body_into(&[], &mut out).unwrap();
560 assert!(out.ends_with(b"\r\n\r\nabc"));
561 }
562
563 #[test]
564 fn test_output_by_reference_and_reserve_hint() {
565 let mut v = vec![1u8];
566 {
567 let mut r: &mut Vec<u8> = &mut v;
568 Output::reserve(&mut r, 64);
569 r.put(b"23");
570 }
571 assert_eq!(v, b"\x0123");
572 assert!(v.capacity() >= 3);
573 }
574}