Merge pull request #19 from reem/static-response-states
Statically track the status of a Response by using a Phantom Type
This commit is contained in:
		| @@ -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<Fresh>)> for Incoming<'a> { | ||||
|     fn next(&mut self) -> Option<(Request, Response<Fresh>)> { | ||||
|         for conn in self.from { | ||||
|             match conn { | ||||
|                 Ok(stream) => { | ||||
|   | ||||
| @@ -13,39 +13,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)); | ||||
|  | ||||
| @@ -59,9 +72,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<Streaming> { | ||||
|     /// Flushes all writing of a response to the client. | ||||
|     pub fn end(mut self) -> IoResult<()> { | ||||
|         debug!("ending"); | ||||
| @@ -69,20 +99,14 @@ impl Response { | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| 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() | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user