Skip to content

Commit ae00fce

Browse files
authored
Merge pull request #44 from sunfishcode/sunfishcode/refactor-incoming-body
Move `IncomingBody` into the `http::body` module.
2 parents 97200cb + 9dddfa7 commit ae00fce

File tree

2 files changed

+101
-66
lines changed

2 files changed

+101
-66
lines changed

src/http/body.rs

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,35 @@
11
//! HTTP body types
22
3-
use crate::io::{AsyncRead, Cursor, Empty};
3+
use crate::io::{AsyncInputStream, AsyncRead, Cursor, Empty};
4+
use core::fmt;
5+
use wasi::http::types::IncomingBody as WasiIncomingBody;
46

5-
pub use super::response::IncomingBody;
7+
pub use super::{
8+
error::{Error, ErrorVariant},
9+
HeaderMap,
10+
};
11+
12+
#[derive(Debug)]
13+
pub(crate) enum BodyKind {
14+
Fixed(u64),
15+
Chunked,
16+
}
17+
18+
impl BodyKind {
19+
pub(crate) fn from_headers(headers: &HeaderMap) -> Result<BodyKind, InvalidContentLength> {
20+
if let Some(value) = headers.get("content-length") {
21+
let content_length = std::str::from_utf8(value.as_ref())
22+
.unwrap()
23+
.parse::<u64>()
24+
.map_err(|_| InvalidContentLength)?;
25+
Ok(BodyKind::Fixed(content_length))
26+
} else if headers.contains_key("transfer-encoding") {
27+
Ok(BodyKind::Chunked)
28+
} else {
29+
Ok(BodyKind::Chunked)
30+
}
31+
}
32+
}
633

734
/// A trait representing an HTTP body.
835
#[doc(hidden)]
@@ -82,3 +109,66 @@ impl Body for Empty {
82109
Some(0)
83110
}
84111
}
112+
113+
/// An incoming HTTP body
114+
#[derive(Debug)]
115+
pub struct IncomingBody {
116+
kind: BodyKind,
117+
// IMPORTANT: the order of these fields here matters. `body_stream` must
118+
// be dropped before `_incoming_body`.
119+
body_stream: AsyncInputStream,
120+
_incoming_body: WasiIncomingBody,
121+
}
122+
123+
impl IncomingBody {
124+
pub(crate) fn new(
125+
kind: BodyKind,
126+
body_stream: AsyncInputStream,
127+
incoming_body: WasiIncomingBody,
128+
) -> Self {
129+
Self {
130+
kind,
131+
body_stream,
132+
_incoming_body: incoming_body,
133+
}
134+
}
135+
}
136+
137+
impl AsyncRead for IncomingBody {
138+
async fn read(&mut self, out_buf: &mut [u8]) -> crate::io::Result<usize> {
139+
self.body_stream.read(out_buf).await
140+
}
141+
}
142+
143+
impl Body for IncomingBody {
144+
fn len(&self) -> Option<usize> {
145+
match self.kind {
146+
BodyKind::Fixed(l) => {
147+
if l > (usize::MAX as u64) {
148+
None
149+
} else {
150+
Some(l as usize)
151+
}
152+
}
153+
BodyKind::Chunked => None,
154+
}
155+
}
156+
}
157+
158+
#[derive(Debug)]
159+
pub struct InvalidContentLength;
160+
161+
impl fmt::Display for InvalidContentLength {
162+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163+
"incoming content-length should be a u64; violates HTTP/1.1".fmt(f)
164+
}
165+
}
166+
167+
impl std::error::Error for InvalidContentLength {}
168+
169+
impl From<InvalidContentLength> for Error {
170+
fn from(e: InvalidContentLength) -> Self {
171+
// TODO: What's the right error code here?
172+
ErrorVariant::Other(e.to_string()).into()
173+
}
174+
}

src/http/response.rs

Lines changed: 9 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,15 @@
1-
use wasi::http::types::{IncomingBody as WasiIncomingBody, IncomingResponse};
2-
3-
use super::{fields::header_map_from_wasi, Body, Error, HeaderMap, Result};
4-
use crate::io::{AsyncInputStream, AsyncRead};
1+
use wasi::http::types::IncomingResponse;
2+
3+
use super::{
4+
body::{BodyKind, IncomingBody},
5+
fields::header_map_from_wasi,
6+
Error, HeaderMap, Result,
7+
};
8+
use crate::io::AsyncInputStream;
59
use http::StatusCode;
610

711
pub use http::Response;
812

9-
#[derive(Debug)]
10-
enum BodyKind {
11-
Fixed(u64),
12-
Chunked,
13-
}
14-
15-
impl BodyKind {
16-
fn from_headers(headers: &HeaderMap) -> Result<BodyKind> {
17-
if let Some(value) = headers.get("content-length") {
18-
let content_length = std::str::from_utf8(value.as_ref())
19-
.unwrap()
20-
.parse::<u64>()
21-
.map_err(|_| {
22-
Error::other("incoming content-length should be a u64; violates HTTP/1.1")
23-
})?;
24-
Ok(BodyKind::Fixed(content_length))
25-
} else if headers.contains_key("transfer-encoding") {
26-
Ok(BodyKind::Chunked)
27-
} else {
28-
Ok(BodyKind::Chunked)
29-
}
30-
}
31-
}
32-
3313
pub(crate) fn try_from_incoming_response(
3414
incoming: IncomingResponse,
3515
) -> Result<Response<IncomingBody>> {
@@ -48,11 +28,7 @@ pub(crate) fn try_from_incoming_response(
4828
.stream()
4929
.expect("cannot call `stream` twice on an incoming body");
5030

51-
let body = IncomingBody {
52-
kind,
53-
body_stream: AsyncInputStream::new(body_stream),
54-
_incoming_body: incoming_body,
55-
};
31+
let body = IncomingBody::new(kind, AsyncInputStream::new(body_stream), incoming_body);
5632

5733
let mut builder = Response::builder().status(status);
5834

@@ -64,34 +40,3 @@ pub(crate) fn try_from_incoming_response(
6440
.body(body)
6541
.map_err(|err| Error::other(err.to_string()))
6642
}
67-
68-
/// An incoming HTTP body
69-
#[derive(Debug)]
70-
pub struct IncomingBody {
71-
kind: BodyKind,
72-
// IMPORTANT: the order of these fields here matters. `body_stream` must
73-
// be dropped before `_incoming_body`.
74-
body_stream: AsyncInputStream,
75-
_incoming_body: WasiIncomingBody,
76-
}
77-
78-
impl AsyncRead for IncomingBody {
79-
async fn read(&mut self, out_buf: &mut [u8]) -> crate::io::Result<usize> {
80-
self.body_stream.read(out_buf).await
81-
}
82-
}
83-
84-
impl Body for IncomingBody {
85-
fn len(&self) -> Option<usize> {
86-
match self.kind {
87-
BodyKind::Fixed(l) => {
88-
if l > (usize::MAX as u64) {
89-
None
90-
} else {
91-
Some(l as usize)
92-
}
93-
}
94-
BodyKind::Chunked => None,
95-
}
96-
}
97-
}

0 commit comments

Comments
 (0)