Skip to content

Commit 97200cb

Browse files
authored
Merge pull request #41 from sunfishcode/sunfishcode/http-types
Switch to the http crate's Request, Response, and StatusCode.
2 parents a6e0d74 + 75977a4 commit 97200cb

File tree

10 files changed

+97
-381
lines changed

10 files changed

+97
-381
lines changed

src/http/body.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub trait Body: AsyncRead {
1010
/// Returns the exact remaining length of the iterator, if known.
1111
fn len(&self) -> Option<usize>;
1212

13-
/// Returns `true`` if the body is known to be empty.
13+
/// Returns `true` if the body is known to be empty.
1414
fn is_empty(&self) -> bool {
1515
matches!(self.len(), Some(0))
1616
}

src/http/client.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use super::{response::IncomingBody, Body, Error, Request, Response, Result};
1+
use super::{body::IncomingBody, Body, Error, Request, Response, Result};
2+
use crate::http::request::into_outgoing;
3+
use crate::http::response::try_from_incoming_response;
24
use crate::io::{self, AsyncOutputStream, AsyncPollable};
35
use crate::time::Duration;
46
use wasi::http::types::{OutgoingBody, RequestOptions as WasiRequestOptions};
@@ -18,7 +20,7 @@ impl Client {
1820

1921
/// Send an HTTP request.
2022
pub async fn send<B: Body>(&self, req: Request<B>) -> Result<Response<IncomingBody>> {
21-
let (wasi_req, body) = req.into_outgoing()?;
23+
let (wasi_req, body) = into_outgoing(req)?;
2224
let wasi_body = wasi_req.body().unwrap();
2325
let body_stream = wasi_body.write().unwrap();
2426

@@ -39,7 +41,7 @@ impl Client {
3941
// is to trap if we try and get the response more than once. The final
4042
// `?` is to raise the actual error if there is one.
4143
let res = res.get().unwrap().unwrap()?;
42-
Ok(Response::try_from_incoming_response(res)?)
44+
try_from_incoming_response(res)
4345
}
4446

4547
/// Set timeout on connecting to HTTP server

src/http/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! HTTP networking support
22
//!
3+
pub use http::status::StatusCode;
34
pub use http::uri::Uri;
45

56
#[doc(inline)]
@@ -10,7 +11,6 @@ pub use fields::{HeaderMap, HeaderName, HeaderValue};
1011
pub use method::Method;
1112
pub use request::Request;
1213
pub use response::Response;
13-
pub use status_code::StatusCode;
1414

1515
pub mod body;
1616

@@ -20,4 +20,3 @@ mod fields;
2020
mod method;
2121
mod request;
2222
mod response;
23-
mod status_code;

src/http/request.rs

Lines changed: 39 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,45 @@
1-
use crate::io::{empty, Empty};
2-
3-
use super::{
4-
fields::header_map_to_wasi, method::to_wasi_method, Body, Error, HeaderMap, IntoBody, Method,
5-
Result,
6-
};
7-
use http::uri::Uri;
1+
use super::{fields::header_map_to_wasi, method::to_wasi_method, Error, Result};
82
use wasi::http::outgoing_handler::OutgoingRequest;
93
use wasi::http::types::Scheme;
104

11-
/// An HTTP request
12-
#[derive(Debug)]
13-
pub struct Request<B: Body> {
14-
method: Method,
15-
uri: Uri,
16-
headers: HeaderMap,
17-
body: B,
18-
}
19-
20-
impl Request<Empty> {
21-
/// Create a new HTTP request to send off to the client.
22-
pub fn new(method: Method, uri: Uri) -> Self {
23-
Self {
24-
body: empty(),
25-
method,
26-
uri,
27-
headers: HeaderMap::new(),
28-
}
29-
}
30-
}
31-
32-
impl<B: Body> Request<B> {
33-
/// Get the HTTP headers from the impl
34-
pub fn headers(&self) -> &HeaderMap {
35-
&self.headers
36-
}
37-
38-
/// Mutably get the HTTP headers from the impl
39-
pub fn headers_mut(&mut self) -> &mut HeaderMap {
40-
&mut self.headers
41-
}
42-
43-
/// Set an HTTP body.
44-
pub fn set_body<C: IntoBody>(self, body: C) -> Request<C::IntoBody> {
45-
let Self {
46-
method,
47-
uri,
48-
headers,
49-
..
50-
} = self;
51-
Request {
52-
method,
53-
uri,
54-
headers,
55-
body: body.into_body(),
56-
}
57-
}
58-
59-
pub(crate) fn into_outgoing(self) -> Result<(OutgoingRequest, B)> {
60-
let wasi_req = OutgoingRequest::new(header_map_to_wasi(&self.headers)?);
61-
62-
// Set the HTTP method
63-
let method = to_wasi_method(self.method);
64-
wasi_req
65-
.set_method(&method)
66-
.map_err(|()| Error::other(format!("method rejected by wasi-http: {method:?}",)))?;
67-
68-
// Set the url scheme
69-
let scheme = match self.uri.scheme().map(|s| s.as_str()) {
70-
Some("http") => Scheme::Http,
71-
Some("https") | None => Scheme::Https,
72-
Some(other) => Scheme::Other(other.to_owned()),
73-
};
5+
pub use http::Request;
6+
7+
pub(crate) fn into_outgoing<T>(request: Request<T>) -> Result<(OutgoingRequest, T)> {
8+
let wasi_req = OutgoingRequest::new(header_map_to_wasi(request.headers())?);
9+
10+
let (parts, body) = request.into_parts();
11+
12+
// Set the HTTP method
13+
let method = to_wasi_method(parts.method);
14+
wasi_req
15+
.set_method(&method)
16+
.map_err(|()| Error::other(format!("method rejected by wasi-http: {method:?}",)))?;
17+
18+
// Set the url scheme
19+
let scheme = match parts.uri.scheme().map(|s| s.as_str()) {
20+
Some("http") => Scheme::Http,
21+
Some("https") | None => Scheme::Https,
22+
Some(other) => Scheme::Other(other.to_owned()),
23+
};
24+
wasi_req
25+
.set_scheme(Some(&scheme))
26+
.map_err(|()| Error::other(format!("scheme rejected by wasi-http: {scheme:?}")))?;
27+
28+
// Set authority
29+
let authority = parts.uri.authority().map(|a| a.as_str());
30+
wasi_req
31+
.set_authority(authority)
32+
.map_err(|()| Error::other(format!("authority rejected by wasi-http {authority:?}")))?;
33+
34+
// Set the url path + query string
35+
if let Some(p_and_q) = parts.uri.path_and_query() {
7436
wasi_req
75-
.set_scheme(Some(&scheme))
76-
.map_err(|()| Error::other(format!("scheme rejected by wasi-http: {scheme:?}")))?;
77-
78-
// Set authority
79-
let authority = self.uri.authority().map(|a| a.as_str());
80-
wasi_req
81-
.set_authority(authority)
82-
.map_err(|()| Error::other(format!("authority rejected by wasi-http {authority:?}")))?;
83-
84-
// Set the url path + query string
85-
if let Some(p_and_q) = self.uri.path_and_query() {
86-
wasi_req
87-
.set_path_with_query(Some(&p_and_q.to_string()))
88-
.map_err(|()| {
89-
Error::other(format!("path and query rejected by wasi-http {p_and_q:?}"))
90-
})?;
91-
}
92-
93-
// All done; request is ready for send-off
94-
Ok((wasi_req, self.body))
37+
.set_path_with_query(Some(&p_and_q.to_string()))
38+
.map_err(|()| {
39+
Error::other(format!("path and query rejected by wasi-http {p_and_q:?}"))
40+
})?;
9541
}
42+
43+
// All done; request is ready for send-off
44+
Ok((wasi_req, body))
9645
}

src/http/response.rs

Lines changed: 30 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
use wasi::http::types::{IncomingBody as WasiIncomingBody, IncomingResponse};
22

3-
use super::{fields::header_map_from_wasi, Body, Error, HeaderMap, Result, StatusCode};
3+
use super::{fields::header_map_from_wasi, Body, Error, HeaderMap, Result};
44
use crate::io::{AsyncInputStream, AsyncRead};
5+
use http::StatusCode;
56

6-
/// An HTTP response
7-
#[derive(Debug)]
8-
pub struct Response<B: Body> {
9-
headers: HeaderMap,
10-
status: StatusCode,
11-
body: B,
12-
}
7+
pub use http::Response;
138

149
#[derive(Debug)]
1510
enum BodyKind {
@@ -35,54 +30,39 @@ impl BodyKind {
3530
}
3631
}
3732

38-
impl Response<IncomingBody> {
39-
pub(crate) fn try_from_incoming_response(incoming: IncomingResponse) -> Result<Self> {
40-
let headers: HeaderMap = header_map_from_wasi(incoming.headers())?;
41-
let status = incoming.status().into();
33+
pub(crate) fn try_from_incoming_response(
34+
incoming: IncomingResponse,
35+
) -> Result<Response<IncomingBody>> {
36+
let headers: HeaderMap = header_map_from_wasi(incoming.headers())?;
37+
// TODO: Does WASI guarantee that the incoming status is valid?
38+
let status =
39+
StatusCode::from_u16(incoming.status()).map_err(|err| Error::other(err.to_string()))?;
4240

43-
let kind = BodyKind::from_headers(&headers)?;
44-
// `body_stream` is a child of `incoming_body` which means we cannot
45-
// drop the parent before we drop the child
46-
let incoming_body = incoming
47-
.consume()
48-
.expect("cannot call `consume` twice on incoming response");
49-
let body_stream = incoming_body
50-
.stream()
51-
.expect("cannot call `stream` twice on an incoming body");
41+
let kind = BodyKind::from_headers(&headers)?;
42+
// `body_stream` is a child of `incoming_body` which means we cannot
43+
// drop the parent before we drop the child
44+
let incoming_body = incoming
45+
.consume()
46+
.expect("cannot call `consume` twice on incoming response");
47+
let body_stream = incoming_body
48+
.stream()
49+
.expect("cannot call `stream` twice on an incoming body");
5250

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

59-
Ok(Self {
60-
headers,
61-
body,
62-
status,
63-
})
64-
}
65-
}
57+
let mut builder = Response::builder().status(status);
6658

67-
impl<B: Body> Response<B> {
68-
// Get the HTTP status code
69-
pub fn status_code(&self) -> StatusCode {
70-
self.status
59+
if let Some(headers_mut) = builder.headers_mut() {
60+
*headers_mut = headers;
7161
}
7262

73-
/// Get the HTTP headers from the impl
74-
pub fn headers(&self) -> &HeaderMap {
75-
&self.headers
76-
}
77-
78-
/// Mutably get the HTTP headers from the impl
79-
pub fn headers_mut(&mut self) -> &mut HeaderMap {
80-
&mut self.headers
81-
}
82-
83-
pub fn body(&mut self) -> &mut B {
84-
&mut self.body
85-
}
63+
builder
64+
.body(body)
65+
.map_err(|err| Error::other(err.to_string()))
8666
}
8767

8868
/// An incoming HTTP body

0 commit comments

Comments
 (0)