async_h1b/client/
decode.rs

1use futures_lite::io::{AsyncRead as Read, BufReader};
2use futures_lite::prelude::*;
3
4use http_types::{
5    //ensure, ensure_eq,
6    format_err
7};
8use http_types::content::ContentLength;
9use http_types::{
10    headers::{DATE, TRANSFER_ENCODING},
11    Body, Response, StatusCode,
12};
13
14use std::convert::TryFrom;
15
16use crate::date::fmt_http_date;
17use crate::{chunked::ChunkedDecoder, Error};
18use crate::{MAX_HEADERS, MAX_HEAD_LENGTH};
19
20const CR: u8 = b'\r';
21const LF: u8 = b'\n';
22
23/// Decode an HTTP response on the client.
24pub async fn decode<R>(reader: R) -> crate::Result<Option<Response>>
25where
26    R: Read + Unpin + Send + Sync + 'static,
27{
28    let mut reader = BufReader::new(reader);
29    let mut buf = Vec::new();
30    let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS];
31    let mut httparse_res = httparse::Response::new(&mut headers);
32
33    // Keep reading bytes from the stream until we hit the end of the stream.
34    loop {
35        let bytes_read = reader.read_until(LF, &mut buf).await?;
36        // No more bytes are yielded from the stream.
37
38        match (bytes_read, buf.len()) {
39            (0, 0) => return Err(format_err!("connection closed").into()),
40            (0, _) => return Err(format_err!("empty response").into()),
41            _ => {}
42        }
43
44        // Prevent CWE-400 DDOS with large HTTP Headers.
45        if buf.len() >= MAX_HEAD_LENGTH {
46            return Err(Error::HeadersTooLong);
47        }
48
49        // We've hit the end delimiter of the stream.
50        let idx = buf.len() - 1;
51        if idx >= 3 && buf[idx - 3..=idx] == [CR, LF, CR, LF] {
52            break;
53        }
54        if idx >= 1 && buf[idx - 1..=idx] == [LF, LF] {
55            break;
56        }
57    }
58
59    // Convert our header buf into an httparse instance, and validate.
60    let status = httparse_res.parse(&buf)?;
61    if status.is_partial() {
62        return Err(Error::PartialHead);
63    }
64
65    let code = httparse_res.code.ok_or(Error::MissingStatusCode)?;
66
67    // Convert httparse headers + body into a `http_types::Response` type.
68    let version = httparse_res.version.ok_or(Error::MissingVersion)?;
69
70    if version != 1 {
71        return Err(Error::UnsupportedVersion(version));
72    }
73
74    let status_code =
75        StatusCode::try_from(code).map_err(|_| Error::UnrecognizedStatusCode(code))?;
76    let mut res = Response::new(status_code);
77
78    for header in httparse_res.headers.iter() {
79        res.append_header(header.name, std::str::from_utf8(header.value)?);
80    }
81
82    if res.header(DATE).is_none() {
83        let date = fmt_http_date(std::time::SystemTime::now());
84        res.insert_header(DATE, &format!("date: {}\r\n", date)[..]);
85    }
86
87    let content_length =
88        ContentLength::from_headers(&res).map_err(|_| Error::MalformedHeader("content-length"))?;
89    let transfer_encoding = res.header(TRANSFER_ENCODING);
90
91    if content_length.is_some() && transfer_encoding.is_some() {
92        return Err(Error::UnexpectedHeader("content-length"));
93    }
94
95    if let Some(encoding) = transfer_encoding {
96        if encoding.last().as_str() == "chunked" {
97            let trailers_sender = res.send_trailers();
98            let reader = BufReader::new(ChunkedDecoder::new(reader, trailers_sender));
99            res.set_body(Body::from_reader(reader, None));
100
101            // Return the response.
102            return Ok(Some(res));
103        }
104    }
105
106    // Check for Content-Length.
107    if let Some(content_length) = content_length {
108        let len = content_length.len();
109        res.set_body(Body::from_reader(reader.take(len), Some(len as usize)));
110    }
111
112    // Return the response.
113    Ok(Some(res))
114}