cyfs_async_h1/client/
decode.rs1use async_std::io::{BufReader, Read};
2use async_std::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
18pub 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 loop {
30 let bytes_read = reader.read_until(LF, &mut buf).await?;
31 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 ensure!(
41 buf.len() < MAX_HEAD_LENGTH,
42 "Head byte length should be less than 8kb"
43 );
44
45 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 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 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 let value = std::str::from_utf8(header.value)?;
70 use http_types::headers::ToHeaderValues;
71 match value.to_header_values() {
72 Ok(headers) => {
73 for item in headers {
74 res.append_header(header.name, item);
75 }
76 }
77 Err(e) => {
78 log::warn!("got non ascii header: {} -- {}, {}", header.name, value, e);
79 let value = percent_encoding::utf8_percent_encode(value, percent_encoding::NON_ALPHANUMERIC).to_string();
80 res.append_header(header.name, value);
81 }
82 }
83 }
84
85 if res.header(DATE).is_none() {
86 let date = fmt_http_date(std::time::SystemTime::now());
87 res.insert_header(DATE, &format!("date: {}\r\n", date)[..]);
88 }
89
90 let content_length = res.header(CONTENT_LENGTH);
91 let transfer_encoding = res.header(TRANSFER_ENCODING);
92
93 ensure!(
94 content_length.is_none() || transfer_encoding.is_none(),
95 "Unexpected Content-Length header"
96 );
97
98 if let Some(encoding) = transfer_encoding {
99 if encoding.last().as_str() == "chunked" {
100 let trailers_sender = res.send_trailers();
101 let reader = BufReader::new(ChunkedDecoder::new(reader, trailers_sender));
102 res.set_body(Body::from_reader(reader, None));
103
104 return Ok(res);
106 }
107 }
108
109 if let Some(len) = content_length {
111 let len = len.last().as_str().parse::<usize>()?;
112 res.set_body(Body::from_reader(reader.take(len as u64), Some(len)));
113 }
114
115 Ok(res)
117}