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