Skip to content

Commit

Permalink
feat(http): make Response more body-generic
Browse files Browse the repository at this point in the history
Make methods on Response more generic over the body type, including
methods to extract and modify the body (including changing body type)
and moving the optionality of the body into the type parameter, and
remove reliance on Body in several places.
  • Loading branch information
Twey committed May 4, 2017
1 parent 696dbe5 commit 9fdad06
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 33 deletions.
2 changes: 1 addition & 1 deletion examples/client.rs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn main() {
println!("Response: {}", res.status());
println!("Headers: \n{}", res.headers());

res.body().for_each(|chunk| {
res.into_body().unwrap_or_default().for_each(|chunk| {
io::stdout().write_all(&chunk).map_err(From::from)
})
}).map(|_| {
Expand Down
2 changes: 1 addition & 1 deletion examples/hello.rs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl Service for Hello {
Response::new()
.with_header(ContentLength(PHRASE.len() as u64))
.with_header(ContentType::plaintext())
.with_body(PHRASE)
.with_body(Some(PHRASE.into()))
)
}

Expand Down
4 changes: 2 additions & 2 deletions examples/server.rs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ impl Service for Echo {
(&Get, "/") | (&Get, "/echo") => {
Response::new()
.with_header(ContentLength(INDEX.len() as u64))
.with_body(INDEX)
.with_body(Some(INDEX.into()))
},
(&Post, "/echo") => {
let mut res = Response::new();
if let Some(len) = req.headers().get::<ContentLength>() {
res.headers_mut().set(len.clone());
}
res.with_body(req.body())
res.with_body(Some(req.body()))
},
_ => {
Response::new()
Expand Down
78 changes: 59 additions & 19 deletions src/http/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,28 @@ use status::StatusCode;
use version::HttpVersion;

/// A response for a client request to a remote server.
pub struct Response<B = Body> {
pub struct Response<B = Option<Body>> {
version: HttpVersion,
headers: Headers,
status: StatusCode,
raw_status: RawStatus,
body: Option<B>,
body: B,
}

impl<B> Response<B> {
impl Response {
/// Constructs a default response
#[inline]
pub fn new() -> Response<B> {
pub fn new() -> Response {
Response::default()
}
}

impl<B> Response<B> {
/// Constructs a default response with a given body
#[inline]
pub fn new_with_body(body: B) -> Response<B> {
Response::<()>::default().with_body(body)
}

/// Get the HTTP version of this response.
#[inline]
Expand Down Expand Up @@ -80,40 +88,71 @@ impl<B> Response<B> {
/// Set the body.
#[inline]
pub fn set_body<T: Into<B>>(&mut self, body: T) {
self.body = Some(body.into());
self.body = body.into();
}

/// Set the body and move the Response.
///
/// Useful for the "builder-style" pattern.
#[inline]
pub fn with_body<T: Into<B>>(mut self, body: T) -> Self {
self.set_body(body);
self
pub fn with_body<B_>(self, body: B_) -> Response<B_> {
let Response {
version,
headers,
status,
raw_status,
..
} = self;

Response {
version,
headers,
status,
raw_status,
body,
}
}

/// Take the body, moving it out of the Response.
#[inline]
pub fn take_body(self) -> (B, Response<()>) {
let Response {
version,
headers,
status,
raw_status,
body,
} = self;

(body, Response {
version,
headers,
status,
raw_status,
body: (),
})
}
}

impl Response<Body> {
/// Take the `Body` of this response.
/// Take the body of this response.
#[inline]
pub fn body(self) -> Body {
self.body.unwrap_or_default()
pub fn into_body(self) -> B {
self.body
}
}

impl<B> Default for Response<B> {
impl<B: Default> Default for Response<B> {
fn default() -> Response<B> {
Response::<B> {
version: Default::default(),
headers: Default::default(),
status: Default::default(),
raw_status: Default::default(),
body: None,
body: Default::default(),
}
}
}

impl fmt::Debug for Response {
impl<B> fmt::Debug for Response<B> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Response")
.field("status", &self.status)
Expand All @@ -125,13 +164,14 @@ impl fmt::Debug for Response {

/// Constructs a response using a received ResponseHead and optional body
#[inline]
pub fn from_wire<B>(incoming: ResponseHead, body: Option<B>) -> Response<B> {
pub fn from_wire<B: Default>(incoming: ResponseHead, body: Option<B>)
-> Response<Option<B>> {
let status = incoming.status();
trace!("Response::new");
debug!("version={:?}, status={:?}", incoming.version, status);
debug!("headers={:?}", incoming.headers);

Response::<B> {
Response {
status: status,
version: incoming.version,
headers: incoming.headers,
Expand All @@ -142,7 +182,7 @@ pub fn from_wire<B>(incoming: ResponseHead, body: Option<B>) -> Response<B> {

/// Splits this response into a MessageHead<StatusCode> and its body
#[inline]
pub fn split<B>(res: Response<B>) -> (MessageHead<StatusCode>, Option<B>) {
pub fn split<B>(res: Response<B>) -> (MessageHead<StatusCode>, B) {
let head = MessageHead::<StatusCode> {
version: res.version,
headers: res.headers,
Expand Down
18 changes: 9 additions & 9 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl<B: AsRef<[u8]> + 'static> Http<B> {
/// The returned `Server` contains one method, `run`, which is used to
/// actually run the server.
pub fn bind<S, Bd>(&self, addr: &SocketAddr, new_service: S) -> ::Result<Server<S, Bd>>
where S: NewService<Request = Request, Response = Response<Bd>, Error = ::Error> +
where S: NewService<Request = Request, Response = Response<Option<Bd>>, Error = ::Error> +
Send + Sync + 'static,
Bd: Stream<Item=B, Error=::Error>,
{
Expand Down Expand Up @@ -122,7 +122,7 @@ impl<B: AsRef<[u8]> + 'static> Http<B> {
io: I,
remote_addr: SocketAddr,
service: S)
where S: Service<Request = Request, Response = Response<Bd>, Error = ::Error> + 'static,
where S: Service<Request = Request, Response = Response<Option<Bd>>, Error = ::Error> + 'static,
Bd: Stream<Item=B, Error=::Error> + 'static,
I: AsyncRead + AsyncWrite + 'static,
{
Expand Down Expand Up @@ -287,11 +287,11 @@ impl From<Message<__ProtoRequest, http::TokioBody>> for Request {
}
}

impl<B> Into<Message<__ProtoResponse, B>> for Response<B> {
fn into(self) -> Message<__ProtoResponse, B> {
impl<B_, B: Into<Option<B_>>> Into<Message<__ProtoResponse, B_>> for Response<B> {
fn into(self) -> Message<__ProtoResponse, B_> {
let (head, body) = response::split(self);
if let Some(body) = body {
Message::WithBody(__ProtoResponse(head), body.into())
if let Some(body) = body.into() {
Message::WithBody(__ProtoResponse(head), body)
} else {
Message::WithoutBody(__ProtoResponse(head))
}
Expand All @@ -306,14 +306,14 @@ struct HttpService<T> {
type ResponseHead = http::MessageHead<::StatusCode>;

impl<T, B> Service for HttpService<T>
where T: Service<Request=Request, Response=Response<B>, Error=::Error>,
where T: Service<Request=Request, Response=Response<Option<B>>, Error=::Error>,
B: Stream<Error=::Error>,
B::Item: AsRef<[u8]>,
{
type Request = Message<__ProtoRequest, http::TokioBody>;
type Response = Message<__ProtoResponse, B>;
type Error = ::Error;
type Future = Map<T::Future, fn(Response<B>) -> Message<__ProtoResponse, B>>;
type Future = Map<T::Future, fn(Response<Option<B>>) -> Message<__ProtoResponse, B>>;

fn call(&self, message: Self::Request) -> Self::Future {
let (head, body) = match message {
Expand All @@ -326,7 +326,7 @@ impl<T, B> Service for HttpService<T>
}

impl<S, B> Server<S, B>
where S: NewService<Request = Request, Response = Response<B>, Error = ::Error>
where S: NewService<Request = Request, Response = Response<Option<B>>, Error = ::Error>
+ Send + Sync + 'static,
B: Stream<Error=::Error> + 'static,
B::Item: AsRef<[u8]>,
Expand Down
2 changes: 1 addition & 1 deletion tests/server.rs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl Service for TestService {
*res.headers_mut() = headers;
},
Reply::Body(body) => {
res.set_body(body);
res.set_body(Some(body.into()));
},
}
}
Expand Down

0 comments on commit 9fdad06

Please sign in to comment.