Statically track the status of a Response by using a Phantom Type
Introduces two Phantom Types, Fresh and Streaming, which indicate the status of a Response. Response::start translates an Response<Fresh> into a Response<Streaming> by writing the StatusCode and Headers. Response<Fresh> allows modification of Headers and StatusCode, but does not allow writing to the body. Response<Streaming> has the opposite privileges.
This commit is contained in:
		| @@ -36,8 +36,13 @@ impl Handler for Echo { | |||||||
|                     }, |                     }, | ||||||
|                     (&Post, "/echo") => (), // fall through, fighting mutable borrows |                     (&Post, "/echo") => (), // fall through, fighting mutable borrows | ||||||
|                     _ => { |                     _ => { | ||||||
|  | <<<<<<< Updated upstream | ||||||
|                         res.status = hyper::status::NotFound; |                         res.status = hyper::status::NotFound; | ||||||
|                         try_continue!(res.end()); |                         try_continue!(res.end()); | ||||||
|  | ======= | ||||||
|  |                         *res.status_mut() = hyper::status::NotFound; | ||||||
|  |                         try_continue!(res.start().and_then(|res| res.end())); | ||||||
|  | >>>>>>> Stashed changes | ||||||
|                         continue; |                         continue; | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ use std::io::{Acceptor, Listener, IoResult, EndOfFile, IncomingConnections}; | |||||||
| use std::io::net::ip::{IpAddr, Port, SocketAddr}; | use std::io::net::ip::{IpAddr, Port, SocketAddr}; | ||||||
|  |  | ||||||
| pub use self::request::Request; | pub use self::request::Request; | ||||||
| pub use self::response::Response; | pub use self::response::{Response, Fresh, Streaming}; | ||||||
|  |  | ||||||
| pub mod request; | pub mod request; | ||||||
| pub mod response; | pub mod response; | ||||||
| @@ -55,8 +55,8 @@ pub struct Incoming<'a> { | |||||||
|     from: IncomingConnections<'a, TcpAcceptor> |     from: IncomingConnections<'a, TcpAcceptor> | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<'a> Iterator<(Request, Response)> for Incoming<'a> { | impl<'a> Iterator<(Request, Response<Fresh>)> for Incoming<'a> { | ||||||
|     fn next(&mut self) -> Option<(Request, Response)> { |     fn next(&mut self) -> Option<(Request, Response<Fresh>)> { | ||||||
|         for conn in self.from { |         for conn in self.from { | ||||||
|             match conn { |             match conn { | ||||||
|                 Ok(stream) => { |                 Ok(stream) => { | ||||||
|   | |||||||
| @@ -12,39 +12,52 @@ use status; | |||||||
| use version; | use version; | ||||||
| use rfc7230::{CR, LF, LINE_ENDING}; | 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`. | /// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`. | ||||||
| pub struct Response { | pub struct Response<W: WriteStatus> { | ||||||
|     /// The status code for the request. |  | ||||||
|     pub status: status::StatusCode, |  | ||||||
|     /// The outgoing headers on this response. |  | ||||||
|     pub headers: header::Headers, |  | ||||||
|     /// The HTTP version of this response. |     /// The HTTP version of this response. | ||||||
|     pub version: version::HttpVersion, |     pub version: version::HttpVersion, | ||||||
|  |     // Stream the Response is writing to, not accessible through UnwrittenResponse | ||||||
|     headers_written: bool, // TODO: can this check be moved to compile time? |  | ||||||
|     body: BufferedWriter<TcpStream>, // TODO: use a HttpWriter from rfc7230 |     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. |     /// 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 { |         Response { | ||||||
|             status: status::Ok, |             status: status::Ok, | ||||||
|             version: version::Http11, |             version: version::Http11, | ||||||
|             headers: header::Headers::new(), |             headers: header::Headers::new(), | ||||||
|             headers_written: false, |  | ||||||
|             body: BufferedWriter::new(tcp) |             body: BufferedWriter::new(tcp) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn write_head(&mut self) -> IoResult<()> { |     /// Consume this Response<Fresh>, writing the Headers and Status and creating a Response<Streaming> | ||||||
|         if self.headers_written { |     pub fn start(mut self) -> IoResult<Response<Streaming>> { | ||||||
|             debug!("headers previously written, nooping"); |  | ||||||
|             return Ok(()); |  | ||||||
|         } |  | ||||||
|         self.headers_written = true; |  | ||||||
|         debug!("writing head: {} {}", self.version, self.status); |         debug!("writing head: {} {}", self.version, self.status); | ||||||
|         try!(write!(self.body, "{} {}{}{}", self.version, self.status, CR as char, LF as char)); |         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)); |             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. |     /// Flushes all writing of a response to the client. | ||||||
|     pub fn end(mut self) -> IoResult<()> { |     pub fn end(mut self) -> IoResult<()> { | ||||||
|         debug!("ending"); |         debug!("ending"); | ||||||
| @@ -68,20 +98,14 @@ impl Response { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl Writer for Response<Streaming> { | ||||||
| impl Writer for Response { |  | ||||||
|     fn write(&mut self, msg: &[u8]) -> IoResult<()> { |     fn write(&mut self, msg: &[u8]) -> IoResult<()> { | ||||||
|         if !self.headers_written { |  | ||||||
|             try!(self.write_head()); |  | ||||||
|         } |  | ||||||
|         debug!("write {:u} bytes", msg.len()); |         debug!("write {:u} bytes", msg.len()); | ||||||
|         self.body.write(msg) |         self.body.write(msg) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn flush(&mut self) -> IoResult<()> { |     fn flush(&mut self) -> IoResult<()> { | ||||||
|         if !self.headers_written { |  | ||||||
|             try!(self.write_head()); |  | ||||||
|         } |  | ||||||
|         self.body.flush() |         self.body.flush() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user