Skip to main content

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}