refactor(http): move h1 and h2 into http module
This commit is contained in:
		| @@ -8,7 +8,7 @@ use std::io; | |||||||
|  |  | ||||||
| use hyper::Client; | use hyper::Client; | ||||||
| use hyper::header::Connection; | use hyper::header::Connection; | ||||||
| use hyper::http2; | use hyper::http::h2; | ||||||
|  |  | ||||||
| fn main() { | fn main() { | ||||||
|     env_logger::init().unwrap(); |     env_logger::init().unwrap(); | ||||||
| @@ -21,7 +21,7 @@ fn main() { | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let mut client = Client::with_protocol(http2::new_protocol()); |     let mut client = Client::with_protocol(h2::new_protocol()); | ||||||
|  |  | ||||||
|     // `Connection: Close` is not a valid header for HTTP/2, but the client handles it gracefully. |     // `Connection: Close` is not a valid header for HTTP/2, but the client handles it gracefully. | ||||||
|     let mut res = client.get(&*url) |     let mut res = client.get(&*url) | ||||||
|   | |||||||
| @@ -54,8 +54,8 @@ pub mod pool; | |||||||
| pub mod request; | pub mod request; | ||||||
| pub mod response; | pub mod response; | ||||||
|  |  | ||||||
| use message::Protocol; | use http::Protocol; | ||||||
| use http11::Http11Protocol; | use http::h1::Http11Protocol; | ||||||
|  |  | ||||||
| /// A Client to use additional features with Requests. | /// A Client to use additional features with Requests. | ||||||
| /// | /// | ||||||
|   | |||||||
| @@ -11,8 +11,8 @@ use net::{NetworkStream, NetworkConnector, HttpConnector, Fresh, Streaming}; | |||||||
| use version; | use version; | ||||||
| use client::{Response, get_host_and_port}; | use client::{Response, get_host_and_port}; | ||||||
|  |  | ||||||
| use message::{HttpMessage, RequestHead}; | use http::{HttpMessage, RequestHead}; | ||||||
| use http11::Http11Message; | use http::h1::Http11Message; | ||||||
|  |  | ||||||
|  |  | ||||||
| /// A client request to a remote server. | /// A client request to a remote server. | ||||||
| @@ -136,7 +136,7 @@ mod tests { | |||||||
|     use header::{ContentLength,TransferEncoding,Encoding}; |     use header::{ContentLength,TransferEncoding,Encoding}; | ||||||
|     use url::form_urlencoded; |     use url::form_urlencoded; | ||||||
|     use super::Request; |     use super::Request; | ||||||
|     use http11::Http11Message; |     use http::h1::Http11Message; | ||||||
|  |  | ||||||
|     fn run_request(req: Request<Fresh>) -> Vec<u8> { |     fn run_request(req: Request<Fresh>) -> Vec<u8> { | ||||||
|         let req = req.start().unwrap(); |         let req = req.start().unwrap(); | ||||||
|   | |||||||
| @@ -3,11 +3,10 @@ use std::io::{self, Read}; | |||||||
|  |  | ||||||
| use header; | use header; | ||||||
| use net::NetworkStream; | use net::NetworkStream; | ||||||
| use http::{self, RawStatus}; | use http::{self, RawStatus, ResponseHead, HttpMessage}; | ||||||
| use status; | use status; | ||||||
| use version; | use version; | ||||||
| use message::{ResponseHead, HttpMessage}; | use http::h1::Http11Message; | ||||||
| use http11::Http11Message; |  | ||||||
|  |  | ||||||
| /// A response for a client request to a remote server. | /// A response for a client request to a remote server. | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| @@ -82,7 +81,7 @@ mod tests { | |||||||
|     use mock::MockStream; |     use mock::MockStream; | ||||||
|     use status; |     use status; | ||||||
|     use version; |     use version; | ||||||
|     use http11::Http11Message; |     use http::h1::Http11Message; | ||||||
|  |  | ||||||
|     use super::Response; |     use super::Response; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,22 +1,318 @@ | |||||||
| //! Pieces pertaining to the HTTP message protocol.
 | //! Adapts the HTTP/1.1 implementation into the `HttpMessage` API.
 | ||||||
| use std::borrow::{Cow, ToOwned}; | use std::borrow::Cow; | ||||||
| use std::cmp::min; | use std::cmp::min; | ||||||
| use std::io::{self, Read, Write, BufRead}; |  | ||||||
| use std::fmt; | use std::fmt; | ||||||
|  | use std::io::{self, Write, BufWriter, BufRead, Read}; | ||||||
|  | use std::net::Shutdown; | ||||||
| 
 | 
 | ||||||
| use httparse; | use httparse; | ||||||
| 
 | 
 | ||||||
| use buffer::BufReader; | use buffer::BufReader; | ||||||
| use header::{Headers, Connection}; | use Error; | ||||||
| use header::ConnectionOption::{Close, KeepAlive}; | use header::{Headers, ContentLength, TransferEncoding}; | ||||||
| use method::Method; | use header::Encoding::Chunked; | ||||||
|  | use method::{Method}; | ||||||
|  | use net::{NetworkConnector, NetworkStream, ContextVerifier}; | ||||||
| use status::StatusCode; | use status::StatusCode; | ||||||
|  | use version::HttpVersion; | ||||||
|  | use version::HttpVersion::{Http10, Http11}; | ||||||
| use uri::RequestUri; | use uri::RequestUri; | ||||||
| use version::HttpVersion::{self, Http10, Http11}; |  | ||||||
| use {Error}; |  | ||||||
| 
 | 
 | ||||||
| use self::HttpReader::{SizedReader, ChunkedReader, EofReader, EmptyReader}; | use self::HttpReader::{SizedReader, ChunkedReader, EofReader, EmptyReader}; | ||||||
| use self::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter, EmptyWriter}; | use self::HttpWriter::{ChunkedWriter, SizedWriter, EmptyWriter, ThroughWriter}; | ||||||
|  | 
 | ||||||
|  | use http::{ | ||||||
|  |     RawStatus, | ||||||
|  |     Protocol, | ||||||
|  |     HttpMessage, | ||||||
|  |     RequestHead, | ||||||
|  |     ResponseHead, | ||||||
|  | }; | ||||||
|  | use header; | ||||||
|  | use version; | ||||||
|  | 
 | ||||||
|  | /// An implementation of the `HttpMessage` trait for HTTP/1.1.
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct Http11Message { | ||||||
|  |     stream: Option<Box<NetworkStream + Send>>, | ||||||
|  |     writer: Option<HttpWriter<BufWriter<Box<NetworkStream + Send>>>>, | ||||||
|  |     reader: Option<HttpReader<BufReader<Box<NetworkStream + Send>>>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Write for Http11Message { | ||||||
|  |     #[inline] | ||||||
|  |     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | ||||||
|  |         match self.writer { | ||||||
|  |             None => Err(io::Error::new(io::ErrorKind::Other, | ||||||
|  |                                           "Not in a writable state")), | ||||||
|  |             Some(ref mut writer) => writer.write(buf), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     #[inline] | ||||||
|  |     fn flush(&mut self) -> io::Result<()> { | ||||||
|  |         match self.writer { | ||||||
|  |             None => Err(io::Error::new(io::ErrorKind::Other, | ||||||
|  |                                           "Not in a writable state")), | ||||||
|  |             Some(ref mut writer) => writer.flush(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Read for Http11Message { | ||||||
|  |     #[inline] | ||||||
|  |     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||||
|  |         match self.reader { | ||||||
|  |             None => Err(io::Error::new(io::ErrorKind::Other, | ||||||
|  |                                           "Not in a readable state")), | ||||||
|  |             Some(ref mut reader) => reader.read(buf), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl HttpMessage for Http11Message { | ||||||
|  |     fn set_outgoing(&mut self, mut head: RequestHead) -> ::Result<RequestHead> { | ||||||
|  |         if self.stream.is_none() { | ||||||
|  |             return Err(From::from(io::Error::new( | ||||||
|  |                         io::ErrorKind::Other, | ||||||
|  |                         "Message not idle, cannot start new outgoing"))); | ||||||
|  |         } | ||||||
|  |         let mut stream = BufWriter::new(self.stream.take().unwrap()); | ||||||
|  | 
 | ||||||
|  |         let mut uri = head.url.serialize_path().unwrap(); | ||||||
|  |         if let Some(ref q) = head.url.query { | ||||||
|  |             uri.push('?'); | ||||||
|  |             uri.push_str(&q[..]); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let version = version::HttpVersion::Http11; | ||||||
|  |         debug!("request line: {:?} {:?} {:?}", head.method, uri, version); | ||||||
|  |         try!(write!(&mut stream, "{} {} {}{}", | ||||||
|  |                     head.method, uri, version, LINE_ENDING)); | ||||||
|  | 
 | ||||||
|  |         let stream = match head.method { | ||||||
|  |             Method::Get | Method::Head => { | ||||||
|  |                 debug!("headers={:?}", head.headers); | ||||||
|  |                 try!(write!(&mut stream, "{}{}", head.headers, LINE_ENDING)); | ||||||
|  |                 EmptyWriter(stream) | ||||||
|  |             }, | ||||||
|  |             _ => { | ||||||
|  |                 let mut chunked = true; | ||||||
|  |                 let mut len = 0; | ||||||
|  | 
 | ||||||
|  |                 match head.headers.get::<header::ContentLength>() { | ||||||
|  |                     Some(cl) => { | ||||||
|  |                         chunked = false; | ||||||
|  |                         len = **cl; | ||||||
|  |                     }, | ||||||
|  |                     None => () | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |                 // can't do in match above, thanks borrowck
 | ||||||
|  |                 if chunked { | ||||||
|  |                     let encodings = match head.headers.get_mut::<header::TransferEncoding>() { | ||||||
|  |                         Some(&mut header::TransferEncoding(ref mut encodings)) => { | ||||||
|  |                             //TODO: check if chunked is already in encodings. use HashSet?
 | ||||||
|  |                             encodings.push(header::Encoding::Chunked); | ||||||
|  |                             false | ||||||
|  |                         }, | ||||||
|  |                         None => true | ||||||
|  |                     }; | ||||||
|  | 
 | ||||||
|  |                     if encodings { | ||||||
|  |                         head.headers.set::<header::TransferEncoding>( | ||||||
|  |                             header::TransferEncoding(vec![header::Encoding::Chunked])) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 debug!("headers={:?}", head.headers); | ||||||
|  |                 try!(write!(&mut stream, "{}{}", head.headers, LINE_ENDING)); | ||||||
|  | 
 | ||||||
|  |                 if chunked { | ||||||
|  |                     ChunkedWriter(stream) | ||||||
|  |                 } else { | ||||||
|  |                     SizedWriter(stream, len) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         self.writer = Some(stream); | ||||||
|  | 
 | ||||||
|  |         Ok(head) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_incoming(&mut self) -> ::Result<ResponseHead> { | ||||||
|  |         try!(self.flush_outgoing()); | ||||||
|  |         if self.stream.is_none() { | ||||||
|  |             // The message was already in the reading state...
 | ||||||
|  |             // TODO Decide what happens in case we try to get a new incoming at that point
 | ||||||
|  |             return Err(From::from( | ||||||
|  |                     io::Error::new(io::ErrorKind::Other, | ||||||
|  |                     "Read already in progress"))); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let stream = self.stream.take().unwrap(); | ||||||
|  |         let mut stream = BufReader::new(stream); | ||||||
|  | 
 | ||||||
|  |         let head = try!(parse_response(&mut stream)); | ||||||
|  |         let raw_status = head.subject; | ||||||
|  |         let headers = head.headers; | ||||||
|  | 
 | ||||||
|  |         let body = if headers.has::<TransferEncoding>() { | ||||||
|  |             match headers.get::<TransferEncoding>() { | ||||||
|  |                 Some(&TransferEncoding(ref codings)) => { | ||||||
|  |                     if codings.len() > 1 { | ||||||
|  |                         trace!("TODO: #2 handle other codings: {:?}", codings); | ||||||
|  |                     }; | ||||||
|  | 
 | ||||||
|  |                     if codings.contains(&Chunked) { | ||||||
|  |                         ChunkedReader(stream, None) | ||||||
|  |                     } else { | ||||||
|  |                         trace!("not chuncked. read till eof"); | ||||||
|  |                         EofReader(stream) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 None => unreachable!() | ||||||
|  |             } | ||||||
|  |         } else if headers.has::<ContentLength>() { | ||||||
|  |             match headers.get::<ContentLength>() { | ||||||
|  |                 Some(&ContentLength(len)) => SizedReader(stream, len), | ||||||
|  |                 None => unreachable!() | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             trace!("neither Transfer-Encoding nor Content-Length"); | ||||||
|  |             EofReader(stream) | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         self.reader = Some(body); | ||||||
|  | 
 | ||||||
|  |         Ok(ResponseHead { | ||||||
|  |             headers: headers, | ||||||
|  |             raw_status: raw_status, | ||||||
|  |             version: head.version, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn close_connection(&mut self) -> ::Result<()> { | ||||||
|  |         try!(self.get_mut().close(Shutdown::Both)); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Http11Message { | ||||||
|  |     /// Consumes the `Http11Message` and returns the underlying `NetworkStream`.
 | ||||||
|  |     pub fn into_inner(mut self) -> Box<NetworkStream + Send> { | ||||||
|  |         if self.stream.is_some() { | ||||||
|  |             self.stream.take().unwrap() | ||||||
|  |         } else if self.writer.is_some() { | ||||||
|  |             self.writer.take().unwrap().into_inner().into_inner().unwrap() | ||||||
|  |         } else if self.reader.is_some() { | ||||||
|  |             self.reader.take().unwrap().into_inner().into_inner() | ||||||
|  |         } else { | ||||||
|  |             panic!("Http11Message lost its underlying stream somehow"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Gets a mutable reference to the underlying `NetworkStream`, regardless of the state of the
 | ||||||
|  |     /// `Http11Message`.
 | ||||||
|  |     pub fn get_mut(&mut self) -> &mut Box<NetworkStream + Send> { | ||||||
|  |         if self.stream.is_some() { | ||||||
|  |             self.stream.as_mut().unwrap() | ||||||
|  |         } else if self.writer.is_some() { | ||||||
|  |             self.writer.as_mut().unwrap().get_mut().get_mut() | ||||||
|  |         } else if self.reader.is_some() { | ||||||
|  |             self.reader.as_mut().unwrap().get_mut().get_mut() | ||||||
|  |         } else { | ||||||
|  |             panic!("Http11Message lost its underlying stream somehow"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Creates a new `Http11Message` that will use the given `NetworkStream` for communicating to
 | ||||||
|  |     /// the peer.
 | ||||||
|  |     pub fn with_stream(stream: Box<NetworkStream + Send>) -> Http11Message { | ||||||
|  |         Http11Message { | ||||||
|  |             stream: Some(stream), | ||||||
|  |             writer: None, | ||||||
|  |             reader: None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Flushes the current outgoing content and moves the stream into the `stream` property.
 | ||||||
|  |     ///
 | ||||||
|  |     /// TODO It might be sensible to lift this up to the `HttpMessage` trait itself...
 | ||||||
|  |     pub fn flush_outgoing(&mut self) -> ::Result<()> { | ||||||
|  |         match self.writer { | ||||||
|  |             None => return Ok(()), | ||||||
|  |             Some(_) => {}, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         let writer = self.writer.take().unwrap(); | ||||||
|  |         let raw = try!(writer.end()).into_inner().unwrap(); // end() already flushes
 | ||||||
|  |         self.stream = Some(raw); | ||||||
|  | 
 | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// The `Protocol` implementation provides HTTP/1.1 messages.
 | ||||||
|  | pub struct Http11Protocol { | ||||||
|  |     connector: Connector, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Protocol for Http11Protocol { | ||||||
|  |     fn new_message(&self, host: &str, port: u16, scheme: &str) -> ::Result<Box<HttpMessage>> { | ||||||
|  |         let stream = try!(self.connector.connect(host, port, scheme)).into(); | ||||||
|  | 
 | ||||||
|  |         Ok(Box::new(Http11Message::with_stream(stream))) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[inline] | ||||||
|  |     fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { | ||||||
|  |         self.connector.set_ssl_verifier(verifier); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Http11Protocol { | ||||||
|  |     /// Creates a new `Http11Protocol` instance that will use the given `NetworkConnector` for
 | ||||||
|  |     /// establishing HTTP connections.
 | ||||||
|  |     pub fn with_connector<C, S>(c: C) -> Http11Protocol | ||||||
|  |             where C: NetworkConnector<Stream=S> + Send + 'static, | ||||||
|  |                   S: NetworkStream + Send { | ||||||
|  |         Http11Protocol { | ||||||
|  |             connector: Connector(Box::new(ConnAdapter(c))), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct ConnAdapter<C: NetworkConnector + Send>(C); | ||||||
|  | 
 | ||||||
|  | impl<C: NetworkConnector<Stream=S> + Send, S: NetworkStream + Send> NetworkConnector for ConnAdapter<C> { | ||||||
|  |     type Stream = Box<NetworkStream + Send>; | ||||||
|  |     #[inline] | ||||||
|  |     fn connect(&self, host: &str, port: u16, scheme: &str) | ||||||
|  |         -> ::Result<Box<NetworkStream + Send>> { | ||||||
|  |         Ok(try!(self.0.connect(host, port, scheme)).into()) | ||||||
|  |     } | ||||||
|  |     #[inline] | ||||||
|  |     fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { | ||||||
|  |         self.0.set_ssl_verifier(verifier); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct Connector(Box<NetworkConnector<Stream=Box<NetworkStream + Send>> + Send>); | ||||||
|  | 
 | ||||||
|  | impl NetworkConnector for Connector { | ||||||
|  |     type Stream = Box<NetworkStream + Send>; | ||||||
|  |     #[inline] | ||||||
|  |     fn connect(&self, host: &str, port: u16, scheme: &str) | ||||||
|  |         -> ::Result<Box<NetworkStream + Send>> { | ||||||
|  |         Ok(try!(self.0.connect(host, port, scheme)).into()) | ||||||
|  |     } | ||||||
|  |     #[inline] | ||||||
|  |     fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { | ||||||
|  |         self.0.set_ssl_verifier(verifier); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| /// Readers to handle different Transfer-Encodings.
 | /// Readers to handle different Transfer-Encodings.
 | ||||||
| ///
 | ///
 | ||||||
| @@ -451,19 +747,6 @@ pub const LF: u8 = b'\n'; | |||||||
| pub const STAR: u8 = b'*'; | pub const STAR: u8 = b'*'; | ||||||
| pub const LINE_ENDING: &'static str = "\r\n"; | pub const LINE_ENDING: &'static str = "\r\n"; | ||||||
| 
 | 
 | ||||||
| /// The raw status code and reason-phrase.
 |  | ||||||
| #[derive(Clone, PartialEq, Debug)] |  | ||||||
| pub struct RawStatus(pub u16, pub Cow<'static, str>); |  | ||||||
| 
 |  | ||||||
| /// Checks if a connection should be kept alive.
 |  | ||||||
| pub fn should_keep_alive(version: HttpVersion, headers: &Headers) -> bool { |  | ||||||
|     match (version, headers.get::<Connection>()) { |  | ||||||
|         (Http10, Some(conn)) if !conn.contains(&KeepAlive) => false, |  | ||||||
|         (Http11, Some(conn)) if conn.contains(&Close)  => false, |  | ||||||
|         _ => true |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use std::io::{self, Write}; |     use std::io::{self, Write}; | ||||||
| @@ -5,16 +5,16 @@ use std::net::Shutdown; | |||||||
| use std::ascii::AsciiExt; | use std::ascii::AsciiExt; | ||||||
| use std::mem; | use std::mem; | ||||||
| 
 | 
 | ||||||
| use message::{ | use http::{ | ||||||
|     Protocol, |     Protocol, | ||||||
|     HttpMessage, |     HttpMessage, | ||||||
|     RequestHead, |     RequestHead, | ||||||
|     ResponseHead, |     ResponseHead, | ||||||
|  |     RawStatus, | ||||||
| }; | }; | ||||||
| use net::{NetworkStream, NetworkConnector, ContextVerifier}; | use net::{NetworkStream, NetworkConnector, ContextVerifier}; | ||||||
| use net::{HttpConnector, HttpStream}; | use net::{HttpConnector, HttpStream}; | ||||||
| use url::Url; | use url::Url; | ||||||
| use http::RawStatus; |  | ||||||
| use header::Headers; | use header::Headers; | ||||||
| 
 | 
 | ||||||
| use header; | use header; | ||||||
| @@ -397,7 +397,7 @@ mod tests { | |||||||
|     use std::io::{Read}; |     use std::io::{Read}; | ||||||
| 
 | 
 | ||||||
|     use mock::{MockHttp2Connector, MockStream}; |     use mock::{MockHttp2Connector, MockStream}; | ||||||
|     use message::{RequestHead, ResponseHead, Protocol}; |     use http::{RequestHead, ResponseHead, Protocol}; | ||||||
| 
 | 
 | ||||||
|     use header::Headers; |     use header::Headers; | ||||||
|     use header; |     use header; | ||||||
							
								
								
									
										28
									
								
								src/http/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/http/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | //! Pieces pertaining to the HTTP message protocol. | ||||||
|  | use std::borrow::Cow; | ||||||
|  |  | ||||||
|  | use header::Connection; | ||||||
|  | use header::ConnectionOption::{KeepAlive, Close}; | ||||||
|  | use header::Headers; | ||||||
|  | use version::HttpVersion; | ||||||
|  | use version::HttpVersion::{Http10, Http11}; | ||||||
|  |  | ||||||
|  | pub use self::message::{HttpMessage, RequestHead, ResponseHead, Protocol}; | ||||||
|  |  | ||||||
|  | pub mod h1; | ||||||
|  | pub mod h2; | ||||||
|  | pub mod message; | ||||||
|  |  | ||||||
|  | /// The raw status code and reason-phrase. | ||||||
|  | #[derive(Clone, PartialEq, Debug)] | ||||||
|  | pub struct RawStatus(pub u16, pub Cow<'static, str>); | ||||||
|  |  | ||||||
|  | /// Checks if a connection should be kept alive. | ||||||
|  | #[inline] | ||||||
|  | pub fn should_keep_alive(version: HttpVersion, headers: &Headers) -> bool { | ||||||
|  |     match (version, headers.get::<Connection>()) { | ||||||
|  |         (Http10, Some(conn)) if !conn.contains(&KeepAlive) => false, | ||||||
|  |         (Http11, Some(conn)) if conn.contains(&Close)  => false, | ||||||
|  |         _ => true | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										305
									
								
								src/http11.rs
									
									
									
									
									
								
							
							
						
						
									
										305
									
								
								src/http11.rs
									
									
									
									
									
								
							| @@ -1,305 +0,0 @@ | |||||||
| //! Adapts the HTTP/1.1 implementation into the `HttpMessage` API. |  | ||||||
| use std::io::{self, Write, BufWriter, Read}; |  | ||||||
| use std::net::Shutdown; |  | ||||||
|  |  | ||||||
| use method::{Method}; |  | ||||||
| use header::{ContentLength, TransferEncoding}; |  | ||||||
| use header::Encoding::Chunked; |  | ||||||
|  |  | ||||||
| use net::{NetworkConnector, NetworkStream, ContextVerifier}; |  | ||||||
| use http::{HttpWriter, LINE_ENDING}; |  | ||||||
| use http::HttpReader::{SizedReader, ChunkedReader, EofReader}; |  | ||||||
| use http::HttpWriter::{ChunkedWriter, SizedWriter, EmptyWriter}; |  | ||||||
| use buffer::BufReader; |  | ||||||
| use http::{self, HttpReader}; |  | ||||||
|  |  | ||||||
| use message::{ |  | ||||||
|     Protocol, |  | ||||||
|     HttpMessage, |  | ||||||
|     RequestHead, |  | ||||||
|     ResponseHead, |  | ||||||
| }; |  | ||||||
| use header; |  | ||||||
| use version; |  | ||||||
|  |  | ||||||
| /// An implementation of the `HttpMessage` trait for HTTP/1.1. |  | ||||||
| #[derive(Debug)] |  | ||||||
| pub struct Http11Message { |  | ||||||
|     stream: Option<Box<NetworkStream + Send>>, |  | ||||||
|     writer: Option<HttpWriter<BufWriter<Box<NetworkStream + Send>>>>, |  | ||||||
|     reader: Option<HttpReader<BufReader<Box<NetworkStream + Send>>>>, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Write for Http11Message { |  | ||||||
|     #[inline] |  | ||||||
|     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |  | ||||||
|         match self.writer { |  | ||||||
|             None => Err(io::Error::new(io::ErrorKind::Other, |  | ||||||
|                                           "Not in a writable state")), |  | ||||||
|             Some(ref mut writer) => writer.write(buf), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     #[inline] |  | ||||||
|     fn flush(&mut self) -> io::Result<()> { |  | ||||||
|         match self.writer { |  | ||||||
|             None => Err(io::Error::new(io::ErrorKind::Other, |  | ||||||
|                                           "Not in a writable state")), |  | ||||||
|             Some(ref mut writer) => writer.flush(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Read for Http11Message { |  | ||||||
|     #[inline] |  | ||||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |  | ||||||
|         match self.reader { |  | ||||||
|             None => Err(io::Error::new(io::ErrorKind::Other, |  | ||||||
|                                           "Not in a readable state")), |  | ||||||
|             Some(ref mut reader) => reader.read(buf), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl HttpMessage for Http11Message { |  | ||||||
|     fn set_outgoing(&mut self, mut head: RequestHead) -> ::Result<RequestHead> { |  | ||||||
|         if self.stream.is_none() { |  | ||||||
|             return Err(From::from(io::Error::new( |  | ||||||
|                         io::ErrorKind::Other, |  | ||||||
|                         "Message not idle, cannot start new outgoing"))); |  | ||||||
|         } |  | ||||||
|         let mut stream = BufWriter::new(self.stream.take().unwrap()); |  | ||||||
|  |  | ||||||
|         let mut uri = head.url.serialize_path().unwrap(); |  | ||||||
|         if let Some(ref q) = head.url.query { |  | ||||||
|             uri.push('?'); |  | ||||||
|             uri.push_str(&q[..]); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         let version = version::HttpVersion::Http11; |  | ||||||
|         debug!("request line: {:?} {:?} {:?}", head.method, uri, version); |  | ||||||
|         try!(write!(&mut stream, "{} {} {}{}", |  | ||||||
|                     head.method, uri, version, LINE_ENDING)); |  | ||||||
|  |  | ||||||
|         let stream = match head.method { |  | ||||||
|             Method::Get | Method::Head => { |  | ||||||
|                 debug!("headers={:?}", head.headers); |  | ||||||
|                 try!(write!(&mut stream, "{}{}", head.headers, LINE_ENDING)); |  | ||||||
|                 EmptyWriter(stream) |  | ||||||
|             }, |  | ||||||
|             _ => { |  | ||||||
|                 let mut chunked = true; |  | ||||||
|                 let mut len = 0; |  | ||||||
|  |  | ||||||
|                 match head.headers.get::<header::ContentLength>() { |  | ||||||
|                     Some(cl) => { |  | ||||||
|                         chunked = false; |  | ||||||
|                         len = **cl; |  | ||||||
|                     }, |  | ||||||
|                     None => () |  | ||||||
|                 }; |  | ||||||
|  |  | ||||||
|                 // can't do in match above, thanks borrowck |  | ||||||
|                 if chunked { |  | ||||||
|                     let encodings = match head.headers.get_mut::<header::TransferEncoding>() { |  | ||||||
|                         Some(&mut header::TransferEncoding(ref mut encodings)) => { |  | ||||||
|                             //TODO: check if chunked is already in encodings. use HashSet? |  | ||||||
|                             encodings.push(header::Encoding::Chunked); |  | ||||||
|                             false |  | ||||||
|                         }, |  | ||||||
|                         None => true |  | ||||||
|                     }; |  | ||||||
|  |  | ||||||
|                     if encodings { |  | ||||||
|                         head.headers.set::<header::TransferEncoding>( |  | ||||||
|                             header::TransferEncoding(vec![header::Encoding::Chunked])) |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 debug!("headers={:?}", head.headers); |  | ||||||
|                 try!(write!(&mut stream, "{}{}", head.headers, LINE_ENDING)); |  | ||||||
|  |  | ||||||
|                 if chunked { |  | ||||||
|                     ChunkedWriter(stream) |  | ||||||
|                 } else { |  | ||||||
|                     SizedWriter(stream, len) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         self.writer = Some(stream); |  | ||||||
|  |  | ||||||
|         Ok(head) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn get_incoming(&mut self) -> ::Result<ResponseHead> { |  | ||||||
|         try!(self.flush_outgoing()); |  | ||||||
|         if self.stream.is_none() { |  | ||||||
|             // The message was already in the reading state... |  | ||||||
|             // TODO Decide what happens in case we try to get a new incoming at that point |  | ||||||
|             return Err(From::from( |  | ||||||
|                     io::Error::new(io::ErrorKind::Other, |  | ||||||
|                     "Read already in progress"))); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         let stream = self.stream.take().unwrap(); |  | ||||||
|         let mut stream = BufReader::new(stream); |  | ||||||
|  |  | ||||||
|         let head = try!(http::parse_response(&mut stream)); |  | ||||||
|         let raw_status = head.subject; |  | ||||||
|         let headers = head.headers; |  | ||||||
|  |  | ||||||
|         let body = if headers.has::<TransferEncoding>() { |  | ||||||
|             match headers.get::<TransferEncoding>() { |  | ||||||
|                 Some(&TransferEncoding(ref codings)) => { |  | ||||||
|                     if codings.len() > 1 { |  | ||||||
|                         trace!("TODO: #2 handle other codings: {:?}", codings); |  | ||||||
|                     }; |  | ||||||
|  |  | ||||||
|                     if codings.contains(&Chunked) { |  | ||||||
|                         ChunkedReader(stream, None) |  | ||||||
|                     } else { |  | ||||||
|                         trace!("not chuncked. read till eof"); |  | ||||||
|                         EofReader(stream) |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 None => unreachable!() |  | ||||||
|             } |  | ||||||
|         } else if headers.has::<ContentLength>() { |  | ||||||
|             match headers.get::<ContentLength>() { |  | ||||||
|                 Some(&ContentLength(len)) => SizedReader(stream, len), |  | ||||||
|                 None => unreachable!() |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             trace!("neither Transfer-Encoding nor Content-Length"); |  | ||||||
|             EofReader(stream) |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         self.reader = Some(body); |  | ||||||
|  |  | ||||||
|         Ok(ResponseHead { |  | ||||||
|             headers: headers, |  | ||||||
|             raw_status: raw_status, |  | ||||||
|             version: head.version, |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn close_connection(&mut self) -> ::Result<()> { |  | ||||||
|         try!(self.get_mut().close(Shutdown::Both)); |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Http11Message { |  | ||||||
|     /// Consumes the `Http11Message` and returns the underlying `NetworkStream`. |  | ||||||
|     pub fn into_inner(mut self) -> Box<NetworkStream + Send> { |  | ||||||
|         if self.stream.is_some() { |  | ||||||
|             self.stream.take().unwrap() |  | ||||||
|         } else if self.writer.is_some() { |  | ||||||
|             self.writer.take().unwrap().into_inner().into_inner().unwrap() |  | ||||||
|         } else if self.reader.is_some() { |  | ||||||
|             self.reader.take().unwrap().into_inner().into_inner() |  | ||||||
|         } else { |  | ||||||
|             panic!("Http11Message lost its underlying stream somehow"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Gets a mutable reference to the underlying `NetworkStream`, regardless of the state of the |  | ||||||
|     /// `Http11Message`. |  | ||||||
|     pub fn get_mut(&mut self) -> &mut Box<NetworkStream + Send> { |  | ||||||
|         if self.stream.is_some() { |  | ||||||
|             self.stream.as_mut().unwrap() |  | ||||||
|         } else if self.writer.is_some() { |  | ||||||
|             self.writer.as_mut().unwrap().get_mut().get_mut() |  | ||||||
|         } else if self.reader.is_some() { |  | ||||||
|             self.reader.as_mut().unwrap().get_mut().get_mut() |  | ||||||
|         } else { |  | ||||||
|             panic!("Http11Message lost its underlying stream somehow"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Creates a new `Http11Message` that will use the given `NetworkStream` for communicating to |  | ||||||
|     /// the peer. |  | ||||||
|     pub fn with_stream(stream: Box<NetworkStream + Send>) -> Http11Message { |  | ||||||
|         Http11Message { |  | ||||||
|             stream: Some(stream), |  | ||||||
|             writer: None, |  | ||||||
|             reader: None, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Flushes the current outgoing content and moves the stream into the `stream` property. |  | ||||||
|     /// |  | ||||||
|     /// TODO It might be sensible to lift this up to the `HttpMessage` trait itself... |  | ||||||
|     pub fn flush_outgoing(&mut self) -> ::Result<()> { |  | ||||||
|         match self.writer { |  | ||||||
|             None => return Ok(()), |  | ||||||
|             Some(_) => {}, |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         let writer = self.writer.take().unwrap(); |  | ||||||
|         let raw = try!(writer.end()).into_inner().unwrap(); // end() already flushes |  | ||||||
|         self.stream = Some(raw); |  | ||||||
|  |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// The `Protocol` implementation provides HTTP/1.1 messages. |  | ||||||
| pub struct Http11Protocol { |  | ||||||
|     connector: Connector, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Protocol for Http11Protocol { |  | ||||||
|     fn new_message(&self, host: &str, port: u16, scheme: &str) -> ::Result<Box<HttpMessage>> { |  | ||||||
|         let stream = try!(self.connector.connect(host, port, scheme)).into(); |  | ||||||
|  |  | ||||||
|         Ok(Box::new(Http11Message::with_stream(stream))) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     #[inline] |  | ||||||
|     fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { |  | ||||||
|         self.connector.set_ssl_verifier(verifier); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Http11Protocol { |  | ||||||
|     /// Creates a new `Http11Protocol` instance that will use the given `NetworkConnector` for |  | ||||||
|     /// establishing HTTP connections. |  | ||||||
|     pub fn with_connector<C, S>(c: C) -> Http11Protocol |  | ||||||
|             where C: NetworkConnector<Stream=S> + Send + 'static, |  | ||||||
|                   S: NetworkStream + Send { |  | ||||||
|         Http11Protocol { |  | ||||||
|             connector: Connector(Box::new(ConnAdapter(c))), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct ConnAdapter<C: NetworkConnector + Send>(C); |  | ||||||
|  |  | ||||||
| impl<C: NetworkConnector<Stream=S> + Send, S: NetworkStream + Send> NetworkConnector for ConnAdapter<C> { |  | ||||||
|     type Stream = Box<NetworkStream + Send>; |  | ||||||
|     #[inline] |  | ||||||
|     fn connect(&self, host: &str, port: u16, scheme: &str) |  | ||||||
|         -> ::Result<Box<NetworkStream + Send>> { |  | ||||||
|         Ok(try!(self.0.connect(host, port, scheme)).into()) |  | ||||||
|     } |  | ||||||
|     #[inline] |  | ||||||
|     fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { |  | ||||||
|         self.0.set_ssl_verifier(verifier); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct Connector(Box<NetworkConnector<Stream=Box<NetworkStream + Send>> + Send>); |  | ||||||
|  |  | ||||||
| impl NetworkConnector for Connector { |  | ||||||
|     type Stream = Box<NetworkStream + Send>; |  | ||||||
|     #[inline] |  | ||||||
|     fn connect(&self, host: &str, port: u16, scheme: &str) |  | ||||||
|         -> ::Result<Box<NetworkStream + Send>> { |  | ||||||
|         Ok(try!(self.0.connect(host, port, scheme)).into()) |  | ||||||
|     } |  | ||||||
|     #[inline] |  | ||||||
|     fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { |  | ||||||
|         self.0.set_ssl_verifier(verifier); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -186,10 +186,6 @@ pub mod server; | |||||||
| pub mod status; | pub mod status; | ||||||
| pub mod uri; | pub mod uri; | ||||||
| pub mod version; | pub mod version; | ||||||
| pub mod message; |  | ||||||
| pub mod http11; |  | ||||||
| pub mod http2; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /// Re-exporting the mime crate, for convenience. | /// Re-exporting the mime crate, for convenience. | ||||||
| pub mod mime { | pub mod mime { | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ use net::NetworkStream; | |||||||
| use version::{HttpVersion}; | use version::{HttpVersion}; | ||||||
| use method::Method::{self, Get, Head}; | use method::Method::{self, Get, Head}; | ||||||
| use header::{Headers, ContentLength, TransferEncoding}; | use header::{Headers, ContentLength, TransferEncoding}; | ||||||
| use http::{self, Incoming, HttpReader}; | use http::h1::{self, Incoming, HttpReader}; | ||||||
| use http::HttpReader::{SizedReader, ChunkedReader, EmptyReader}; | use http::h1::HttpReader::{SizedReader, ChunkedReader, EmptyReader}; | ||||||
| use uri::RequestUri; | use uri::RequestUri; | ||||||
|  |  | ||||||
| /// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`. | /// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`. | ||||||
| @@ -36,7 +36,7 @@ impl<'a, 'b: 'a> Request<'a, 'b> { | |||||||
|     pub fn new(mut stream: &'a mut BufReader<&'b mut NetworkStream>, addr: SocketAddr) |     pub fn new(mut stream: &'a mut BufReader<&'b mut NetworkStream>, addr: SocketAddr) | ||||||
|         -> ::Result<Request<'a, 'b>> { |         -> ::Result<Request<'a, 'b>> { | ||||||
|  |  | ||||||
|         let Incoming { version, subject: (method, uri), headers } = try!(http::parse_request(stream)); |         let Incoming { version, subject: (method, uri), headers } = try!(h1::parse_request(stream)); | ||||||
|         debug!("Request Line: {:?} {:?} {:?}", method, uri, version); |         debug!("Request Line: {:?} {:?} {:?}", method, uri, version); | ||||||
|         debug!("{:?}", headers); |         debug!("{:?}", headers); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,8 +11,8 @@ use std::ptr; | |||||||
| use time::now_utc; | use time::now_utc; | ||||||
|  |  | ||||||
| use header; | use header; | ||||||
| use http::{CR, LF, LINE_ENDING, HttpWriter}; | use http::h1::{CR, LF, LINE_ENDING, HttpWriter}; | ||||||
| use http::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter}; | use http::h1::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter}; | ||||||
| use status; | use status; | ||||||
| use net::{Fresh, Streaming}; | use net::{Fresh, Streaming}; | ||||||
| use version; | use version; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user