async_h1/client/
decode.rs

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