uhttp_response_header/
lib.rs

1//! This crate provides a simple formatter for building the lines of an [HTTP
2//! response](https://tools.ietf.org/html/rfc7230#section-3) header. The result can be
3//! written directly into a `TcpStream` or any other object that implements `Write`.
4//!
5//! ## Example
6//!
7//! ```rust
8//! use uhttp_response_header::HeaderLines;
9//! use std::io::{Cursor, Write};
10//!
11//! let mut buf = [0; 40];
12//! let mut cursor = Cursor::new(&mut buf[..]);
13//!
14//! // Write a header with response code `200` and a `Host: iana.org` header field.
15//! {
16//!     let mut h = HeaderLines::new(&mut cursor);
17//!     write!(h.line(), "{} {}", "HTTP/1.1", "200 OK").unwrap();
18//!     write!(h.line(), "Host: {}", "iana.org").unwrap();
19//! }
20//!
21//! // Now write the body.
22//! write!(&mut cursor, "hello").unwrap();
23//!
24//! assert_eq!(cursor.into_inner(), &b"HTTP/1.1 200 OK\r\nHost: iana.org\r\n\r\nhello"[..]);
25//! ```
26
27use std::io::Write;
28
29/// Writes out the lines in an HTTP response header.
30///
31/// A response header is made of any number of lines, each terminated by a CRLF, followed
32/// by a final terminating CRLF before the response body begins.
33///
34/// When this object goes out of scope the header is terminated and the stream is flushed.
35pub struct HeaderLines<W: Write>(W);
36
37impl<W: Write> HeaderLines<W> {
38    /// Create a new `HeaderLines` writing into the given stream.
39    pub fn new(sink: W) -> Self {
40        HeaderLines(sink)
41    }
42
43    /// Add a new line to the header, which can be written into.
44    pub fn line(&mut self) -> HeaderLine<&mut W> {
45        HeaderLine(&mut self.0)
46    }
47}
48
49impl<W: Write> Drop for HeaderLines<W> {
50    fn drop(&mut self) {
51        // Output an empty line and flush the buffer.
52        self.line();
53        self.0.flush().is_ok();
54    }
55}
56
57/// Writes out a header line.
58///
59/// When this object goes out of scope the line is terminated. The string written into the
60/// line must not contain any CRLFs (`\r\n`.)
61pub struct HeaderLine<W: Write>(W);
62
63impl<W: Write> Write for HeaderLine<W> {
64    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { self.0.write(buf) }
65    fn flush(&mut self) -> std::io::Result<()> { self.0.flush() }
66}
67
68impl<W: Write> Drop for HeaderLine<W> {
69    fn drop(&mut self) { self.write(&b"\r\n"[..]).is_ok(); }
70}
71
72#[cfg(test)]
73mod test {
74    use super::*;
75    use std::io::Cursor;
76
77    #[test]
78    fn test_header_line() {
79        let mut buf = [0u8; 13];
80
81        {
82            let mut c = Cursor::new(&mut buf[..]);
83            let mut h = HeaderLine(&mut c);
84
85            write!(&mut h, "ABC: DEF").unwrap();
86            write!(&mut h, " {}", 42).unwrap();
87        }
88
89        assert_eq!(&buf[..], b"ABC: DEF 42\r\n");
90    }
91
92    #[test]
93    fn test_header_lines() {
94        let mut buf = [0u8; 30];
95
96        {
97            let mut c = Cursor::new(&mut buf[..]);
98            let mut h = HeaderLines::new(&mut c);
99
100            write!(h.line(), "header: value").unwrap();
101            write!(h.line(), "field: {}", 1337).unwrap();
102        }
103
104        assert_eq!(&buf[..], b"header: value\r\nfield: 1337\r\n\r\n");
105    }
106}