Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions benches/client.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![feature(macro_rules)]
extern crate curl;
extern crate http;
extern crate hyper;
Expand All @@ -13,10 +14,19 @@ fn listen() -> hyper::server::Listening {
server.listen(handle).unwrap()
}

macro_rules! try_continue(
($e:expr) => {{
match $e {
Ok(v) => v,
Err(..) => continue
}
}})

fn handle(mut incoming: Incoming) {
for (_, mut res) in incoming {
res.write(b"Benchmarking hyper vs others!").unwrap();
res.end().unwrap();
for (_, res) in incoming {
let mut res = try_continue!(res.start());
try_continue!(res.write(b"Benchmarking hyper vs others!"))
try_continue!(res.end());
}
}

Expand Down
12 changes: 7 additions & 5 deletions examples/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,26 @@ impl Handler for Echo {
(&Get, "/") | (&Get, "/echo") => {
let out = b"Try POST /echo";

res.headers.set(ContentLength(out.len()));
res.headers_mut().set(ContentLength(out.len()));
let mut res = try_continue!(res.start());
try_continue!(res.write(out));
try_continue!(res.end());
continue;
},
(&Post, "/echo") => (), // fall through, fighting mutable borrows
_ => {
res.status = hyper::status::NotFound;
try_continue!(res.end());
*res.status_mut() = hyper::status::NotFound;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never been a fan of assignment via derefing a pointer... What if it were fn set_status(StatusCode)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I think people in Rust are used to assign-via-deref because this is a relatively common pattern, but I'm not opposed to set_status in principle. I wonder if there is a style guide convention for this case.

try_continue!(res.start().and_then(|res| res.end()));
continue;
}
},
_ => {
try_continue!(res.end());
continue;
try_continue!(res.start().and_then(|res| res.end()));
continue;
}
};

let mut res = try_continue!(res.start());
try_continue!(copy(&mut req, &mut res));
try_continue!(res.end());
}
Expand Down
6 changes: 3 additions & 3 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::io::{Acceptor, Listener, IoResult, EndOfFile, IncomingConnections};
use std::io::net::ip::{IpAddr, Port, SocketAddr};

pub use self::request::Request;
pub use self::response::Response;
pub use self::response::{Response, Fresh, Streaming};

pub mod request;
pub mod response;
Expand Down Expand Up @@ -55,8 +55,8 @@ pub struct Incoming<'a> {
from: IncomingConnections<'a, TcpAcceptor>
}

impl<'a> Iterator<(Request, Response)> for Incoming<'a> {
fn next(&mut self) -> Option<(Request, Response)> {
impl<'a> Iterator<(Request, Response<Fresh>)> for Incoming<'a> {
fn next(&mut self) -> Option<(Request, Response<Fresh>)> {
for conn in self.from {
match conn {
Ok(stream) => {
Expand Down
74 changes: 49 additions & 25 deletions src/server/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,52 @@ use status;
use version;
use rfc7230::{CR, LF, LINE_ENDING};

/// Phantom type indicating Headers and StatusCode have not been written.
pub struct Fresh;

/// Phantom type indicating Headers and StatusCode have been written.
pub struct Streaming;

/// The status of a Response, indicating if the headers and status have been written.
pub trait WriteStatus {}

impl WriteStatus for Streaming {}
impl WriteStatus for Fresh {}

/// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`.
pub struct Response {
/// The status code for the request.
pub status: status::StatusCode,
/// The outgoing headers on this response.
pub headers: header::Headers,
pub struct Response<W: WriteStatus> {
/// The HTTP version of this response.
pub version: version::HttpVersion,

headers_written: bool, // TODO: can this check be moved to compile time?
// Stream the Response is writing to, not accessible through UnwrittenResponse
body: BufferedWriter<TcpStream>, // TODO: use a HttpWriter from rfc7230
// The status code for the request.
status: status::StatusCode,
// The outgoing headers on this response.
headers: header::Headers
}

impl Response {
impl<W: WriteStatus> Response<W> {
/// The status of this response.
#[inline]
pub fn status(&self) -> status::StatusCode { self.status }

/// The headers of this response.
pub fn headers(&self) -> &header::Headers { &self.headers }
}

impl Response<Fresh> {
/// Creates a new Response that can be used to write to a network stream.
pub fn new(tcp: TcpStream) -> Response {
pub fn new(tcp: TcpStream) -> Response<Fresh> {
Response {
status: status::Ok,
version: version::Http11,
headers: header::Headers::new(),
headers_written: false,
body: BufferedWriter::new(tcp)
}
}

fn write_head(&mut self) -> IoResult<()> {
if self.headers_written {
debug!("headers previously written, nooping");
return Ok(());
}
self.headers_written = true;
/// Consume this Response<Fresh>, writing the Headers and Status and creating a Response<Streaming>
pub fn start(mut self) -> IoResult<Response<Streaming>> {
debug!("writing head: {} {}", self.version, self.status);
try!(write!(self.body, "{} {}{}{}", self.version, self.status, CR as char, LF as char));

Expand All @@ -58,30 +71,41 @@ impl Response {
try!(self.body.write(LINE_ENDING));
}

self.body.write(LINE_ENDING)
try!(self.body.write(LINE_ENDING));

// "copy" to change the phantom type
Ok(Response {
version: self.version,
body: self.body,
status: self.status,
headers: self.headers
})
}

/// Get a mutable reference to the status.
#[inline]
pub fn status_mut(&mut self) -> &mut status::StatusCode { &mut self.status }

/// Get a mutable reference to the Headers.
pub fn headers_mut(&mut self) -> &mut header::Headers { &mut self.headers }
}

impl Response<Streaming> {
/// Flushes all writing of a response to the client.
pub fn end(mut self) -> IoResult<()> {
debug!("ending");
self.flush()
}
}


impl Writer for Response {
impl Writer for Response<Streaming> {
fn write(&mut self, msg: &[u8]) -> IoResult<()> {
if !self.headers_written {
try!(self.write_head());
}
debug!("write {:u} bytes", msg.len());
self.body.write(msg)
}

fn flush(&mut self) -> IoResult<()> {
if !self.headers_written {
try!(self.write_head());
}
self.body.flush()
}
}