Skip to content

Commit 77d2e33

Browse files
stub out chunked-encoding
1 parent 0e39577 commit 77d2e33

File tree

4 files changed

+83
-8
lines changed

4 files changed

+83
-8
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ futures-io = "0.3.0"
1818
async-std = { version = "1", features = ["unstable"] }
1919
futures-core-preview = "0.3.0-alpha.18"
2020
http-types = { path = '../http-types' }
21+
thiserror = "1.0.9"
22+
pin-project-lite = "0.1.1"
2123

2224
[dev-dependencies]
2325
futures-util = "0.3.0"

src/chunked.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use async_std::io::{self, BufRead, Read};
2+
use std::pin::Pin;
3+
use std::task::{Context, Poll};
4+
5+
pin_project_lite::pin_project! {
6+
pub struct ChunkedDecoder<R> {
7+
#[pin]
8+
reader: R,
9+
}
10+
}
11+
12+
impl<R> ChunkedDecoder<R> {
13+
pub fn new(reader: R) -> Self {
14+
ChunkedDecoder { reader }
15+
}
16+
}
17+
18+
impl<R: BufRead + Unpin + Send + 'static> Read for ChunkedDecoder<R> {
19+
#[allow(missing_doc_code_examples)]
20+
fn poll_read(
21+
self: Pin<&mut Self>,
22+
cx: &mut Context<'_>,
23+
buf: &mut [u8],
24+
) -> Poll<io::Result<usize>> {
25+
let this = self.project();
26+
this.reader.poll_read(cx, buf)
27+
}
28+
}
29+
30+
impl<R: BufRead + Unpin + Send + 'static> BufRead for ChunkedDecoder<R> {
31+
#[allow(missing_doc_code_examples)]
32+
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&'_ [u8]>> {
33+
let this = self.project();
34+
this.reader.poll_fill_buf(cx)
35+
}
36+
37+
fn consume(self: Pin<&mut Self>, amt: usize) {
38+
let this = self.project();
39+
this.reader.consume(amt);
40+
}
41+
}

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
//! // tbi
2929
//! ```
3030
31-
#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
31+
#![forbid(unsafe_code, rust_2018_idioms)]
3232
#![deny(missing_debug_implementations, nonstandard_style)]
3333
// #![warn(missing_docs, missing_doc_code_examples, unreachable_pub)]
3434
#![cfg_attr(test, deny(warnings))]
@@ -43,6 +43,7 @@ mod check;
4343
pub mod client;
4444
pub mod server;
4545

46+
mod chunked;
4647
mod date;
4748

4849
/// A generic fallible type.

src/server.rs

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,25 @@ use async_std::io::{Read, Write};
66
use async_std::prelude::*;
77
use async_std::task::{Context, Poll};
88
use futures_core::ready;
9-
use http_types::headers::{HeaderName, HeaderValue, CONTENT_LENGTH};
9+
use http_types::headers::{HeaderName, HeaderValue, CONTENT_LENGTH, TRANSFER_ENCODING};
1010
use http_types::{Body, Method, Request, Response};
1111
use std::str::FromStr;
1212
use std::time::Duration;
13+
use thiserror::Error;
1314

1415
use std::pin::Pin;
1516

17+
use crate::chunked::ChunkedDecoder;
1618
use crate::date::fmt_http_date;
1719
use crate::{Exception, MAX_HEADERS};
1820

21+
/// Errors when handling incoming requests.
22+
#[derive(Debug, Error)]
23+
pub enum HttpError {
24+
#[error("unexpected content-length header")]
25+
UnexpectedContentLengthHeader,
26+
}
27+
1928
/// Parse an incoming HTTP connection.
2029
///
2130
/// Supports `KeepAlive` requests by default.
@@ -375,14 +384,36 @@ where
375384
req.insert_header(name, value)?;
376385
}
377386

387+
let content_length = req.header(&CONTENT_LENGTH);
388+
let transfer_encoding = req.header(&TRANSFER_ENCODING);
389+
390+
if content_length.is_some() && transfer_encoding.is_some() {
391+
// This is always an error.
392+
return Err(HttpError::UnexpectedContentLengthHeader.into());
393+
}
394+
395+
match transfer_encoding {
396+
Some(encoding) if !encoding.is_empty() => {
397+
if encoding.last().unwrap().as_str() == "chunked" {
398+
req.set_body(Body::from_reader(ChunkedDecoder::new(reader), None));
399+
return Ok(Some(req));
400+
}
401+
// Fall through to Content-Length
402+
}
403+
_ => {
404+
// Fall through to Content-Length
405+
}
406+
}
407+
378408
// Check for content-length, that determines determines whether we can parse
379409
// it with a known length, or need to use chunked encoding.
380-
let len = match req.header(&CONTENT_LENGTH) {
381-
Some(len) => len.last().unwrap().as_str().parse::<usize>()?,
382-
None => return Ok(Some(req)),
383-
};
384-
385-
req.set_body(Body::from_reader(reader.take(len as u64), Some(len)));
410+
match content_length {
411+
Some(len) => {
412+
let len = len.last().unwrap().as_str().parse::<usize>()?;
413+
req.set_body(Body::from_reader(reader.take(len as u64), Some(len)));
414+
}
415+
None => {}
416+
}
386417

387418
Ok(Some(req))
388419
}

0 commit comments

Comments
 (0)