diff --git a/benches/client.rs b/benches/client.rs index 3694473f45..680084f1f1 100644 --- a/benches/client.rs +++ b/benches/client.rs @@ -1,3 +1,4 @@ +#![feature(macro_rules)] extern crate curl; extern crate http; extern crate hyper; @@ -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()); } } diff --git a/examples/server.rs b/examples/server.rs index 30e24969d1..ebddbc35a2 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -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; + 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()); } diff --git a/src/server/mod.rs b/src/server/mod.rs index 6f4d0816e3..e33b390283 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -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; @@ -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)> for Incoming<'a> { + fn next(&mut self) -> Option<(Request, Response)> { for conn in self.from { match conn { Ok(stream) => { diff --git a/src/server/response.rs b/src/server/response.rs index 4658dd9c72..240abbefd2 100644 --- a/src/server/response.rs +++ b/src/server/response.rs @@ -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 { /// 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, // 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 Response { + /// 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 { /// 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 { 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, writing the Headers and Status and creating a Response + pub fn start(mut self) -> IoResult> { debug!("writing head: {} {}", self.version, self.status); try!(write!(self.body, "{} {}{}{}", self.version, self.status, CR as char, LF as char)); @@ -58,9 +71,26 @@ 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 { /// Flushes all writing of a response to the client. pub fn end(mut self) -> IoResult<()> { debug!("ending"); @@ -68,20 +98,14 @@ impl Response { } } - -impl Writer for Response { +impl Writer for Response { 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() } } +