Skip to content

Commit 5b9e6ca

Browse files
authored
Merge pull request #75 from http-rs/split-files
Split files
2 parents d3a5f0b + 9467c49 commit 5b9e6ca

File tree

8 files changed

+764
-712
lines changed

8 files changed

+764
-712
lines changed

src/client.rs

-262
This file was deleted.

src/client/decode.rs

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

0 commit comments

Comments
 (0)