| @@ -3,6 +3,8 @@ matrix: | ||||
|     fast_finish: true | ||||
|     include: | ||||
|         - os: osx | ||||
|           rust: stable | ||||
|           env: FEATURES="--no-default-features --features security-framework" | ||||
|         - rust: nightly | ||||
|           env: FEATURES="--features nightly" | ||||
|         - rust: beta | ||||
|   | ||||
| @@ -16,13 +16,15 @@ httparse = "1.0" | ||||
| language-tags = "0.2" | ||||
| log = "0.3" | ||||
| mime = "0.2" | ||||
| num_cpus = "0.2" | ||||
| rotor = "0.6" | ||||
| rustc-serialize = "0.3" | ||||
| spmc = "0.2" | ||||
| time = "0.1" | ||||
| traitobject = "0.0.1" | ||||
| typeable = "0.1" | ||||
| unicase = "1.0" | ||||
| url = "1.0" | ||||
| vecio = "0.1" | ||||
|  | ||||
| [dependencies.cookie] | ||||
| version = "0.2" | ||||
| @@ -40,16 +42,13 @@ optional = true | ||||
| version = "0.1.4" | ||||
| optional = true | ||||
|  | ||||
| [dependencies.solicit] | ||||
| version = "0.4" | ||||
| default-features = false | ||||
|  | ||||
| [dependencies.serde] | ||||
| version = "0.7" | ||||
| optional = true | ||||
|  | ||||
| [dev-dependencies] | ||||
| env_logger = "0.3" | ||||
| num_cpus = "0.2" | ||||
|  | ||||
| [features] | ||||
| default = ["ssl"] | ||||
|   | ||||
							
								
								
									
										54
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								README.md
									
									
									
									
									
								
							| @@ -10,12 +10,12 @@ A Modern HTTP library for Rust. | ||||
|  | ||||
| ### Documentation | ||||
|  | ||||
| - [Stable](http://hyperium.github.io/hyper) | ||||
| - [Released](http://hyperium.github.io/hyper) | ||||
| - [Master](http://hyperium.github.io/hyper/master) | ||||
|  | ||||
| ## Overview | ||||
|  | ||||
| Hyper is a fast, modern HTTP implementation written in and for Rust. It | ||||
| hyper is a fast, modern HTTP implementation written in and for Rust. It | ||||
| is a low-level typesafe abstraction over raw HTTP, providing an elegant | ||||
| layer over "stringly-typed" HTTP. | ||||
|  | ||||
| @@ -23,53 +23,3 @@ Hyper offers both an HTTP client and server which can be used to drive | ||||
| complex web applications written entirely in Rust. | ||||
|  | ||||
| The documentation is located at [http://hyperium.github.io/hyper](http://hyperium.github.io/hyper). | ||||
|  | ||||
| ## Example | ||||
|  | ||||
| ### Hello World Server: | ||||
|  | ||||
| ```rust | ||||
| extern crate hyper; | ||||
|  | ||||
| use hyper::Server; | ||||
| use hyper::server::Request; | ||||
| use hyper::server::Response; | ||||
|  | ||||
| fn hello(_: Request, res: Response) { | ||||
|     res.send(b"Hello World!").unwrap(); | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
|     Server::http("127.0.0.1:3000").unwrap() | ||||
|         .handle(hello).unwrap(); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### Client: | ||||
|  | ||||
| ```rust | ||||
| extern crate hyper; | ||||
|  | ||||
| use std::io::Read; | ||||
|  | ||||
| use hyper::Client; | ||||
| use hyper::header::Connection; | ||||
|  | ||||
| fn main() { | ||||
|     // Create a client. | ||||
|     let client = Client::new(); | ||||
|  | ||||
|     // Creating an outgoing request. | ||||
|     let mut res = client.get("http://rust-lang.org/") | ||||
|         // set a header | ||||
|         .header(Connection::close()) | ||||
|         // let 'er go! | ||||
|         .send().unwrap(); | ||||
|  | ||||
|     // Read the Response. | ||||
|     let mut body = String::new(); | ||||
|     res.read_to_string(&mut body).unwrap(); | ||||
|  | ||||
|     println!("Response: {}", body); | ||||
| } | ||||
| ``` | ||||
|   | ||||
| @@ -1,111 +0,0 @@ | ||||
| #![deny(warnings)] | ||||
| #![feature(test)] | ||||
| extern crate hyper; | ||||
|  | ||||
| extern crate test; | ||||
|  | ||||
| use std::fmt; | ||||
| use std::io::{self, Read, Write, Cursor}; | ||||
| use std::net::SocketAddr; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use hyper::net; | ||||
|  | ||||
| static README: &'static [u8] = include_bytes!("../README.md"); | ||||
|  | ||||
| struct MockStream { | ||||
|     read: Cursor<Vec<u8>> | ||||
| } | ||||
|  | ||||
| impl MockStream { | ||||
|     fn new() -> MockStream { | ||||
|         let head = b"HTTP/1.1 200 OK\r\nServer: Mock\r\n\r\n"; | ||||
|         let mut res = head.to_vec(); | ||||
|         res.extend_from_slice(README); | ||||
|         MockStream { | ||||
|             read: Cursor::new(res) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Clone for MockStream { | ||||
|     fn clone(&self) -> MockStream { | ||||
|         MockStream { | ||||
|             read: Cursor::new(self.read.get_ref().clone()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Read for MockStream { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         self.read.read(buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Write for MockStream { | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         // we're mocking, what do we care. | ||||
|         Ok(msg.len()) | ||||
|     } | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| struct Foo; | ||||
|  | ||||
| impl hyper::header::Header for Foo { | ||||
|     fn header_name() -> &'static str { | ||||
|         "x-foo" | ||||
|     } | ||||
|     fn parse_header(_: &[Vec<u8>]) -> hyper::Result<Foo> { | ||||
|         Err(hyper::Error::Header) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl hyper::header::HeaderFormat for Foo { | ||||
|     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt.write_str("Bar") | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl net::NetworkStream for MockStream { | ||||
|     fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         Ok("127.0.0.1:1337".parse().unwrap()) | ||||
|     } | ||||
|     fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { | ||||
|         // can't time out | ||||
|         Ok(()) | ||||
|     } | ||||
|     fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { | ||||
|         // can't time out | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct MockConnector; | ||||
|  | ||||
| impl net::NetworkConnector for MockConnector { | ||||
|     type Stream = MockStream; | ||||
|     fn connect(&self, _: &str, _: u16, _: &str) -> hyper::Result<MockStream> { | ||||
|         Ok(MockStream::new()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[bench] | ||||
| fn bench_mock_hyper(b: &mut test::Bencher) { | ||||
|     let url = "http://127.0.0.1:1337/"; | ||||
|     b.iter(|| { | ||||
|         let mut req = hyper::client::Request::with_connector( | ||||
|             hyper::Get, hyper::Url::parse(url).unwrap(), &MockConnector | ||||
|         ).unwrap(); | ||||
|         req.headers_mut().set(Foo); | ||||
|  | ||||
|         let mut s = String::new(); | ||||
|         req | ||||
|             .start().unwrap() | ||||
|             .send().unwrap() | ||||
|             .read_to_string(&mut s).unwrap() | ||||
|     }); | ||||
| } | ||||
| @@ -5,9 +5,61 @@ extern crate env_logger; | ||||
|  | ||||
| use std::env; | ||||
| use std::io; | ||||
| use std::sync::mpsc; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use hyper::Client; | ||||
| use hyper::client::{Client, Request, Response, DefaultTransport as HttpStream}; | ||||
| use hyper::header::Connection; | ||||
| use hyper::{Decoder, Encoder, Next}; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct Dump(mpsc::Sender<()>); | ||||
|  | ||||
| impl Drop for Dump { | ||||
|     fn drop(&mut self) { | ||||
|         let _ = self.0.send(()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn read() -> Next { | ||||
|     Next::read().timeout(Duration::from_secs(10)) | ||||
| } | ||||
|  | ||||
| impl hyper::client::Handler<HttpStream> for Dump { | ||||
|     fn on_request(&mut self, req: &mut Request) -> Next { | ||||
|         req.headers_mut().set(Connection::close()); | ||||
|         read() | ||||
|     } | ||||
|  | ||||
|     fn on_request_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next { | ||||
|         read() | ||||
|     } | ||||
|  | ||||
|     fn on_response(&mut self, res: Response) -> Next { | ||||
|         println!("Response: {}", res.status()); | ||||
|         println!("Headers:\n{}", res.headers()); | ||||
|         read() | ||||
|     } | ||||
|  | ||||
|     fn on_response_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next { | ||||
|         match io::copy(decoder, &mut io::stdout()) { | ||||
|             Ok(0) => Next::end(), | ||||
|             Ok(_) => read(), | ||||
|             Err(e) => match e.kind() { | ||||
|                 io::ErrorKind::WouldBlock => Next::read(), | ||||
|                 _ => { | ||||
|                     println!("ERROR: {}", e); | ||||
|                     Next::end() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn on_error(&mut self, err: hyper::Error) -> Next { | ||||
|         println!("ERROR: {}", err); | ||||
|         Next::remove() | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
|     env_logger::init().unwrap(); | ||||
| @@ -20,26 +72,11 @@ fn main() { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let client = match env::var("HTTP_PROXY") { | ||||
|         Ok(mut proxy) => { | ||||
|             // parse the proxy, message if it doesn't make sense | ||||
|             let mut port = 80; | ||||
|             if let Some(colon) = proxy.rfind(':') { | ||||
|                 port = proxy[colon + 1..].parse().unwrap_or_else(|e| { | ||||
|                     panic!("HTTP_PROXY is malformed: {:?}, port parse error: {}", proxy, e); | ||||
|                 }); | ||||
|                 proxy.truncate(colon); | ||||
|             } | ||||
|             Client::with_http_proxy(proxy, port) | ||||
|         }, | ||||
|         _ => Client::new() | ||||
|     }; | ||||
|     let (tx, rx) = mpsc::channel(); | ||||
|     let client = Client::new().expect("Failed to create a Client"); | ||||
|     client.request(url.parse().unwrap(), Dump(tx)).unwrap(); | ||||
|  | ||||
|     let mut res = client.get(&*url) | ||||
|         .header(Connection::close()) | ||||
|         .send().unwrap(); | ||||
|  | ||||
|     println!("Response: {}", res.status); | ||||
|     println!("Headers:\n{}", res.headers); | ||||
|     io::copy(&mut res, &mut io::stdout()).unwrap(); | ||||
|     // wait till done | ||||
|     let _  = rx.recv(); | ||||
|     client.close(); | ||||
| } | ||||
|   | ||||
| @@ -1,34 +0,0 @@ | ||||
| #![deny(warnings)] | ||||
| extern crate hyper; | ||||
|  | ||||
| extern crate env_logger; | ||||
|  | ||||
| use std::env; | ||||
| use std::io; | ||||
|  | ||||
| use hyper::Client; | ||||
| use hyper::header::Connection; | ||||
| use hyper::http::h2; | ||||
|  | ||||
| fn main() { | ||||
|     env_logger::init().unwrap(); | ||||
|  | ||||
|     let url = match env::args().nth(1) { | ||||
|         Some(url) => url, | ||||
|         None => { | ||||
|             println!("Usage: client <url>"); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let client = Client::with_protocol(h2::new_protocol()); | ||||
|  | ||||
|     // `Connection: Close` is not a valid header for HTTP/2, but the client handles it gracefully. | ||||
|     let mut res = client.get(&*url) | ||||
|         .header(Connection::close()) | ||||
|         .send().unwrap(); | ||||
|  | ||||
|     println!("Response: {}", res.status); | ||||
|     println!("Headers:\n{}", res.headers); | ||||
|     io::copy(&mut res, &mut io::stdout()).unwrap(); | ||||
| } | ||||
| @@ -1,16 +0,0 @@ | ||||
| #![deny(warnings)] | ||||
|  | ||||
| #[macro_use] | ||||
| // TODO: only import header!, blocked by https://github.com/rust-lang/rust/issues/25003 | ||||
| extern crate hyper; | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| extern crate serde; | ||||
|  | ||||
| // A header in the form of `X-Foo: some random string` | ||||
| header! { | ||||
|     (Foo, "X-Foo") => [String] | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
| } | ||||
| @@ -1,18 +1,53 @@ | ||||
| #![deny(warnings)] | ||||
| extern crate hyper; | ||||
| extern crate env_logger; | ||||
| extern crate num_cpus; | ||||
|  | ||||
| use hyper::server::{Request, Response}; | ||||
| use std::io::Write; | ||||
|  | ||||
| use hyper::{Decoder, Encoder, Next}; | ||||
| use hyper::net::{HttpStream, HttpListener}; | ||||
| use hyper::server::{Server, Handler, Request, Response}; | ||||
|  | ||||
| static PHRASE: &'static [u8] = b"Hello World!"; | ||||
|  | ||||
| fn hello(_: Request, res: Response) { | ||||
|     res.send(PHRASE).unwrap(); | ||||
| struct Hello; | ||||
|  | ||||
| impl Handler<HttpStream> for Hello { | ||||
|     fn on_request(&mut self, _: Request) -> Next { | ||||
|         Next::write() | ||||
|     } | ||||
|     fn on_request_readable(&mut self, _: &mut Decoder<HttpStream>) -> Next { | ||||
|         Next::write() | ||||
|     } | ||||
|     fn on_response(&mut self, response: &mut Response) -> Next { | ||||
|         use hyper::header::ContentLength; | ||||
|         response.headers_mut().set(ContentLength(PHRASE.len() as u64)); | ||||
|         Next::write() | ||||
|     } | ||||
|     fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next { | ||||
|         let n = encoder.write(PHRASE).unwrap(); | ||||
|         debug_assert_eq!(n, PHRASE.len()); | ||||
|         Next::end() | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
|     env_logger::init().unwrap(); | ||||
|     let _listening = hyper::Server::http("127.0.0.1:3000").unwrap() | ||||
|         .handle(hello); | ||||
|     println!("Listening on http://127.0.0.1:3000"); | ||||
|   | ||||
|     let listener = HttpListener::bind(&"127.0.0.1:3000".parse().unwrap()).unwrap(); | ||||
|     let mut handles = Vec::new(); | ||||
|  | ||||
|     for _ in 0..num_cpus::get() { | ||||
|         let listener = listener.try_clone().unwrap(); | ||||
|         handles.push(::std::thread::spawn(move || { | ||||
|             Server::new(listener) | ||||
|                 .handle(|_| Hello).unwrap() | ||||
|         })); | ||||
|     } | ||||
|     println!("Listening on http://127.0.0.1:3000"); | ||||
|  | ||||
|     for handle in handles { | ||||
|         handle.join().unwrap(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,47 +1,171 @@ | ||||
| #![deny(warnings)] | ||||
| extern crate hyper; | ||||
| extern crate env_logger; | ||||
| #[macro_use] | ||||
| extern crate log; | ||||
|  | ||||
| use std::io::copy; | ||||
| use std::io::{self, Read, Write}; | ||||
|  | ||||
| use hyper::{Get, Post}; | ||||
| use hyper::server::{Server, Request, Response}; | ||||
| use hyper::uri::RequestUri::AbsolutePath; | ||||
| use hyper::{Get, Post, StatusCode, RequestUri, Decoder, Encoder, Next}; | ||||
| use hyper::header::ContentLength; | ||||
| use hyper::net::HttpStream; | ||||
| use hyper::server::{Server, Handler, Request, Response}; | ||||
|  | ||||
| macro_rules! try_return( | ||||
|     ($e:expr) => {{ | ||||
|         match $e { | ||||
|             Ok(v) => v, | ||||
|             Err(e) => { println!("Error: {}", e); return; } | ||||
| struct Echo { | ||||
|     buf: Vec<u8>, | ||||
|     read_pos: usize, | ||||
|     write_pos: usize, | ||||
|     eof: bool, | ||||
|     route: Route, | ||||
| } | ||||
|     }} | ||||
| ); | ||||
|  | ||||
| fn echo(mut req: Request, mut res: Response) { | ||||
|     match req.uri { | ||||
|         AbsolutePath(ref path) => match (&req.method, &path[..]) { | ||||
| enum Route { | ||||
|     NotFound, | ||||
|     Index, | ||||
|     Echo(Body), | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| enum Body { | ||||
|     Len(u64), | ||||
|     Chunked | ||||
| } | ||||
|  | ||||
| static INDEX: &'static [u8] = b"Try POST /echo"; | ||||
|  | ||||
| impl Echo { | ||||
|     fn new() -> Echo { | ||||
|         Echo { | ||||
|             buf: vec![0; 4096], | ||||
|             read_pos: 0, | ||||
|             write_pos: 0, | ||||
|             eof: false, | ||||
|             route: Route::NotFound, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Handler<HttpStream> for Echo { | ||||
|     fn on_request(&mut self, req: Request) -> Next { | ||||
|         match *req.uri() { | ||||
|             RequestUri::AbsolutePath(ref path) => match (req.method(), &path[..]) { | ||||
|                 (&Get, "/") | (&Get, "/echo") => { | ||||
|                 try_return!(res.send(b"Try POST /echo")); | ||||
|                 return; | ||||
|             }, | ||||
|             (&Post, "/echo") => (), // fall through, fighting mutable borrows | ||||
|             _ => { | ||||
|                 *res.status_mut() = hyper::NotFound; | ||||
|                 return; | ||||
|             } | ||||
|         }, | ||||
|         _ => { | ||||
|             return; | ||||
|                     info!("GET Index"); | ||||
|                     self.route = Route::Index; | ||||
|                     Next::write() | ||||
|                 } | ||||
|                 (&Post, "/echo") => { | ||||
|                     info!("POST Echo"); | ||||
|                     let mut is_more = true; | ||||
|                     self.route = if let Some(len) = req.headers().get::<ContentLength>() { | ||||
|                         is_more = **len > 0; | ||||
|                         Route::Echo(Body::Len(**len)) | ||||
|                     } else { | ||||
|                         Route::Echo(Body::Chunked) | ||||
|                     }; | ||||
|                     if is_more { | ||||
|                         Next::read_and_write() | ||||
|                     } else { | ||||
|                         Next::write() | ||||
|                     } | ||||
|                 } | ||||
|                 _ => Next::write(), | ||||
|             }, | ||||
|             _ => Next::write() | ||||
|         } | ||||
|     } | ||||
|     fn on_request_readable(&mut self, transport: &mut Decoder<HttpStream>) -> Next { | ||||
|         match self.route { | ||||
|             Route::Echo(ref body) => { | ||||
|                 if self.read_pos < self.buf.len() { | ||||
|                     match transport.read(&mut self.buf[self.read_pos..]) { | ||||
|                         Ok(0) => { | ||||
|                             debug!("Read 0, eof"); | ||||
|                             self.eof = true; | ||||
|                             Next::write() | ||||
|                         }, | ||||
|                         Ok(n) => { | ||||
|                             self.read_pos += n; | ||||
|                             match *body { | ||||
|                                 Body::Len(max) if max <= self.read_pos as u64 => { | ||||
|                                     self.eof = true; | ||||
|                                     Next::write() | ||||
|                                 }, | ||||
|                                 _ => Next::read_and_write() | ||||
|                             } | ||||
|                         } | ||||
|                         Err(e) => match e.kind() { | ||||
|                             io::ErrorKind::WouldBlock => Next::read_and_write(), | ||||
|                             _ => { | ||||
|                                 println!("read error {:?}", e); | ||||
|                                 Next::end() | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     Next::write() | ||||
|                 } | ||||
|             } | ||||
|             _ => unreachable!() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let mut res = try_return!(res.start()); | ||||
|     try_return!(copy(&mut req, &mut res)); | ||||
|     fn on_response(&mut self, res: &mut Response) -> Next { | ||||
|         match self.route { | ||||
|             Route::NotFound => { | ||||
|                 res.set_status(StatusCode::NotFound); | ||||
|                 Next::end() | ||||
|             } | ||||
|             Route::Index => { | ||||
|                 res.headers_mut().set(ContentLength(INDEX.len() as u64)); | ||||
|                 Next::write() | ||||
|             } | ||||
|             Route::Echo(body) => { | ||||
|                 if let Body::Len(len) = body { | ||||
|                     res.headers_mut().set(ContentLength(len)); | ||||
|                 } | ||||
|                 Next::read_and_write() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn on_response_writable(&mut self, transport: &mut Encoder<HttpStream>) -> Next { | ||||
|         match self.route { | ||||
|             Route::Index => { | ||||
|                 transport.write(INDEX).unwrap(); | ||||
|                 Next::end() | ||||
|             } | ||||
|             Route::Echo(..) => { | ||||
|                 if self.write_pos < self.read_pos { | ||||
|                     match transport.write(&self.buf[self.write_pos..self.read_pos]) { | ||||
|                         Ok(0) => panic!("write ZERO"), | ||||
|                         Ok(n) => { | ||||
|                             self.write_pos += n; | ||||
|                             Next::write() | ||||
|                         } | ||||
|                         Err(e) => match e.kind() { | ||||
|                             io::ErrorKind::WouldBlock => Next::write(), | ||||
|                             _ => { | ||||
|                                 println!("write error {:?}", e); | ||||
|                                 Next::end() | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } else if !self.eof { | ||||
|                     Next::read() | ||||
|                 } else { | ||||
|                     Next::end() | ||||
|                 } | ||||
|             } | ||||
|             _ => unreachable!() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
|     env_logger::init().unwrap(); | ||||
|     let server = Server::http("127.0.0.1:1337").unwrap(); | ||||
|     let _guard = server.handle(echo); | ||||
|     println!("Listening on http://127.0.0.1:1337"); | ||||
|     let server = Server::http(&"127.0.0.1:1337".parse().unwrap()).unwrap(); | ||||
|     let (listening, server) = server.handle(|_| Echo::new()).unwrap(); | ||||
|     println!("Listening on http://{}", listening); | ||||
|     server.run(); | ||||
| } | ||||
|   | ||||
							
								
								
									
										278
									
								
								examples/sync.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								examples/sync.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,278 @@ | ||||
| extern crate hyper; | ||||
| extern crate env_logger; | ||||
| extern crate time; | ||||
|  | ||||
| use std::io::{self, Read, Write}; | ||||
| use std::marker::PhantomData; | ||||
| use std::thread; | ||||
| use std::sync::{Arc, mpsc}; | ||||
|  | ||||
| pub struct Server { | ||||
|     listening: hyper::server::Listening, | ||||
| } | ||||
|  | ||||
| pub struct Request<'a> { | ||||
|     #[allow(dead_code)] | ||||
|     inner: hyper::server::Request, | ||||
|     tx: &'a mpsc::Sender<Action>, | ||||
|     rx: &'a mpsc::Receiver<io::Result<usize>>, | ||||
|     ctrl: &'a hyper::Control, | ||||
| } | ||||
|  | ||||
| impl<'a> Request<'a> { | ||||
|     fn new(inner: hyper::server::Request, tx: &'a mpsc::Sender<Action>, rx: &'a mpsc::Receiver<io::Result<usize>>, ctrl: &'a hyper::Control) -> Request<'a> { | ||||
|         Request { | ||||
|             inner: inner, | ||||
|             tx: tx, | ||||
|             rx: rx, | ||||
|             ctrl: ctrl, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> io::Read for Request<'a> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         self.tx.send(Action::Read(buf.as_mut_ptr(), buf.len())).unwrap(); | ||||
|         self.ctrl.ready(hyper::Next::read()).unwrap(); | ||||
|         self.rx.recv().unwrap() | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub enum Fresh {} | ||||
| pub enum Streaming {} | ||||
|  | ||||
| pub struct Response<'a, W = Fresh> { | ||||
|     status: hyper::StatusCode, | ||||
|     headers: hyper::Headers, | ||||
|     version: hyper::HttpVersion, | ||||
|     tx: &'a mpsc::Sender<Action>, | ||||
|     rx: &'a mpsc::Receiver<io::Result<usize>>, | ||||
|     ctrl: &'a hyper::Control, | ||||
|     _marker: PhantomData<W>, | ||||
| } | ||||
|  | ||||
| impl<'a> Response<'a, Fresh> { | ||||
|     fn new(tx: &'a mpsc::Sender<Action>, rx: &'a mpsc::Receiver<io::Result<usize>>, ctrl: &'a hyper::Control) -> Response<'a, Fresh> { | ||||
|         Response { | ||||
|             status: hyper::Ok, | ||||
|             headers: hyper::Headers::new(), | ||||
|             version: hyper::HttpVersion::Http11, | ||||
|             tx: tx, | ||||
|             rx: rx, | ||||
|             ctrl: ctrl, | ||||
|             _marker: PhantomData, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn start(self) -> io::Result<Response<'a, Streaming>> { | ||||
|         self.tx.send(Action::Respond(self.version.clone(), self.status.clone(), self.headers.clone())).unwrap(); | ||||
|         self.ctrl.ready(hyper::Next::write()).unwrap(); | ||||
|         let res = self.rx.recv().unwrap(); | ||||
|         res.map(move |_| Response { | ||||
|             status: self.status, | ||||
|             headers: self.headers, | ||||
|             version: self.version, | ||||
|             tx: self.tx, | ||||
|             rx: self.rx, | ||||
|             ctrl: self.ctrl, | ||||
|             _marker: PhantomData, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     pub fn send(mut self, msg: &[u8]) -> io::Result<()> { | ||||
|         self.headers.set(hyper::header::ContentLength(msg.len() as u64)); | ||||
|         self.start().and_then(|mut res| res.write_all(msg)).map(|_| ()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Write for Response<'a, Streaming> { | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         self.tx.send(Action::Write(msg.as_ptr(), msg.len())).unwrap(); | ||||
|         self.ctrl.ready(hyper::Next::write()).unwrap(); | ||||
|         let res = self.rx.recv().unwrap(); | ||||
|         res | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         panic!("Response.flush() not impemented") | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct SynchronousHandler { | ||||
|     req_tx: mpsc::Sender<hyper::server::Request>, | ||||
|     tx: mpsc::Sender<io::Result<usize>>, | ||||
|     rx: mpsc::Receiver<Action>, | ||||
|     reading: Option<(*mut u8, usize)>, | ||||
|     writing: Option<(*const u8, usize)>, | ||||
|     respond: Option<(hyper::HttpVersion, hyper::StatusCode, hyper::Headers)> | ||||
| } | ||||
|  | ||||
| unsafe impl Send for SynchronousHandler {} | ||||
|  | ||||
| impl SynchronousHandler { | ||||
|     fn next(&mut self) -> hyper::Next { | ||||
|         match self.rx.try_recv() { | ||||
|             Ok(Action::Read(ptr, len)) => { | ||||
|                 self.reading = Some((ptr, len)); | ||||
|                 hyper::Next::read() | ||||
|             }, | ||||
|             Ok(Action::Respond(ver, status, headers)) => { | ||||
|                 self.respond = Some((ver, status, headers)); | ||||
|                 hyper::Next::write() | ||||
|             }, | ||||
|             Ok(Action::Write(ptr, len)) => { | ||||
|                 self.writing = Some((ptr, len)); | ||||
|                 hyper::Next::write() | ||||
|             } | ||||
|             Err(mpsc::TryRecvError::Empty) => { | ||||
|                 // we're too fast, the other thread hasn't had a chance to respond | ||||
|                 hyper::Next::wait() | ||||
|             } | ||||
|             Err(mpsc::TryRecvError::Disconnected) => { | ||||
|                 // they dropped it | ||||
|                 // TODO: should finish up sending response, whatever it was | ||||
|                 hyper::Next::end() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn reading(&mut self) -> Option<(*mut u8, usize)> { | ||||
|         self.reading.take().or_else(|| { | ||||
|             match self.rx.try_recv() { | ||||
|                 Ok(Action::Read(ptr, len)) => { | ||||
|                     Some((ptr, len)) | ||||
|                 }, | ||||
|                 _ => None | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     fn writing(&mut self) -> Option<(*const u8, usize)> { | ||||
|         self.writing.take().or_else(|| { | ||||
|             match self.rx.try_recv() { | ||||
|                 Ok(Action::Write(ptr, len)) => { | ||||
|                     Some((ptr, len)) | ||||
|                 }, | ||||
|                 _ => None | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|     fn respond(&mut self) -> Option<(hyper::HttpVersion, hyper::StatusCode, hyper::Headers)> { | ||||
|         self.respond.take().or_else(|| { | ||||
|             match self.rx.try_recv() { | ||||
|                 Ok(Action::Respond(ver, status, headers)) => { | ||||
|                     Some((ver, status, headers)) | ||||
|                 }, | ||||
|                 _ => None | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl hyper::server::Handler<hyper::net::HttpStream> for SynchronousHandler { | ||||
|     fn on_request(&mut self, req: hyper::server::Request) -> hyper::Next { | ||||
|         if let Err(_) = self.req_tx.send(req) { | ||||
|             return hyper::Next::end(); | ||||
|         } | ||||
|  | ||||
|         self.next() | ||||
|     } | ||||
|  | ||||
|     fn on_request_readable(&mut self, decoder: &mut hyper::Decoder<hyper::net::HttpStream>) -> hyper::Next { | ||||
|         if let Some(raw) = self.reading() { | ||||
|             let slice = unsafe { ::std::slice::from_raw_parts_mut(raw.0, raw.1) }; | ||||
|             if self.tx.send(decoder.read(slice)).is_err() { | ||||
|                 return hyper::Next::end(); | ||||
|             } | ||||
|         } | ||||
|         self.next() | ||||
|     } | ||||
|  | ||||
|     fn on_response(&mut self, req: &mut hyper::server::Response) -> hyper::Next { | ||||
|         use std::iter::Extend; | ||||
|         if let Some(head) = self.respond() { | ||||
|             req.set_status(head.1); | ||||
|             req.headers_mut().extend(head.2.iter()); | ||||
|             if self.tx.send(Ok(0)).is_err() { | ||||
|                 return hyper::Next::end(); | ||||
|             } | ||||
|         } else { | ||||
|             // wtf happened? | ||||
|             panic!("no head to respond with"); | ||||
|         } | ||||
|         self.next() | ||||
|     } | ||||
|  | ||||
|     fn on_response_writable(&mut self, encoder: &mut hyper::Encoder<hyper::net::HttpStream>) -> hyper::Next { | ||||
|         if let Some(raw) = self.writing() { | ||||
|             let slice = unsafe { ::std::slice::from_raw_parts(raw.0, raw.1) }; | ||||
|             if self.tx.send(encoder.write(slice)).is_err() { | ||||
|                 return hyper::Next::end(); | ||||
|             } | ||||
|         } | ||||
|         self.next() | ||||
|     } | ||||
| } | ||||
|  | ||||
| enum Action { | ||||
|     Read(*mut u8, usize), | ||||
|     Write(*const u8, usize), | ||||
|     Respond(hyper::HttpVersion, hyper::StatusCode, hyper::Headers), | ||||
| } | ||||
|  | ||||
| unsafe impl Send for Action {} | ||||
|  | ||||
| trait Handler: Send + Sync + 'static { | ||||
|     fn handle(&self, req: Request, res: Response); | ||||
| } | ||||
|  | ||||
| impl<F> Handler for F where F: Fn(Request, Response) + Send + Sync + 'static { | ||||
|     fn handle(&self, req: Request, res: Response) { | ||||
|         (self)(req, res) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Server { | ||||
|     fn handle<H: Handler>(addr: &str, handler: H) -> Server { | ||||
|         let handler = Arc::new(handler); | ||||
|         let (listening, server) = hyper::Server::http(&addr.parse().unwrap()).unwrap() | ||||
|             .handle(move |ctrl| { | ||||
|                 let (req_tx, req_rx) = mpsc::channel(); | ||||
|                 let (blocking_tx, blocking_rx) = mpsc::channel(); | ||||
|                 let (async_tx, async_rx) = mpsc::channel(); | ||||
|                 let handler = handler.clone(); | ||||
|                 thread::Builder::new().name("handler-thread".into()).spawn(move || { | ||||
|                     let req = Request::new(req_rx.recv().unwrap(), &blocking_tx, &async_rx, &ctrl); | ||||
|                     let res = Response::new(&blocking_tx, &async_rx, &ctrl); | ||||
|                     handler.handle(req, res); | ||||
|                 }).unwrap(); | ||||
|  | ||||
|                 SynchronousHandler { | ||||
|                     req_tx: req_tx, | ||||
|                     tx: async_tx, | ||||
|                     rx: blocking_rx, | ||||
|                     reading: None, | ||||
|                     writing: None, | ||||
|                     respond: None, | ||||
|                 } | ||||
|             }).unwrap(); | ||||
|         thread::spawn(move || { | ||||
|             server.run(); | ||||
|         }); | ||||
|         Server { | ||||
|             listening: listening | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
|     env_logger::init().unwrap(); | ||||
|     let s = Server::handle("127.0.0.1:0", |mut req: Request, res: Response| { | ||||
|         let mut body = [0; 256]; | ||||
|         let n = req.read(&mut body).unwrap(); | ||||
|         println!("!!!: received: {:?}", ::std::str::from_utf8(&body[..n]).unwrap()); | ||||
|  | ||||
|         res.send(b"Hello World!").unwrap(); | ||||
|     }); | ||||
|     println!("listening on {}", s.listening.addr()); | ||||
| } | ||||
							
								
								
									
										164
									
								
								src/buffer.rs
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								src/buffer.rs
									
									
									
									
									
								
							| @@ -1,164 +0,0 @@ | ||||
| use std::cmp; | ||||
| use std::io::{self, Read, BufRead}; | ||||
|  | ||||
| pub struct BufReader<R> { | ||||
|     inner: R, | ||||
|     buf: Vec<u8>, | ||||
|     pos: usize, | ||||
|     cap: usize, | ||||
| } | ||||
|  | ||||
| const INIT_BUFFER_SIZE: usize = 4096; | ||||
| const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100; | ||||
|  | ||||
| impl<R: Read> BufReader<R> { | ||||
|     #[inline] | ||||
|     pub fn new(rdr: R) -> BufReader<R> { | ||||
|         BufReader::with_capacity(rdr, INIT_BUFFER_SIZE) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn with_capacity(rdr: R, cap: usize) -> BufReader<R> { | ||||
|         BufReader { | ||||
|             inner: rdr, | ||||
|             buf: vec![0; cap], | ||||
|             pos: 0, | ||||
|             cap: 0, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn get_ref(&self) -> &R { &self.inner } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn get_mut(&mut self) -> &mut R { &mut self.inner } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn get_buf(&self) -> &[u8] { | ||||
|         if self.pos < self.cap { | ||||
|             trace!("get_buf [u8; {}][{}..{}]", self.buf.len(), self.pos, self.cap); | ||||
|             &self.buf[self.pos..self.cap] | ||||
|         } else { | ||||
|             trace!("get_buf []"); | ||||
|             &[] | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn into_inner(self) -> R { self.inner } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn read_into_buf(&mut self) -> io::Result<usize> { | ||||
|         self.maybe_reserve(); | ||||
|         let v = &mut self.buf; | ||||
|         trace!("read_into_buf buf[{}..{}]", self.cap, v.len()); | ||||
|         if self.cap < v.capacity() { | ||||
|             let nread = try!(self.inner.read(&mut v[self.cap..])); | ||||
|             self.cap += nread; | ||||
|             Ok(nread) | ||||
|         } else { | ||||
|             trace!("read_into_buf at full capacity"); | ||||
|             Ok(0) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn maybe_reserve(&mut self) { | ||||
|         let cap = self.buf.capacity(); | ||||
|         if self.cap == cap && cap < MAX_BUFFER_SIZE { | ||||
|             self.buf.reserve(cmp::min(cap * 4, MAX_BUFFER_SIZE) - cap); | ||||
|             let new = self.buf.capacity() - self.buf.len(); | ||||
|             trace!("reserved {}", new); | ||||
|             unsafe { grow_zerofill(&mut self.buf, new) } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[inline] | ||||
| unsafe fn grow_zerofill(buf: &mut Vec<u8>, additional: usize) { | ||||
|     use std::ptr; | ||||
|     let len = buf.len(); | ||||
|     buf.set_len(len + additional); | ||||
|     ptr::write_bytes(buf.as_mut_ptr().offset(len as isize), 0, additional); | ||||
| } | ||||
|  | ||||
| impl<R: Read> Read for BufReader<R> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         if self.cap == self.pos && buf.len() >= self.buf.len() { | ||||
|             return self.inner.read(buf); | ||||
|         } | ||||
|         let nread = { | ||||
|            let mut rem = try!(self.fill_buf()); | ||||
|            try!(rem.read(buf)) | ||||
|         }; | ||||
|         self.consume(nread); | ||||
|         Ok(nread) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<R: Read> BufRead for BufReader<R> { | ||||
|     fn fill_buf(&mut self) -> io::Result<&[u8]> { | ||||
|         if self.pos == self.cap { | ||||
|             self.cap = try!(self.inner.read(&mut self.buf)); | ||||
|             self.pos = 0; | ||||
|         } | ||||
|         Ok(&self.buf[self.pos..self.cap]) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn consume(&mut self, amt: usize) { | ||||
|         self.pos = cmp::min(self.pos + amt, self.cap); | ||||
|         if self.pos == self.cap { | ||||
|             self.pos = 0; | ||||
|             self.cap = 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|  | ||||
|     use std::io::{self, Read, BufRead}; | ||||
|     use super::BufReader; | ||||
|  | ||||
|     struct SlowRead(u8); | ||||
|  | ||||
|     impl Read for SlowRead { | ||||
|         fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|             let state = self.0; | ||||
|             self.0 += 1; | ||||
|             (&match state % 3 { | ||||
|                 0 => b"foo", | ||||
|                 1 => b"bar", | ||||
|                 _ => b"baz", | ||||
|             }[..]).read(buf) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_consume_and_get_buf() { | ||||
|         let mut rdr = BufReader::new(SlowRead(0)); | ||||
|         rdr.read_into_buf().unwrap(); | ||||
|         rdr.consume(1); | ||||
|         assert_eq!(rdr.get_buf(), b"oo"); | ||||
|         rdr.read_into_buf().unwrap(); | ||||
|         rdr.read_into_buf().unwrap(); | ||||
|         assert_eq!(rdr.get_buf(), b"oobarbaz"); | ||||
|         rdr.consume(5); | ||||
|         assert_eq!(rdr.get_buf(), b"baz"); | ||||
|         rdr.consume(3); | ||||
|         assert_eq!(rdr.get_buf(), b""); | ||||
|         assert_eq!(rdr.pos, 0); | ||||
|         assert_eq!(rdr.cap, 0); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_resize() { | ||||
|         let raw = b"hello world"; | ||||
|         let mut rdr = BufReader::with_capacity(&raw[..], 5); | ||||
|         rdr.read_into_buf().unwrap(); | ||||
|         assert_eq!(rdr.get_buf(), b"hello"); | ||||
|         rdr.read_into_buf().unwrap(); | ||||
|         assert_eq!(rdr.get_buf(), b"hello world"); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										219
									
								
								src/client/connect.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								src/client/connect.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,219 @@ | ||||
| use std::collections::hash_map::{HashMap, Entry}; | ||||
| use std::hash::Hash; | ||||
| use std::fmt; | ||||
| use std::io; | ||||
| use std::net::SocketAddr; | ||||
|  | ||||
| use rotor::mio::tcp::TcpStream; | ||||
| use url::Url; | ||||
|  | ||||
| use net::{HttpStream, HttpsStream, Transport, SslClient}; | ||||
| use super::dns::Dns; | ||||
| use super::Registration; | ||||
|  | ||||
| /// A connector creates a Transport to a remote address.. | ||||
| pub trait Connect { | ||||
|     /// Type of Transport to create | ||||
|     type Output: Transport; | ||||
|     /// The key used to determine if an existing socket can be used. | ||||
|     type Key: Eq + Hash + Clone; | ||||
|     /// Returns the key based off the Url. | ||||
|     fn key(&self, &Url) -> Option<Self::Key>; | ||||
|     /// Connect to a remote address. | ||||
|     fn connect(&mut self, &Url) -> io::Result<Self::Key>; | ||||
|     /// Returns a connected socket and associated host. | ||||
|     fn connected(&mut self) -> Option<(Self::Key, io::Result<Self::Output>)>; | ||||
|     #[doc(hidden)] | ||||
|     fn register(&mut self, Registration); | ||||
| } | ||||
|  | ||||
| type Scheme = String; | ||||
| type Port = u16; | ||||
|  | ||||
| /// A connector for the `http` scheme. | ||||
| pub struct HttpConnector { | ||||
|     dns: Option<Dns>, | ||||
|     threads: usize, | ||||
|     resolving: HashMap<String, Vec<(&'static str, String, u16)>>, | ||||
| } | ||||
|  | ||||
| impl HttpConnector { | ||||
|     /// Set the number of resolver threads. | ||||
|     /// | ||||
|     /// Default is 4. | ||||
|     pub fn threads(mut self, threads: usize) -> HttpConnector { | ||||
|         debug_assert!(self.dns.is_none(), "setting threads after Dns is created does nothing"); | ||||
|         self.threads = threads; | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for HttpConnector { | ||||
|     fn default() -> HttpConnector { | ||||
|         HttpConnector { | ||||
|             dns: None, | ||||
|             threads: 4, | ||||
|             resolving: HashMap::new(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for HttpConnector { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("HttpConnector") | ||||
|             .field("threads", &self.threads) | ||||
|             .field("resolving", &self.resolving) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Connect for HttpConnector { | ||||
|     type Output = HttpStream; | ||||
|     type Key = (&'static str, String, u16); | ||||
|  | ||||
|     fn key(&self, url: &Url) -> Option<Self::Key> { | ||||
|         if url.scheme() == "http" { | ||||
|             Some(( | ||||
|                 "http", | ||||
|                 url.host_str().expect("http scheme must have host").to_owned(), | ||||
|                 url.port().unwrap_or(80), | ||||
|             )) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn connect(&mut self, url: &Url) -> io::Result<Self::Key> { | ||||
|         debug!("Http::connect({:?})", url); | ||||
|         if let Some(key) = self.key(url) { | ||||
|             let host = url.host_str().expect("http scheme must have a host"); | ||||
|             self.dns.as_ref().expect("dns workers lost").resolve(host); | ||||
|             self.resolving.entry(host.to_owned()).or_insert(Vec::new()).push(key.clone()); | ||||
|             Ok(key) | ||||
|         } else { | ||||
|             Err(io::Error::new(io::ErrorKind::InvalidInput, "scheme must be http")) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn connected(&mut self) -> Option<(Self::Key, io::Result<HttpStream>)> { | ||||
|         let (host, addr) = match self.dns.as_ref().expect("dns workers lost").resolved() { | ||||
|             Ok(res) => res, | ||||
|             Err(_) => return None | ||||
|         }; | ||||
|         debug!("Http::resolved <- ({:?}, {:?})", host, addr); | ||||
|         match self.resolving.entry(host) { | ||||
|             Entry::Occupied(mut entry) => { | ||||
|                 let resolved = entry.get_mut().remove(0); | ||||
|                 if entry.get().is_empty() { | ||||
|                     entry.remove(); | ||||
|                 } | ||||
|                 let port = resolved.2; | ||||
|                 match addr { | ||||
|                     Ok(addr) => { | ||||
|                         Some((resolved, TcpStream::connect(&SocketAddr::new(addr, port)) | ||||
|                             .map(HttpStream))) | ||||
|                     }, | ||||
|                     Err(e) => Some((resolved, Err(e))) | ||||
|                 } | ||||
|             } | ||||
|             _ => { | ||||
|                 trace!("^--  resolved but not in hashmap?"); | ||||
|                 return None | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn register(&mut self, reg: Registration) { | ||||
|         self.dns = Some(Dns::new(reg.notify, 4)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A connector that can protect HTTP streams using SSL. | ||||
| #[derive(Debug, Default)] | ||||
| pub struct HttpsConnector<S: SslClient> { | ||||
|     http: HttpConnector, | ||||
|     ssl: S | ||||
| } | ||||
|  | ||||
| impl<S: SslClient> HttpsConnector<S> { | ||||
|     /// Create a new connector using the provided SSL implementation. | ||||
|     pub fn new(s: S) -> HttpsConnector<S> { | ||||
|         HttpsConnector { | ||||
|             http: HttpConnector::default(), | ||||
|             ssl: s, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: SslClient> Connect for HttpsConnector<S> { | ||||
|     type Output = HttpsStream<S::Stream>; | ||||
|     type Key = (&'static str, String, u16); | ||||
|  | ||||
|     fn key(&self, url: &Url) -> Option<Self::Key> { | ||||
|         let scheme = match url.scheme() { | ||||
|             "http" => "http", | ||||
|             "https" => "https", | ||||
|             _ => return None | ||||
|         }; | ||||
|         Some(( | ||||
|             scheme, | ||||
|             url.host_str().expect("http scheme must have host").to_owned(), | ||||
|             url.port_or_known_default().expect("http scheme must have a port"), | ||||
|         )) | ||||
|     } | ||||
|  | ||||
|     fn connect(&mut self, url: &Url) -> io::Result<Self::Key> { | ||||
|         debug!("Https::connect({:?})", url); | ||||
|         if let Some(key) = self.key(url) { | ||||
|             let host = url.host_str().expect("http scheme must have a host"); | ||||
|             self.http.dns.as_ref().expect("dns workers lost").resolve(host); | ||||
|             self.http.resolving.entry(host.to_owned()).or_insert(Vec::new()).push(key.clone()); | ||||
|             Ok(key) | ||||
|         } else { | ||||
|             Err(io::Error::new(io::ErrorKind::InvalidInput, "scheme must be http or https")) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn connected(&mut self) -> Option<(Self::Key, io::Result<Self::Output>)> { | ||||
|         self.http.connected().map(|(key, res)| { | ||||
|             let res = res.and_then(|http| { | ||||
|                 if key.0 == "https" { | ||||
|                     self.ssl.wrap_client(http, &key.1) | ||||
|                         .map(HttpsStream::Https) | ||||
|                         .map_err(|e| match e { | ||||
|                             ::Error::Io(e) => e, | ||||
|                             e => io::Error::new(io::ErrorKind::Other, e) | ||||
|                         }) | ||||
|                 } else { | ||||
|                     Ok(HttpsStream::Http(http)) | ||||
|                 } | ||||
|             }); | ||||
|             (key, res) | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     fn register(&mut self, reg: Registration) { | ||||
|         self.http.register(reg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(not(any(feature = "openssl", feature = "security-framework")))] | ||||
| #[doc(hidden)] | ||||
| pub type DefaultConnector = HttpConnector; | ||||
|  | ||||
| #[cfg(all(feature = "openssl", not(feature = "security-framework")))] | ||||
| #[doc(hidden)] | ||||
| pub type DefaultConnector = HttpsConnector<::net::Openssl>; | ||||
|  | ||||
| #[cfg(feature = "security-framework")] | ||||
| #[doc(hidden)] | ||||
| pub type DefaultConnector = HttpsConnector<::net::SecureTransportClient>; | ||||
|  | ||||
| #[doc(hidden)] | ||||
| pub type DefaultTransport = <DefaultConnector as Connect>::Output; | ||||
|  | ||||
| fn _assert_defaults() { | ||||
|     fn _assert<T, U>() where T: Connect<Output=U>, U: Transport {} | ||||
|  | ||||
|     _assert::<DefaultConnector, DefaultTransport>(); | ||||
| } | ||||
							
								
								
									
										84
									
								
								src/client/dns.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/client/dns.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| use std::io; | ||||
| use std::net::{IpAddr, ToSocketAddrs}; | ||||
| use std::thread; | ||||
|  | ||||
| use ::spmc; | ||||
|  | ||||
| use http::channel; | ||||
|  | ||||
| pub struct Dns { | ||||
|     tx: spmc::Sender<String>, | ||||
|     rx: channel::Receiver<Answer>, | ||||
| } | ||||
|  | ||||
| pub type Answer = (String, io::Result<IpAddr>); | ||||
|  | ||||
| impl Dns { | ||||
|     pub fn new(notify: (channel::Sender<Answer>, channel::Receiver<Answer>), threads: usize) -> Dns { | ||||
|         let (tx, rx) = spmc::channel(); | ||||
|         for _ in 0..threads { | ||||
|             work(rx.clone(), notify.0.clone()); | ||||
|         } | ||||
|         Dns { | ||||
|             tx: tx, | ||||
|             rx: notify.1, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn resolve<T: Into<String>>(&self, hostname: T) { | ||||
|         self.tx.send(hostname.into()).expect("Workers all died horribly"); | ||||
|     } | ||||
|  | ||||
|     pub fn resolved(&self) -> Result<Answer, channel::TryRecvError> { | ||||
|         self.rx.try_recv() | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn work(rx: spmc::Receiver<String>, notify: channel::Sender<Answer>) { | ||||
|     thread::spawn(move || { | ||||
|         let mut worker = Worker::new(rx, notify); | ||||
|         let rx = worker.rx.as_ref().expect("Worker lost rx"); | ||||
|         let notify = worker.notify.as_ref().expect("Worker lost notify"); | ||||
|         while let Ok(host) = rx.recv() { | ||||
|             debug!("resolve {:?}", host); | ||||
|             let res = match (&*host, 80).to_socket_addrs().map(|mut i| i.next()) { | ||||
|                 Ok(Some(addr)) => (host, Ok(addr.ip())), | ||||
|                 Ok(None) => (host, Err(io::Error::new(io::ErrorKind::Other, "no addresses found"))), | ||||
|                 Err(e) => (host, Err(e)) | ||||
|             }; | ||||
|  | ||||
|             if let Err(_) = notify.send(res) { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         worker.shutdown = true; | ||||
|     }); | ||||
| } | ||||
|  | ||||
| struct Worker { | ||||
|     rx: Option<spmc::Receiver<String>>, | ||||
|     notify: Option<channel::Sender<Answer>>, | ||||
|     shutdown: bool, | ||||
| } | ||||
|  | ||||
| impl Worker { | ||||
|     fn new(rx: spmc::Receiver<String>, notify: channel::Sender<Answer>) -> Worker { | ||||
|         Worker { | ||||
|             rx: Some(rx), | ||||
|             notify: Some(notify), | ||||
|             shutdown: false, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Drop for Worker { | ||||
|     fn drop(&mut self) { | ||||
|         if !self.shutdown { | ||||
|             trace!("Worker.drop panicked, restarting"); | ||||
|             work(self.rx.take().expect("Worker lost rx"), | ||||
|                 self.notify.take().expect("Worker lost notify")); | ||||
|         } else { | ||||
|             trace!("Worker.drop shutdown, closing"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1032
									
								
								src/client/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										1032
									
								
								src/client/mod.rs
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,240 +0,0 @@ | ||||
| use std::borrow::Cow; | ||||
| use std::io; | ||||
| use std::net::{SocketAddr, Shutdown}; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use method::Method; | ||||
| use net::{NetworkConnector, HttpConnector, NetworkStream, SslClient}; | ||||
|  | ||||
| #[cfg(all(feature = "openssl", not(feature = "security-framework")))] | ||||
| pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::Openssl> { | ||||
|     Proxy { | ||||
|         connector: HttpConnector, | ||||
|         proxy: proxy, | ||||
|         ssl: Default::default() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "security-framework")] | ||||
| pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::Openssl> { | ||||
|     Proxy { | ||||
|         connector: HttpConnector, | ||||
|         proxy: proxy, | ||||
|         ssl: Default::default() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(not(any(feature = "openssl", feature = "security-framework")))] | ||||
| pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, self::no_ssl::Plaintext> { | ||||
|     Proxy { | ||||
|         connector: HttpConnector, | ||||
|         proxy: proxy, | ||||
|         ssl: self::no_ssl::Plaintext, | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| pub struct Proxy<C, S> | ||||
| where C: NetworkConnector + Send + Sync + 'static, | ||||
|       C::Stream: NetworkStream + Send + Clone, | ||||
|       S: SslClient<C::Stream> { | ||||
|     pub connector: C, | ||||
|     pub proxy: (Cow<'static, str>, u16), | ||||
|     pub ssl: S, | ||||
| } | ||||
|  | ||||
|  | ||||
| impl<C, S> NetworkConnector for Proxy<C, S> | ||||
| where C: NetworkConnector + Send + Sync + 'static, | ||||
|       C::Stream: NetworkStream + Send + Clone, | ||||
|       S: SslClient<C::Stream> { | ||||
|     type Stream = Proxied<C::Stream, S::Stream>; | ||||
|  | ||||
|     fn connect(&self, host: &str, port: u16, scheme: &str) -> ::Result<Self::Stream> { | ||||
|         use httparse; | ||||
|         use std::io::{Read, Write}; | ||||
|         use ::version::HttpVersion::Http11; | ||||
|         trace!("{:?} proxy for '{}://{}:{}'", self.proxy, scheme, host, port); | ||||
|         match scheme { | ||||
|             "http" => { | ||||
|                 self.connector.connect(self.proxy.0.as_ref(), self.proxy.1, "http") | ||||
|                     .map(Proxied::Normal) | ||||
|             }, | ||||
|             "https" => { | ||||
|                 let mut stream = try!(self.connector.connect(self.proxy.0.as_ref(), self.proxy.1, "http")); | ||||
|                 trace!("{:?} CONNECT {}:{}", self.proxy, host, port); | ||||
|                 try!(write!(&mut stream, "{method} {host}:{port} {version}\r\nHost: {host}:{port}\r\n\r\n", | ||||
|                             method=Method::Connect, host=host, port=port, version=Http11)); | ||||
|                 try!(stream.flush()); | ||||
|                 let mut buf = [0; 1024]; | ||||
|                 let mut n = 0; | ||||
|                 while n < buf.len() { | ||||
|                     n += try!(stream.read(&mut buf[n..])); | ||||
|                     let mut headers = [httparse::EMPTY_HEADER; 10]; | ||||
|                     let mut res = httparse::Response::new(&mut headers); | ||||
|                     if try!(res.parse(&buf[..n])).is_complete() { | ||||
|                         let code = res.code.expect("complete parsing lost code"); | ||||
|                         if code >= 200 && code < 300 { | ||||
|                             trace!("CONNECT success = {:?}", code); | ||||
|                             return self.ssl.wrap_client(stream, host) | ||||
|                                 .map(Proxied::Tunneled) | ||||
|                         } else { | ||||
|                             trace!("CONNECT response = {:?}", code); | ||||
|                             return Err(::Error::Status); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 Err(::Error::TooLarge) | ||||
|             }, | ||||
|             _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid scheme").into()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub enum Proxied<T1, T2> { | ||||
|     Normal(T1), | ||||
|     Tunneled(T2) | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| impl<T1, T2> Proxied<T1, T2> { | ||||
|     pub fn into_normal(self) -> Result<T1, Self> { | ||||
|         match self { | ||||
|             Proxied::Normal(t1) => Ok(t1), | ||||
|             _ => Err(self) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn into_tunneled(self) -> Result<T2, Self> { | ||||
|         match self { | ||||
|             Proxied::Tunneled(t2) => Ok(t2), | ||||
|             _ => Err(self) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T1: NetworkStream, T2: NetworkStream> io::Read for Proxied<T1, T2> { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             Proxied::Normal(ref mut t) => io::Read::read(t, buf), | ||||
|             Proxied::Tunneled(ref mut t) => io::Read::read(t, buf), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T1: NetworkStream, T2: NetworkStream> io::Write for Proxied<T1, T2> { | ||||
|     #[inline] | ||||
|     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             Proxied::Normal(ref mut t) => io::Write::write(t, buf), | ||||
|             Proxied::Tunneled(ref mut t) => io::Write::write(t, buf), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         match *self { | ||||
|             Proxied::Normal(ref mut t) => io::Write::flush(t), | ||||
|             Proxied::Tunneled(ref mut t) => io::Write::flush(t), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T1: NetworkStream, T2: NetworkStream> NetworkStream for Proxied<T1, T2> { | ||||
|     #[inline] | ||||
|     fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         match *self { | ||||
|             Proxied::Normal(ref mut s) => s.peer_addr(), | ||||
|             Proxied::Tunneled(ref mut s) => s.peer_addr() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { | ||||
|         match *self { | ||||
|             Proxied::Normal(ref inner) => inner.set_read_timeout(dur), | ||||
|             Proxied::Tunneled(ref inner) => inner.set_read_timeout(dur) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> { | ||||
|         match *self { | ||||
|             Proxied::Normal(ref inner) => inner.set_write_timeout(dur), | ||||
|             Proxied::Tunneled(ref inner) => inner.set_write_timeout(dur) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn close(&mut self, how: Shutdown) -> io::Result<()> { | ||||
|         match *self { | ||||
|             Proxied::Normal(ref mut s) => s.close(how), | ||||
|             Proxied::Tunneled(ref mut s) => s.close(how) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(not(any(feature = "openssl", feature = "security-framework")))] | ||||
| mod no_ssl { | ||||
|     use std::io; | ||||
|     use std::net::{Shutdown, SocketAddr}; | ||||
|     use std::time::Duration; | ||||
|  | ||||
|     use net::{SslClient, NetworkStream}; | ||||
|  | ||||
|     pub struct Plaintext; | ||||
|  | ||||
|     #[derive(Clone)] | ||||
|     pub enum Void {} | ||||
|  | ||||
|     impl io::Read for Void { | ||||
|         #[inline] | ||||
|         fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> { | ||||
|             match *self {} | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl io::Write for Void { | ||||
|         #[inline] | ||||
|         fn write(&mut self, _buf: &[u8]) -> io::Result<usize> { | ||||
|             match *self {} | ||||
|         } | ||||
|  | ||||
|         #[inline] | ||||
|         fn flush(&mut self) -> io::Result<()> { | ||||
|             match *self {} | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl NetworkStream for Void { | ||||
|         #[inline] | ||||
|         fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|             match *self {} | ||||
|         } | ||||
|  | ||||
|         #[inline] | ||||
|         fn set_read_timeout(&self, _dur: Option<Duration>) -> io::Result<()> { | ||||
|             match *self {} | ||||
|         } | ||||
|  | ||||
|         #[inline] | ||||
|         fn set_write_timeout(&self, _dur: Option<Duration>) -> io::Result<()> { | ||||
|             match *self {} | ||||
|         } | ||||
|  | ||||
|         #[inline] | ||||
|         fn close(&mut self, _how: Shutdown) -> io::Result<()> { | ||||
|             match *self {} | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T: NetworkStream + Send + Clone> SslClient<T> for Plaintext { | ||||
|         type Stream = Void; | ||||
|  | ||||
|         fn wrap_client(&self, _stream: T, _host: &str) -> ::Result<Self::Stream> { | ||||
|             Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid scheme").into()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,177 +1,52 @@ | ||||
| //! Client Requests | ||||
| use std::marker::PhantomData; | ||||
| use std::io::{self, Write}; | ||||
|  | ||||
| use std::time::Duration; | ||||
|  | ||||
| use url::Url; | ||||
|  | ||||
| use method::Method; | ||||
| use header::Headers; | ||||
| use header::Host; | ||||
| use net::{NetworkStream, NetworkConnector, DefaultConnector, Fresh, Streaming}; | ||||
| use version; | ||||
| use client::{Response, get_host_and_port}; | ||||
| use http::RequestHead; | ||||
| use method::Method; | ||||
| use uri::RequestUri; | ||||
| use version::HttpVersion; | ||||
|  | ||||
| use http::{HttpMessage, RequestHead}; | ||||
| use http::h1::Http11Message; | ||||
|  | ||||
|  | ||||
| /// A client request to a remote server. | ||||
| /// The W type tracks the state of the request, Fresh vs Streaming. | ||||
| pub struct Request<W> { | ||||
|     /// The target URI for this request. | ||||
|     pub url: Url, | ||||
|  | ||||
|     /// The HTTP version of this request. | ||||
|     pub version: version::HttpVersion, | ||||
|  | ||||
|     message: Box<HttpMessage>, | ||||
|     headers: Headers, | ||||
|     method: Method, | ||||
|  | ||||
|     _marker: PhantomData<W>, | ||||
| #[derive(Debug)] | ||||
| pub struct Request<'a> { | ||||
|     head: &'a mut RequestHead | ||||
| } | ||||
|  | ||||
| impl<W> Request<W> { | ||||
| impl<'a> Request<'a> { | ||||
|     /// Read the Request Url. | ||||
|     #[inline] | ||||
|     pub fn uri(&self) -> &RequestUri { &self.head.subject.1 } | ||||
|  | ||||
|     /// Readthe Request Version. | ||||
|     #[inline] | ||||
|     pub fn version(&self) -> &HttpVersion { &self.head.version } | ||||
|  | ||||
|     /// Read the Request headers. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &Headers { &self.headers } | ||||
|     pub fn headers(&self) -> &Headers { &self.head.headers } | ||||
|  | ||||
|     /// Read the Request method. | ||||
|     #[inline] | ||||
|     pub fn method(&self) -> Method { self.method.clone() } | ||||
|     pub fn method(&self) -> &Method { &self.head.subject.0 } | ||||
|  | ||||
|     /// Set the write timeout. | ||||
|     /// Set the Method of this request. | ||||
|     #[inline] | ||||
|     pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> { | ||||
|         self.message.set_write_timeout(dur) | ||||
|     } | ||||
|  | ||||
|     /// Set the read timeout. | ||||
|     #[inline] | ||||
|     pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { | ||||
|         self.message.set_read_timeout(dur) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Request<Fresh> { | ||||
|     /// Create a new `Request<Fresh>` that will use the given `HttpMessage` for its communication | ||||
|     /// with the server. This implies that the given `HttpMessage` instance has already been | ||||
|     /// properly initialized by the caller (e.g. a TCP connection's already established). | ||||
|     pub fn with_message(method: Method, url: Url, message: Box<HttpMessage>) | ||||
|             -> ::Result<Request<Fresh>> { | ||||
|         let mut headers = Headers::new(); | ||||
|         { | ||||
|             let (host, port) = try!(get_host_and_port(&url)); | ||||
|             headers.set(Host { | ||||
|                 hostname: host.to_owned(), | ||||
|                 port: Some(port), | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         Ok(Request::with_headers_and_message(method, url, headers, message)) | ||||
|     } | ||||
|  | ||||
|     #[doc(hidden)] | ||||
|     pub fn with_headers_and_message(method: Method, url: Url, headers: Headers,  message: Box<HttpMessage>) | ||||
|                 -> Request<Fresh> { | ||||
|         Request { | ||||
|             method: method, | ||||
|             headers: headers, | ||||
|             url: url, | ||||
|             version: version::HttpVersion::Http11, | ||||
|             message: message, | ||||
|             _marker: PhantomData, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Create a new client request. | ||||
|     pub fn new(method: Method, url: Url) -> ::Result<Request<Fresh>> { | ||||
|         let conn = DefaultConnector::default(); | ||||
|         Request::with_connector(method, url, &conn) | ||||
|     } | ||||
|  | ||||
|     /// Create a new client request with a specific underlying NetworkStream. | ||||
|     pub fn with_connector<C, S>(method: Method, url: Url, connector: &C) | ||||
|         -> ::Result<Request<Fresh>> where | ||||
|         C: NetworkConnector<Stream=S>, | ||||
|         S: Into<Box<NetworkStream + Send>> { | ||||
|         let stream = { | ||||
|             let (host, port) = try!(get_host_and_port(&url)); | ||||
|             try!(connector.connect(host, port, url.scheme())).into() | ||||
|         }; | ||||
|  | ||||
|         Request::with_message(method, url, Box::new(Http11Message::with_stream(stream))) | ||||
|     } | ||||
|  | ||||
|     /// Consume a Fresh Request, writing the headers and method, | ||||
|     /// returning a Streaming Request. | ||||
|     pub fn start(mut self) -> ::Result<Request<Streaming>> { | ||||
|         let head = match self.message.set_outgoing(RequestHead { | ||||
|             headers: self.headers, | ||||
|             method: self.method, | ||||
|             url: self.url, | ||||
|         }) { | ||||
|             Ok(head) => head, | ||||
|             Err(e) => { | ||||
|                 let _ = self.message.close_connection(); | ||||
|                 return Err(From::from(e)); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         Ok(Request { | ||||
|             method: head.method, | ||||
|             headers: head.headers, | ||||
|             url: head.url, | ||||
|             version: self.version, | ||||
|             message: self.message, | ||||
|             _marker: PhantomData, | ||||
|         }) | ||||
|     } | ||||
|     pub fn set_method(&mut self, method: Method) { self.head.subject.0 = method; } | ||||
|  | ||||
|     /// Get a mutable reference to the Request headers. | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers } | ||||
|     pub fn headers_mut(&mut self) -> &mut Headers { &mut self.head.headers } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| impl Request<Streaming> { | ||||
|     /// Completes writing the request, and returns a response to read from. | ||||
|     /// | ||||
|     /// Consumes the Request. | ||||
|     pub fn send(self) -> ::Result<Response> { | ||||
|         Response::with_message(self.url, self.message) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Write for Request<Streaming> { | ||||
|     #[inline] | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         match self.message.write(msg) { | ||||
|             Ok(n) => Ok(n), | ||||
|             Err(e) => { | ||||
|                 let _ = self.message.close_connection(); | ||||
|                 Err(e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         match self.message.flush() { | ||||
|             Ok(r) => Ok(r), | ||||
|             Err(e) => { | ||||
|                 let _ = self.message.close_connection(); | ||||
|                 Err(e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| pub fn new(head: &mut RequestHead) -> Request { | ||||
|     Request { head: head } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     /* | ||||
|     use std::io::Write; | ||||
|     use std::str::from_utf8; | ||||
|     use url::Url; | ||||
| @@ -311,4 +186,5 @@ mod tests { | ||||
|             .get_ref().downcast_ref::<MockStream>().unwrap() | ||||
|             .is_closed); | ||||
|     } | ||||
|     */ | ||||
| } | ||||
|   | ||||
| @@ -1,82 +1,57 @@ | ||||
| //! Client Responses | ||||
| use std::io::{self, Read}; | ||||
|  | ||||
| use url::Url; | ||||
|  | ||||
| use header; | ||||
| use net::NetworkStream; | ||||
| use http::{self, RawStatus, ResponseHead, HttpMessage}; | ||||
| use http::h1::Http11Message; | ||||
| //use net::NetworkStream; | ||||
| use http::{self, RawStatus}; | ||||
| use status; | ||||
| use version; | ||||
|  | ||||
| pub fn new(incoming: http::ResponseHead) -> Response { | ||||
|     trace!("Response::new"); | ||||
|     let status = status::StatusCode::from_u16(incoming.subject.0); | ||||
|     debug!("version={:?}, status={:?}", incoming.version, status); | ||||
|     debug!("headers={:?}", incoming.headers); | ||||
|  | ||||
|     Response { | ||||
|         status: status, | ||||
|         version: incoming.version, | ||||
|         headers: incoming.headers, | ||||
|         status_raw: incoming.subject, | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| /// A response for a client request to a remote server. | ||||
| #[derive(Debug)] | ||||
| pub struct Response { | ||||
|     /// The status from the server. | ||||
|     pub status: status::StatusCode, | ||||
|     /// The headers from the server. | ||||
|     pub headers: header::Headers, | ||||
|     /// The HTTP version of this response from the server. | ||||
|     pub version: version::HttpVersion, | ||||
|     /// The final URL of this response. | ||||
|     pub url: Url, | ||||
|     status: status::StatusCode, | ||||
|     headers: header::Headers, | ||||
|     version: version::HttpVersion, | ||||
|     status_raw: RawStatus, | ||||
|     message: Box<HttpMessage>, | ||||
| } | ||||
|  | ||||
| impl Response { | ||||
|     /// Get the headers from the server. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &header::Headers { &self.headers } | ||||
|  | ||||
|     /// Creates a new response from a server. | ||||
|     pub fn new(url: Url, stream: Box<NetworkStream + Send>) -> ::Result<Response> { | ||||
|         trace!("Response::new"); | ||||
|         Response::with_message(url, Box::new(Http11Message::with_stream(stream))) | ||||
|     } | ||||
|  | ||||
|     /// Creates a new response received from the server on the given `HttpMessage`. | ||||
|     pub fn with_message(url: Url, mut message: Box<HttpMessage>) -> ::Result<Response> { | ||||
|         trace!("Response::with_message"); | ||||
|         let ResponseHead { headers, raw_status, version } = match message.get_incoming() { | ||||
|             Ok(head) => head, | ||||
|             Err(e) => { | ||||
|                 let _ = message.close_connection(); | ||||
|                 return Err(From::from(e)); | ||||
|             } | ||||
|         }; | ||||
|         let status = status::StatusCode::from_u16(raw_status.0); | ||||
|         debug!("version={:?}, status={:?}", version, status); | ||||
|         debug!("headers={:?}", headers); | ||||
|  | ||||
|         Ok(Response { | ||||
|             status: status, | ||||
|             version: version, | ||||
|             headers: headers, | ||||
|             url: url, | ||||
|             status_raw: raw_status, | ||||
|             message: message, | ||||
|         }) | ||||
|     } | ||||
|     /// Get the status from the server. | ||||
|     #[inline] | ||||
|     pub fn status(&self) -> &status::StatusCode { &self.status } | ||||
|  | ||||
|     /// Get the raw status code and reason. | ||||
|     #[inline] | ||||
|     pub fn status_raw(&self) -> &RawStatus { | ||||
|         &self.status_raw | ||||
|     } | ||||
| } | ||||
|     pub fn status_raw(&self) -> &RawStatus { &self.status_raw } | ||||
|  | ||||
| impl Read for Response { | ||||
|     /// Get the final URL of this response. | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match self.message.read(buf) { | ||||
|             Err(e) => { | ||||
|                 let _ = self.message.close_connection(); | ||||
|                 Err(e) | ||||
|             } | ||||
|             r => r | ||||
|         } | ||||
|     } | ||||
|     //pub fn url(&self) -> &Url { &self.url } | ||||
|  | ||||
|     /// Get the HTTP version of this response from the server. | ||||
|     #[inline] | ||||
|     pub fn version(&self) -> &version::HttpVersion { &self.version } | ||||
| } | ||||
|  | ||||
| /* | ||||
| impl Drop for Response { | ||||
|     fn drop(&mut self) { | ||||
|         // if not drained, theres old bits in the Reader. we can't reuse this, | ||||
| @@ -94,9 +69,11 @@ impl Drop for Response { | ||||
|         } | ||||
|     } | ||||
| } | ||||
| */ | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     /* | ||||
|     use std::io::{self, Read}; | ||||
|  | ||||
|     use url::Url; | ||||
| @@ -230,4 +207,5 @@ mod tests { | ||||
|  | ||||
|         assert!(Response::new(url, Box::new(stream)).is_err()); | ||||
|     } | ||||
|     */ | ||||
| } | ||||
|   | ||||
							
								
								
									
										22
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/error.rs
									
									
									
									
									
								
							| @@ -7,7 +7,6 @@ use std::string::FromUtf8Error; | ||||
|  | ||||
| use httparse; | ||||
| use url; | ||||
| use solicit::http::HttpError as Http2Error; | ||||
|  | ||||
| #[cfg(feature = "openssl")] | ||||
| use openssl::ssl::error::SslError; | ||||
| @@ -18,10 +17,11 @@ use self::Error::{ | ||||
|     Version, | ||||
|     Header, | ||||
|     Status, | ||||
|     Timeout, | ||||
|     Io, | ||||
|     Ssl, | ||||
|     TooLarge, | ||||
|     Http2, | ||||
|     Incomplete, | ||||
|     Utf8 | ||||
| }; | ||||
|  | ||||
| @@ -42,14 +42,16 @@ pub enum Error { | ||||
|     Header, | ||||
|     /// A message head is too large to be reasonable. | ||||
|     TooLarge, | ||||
|     /// A message reached EOF before being a complete message. | ||||
|     Incomplete, | ||||
|     /// An invalid `Status`, such as `1337 ELITE`. | ||||
|     Status, | ||||
|     /// A timeout occurred waiting for an IO event. | ||||
|     Timeout, | ||||
|     /// An `io::Error` that occurred while trying to read or write to a network stream. | ||||
|     Io(IoError), | ||||
|     /// An error from a SSL library. | ||||
|     Ssl(Box<StdError + Send + Sync>), | ||||
|     /// An HTTP/2-specific error, coming from the `solicit` library. | ||||
|     Http2(Http2Error), | ||||
|     /// Parsing a field as string failed | ||||
|     Utf8(Utf8Error), | ||||
|  | ||||
| @@ -80,10 +82,11 @@ impl StdError for Error { | ||||
|             Header => "Invalid Header provided", | ||||
|             TooLarge => "Message head is too large", | ||||
|             Status => "Invalid Status provided", | ||||
|             Incomplete => "Message is incomplete", | ||||
|             Timeout => "Timeout", | ||||
|             Uri(ref e) => e.description(), | ||||
|             Io(ref e) => e.description(), | ||||
|             Ssl(ref e) => e.description(), | ||||
|             Http2(ref e) => e.description(), | ||||
|             Utf8(ref e) => e.description(), | ||||
|             Error::__Nonexhaustive(ref void) =>  match *void {} | ||||
|         } | ||||
| @@ -94,7 +97,6 @@ impl StdError for Error { | ||||
|             Io(ref error) => Some(error), | ||||
|             Ssl(ref error) => Some(&**error), | ||||
|             Uri(ref error) => Some(error), | ||||
|             Http2(ref error) => Some(error), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| @@ -148,18 +150,11 @@ impl From<httparse::Error> for Error { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Http2Error> for Error { | ||||
|     fn from(err: Http2Error) -> Error { | ||||
|         Error::Http2(err) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::error::Error as StdError; | ||||
|     use std::io; | ||||
|     use httparse; | ||||
|     use solicit::http::HttpError as Http2Error; | ||||
|     use url; | ||||
|     use super::Error; | ||||
|     use super::Error::*; | ||||
| @@ -201,7 +196,6 @@ mod tests { | ||||
|  | ||||
|         from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => Io(..)); | ||||
|         from_and_cause!(url::ParseError::EmptyHost => Uri(..)); | ||||
|         from_and_cause!(Http2Error::UnknownStreamId => Http2(..)); | ||||
|  | ||||
|         from!(httparse::Error::HeaderName => Header); | ||||
|         from!(httparse::Error::HeaderName => Header); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use std::fmt::{self, Display}; | ||||
| use std::str; | ||||
| use unicase::UniCase; | ||||
| use header::{Header, HeaderFormat}; | ||||
| use header::{Header}; | ||||
|  | ||||
| /// `Access-Control-Allow-Credentials` header, part of | ||||
| /// [CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header) | ||||
| @@ -62,9 +62,7 @@ impl Header for AccessControlAllowCredentials { | ||||
|         } | ||||
|         Err(::Error::Header) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for AccessControlAllowCredentials { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_str("true") | ||||
|     } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| use std::fmt::{self, Display}; | ||||
|  | ||||
| use header::{Header, HeaderFormat}; | ||||
| use header::{Header}; | ||||
|  | ||||
| /// The `Access-Control-Allow-Origin` response header, | ||||
| /// part of [CORS](http://www.w3.org/TR/cors/#access-control-allow-origin-response-header) | ||||
| @@ -70,9 +70,7 @@ impl Header for AccessControlAllowOrigin { | ||||
|             _ => AccessControlAllowOrigin::Value(try!(String::from_utf8(value.clone()))) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for AccessControlAllowOrigin { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         match *self { | ||||
|             AccessControlAllowOrigin::Any => f.write_str("*"), | ||||
|   | ||||
| @@ -3,7 +3,7 @@ use std::fmt::{self, Display}; | ||||
| use std::str::{FromStr, from_utf8}; | ||||
| use std::ops::{Deref, DerefMut}; | ||||
| use serialize::base64::{ToBase64, FromBase64, Standard, Config, Newline}; | ||||
| use header::{Header, HeaderFormat}; | ||||
| use header::{Header}; | ||||
|  | ||||
| /// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2) | ||||
| /// | ||||
| @@ -97,9 +97,7 @@ impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'st | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: Scheme + Any> HeaderFormat for Authorization<S> where <S as FromStr>::Err: 'static { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         if let Some(scheme) = <S as Scheme>::scheme() { | ||||
|             try!(write!(f, "{} ", scheme)) | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| use std::fmt; | ||||
| use std::str::FromStr; | ||||
| use header::{Header, HeaderFormat}; | ||||
| use header::Header; | ||||
| use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | ||||
|  | ||||
| /// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2) | ||||
| @@ -62,9 +62,7 @@ impl Header for CacheControl { | ||||
|             Err(::Error::Header) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for CacheControl { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt_comma_delimited(f, &self[..]) | ||||
|     } | ||||
|   | ||||
| @@ -11,7 +11,7 @@ use std::fmt; | ||||
| use unicase::UniCase; | ||||
| use url::percent_encoding; | ||||
|  | ||||
| use header::{Header, HeaderFormat, parsing}; | ||||
| use header::{Header, parsing}; | ||||
| use header::parsing::{parse_extended_value, HTTP_VALUE}; | ||||
| use header::shared::Charset; | ||||
|  | ||||
| @@ -144,9 +144,7 @@ impl Header for ContentDisposition { | ||||
|             Ok(cd) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for ContentDisposition { | ||||
|     #[inline] | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt::Display::fmt(&self, f) | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| use std::fmt; | ||||
|  | ||||
| use header::{HeaderFormat, Header, parsing}; | ||||
| use header::{Header, parsing}; | ||||
|  | ||||
| /// `Content-Length` header, defined in | ||||
| /// [RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.2) | ||||
| @@ -55,9 +55,7 @@ impl Header for ContentLength { | ||||
|             .unwrap_or(Err(::Error::Header)) | ||||
|             .map(ContentLength) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for ContentLength { | ||||
|     #[inline] | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt::Display::fmt(&self.0, f) | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| use header::{Header, HeaderFormat, CookiePair, CookieJar}; | ||||
| use header::{Header, CookiePair, CookieJar}; | ||||
| use std::fmt::{self, Display}; | ||||
| use std::str::from_utf8; | ||||
|  | ||||
| @@ -61,9 +61,7 @@ impl Header for Cookie { | ||||
|             Err(::Error::Header) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for Cookie { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         let cookies = &self.0; | ||||
|         for (i, cookie) in cookies.iter().enumerate() { | ||||
|   | ||||
| @@ -3,7 +3,7 @@ use std::str; | ||||
|  | ||||
| use unicase::UniCase; | ||||
|  | ||||
| use header::{Header, HeaderFormat}; | ||||
| use header::{Header}; | ||||
|  | ||||
| /// The `Expect` header. | ||||
| /// | ||||
| @@ -53,9 +53,7 @@ impl Header for Expect { | ||||
|             Err(::Error::Header) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for Expect { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_str("100-continue") | ||||
|     } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| use header::{Header, HeaderFormat}; | ||||
| use header::{Header}; | ||||
| use std::fmt; | ||||
| use header::parsing::from_one_raw_str; | ||||
|  | ||||
| @@ -52,9 +52,7 @@ impl Header for Host { | ||||
|             // https://github.com/servo/rust-url/issues/42 | ||||
|             let idx = { | ||||
|                 let slice = &s[..]; | ||||
|                 let mut chars = slice.chars(); | ||||
|                 chars.next(); | ||||
|                 if chars.next().unwrap() == '[' { | ||||
|                 if slice.starts_with('[') { | ||||
|                     match slice.rfind(']') { | ||||
|                         Some(idx) => { | ||||
|                             if slice.len() > idx + 2 { | ||||
| @@ -86,9 +84,7 @@ impl Header for Host { | ||||
|             }) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for Host { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         match self.port { | ||||
|             None | Some(80) | Some(443) => f.write_str(&self.hostname[..]), | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| use std::fmt::{self, Display}; | ||||
| use header::{self, Header, HeaderFormat, EntityTag, HttpDate}; | ||||
| use header::{self, Header, EntityTag, HttpDate}; | ||||
|  | ||||
| /// `If-Range` header, defined in [RFC7233](http://tools.ietf.org/html/rfc7233#section-3.2) | ||||
| /// | ||||
| @@ -59,18 +59,16 @@ impl Header for IfRange { | ||||
|     } | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> { | ||||
|         let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw); | ||||
|         if etag.is_ok() { | ||||
|             return Ok(IfRange::EntityTag(etag.unwrap())); | ||||
|         if let Ok(etag) = etag { | ||||
|             return Ok(IfRange::EntityTag(etag)); | ||||
|         } | ||||
|         let date: ::Result<HttpDate> = header::parsing::from_one_raw_str(raw); | ||||
|         if date.is_ok() { | ||||
|             return Ok(IfRange::Date(date.unwrap())); | ||||
|         if let Ok(date) = date { | ||||
|             return Ok(IfRange::Date(date)); | ||||
|         } | ||||
|         Err(::Error::Header) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for IfRange { | ||||
|     fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|         match *self { | ||||
|             IfRange::EntityTag(ref x) => Display::fmt(x, f), | ||||
|   | ||||
| @@ -66,7 +66,7 @@ macro_rules! bench_header( | ||||
|             use test::Bencher; | ||||
|             use super::*; | ||||
|  | ||||
|             use header::{Header, HeaderFormatter}; | ||||
|             use header::{Header}; | ||||
|  | ||||
|             #[bench] | ||||
|             fn bench_parse(b: &mut Bencher) { | ||||
| @@ -79,7 +79,7 @@ macro_rules! bench_header( | ||||
|             #[bench] | ||||
|             fn bench_format(b: &mut Bencher) { | ||||
|                 let val: $ty = Header::parse_header(&$value[..]).unwrap(); | ||||
|                 let fmt = HeaderFormatter(&val); | ||||
|                 let fmt = ::header::HeaderFormatter(&val); | ||||
|                 b.iter(|| { | ||||
|                     format!("{}", fmt); | ||||
|                 }); | ||||
| @@ -222,15 +222,13 @@ macro_rules! header { | ||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { | ||||
|                 $crate::header::parsing::from_comma_delimited(raw).map($id) | ||||
|             } | ||||
|         } | ||||
|         impl $crate::header::HeaderFormat for $id { | ||||
|             fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|                 $crate::header::parsing::fmt_comma_delimited(f, &self.0[..]) | ||||
|             } | ||||
|         } | ||||
|         impl ::std::fmt::Display for $id { | ||||
|             fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|                 use $crate::header::HeaderFormat; | ||||
|                 use $crate::header::Header; | ||||
|                 self.fmt_header(f) | ||||
|             } | ||||
|         } | ||||
| @@ -250,15 +248,13 @@ macro_rules! header { | ||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { | ||||
|                 $crate::header::parsing::from_comma_delimited(raw).map($id) | ||||
|             } | ||||
|         } | ||||
|         impl $crate::header::HeaderFormat for $id { | ||||
|             fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|                 $crate::header::parsing::fmt_comma_delimited(f, &self.0[..]) | ||||
|             } | ||||
|         } | ||||
|         impl ::std::fmt::Display for $id { | ||||
|             fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|                 use $crate::header::HeaderFormat; | ||||
|                 use $crate::header::Header; | ||||
|                 self.fmt_header(f) | ||||
|             } | ||||
|         } | ||||
| @@ -277,8 +273,6 @@ macro_rules! header { | ||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { | ||||
|                 $crate::header::parsing::from_one_raw_str(raw).map($id) | ||||
|             } | ||||
|         } | ||||
|         impl $crate::header::HeaderFormat for $id { | ||||
|             fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|                 ::std::fmt::Display::fmt(&**self, f) | ||||
|             } | ||||
| @@ -313,8 +307,6 @@ macro_rules! header { | ||||
|                 } | ||||
|                 $crate::header::parsing::from_comma_delimited(raw).map($id::Items) | ||||
|             } | ||||
|         } | ||||
|         impl $crate::header::HeaderFormat for $id { | ||||
|             fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|                 match *self { | ||||
|                     $id::Any => f.write_str("*"), | ||||
| @@ -325,7 +317,7 @@ macro_rules! header { | ||||
|         } | ||||
|         impl ::std::fmt::Display for $id { | ||||
|             fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|                 use $crate::header::HeaderFormat; | ||||
|                 use $crate::header::Header; | ||||
|                 self.fmt_header(f) | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use std::fmt; | ||||
| use std::ascii::AsciiExt; | ||||
|  | ||||
| use header::{Header, HeaderFormat, parsing}; | ||||
| use header::{Header, parsing}; | ||||
|  | ||||
| /// The `Pragma` header defined by HTTP/1.0. | ||||
| /// | ||||
| @@ -52,9 +52,7 @@ impl Header for Pragma { | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for Pragma { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_str(match *self { | ||||
|             Pragma::NoCache => "no-cache", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| use std::fmt; | ||||
| use std::str::FromStr; | ||||
| use header::{Header, HeaderFormat}; | ||||
| use header::{Header}; | ||||
| use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | ||||
|  | ||||
| /// `Prefer` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240) | ||||
| @@ -64,9 +64,7 @@ impl Header for Prefer { | ||||
|             Err(::Error::Header) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for Prefer { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt_comma_delimited(f, &self[..]) | ||||
|     } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| use std::fmt; | ||||
| use header::{Header, HeaderFormat, Preference}; | ||||
| use header::{Header, Preference}; | ||||
| use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | ||||
|  | ||||
| /// `Preference-Applied` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240) | ||||
| @@ -61,9 +61,7 @@ impl Header for PreferenceApplied { | ||||
|             Err(::Error::Header) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for PreferenceApplied { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         let preferences: Vec<_> = self.0.iter().map(|pref| match pref { | ||||
|             // The spec ignores parameters in `Preferences-Applied` | ||||
| @@ -80,7 +78,7 @@ impl HeaderFormat for PreferenceApplied { | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use header::{HeaderFormat, Preference}; | ||||
|     use header::{Header, Preference}; | ||||
|     use super::*; | ||||
|  | ||||
|     #[test] | ||||
| @@ -90,7 +88,7 @@ mod tests { | ||||
|                 "foo".to_owned(), | ||||
|                 "bar".to_owned(), | ||||
|                 vec![("bar".to_owned(), "foo".to_owned()), ("buz".to_owned(), "".to_owned())] | ||||
|             )]) as &(HeaderFormat + Send + Sync)), | ||||
|             )]) as &(Header + Send + Sync)), | ||||
|             "foo=bar".to_owned() | ||||
|         ); | ||||
|     } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use std::fmt::{self, Display}; | ||||
| use std::str::FromStr; | ||||
|  | ||||
| use header::{Header, HeaderFormat}; | ||||
| use header::Header; | ||||
| use header::parsing::{from_one_raw_str, from_comma_delimited}; | ||||
|  | ||||
| /// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1) | ||||
| @@ -182,9 +182,6 @@ impl Header for Range { | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> { | ||||
|         from_one_raw_str(raw) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for Range { | ||||
|  | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         Display::fmt(self, f) | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| use header::{Header, HeaderFormat, CookiePair, CookieJar}; | ||||
| use header::{Header, CookiePair, CookieJar}; | ||||
| use std::fmt::{self, Display}; | ||||
| use std::str::from_utf8; | ||||
|  | ||||
| @@ -104,10 +104,6 @@ impl Header for SetCookie { | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for SetCookie { | ||||
|  | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         for (i, cookie) in self.0.iter().enumerate() { | ||||
|             if i != 0 { | ||||
|   | ||||
| @@ -3,7 +3,7 @@ use std::str::{self, FromStr}; | ||||
|  | ||||
| use unicase::UniCase; | ||||
|  | ||||
| use header::{Header, HeaderFormat, parsing}; | ||||
| use header::{Header, parsing}; | ||||
|  | ||||
| /// `StrictTransportSecurity` header, defined in [RFC6797](https://tools.ietf.org/html/rfc6797) | ||||
| /// | ||||
| @@ -127,9 +127,7 @@ impl Header for StrictTransportSecurity { | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<StrictTransportSecurity> { | ||||
|         parsing::from_one_raw_str(raw) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat for StrictTransportSecurity { | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         if self.include_subdomains { | ||||
|             write!(f, "max-age={}; includeSubdomains", self.max_age) | ||||
|   | ||||
| @@ -49,5 +49,12 @@ header! { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl TransferEncoding { | ||||
|     /// Constructor for the most common Transfer-Encoding, `chunked`. | ||||
|     pub fn chunked() -> TransferEncoding { | ||||
|         TransferEncoding(vec![Encoding::Chunked]) | ||||
|     } | ||||
| } | ||||
|  | ||||
| bench_header!(normal, TransferEncoding, { vec![b"chunked, gzip".to_vec()] }); | ||||
| bench_header!(ext, TransferEncoding, { vec![b"ext".to_vec()] }); | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| use std::any::{Any, TypeId}; | ||||
| use std::cell::UnsafeCell; | ||||
| use std::collections::HashMap; | ||||
| use std::fmt; | ||||
| use std::mem; | ||||
| use std::ops::Deref; | ||||
|  | ||||
| @@ -53,7 +52,7 @@ enum PtrMap<T> { | ||||
|     Many(HashMap<TypeId, T>) | ||||
| } | ||||
|  | ||||
| impl<V: ?Sized + fmt::Debug + Any + 'static> PtrMapCell<V> { | ||||
| impl<V: ?Sized + Any + 'static> PtrMapCell<V> { | ||||
|     #[inline] | ||||
|     pub fn new() -> PtrMapCell<V> { | ||||
|         PtrMapCell(UnsafeCell::new(PtrMap::Empty)) | ||||
| @@ -114,12 +113,12 @@ impl<V: ?Sized + fmt::Debug + Any + 'static> PtrMapCell<V> { | ||||
|         let map = &*self.0.get(); | ||||
|         match *map { | ||||
|             PtrMap::One(_, ref one) => one, | ||||
|             _ => panic!("not PtrMap::One value, {:?}", *map) | ||||
|             _ => panic!("not PtrMap::One value") | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<V: ?Sized + fmt::Debug + Any + 'static> Clone for PtrMapCell<V> where Box<V>: Clone { | ||||
| impl<V: ?Sized + Any + 'static> Clone for PtrMapCell<V> where Box<V>: Clone { | ||||
|     #[inline] | ||||
|     fn clone(&self) -> PtrMapCell<V> { | ||||
|         let cell = PtrMapCell::new(); | ||||
|   | ||||
| @@ -4,13 +4,13 @@ use std::fmt; | ||||
| use std::str::from_utf8; | ||||
|  | ||||
| use super::cell::{OptCell, PtrMapCell}; | ||||
| use header::{Header, HeaderFormat}; | ||||
| use header::{Header}; | ||||
|  | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct Item { | ||||
|     raw: OptCell<Vec<Vec<u8>>>, | ||||
|     typed: PtrMapCell<HeaderFormat + Send + Sync> | ||||
|     typed: PtrMapCell<Header + Send + Sync> | ||||
| } | ||||
|  | ||||
| impl Item { | ||||
| @@ -23,7 +23,7 @@ impl Item { | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn new_typed(ty: Box<HeaderFormat + Send + Sync>) -> Item { | ||||
|     pub fn new_typed(ty: Box<Header + Send + Sync>) -> Item { | ||||
|         let map = PtrMapCell::new(); | ||||
|         unsafe { map.insert((*ty).get_type(), ty); } | ||||
|         Item { | ||||
| @@ -52,7 +52,7 @@ impl Item { | ||||
|         &raw[..] | ||||
|     } | ||||
|  | ||||
|     pub fn typed<H: Header + HeaderFormat + Any>(&self) -> Option<&H> { | ||||
|     pub fn typed<H: Header + Any>(&self) -> Option<&H> { | ||||
|         let tid = TypeId::of::<H>(); | ||||
|         match self.typed.get(tid) { | ||||
|             Some(val) => Some(val), | ||||
| @@ -68,7 +68,7 @@ impl Item { | ||||
|         }.map(|typed| unsafe { typed.downcast_ref_unchecked() }) | ||||
|     } | ||||
|  | ||||
|     pub fn typed_mut<H: Header + HeaderFormat>(&mut self) -> Option<&mut H> { | ||||
|     pub fn typed_mut<H: Header>(&mut self) -> Option<&mut H> { | ||||
|         let tid = TypeId::of::<H>(); | ||||
|         if self.typed.get_mut(tid).is_none() { | ||||
|             match parse::<H>(self.raw.as_ref().expect("item.raw must exist")) { | ||||
| @@ -83,11 +83,11 @@ impl Item { | ||||
| } | ||||
|  | ||||
| #[inline] | ||||
| fn parse<H: Header + HeaderFormat>(raw: &Vec<Vec<u8>>) -> | ||||
|         ::Result<Box<HeaderFormat + Send + Sync>> { | ||||
| fn parse<H: Header>(raw: &Vec<Vec<u8>>) -> | ||||
|         ::Result<Box<Header + Send + Sync>> { | ||||
|     Header::parse_header(&raw[..]).map(|h: H| { | ||||
|         // FIXME: Use Type ascription | ||||
|         let h: Box<HeaderFormat + Send + Sync> = Box::new(h); | ||||
|         let h: Box<Header + Send + Sync> = Box::new(h); | ||||
|         h | ||||
|     }) | ||||
| } | ||||
|   | ||||
| @@ -31,18 +31,17 @@ | ||||
| //! } | ||||
| //! ``` | ||||
| //! | ||||
| //! This works well for simple "string" headers. But the header system | ||||
| //! actually involves 2 parts: parsing, and formatting. If you need to | ||||
| //! customize either part, you can do so. | ||||
| //! This works well for simple "string" headers.  If you need more control, | ||||
| //! you can implement the trait directly. | ||||
| //! | ||||
| //! ## `Header` and `HeaderFormat` | ||||
| //! ## Implementing the `Header` trait | ||||
| //! | ||||
| //! Consider a Do Not Track header. It can be true or false, but it represents | ||||
| //! that via the numerals `1` and `0`. | ||||
| //! | ||||
| //! ``` | ||||
| //! use std::fmt; | ||||
| //! use hyper::header::{Header, HeaderFormat}; | ||||
| //! use hyper::header::Header; | ||||
| //! | ||||
| //! #[derive(Debug, Clone, Copy)] | ||||
| //! struct Dnt(bool); | ||||
| @@ -66,9 +65,7 @@ | ||||
| //!         } | ||||
| //!         Err(hyper::Error::Header) | ||||
| //!     } | ||||
| //! } | ||||
| //! | ||||
| //! impl HeaderFormat for Dnt { | ||||
| //!     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| //!         if self.0 { | ||||
| //!             f.write_str("1") | ||||
| @@ -113,11 +110,11 @@ type HeaderName = UniCase<CowStr>; | ||||
| /// | ||||
| /// This trait represents the construction and identification of headers, | ||||
| /// and contains trait-object unsafe methods. | ||||
| pub trait Header: Clone + Any + Send + Sync { | ||||
| pub trait Header: HeaderClone + Any + Typeable + Send + Sync { | ||||
|     /// Returns the name of the header field this belongs to. | ||||
|     /// | ||||
|     /// This will become an associated constant once available. | ||||
|     fn header_name() -> &'static str; | ||||
|     fn header_name() -> &'static str where Self: Sized; | ||||
|     /// Parse a header from a raw stream of bytes. | ||||
|     /// | ||||
|     /// It's possible that a request can include a header field more than once, | ||||
| @@ -125,35 +122,27 @@ pub trait Header: Clone + Any + Send + Sync { | ||||
|     /// it's not necessarily the case that a Header is *allowed* to have more | ||||
|     /// than one field value. If that's the case, you **should** return `None` | ||||
|     /// if `raw.len() > 1`. | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Self>; | ||||
|  | ||||
| } | ||||
|  | ||||
| /// A trait for any object that will represent a header field and value. | ||||
| /// | ||||
| /// This trait represents the formatting of a `Header` for output to a TcpStream. | ||||
| pub trait HeaderFormat: fmt::Debug + HeaderClone + Any + Typeable + Send + Sync { | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Self> where Self: Sized; | ||||
|     /// Format a header to be output into a TcpStream. | ||||
|     /// | ||||
|     /// This method is not allowed to introduce an Err not produced | ||||
|     /// by the passed-in Formatter. | ||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result; | ||||
|  | ||||
| } | ||||
|  | ||||
| #[doc(hidden)] | ||||
| pub trait HeaderClone { | ||||
|     fn clone_box(&self) -> Box<HeaderFormat + Send + Sync>; | ||||
|     fn clone_box(&self) -> Box<Header + Send + Sync>; | ||||
| } | ||||
|  | ||||
| impl<T: HeaderFormat + Clone> HeaderClone for T { | ||||
| impl<T: Header + Clone> HeaderClone for T { | ||||
|     #[inline] | ||||
|     fn clone_box(&self) -> Box<HeaderFormat + Send + Sync> { | ||||
|     fn clone_box(&self) -> Box<Header + Send + Sync> { | ||||
|         Box::new(self.clone()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HeaderFormat + Send + Sync { | ||||
| impl Header + Send + Sync { | ||||
|     #[inline] | ||||
|     unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T { | ||||
|         mem::transmute(traitobject::data(self)) | ||||
| @@ -165,9 +154,9 @@ impl HeaderFormat + Send + Sync { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Clone for Box<HeaderFormat + Send + Sync> { | ||||
| impl Clone for Box<Header + Send + Sync> { | ||||
|     #[inline] | ||||
|     fn clone(&self) -> Box<HeaderFormat + Send + Sync> { | ||||
|     fn clone(&self) -> Box<Header + Send + Sync> { | ||||
|         self.clone_box() | ||||
|     } | ||||
| } | ||||
| @@ -183,6 +172,12 @@ pub struct Headers { | ||||
|     data: HashMap<HeaderName, Item> | ||||
| } | ||||
|  | ||||
| impl Default for Headers { | ||||
|     fn default() -> Headers { | ||||
|         Headers::new() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Headers { | ||||
|  | ||||
|     /// Creates a new, empty headers map. | ||||
| @@ -212,8 +207,8 @@ impl Headers { | ||||
|     /// Set a header field to the corresponding value. | ||||
|     /// | ||||
|     /// The field is determined by the type of the value being set. | ||||
|     pub fn set<H: Header + HeaderFormat>(&mut self, value: H) { | ||||
|         trace!("Headers.set( {:?}, {:?} )", header_name::<H>(), value); | ||||
|     pub fn set<H: Header>(&mut self, value: H) { | ||||
|         trace!("Headers.set( {:?}, {:?} )", header_name::<H>(), HeaderFormatter(&value)); | ||||
|         self.data.insert(UniCase(CowStr(Cow::Borrowed(header_name::<H>()))), | ||||
|                          Item::new_typed(Box::new(value))); | ||||
|     } | ||||
| @@ -259,13 +254,13 @@ impl Headers { | ||||
|     } | ||||
|  | ||||
|     /// Get a reference to the header field's value, if it exists. | ||||
|     pub fn get<H: Header + HeaderFormat>(&self) -> Option<&H> { | ||||
|     pub fn get<H: Header>(&self) -> Option<&H> { | ||||
|         self.data.get(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))) | ||||
|         .and_then(Item::typed::<H>) | ||||
|     } | ||||
|  | ||||
|     /// Get a mutable reference to the header field's value, if it exists. | ||||
|     pub fn get_mut<H: Header + HeaderFormat>(&mut self) -> Option<&mut H> { | ||||
|     pub fn get_mut<H: Header>(&mut self) -> Option<&mut H> { | ||||
|         self.data.get_mut(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))) | ||||
|         .and_then(Item::typed_mut::<H>) | ||||
|     } | ||||
| @@ -280,13 +275,13 @@ impl Headers { | ||||
|     /// # let mut headers = Headers::new(); | ||||
|     /// let has_type = headers.has::<ContentType>(); | ||||
|     /// ``` | ||||
|     pub fn has<H: Header + HeaderFormat>(&self) -> bool { | ||||
|     pub fn has<H: Header>(&self) -> bool { | ||||
|         self.data.contains_key(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))) | ||||
|     } | ||||
|  | ||||
|     /// Removes a header from the map, if one existed. | ||||
|     /// Returns true if a header has been removed. | ||||
|     pub fn remove<H: Header + HeaderFormat>(&mut self) -> bool { | ||||
|     pub fn remove<H: Header>(&mut self) -> bool { | ||||
|         trace!("Headers.remove( {:?} )", header_name::<H>()); | ||||
|         self.data.remove(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))).is_some() | ||||
|     } | ||||
| @@ -380,6 +375,7 @@ impl Deserialize for Headers { | ||||
| } | ||||
|  | ||||
| /// An `Iterator` over the fields in a `Headers` map. | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct HeadersItems<'a> { | ||||
|     inner: Iter<'a, HeaderName, Item> | ||||
| } | ||||
| @@ -410,7 +406,7 @@ impl<'a> HeaderView<'a> { | ||||
|  | ||||
|     /// Cast the value to a certain Header type. | ||||
|     #[inline] | ||||
|     pub fn value<H: Header + HeaderFormat>(&self) -> Option<&'a H> { | ||||
|     pub fn value<H: Header>(&self) -> Option<&'a H> { | ||||
|         self.1.typed::<H>() | ||||
|     } | ||||
|  | ||||
| @@ -449,7 +445,7 @@ impl<'a> FromIterator<HeaderView<'a>> for Headers { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> fmt::Display for &'a (HeaderFormat + Send + Sync) { | ||||
| impl<'a> fmt::Display for &'a (Header + Send + Sync) { | ||||
|     #[inline] | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         (**self).fmt_header(f) | ||||
| @@ -461,16 +457,16 @@ impl<'a> fmt::Display for &'a (HeaderFormat + Send + Sync) { | ||||
| /// This can be used like so: `format!("{}", HeaderFormatter(&header))` to | ||||
| /// get the representation of a Header which will be written to an | ||||
| /// outgoing `TcpStream`. | ||||
| pub struct HeaderFormatter<'a, H: HeaderFormat>(pub &'a H); | ||||
| pub struct HeaderFormatter<'a, H: Header>(pub &'a H); | ||||
|  | ||||
| impl<'a, H: HeaderFormat> fmt::Display for HeaderFormatter<'a, H> { | ||||
| impl<'a, H: Header> fmt::Display for HeaderFormatter<'a, H> { | ||||
|     #[inline] | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         self.0.fmt_header(f) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, H: HeaderFormat> fmt::Debug for HeaderFormatter<'a, H> { | ||||
| impl<'a, H: Header> fmt::Debug for HeaderFormatter<'a, H> { | ||||
|     #[inline] | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         self.0.fmt_header(f) | ||||
| @@ -519,7 +515,7 @@ mod tests { | ||||
|     use mime::Mime; | ||||
|     use mime::TopLevel::Text; | ||||
|     use mime::SubLevel::Plain; | ||||
|     use super::{Headers, Header, HeaderFormat, ContentLength, ContentType, | ||||
|     use super::{Headers, Header, ContentLength, ContentType, | ||||
|                 Accept, Host, qitem}; | ||||
|     use httparse; | ||||
|  | ||||
| @@ -597,9 +593,7 @@ mod tests { | ||||
|                 None => Err(::Error::Header), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl HeaderFormat for CrazyLength { | ||||
|         fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|             let CrazyLength(ref opt, ref value) = *self; | ||||
|             write!(f, "{:?}, {:?}", opt, value) | ||||
|   | ||||
| @@ -137,6 +137,12 @@ define_encode_set! { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for HTTP_VALUE { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.pad("HTTP_VALUE") | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Display for ExtendedValue { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         let encoded_value = | ||||
|   | ||||
							
								
								
									
										120
									
								
								src/http/buffer.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/http/buffer.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| use std::cmp; | ||||
| use std::io::{self, Read}; | ||||
| use std::ptr; | ||||
|  | ||||
|  | ||||
| const INIT_BUFFER_SIZE: usize = 4096; | ||||
| const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct Buffer { | ||||
|     vec: Vec<u8>, | ||||
|     read_pos: usize, | ||||
|     write_pos: usize, | ||||
| } | ||||
|  | ||||
| impl Buffer { | ||||
|     pub fn new() -> Buffer { | ||||
|         Buffer { | ||||
|             vec: Vec::new(), | ||||
|             read_pos: 0, | ||||
|             write_pos: 0, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn reset(&mut self) { | ||||
|         *self = Buffer::new() | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn len(&self) -> usize { | ||||
|         self.read_pos - self.write_pos | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         self.len() == 0 | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn bytes(&self) -> &[u8] { | ||||
|         &self.vec[self.write_pos..self.read_pos] | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn consume(&mut self, pos: usize) { | ||||
|         debug_assert!(self.read_pos >= self.write_pos + pos); | ||||
|         self.write_pos += pos; | ||||
|         if self.write_pos == self.read_pos { | ||||
|             self.write_pos = 0; | ||||
|             self.read_pos = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn read_from<R: Read>(&mut self, r: &mut R) -> io::Result<usize> { | ||||
|         self.maybe_reserve(); | ||||
|         let n = try!(r.read(&mut self.vec[self.read_pos..])); | ||||
|         self.read_pos += n; | ||||
|         Ok(n) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn maybe_reserve(&mut self) { | ||||
|         let cap = self.vec.len(); | ||||
|         if cap == 0 { | ||||
|             trace!("reserving initial {}", INIT_BUFFER_SIZE); | ||||
|             self.vec = vec![0; INIT_BUFFER_SIZE]; | ||||
|         } else if self.write_pos > 0  && self.read_pos == cap { | ||||
|             let count = self.read_pos - self.write_pos; | ||||
|             trace!("moving buffer bytes over by {}", count); | ||||
|             unsafe { | ||||
|                 ptr::copy( | ||||
|                     self.vec.as_ptr().offset(self.write_pos as isize), | ||||
|                     self.vec.as_mut_ptr(), | ||||
|                     count | ||||
|                 ); | ||||
|             } | ||||
|             self.read_pos -= count; | ||||
|             self.write_pos = 0; | ||||
|         } else if self.read_pos == cap && cap < MAX_BUFFER_SIZE { | ||||
|             self.vec.reserve(cmp::min(cap * 4, MAX_BUFFER_SIZE) - cap); | ||||
|             let new = self.vec.capacity() - cap; | ||||
|             trace!("reserved {}", new); | ||||
|             unsafe { grow_zerofill(&mut self.vec, new) } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn wrap<'a, 'b: 'a, R: io::Read>(&'a mut self, reader: &'b mut R) -> BufReader<'a, R> { | ||||
|         BufReader { | ||||
|             buf: self, | ||||
|             reader: reader | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct BufReader<'a, R: io::Read + 'a> { | ||||
|     buf: &'a mut Buffer, | ||||
|     reader: &'a mut R | ||||
| } | ||||
|  | ||||
| impl<'a, R: io::Read> Read for BufReader<'a, R> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         trace!("BufReader.read self={}, buf={}", self.buf.len(), buf.len()); | ||||
|         let n = try!(self.buf.bytes().read(buf)); | ||||
|         self.buf.consume(n); | ||||
|         if n == 0 { | ||||
|             self.buf.reset(); | ||||
|             self.reader.read(&mut buf[n..]) | ||||
|         } else { | ||||
|             Ok(n) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[inline] | ||||
| unsafe fn grow_zerofill(buf: &mut Vec<u8>, additional: usize) { | ||||
|     let len = buf.len(); | ||||
|     buf.set_len(len + additional); | ||||
|     ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()); | ||||
| } | ||||
							
								
								
									
										96
									
								
								src/http/channel.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/http/channel.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| use std::fmt; | ||||
| use std::sync::{Arc, mpsc}; | ||||
| use std::sync::atomic::{AtomicBool, Ordering}; | ||||
| use ::rotor; | ||||
|  | ||||
| pub use std::sync::mpsc::TryRecvError; | ||||
|  | ||||
| pub fn new<T>(notify: rotor::Notifier) -> (Sender<T>, Receiver<T>) { | ||||
|     let b = Arc::new(AtomicBool::new(false)); | ||||
|     let (tx, rx) = mpsc::channel(); | ||||
|     (Sender { | ||||
|         awake: b.clone(), | ||||
|         notify: notify, | ||||
|         tx: tx, | ||||
|     }, | ||||
|     Receiver { | ||||
|         awake: b, | ||||
|         rx: rx, | ||||
|     }) | ||||
| } | ||||
|  | ||||
| pub fn share<T, U>(other: &Sender<U>) -> (Sender<T>, Receiver<T>) { | ||||
|     let (tx, rx) = mpsc::channel(); | ||||
|     let notify = other.notify.clone(); | ||||
|     let b = other.awake.clone(); | ||||
|     (Sender { | ||||
|         awake: b.clone(), | ||||
|         notify: notify, | ||||
|         tx: tx, | ||||
|     }, | ||||
|     Receiver { | ||||
|         awake: b, | ||||
|         rx: rx, | ||||
|     }) | ||||
| } | ||||
|  | ||||
| pub struct Sender<T> { | ||||
|     awake: Arc<AtomicBool>, | ||||
|     notify: rotor::Notifier, | ||||
|     tx: mpsc::Sender<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Send> Sender<T> { | ||||
|     pub fn send(&self, val: T) -> Result<(), SendError<T>> { | ||||
|         try!(self.tx.send(val)); | ||||
|         if !self.awake.swap(true, Ordering::SeqCst) { | ||||
|             try!(self.notify.wakeup()); | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Clone for Sender<T> { | ||||
|     fn clone(&self) -> Sender<T> { | ||||
|         Sender { | ||||
|             awake: self.awake.clone(), | ||||
|             notify: self.notify.clone(), | ||||
|             tx: self.tx.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> fmt::Debug for Sender<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Sender") | ||||
|             .field("notify", &self.notify) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct SendError<T>(pub Option<T>); | ||||
|  | ||||
| impl<T> From<mpsc::SendError<T>> for SendError<T> { | ||||
|     fn from(e: mpsc::SendError<T>) -> SendError<T> { | ||||
|         SendError(Some(e.0)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> From<rotor::WakeupError> for SendError<T> { | ||||
|     fn from(_e: rotor::WakeupError) -> SendError<T> { | ||||
|         SendError(None) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Receiver<T> { | ||||
|     awake: Arc<AtomicBool>, | ||||
|     rx: mpsc::Receiver<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Send> Receiver<T> { | ||||
|     pub fn try_recv(&self) -> Result<T, mpsc::TryRecvError> { | ||||
|         self.awake.store(false, Ordering::Relaxed); | ||||
|         self.rx.try_recv() | ||||
|     } | ||||
| } | ||||
							
								
								
									
										915
									
								
								src/http/conn.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										915
									
								
								src/http/conn.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,915 @@ | ||||
| use std::borrow::Cow; | ||||
| use std::fmt; | ||||
| use std::hash::Hash; | ||||
| use std::io; | ||||
| use std::marker::PhantomData; | ||||
| use std::mem; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use rotor::{self, EventSet, PollOpt, Scope}; | ||||
|  | ||||
| use http::{self, h1, Http1Message, Encoder, Decoder, Next, Next_, Reg, Control}; | ||||
| use http::channel; | ||||
| use http::internal::WriteBuf; | ||||
| use http::buffer::Buffer; | ||||
| use net::{Transport, Blocked}; | ||||
| use version::HttpVersion; | ||||
|  | ||||
| const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100; | ||||
|  | ||||
| /// This handles a connection, which will have been established over a | ||||
| /// Transport (like a socket), and will likely include multiple | ||||
| /// `Message`s over HTTP. | ||||
| /// | ||||
| /// The connection will determine when a message begins and ends, creating | ||||
| /// a new message `MessageHandler` for each one, as well as determine if this | ||||
| /// connection can be kept alive after the message, or if it is complete. | ||||
| pub struct Conn<K: Key, T: Transport, H: MessageHandler<T>> { | ||||
|     buf: Buffer, | ||||
|     ctrl: (channel::Sender<Next>, channel::Receiver<Next>), | ||||
|     keep_alive_enabled: bool, | ||||
|     key: K, | ||||
|     state: State<H, T>, | ||||
|     transport: T, | ||||
| } | ||||
|  | ||||
| impl<K: Key, T: Transport, H: MessageHandler<T>> fmt::Debug for Conn<K, T, H> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Conn") | ||||
|             .field("keep_alive_enabled", &self.keep_alive_enabled) | ||||
|             .field("state", &self.state) | ||||
|             .field("buf", &self.buf) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<K: Key, T: Transport, H: MessageHandler<T>> Conn<K, T, H> { | ||||
|     pub fn new(key: K, transport: T, notify: rotor::Notifier) -> Conn<K, T, H> { | ||||
|         Conn { | ||||
|             buf: Buffer::new(), | ||||
|             ctrl: channel::new(notify), | ||||
|             keep_alive_enabled: true, | ||||
|             key: key, | ||||
|             state: State::Init, | ||||
|             transport: transport, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn keep_alive(mut self, val: bool) -> Conn<K, T, H> { | ||||
|         self.keep_alive_enabled = val; | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Desired Register interest based on state of current connection. | ||||
|     /// | ||||
|     /// This includes the user interest, such as when they return `Next::read()`. | ||||
|     fn interest(&self) -> Reg { | ||||
|         match self.state { | ||||
|             State::Closed => Reg::Remove, | ||||
|             State::Init => { | ||||
|                 <H as MessageHandler>::Message::initial_interest().interest() | ||||
|             } | ||||
|             State::Http1(Http1 { reading: Reading::Closed, writing: Writing::Closed, .. }) => { | ||||
|                 Reg::Remove | ||||
|             } | ||||
|             State::Http1(Http1 { ref reading, ref writing, .. }) => { | ||||
|                 let read = match *reading { | ||||
|                     Reading::Parse | | ||||
|                     Reading::Body(..) => Reg::Read, | ||||
|                     Reading::Init | | ||||
|                     Reading::Wait(..) | | ||||
|                     Reading::KeepAlive | | ||||
|                     Reading::Closed => Reg::Wait | ||||
|                 }; | ||||
|  | ||||
|                 let write = match *writing { | ||||
|                     Writing::Head | | ||||
|                     Writing::Chunk(..) | | ||||
|                     Writing::Ready(..) => Reg::Write, | ||||
|                     Writing::Init | | ||||
|                     Writing::Wait(..) | | ||||
|                     Writing::KeepAlive => Reg::Wait, | ||||
|                     Writing::Closed => Reg::Wait, | ||||
|                 }; | ||||
|  | ||||
|                 match (read, write) { | ||||
|                     (Reg::Read, Reg::Write) => Reg::ReadWrite, | ||||
|                     (Reg::Read, Reg::Wait) => Reg::Read, | ||||
|                     (Reg::Wait, Reg::Write) => Reg::Write, | ||||
|                     (Reg::Wait, Reg::Wait) => Reg::Wait, | ||||
|                     _ => unreachable!("bad read/write reg combo") | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Actual register action. | ||||
|     /// | ||||
|     /// Considers the user interest(), but also compares if the underlying | ||||
|     /// transport is blocked(), and adjusts accordingly. | ||||
|     fn register(&self) -> Reg { | ||||
|         let reg = self.interest(); | ||||
|         match (reg, self.transport.blocked()) { | ||||
|             (Reg::Remove, _) | | ||||
|             (Reg::Wait, _) | | ||||
|             (_, None) => reg, | ||||
|  | ||||
|             (_, Some(Blocked::Read)) => Reg::Read, | ||||
|             (_, Some(Blocked::Write)) => Reg::Write, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn parse(&mut self) -> ::Result<http::MessageHead<<<H as MessageHandler<T>>::Message as Http1Message>::Incoming>> { | ||||
|         let n = try!(self.buf.read_from(&mut self.transport)); | ||||
|         if n == 0 { | ||||
|             trace!("parse eof"); | ||||
|             return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "parse eof").into()); | ||||
|         } | ||||
|         match try!(http::parse::<<H as MessageHandler<T>>::Message, _>(self.buf.bytes())) { | ||||
|             Some((head, len)) => { | ||||
|                 trace!("parsed {} bytes out of {}", len, self.buf.len()); | ||||
|                 self.buf.consume(len); | ||||
|                 Ok(head) | ||||
|             }, | ||||
|             None => { | ||||
|                 if self.buf.len() >= MAX_BUFFER_SIZE { | ||||
|                     //TODO: Handler.on_too_large_error() | ||||
|                     debug!("MAX_BUFFER_SIZE reached, closing"); | ||||
|                     Err(::Error::TooLarge) | ||||
|                 } else { | ||||
|                     Err(io::Error::new(io::ErrorKind::WouldBlock, "incomplete parse").into()) | ||||
|                 } | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn read<F: MessageHandlerFactory<K, T, Output=H>>(&mut self, scope: &mut Scope<F>, state: State<H, T>) -> State<H, T> { | ||||
|          match state { | ||||
|             State::Init => { | ||||
|                 let head = match self.parse() { | ||||
|                     Ok(head) => head, | ||||
|                     Err(::Error::Io(e)) => match e.kind() { | ||||
|                         io::ErrorKind::WouldBlock | | ||||
|                         io::ErrorKind::Interrupted => return State::Init, | ||||
|                         _ => { | ||||
|                             debug!("io error trying to parse {:?}", e); | ||||
|                             return State::Closed; | ||||
|                         } | ||||
|                     }, | ||||
|                     Err(e) => { | ||||
|                         //TODO: send proper error codes depending on error | ||||
|                         trace!("parse eror: {:?}", e); | ||||
|                         return State::Closed; | ||||
|                     } | ||||
|                 }; | ||||
|                 match <<H as MessageHandler<T>>::Message as Http1Message>::decoder(&head) { | ||||
|                     Ok(decoder) => { | ||||
|                         trace!("decoder = {:?}", decoder); | ||||
|                         let keep_alive = self.keep_alive_enabled && head.should_keep_alive(); | ||||
|                         let mut handler = scope.create(Seed(&self.key, &self.ctrl.0)); | ||||
|                         let next = handler.on_incoming(head); | ||||
|                         trace!("handler.on_incoming() -> {:?}", next); | ||||
|  | ||||
|                         match next.interest { | ||||
|                             Next_::Read => self.read(scope, State::Http1(Http1 { | ||||
|                                 handler: handler, | ||||
|                                 reading: Reading::Body(decoder), | ||||
|                                 writing: Writing::Init, | ||||
|                                 keep_alive: keep_alive, | ||||
|                                 timeout: next.timeout, | ||||
|                                 _marker: PhantomData, | ||||
|                             })), | ||||
|                             Next_::Write => State::Http1(Http1 { | ||||
|                                 handler: handler, | ||||
|                                 reading: if decoder.is_eof() { | ||||
|                                     if keep_alive { | ||||
|                                         Reading::KeepAlive | ||||
|                                     } else { | ||||
|                                         Reading::Closed | ||||
|                                     } | ||||
|                                 } else { | ||||
|                                     Reading::Wait(decoder) | ||||
|                                 }, | ||||
|                                 writing: Writing::Head, | ||||
|                                 keep_alive: keep_alive, | ||||
|                                 timeout: next.timeout, | ||||
|                                 _marker: PhantomData, | ||||
|                             }), | ||||
|                             Next_::ReadWrite => self.read(scope, State::Http1(Http1 { | ||||
|                                 handler: handler, | ||||
|                                 reading: Reading::Body(decoder), | ||||
|                                 writing: Writing::Head, | ||||
|                                 keep_alive: keep_alive, | ||||
|                                 timeout: next.timeout, | ||||
|                                 _marker: PhantomData, | ||||
|                             })), | ||||
|                             Next_::Wait => State::Http1(Http1 { | ||||
|                                 handler: handler, | ||||
|                                 reading: Reading::Wait(decoder), | ||||
|                                 writing: Writing::Init, | ||||
|                                 keep_alive: keep_alive, | ||||
|                                 timeout: next.timeout, | ||||
|                                 _marker: PhantomData, | ||||
|                             }), | ||||
|                             Next_::End | | ||||
|                             Next_::Remove => State::Closed, | ||||
|                         } | ||||
|                     }, | ||||
|                     Err(e) => { | ||||
|                         debug!("error creating decoder: {:?}", e); | ||||
|                         //TODO: respond with 400 | ||||
|                         State::Closed | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             State::Http1(mut http1) => { | ||||
|                 let next = match http1.reading { | ||||
|                     Reading::Init => None, | ||||
|                     Reading::Parse => match self.parse() { | ||||
|                         Ok(head) => match <<H as MessageHandler<T>>::Message as Http1Message>::decoder(&head) { | ||||
|                             Ok(decoder) => { | ||||
|                                 trace!("decoder = {:?}", decoder); | ||||
|                                 // if client request asked for keep alive, | ||||
|                                 // then it depends entirely on if the server agreed | ||||
|                                 if http1.keep_alive { | ||||
|                                     http1.keep_alive = head.should_keep_alive(); | ||||
|                                 } | ||||
|                                 let next = http1.handler.on_incoming(head); | ||||
|                                 http1.reading = Reading::Wait(decoder); | ||||
|                                 trace!("handler.on_incoming() -> {:?}", next); | ||||
|                                 Some(next) | ||||
|                             }, | ||||
|                             Err(e) => { | ||||
|                                 debug!("error creating decoder: {:?}", e); | ||||
|                                 //TODO: respond with 400 | ||||
|                                 return State::Closed; | ||||
|                             } | ||||
|                         }, | ||||
|                         Err(::Error::Io(e)) => match e.kind() { | ||||
|                             io::ErrorKind::WouldBlock | | ||||
|                             io::ErrorKind::Interrupted => None, | ||||
|                             _ => { | ||||
|                                 debug!("io error trying to parse {:?}", e); | ||||
|                                 return State::Closed; | ||||
|                             } | ||||
|                         }, | ||||
|                         Err(e) => { | ||||
|                             //TODO: send proper error codes depending on error | ||||
|                             trace!("parse eror: {:?}", e); | ||||
|                             return State::Closed; | ||||
|                         } | ||||
|                     }, | ||||
|                     Reading::Body(ref mut decoder) => { | ||||
|                         let wrapped = if !self.buf.is_empty() { | ||||
|                             super::Trans::Buf(self.buf.wrap(&mut self.transport)) | ||||
|                         } else { | ||||
|                             super::Trans::Port(&mut self.transport) | ||||
|                         }; | ||||
|  | ||||
|                         Some(http1.handler.on_decode(&mut Decoder::h1(decoder, wrapped))) | ||||
|                     }, | ||||
|                     _ => { | ||||
|                         trace!("Conn.on_readable State::Http1(reading = {:?})", http1.reading); | ||||
|                         None | ||||
|                     } | ||||
|                 }; | ||||
|                 let mut s = State::Http1(http1); | ||||
|                 trace!("h1 read completed, next = {:?}", next); | ||||
|                 if let Some(next) = next { | ||||
|                     s.update(next); | ||||
|                 } | ||||
|                 trace!("h1 read completed, state = {:?}", s); | ||||
|  | ||||
|                 let again = match s { | ||||
|                     State::Http1(Http1 { reading: Reading::Body(ref encoder), .. }) => encoder.is_eof(), | ||||
|                     _ => false | ||||
|                 }; | ||||
|  | ||||
|                 if again { | ||||
|                     self.read(scope, s) | ||||
|                 } else { | ||||
|                     s | ||||
|                 } | ||||
|             }, | ||||
|             State::Closed => { | ||||
|                 error!("on_readable State::Closed"); | ||||
|                 State::Closed | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn write<F: MessageHandlerFactory<K, T, Output=H>>(&mut self, scope: &mut Scope<F>, mut state: State<H, T>) -> State<H, T> { | ||||
|         let next = match state { | ||||
|             State::Init => { | ||||
|                 // this could be a Client request, which writes first, so pay | ||||
|                 // attention to the version written here, which will adjust | ||||
|                 // our internal state to Http1 or Http2 | ||||
|                 let mut handler = scope.create(Seed(&self.key, &self.ctrl.0)); | ||||
|                 let mut head = http::MessageHead::default(); | ||||
|                 let interest = handler.on_outgoing(&mut head); | ||||
|                 if head.version == HttpVersion::Http11 { | ||||
|                     let mut buf = Vec::new(); | ||||
|                     let keep_alive = self.keep_alive_enabled && head.should_keep_alive(); | ||||
|                     let mut encoder = H::Message::encode(head, &mut buf); | ||||
|                     let writing = match interest.interest { | ||||
|                         // user wants to write some data right away | ||||
|                         // try to write the headers and the first chunk | ||||
|                         // together, so they are in the same packet | ||||
|                         Next_::Write | | ||||
|                         Next_::ReadWrite => { | ||||
|                             encoder.prefix(WriteBuf { | ||||
|                                 bytes: buf, | ||||
|                                 pos: 0 | ||||
|                             }); | ||||
|                             Writing::Ready(encoder) | ||||
|                         }, | ||||
|                         _ => Writing::Chunk(Chunk { | ||||
|                             buf: Cow::Owned(buf), | ||||
|                             pos: 0, | ||||
|                             next: (encoder, interest.clone()) | ||||
|                         }) | ||||
|                     }; | ||||
|                     state = State::Http1(Http1 { | ||||
|                         reading: Reading::Init, | ||||
|                         writing: writing, | ||||
|                         handler: handler, | ||||
|                         keep_alive: keep_alive, | ||||
|                         timeout: interest.timeout, | ||||
|                         _marker: PhantomData, | ||||
|                     }) | ||||
|                 } | ||||
|                 Some(interest) | ||||
|             } | ||||
|             State::Http1(Http1 { ref mut handler, ref mut writing, ref mut keep_alive, .. }) => { | ||||
|                 match *writing { | ||||
|                     Writing::Init => { | ||||
|                         trace!("Conn.on_writable Http1::Writing::Init"); | ||||
|                         None | ||||
|                     } | ||||
|                     Writing::Head => { | ||||
|                         let mut head = http::MessageHead::default(); | ||||
|                         let interest = handler.on_outgoing(&mut head); | ||||
|                         // if the request wants to close, server cannot stop it | ||||
|                         if *keep_alive { | ||||
|                             // if the request wants to stay alive, then it depends | ||||
|                             // on the server to agree | ||||
|                             *keep_alive = head.should_keep_alive(); | ||||
|                         } | ||||
|                         let mut buf = Vec::new(); | ||||
|                         let mut encoder = <<H as MessageHandler<T>>::Message as Http1Message>::encode(head, &mut buf); | ||||
|                         *writing = match interest.interest { | ||||
|                             // user wants to write some data right away | ||||
|                             // try to write the headers and the first chunk | ||||
|                             // together, so they are in the same packet | ||||
|                             Next_::Write | | ||||
|                             Next_::ReadWrite => { | ||||
|                                 encoder.prefix(WriteBuf { | ||||
|                                     bytes: buf, | ||||
|                                     pos: 0 | ||||
|                                 }); | ||||
|                                 Writing::Ready(encoder) | ||||
|                             }, | ||||
|                             _ => Writing::Chunk(Chunk { | ||||
|                                 buf: Cow::Owned(buf), | ||||
|                                 pos: 0, | ||||
|                                 next: (encoder, interest.clone()) | ||||
|                             }) | ||||
|                         }; | ||||
|                         Some(interest) | ||||
|                     }, | ||||
|                     Writing::Chunk(ref mut chunk) => { | ||||
|                         trace!("Http1.Chunk on_writable"); | ||||
|                         match self.transport.write(&chunk.buf.as_ref()[chunk.pos..]) { | ||||
|                             Ok(n) => { | ||||
|                                 chunk.pos += n; | ||||
|                                 trace!("Http1.Chunk wrote={}, done={}", n, chunk.is_written()); | ||||
|                                 if chunk.is_written() { | ||||
|                                     Some(chunk.next.1.clone()) | ||||
|                                 } else { | ||||
|                                     None | ||||
|                                 } | ||||
|                             }, | ||||
|                             Err(e) => match e.kind() { | ||||
|                                 io::ErrorKind::WouldBlock | | ||||
|                                 io::ErrorKind::Interrupted => None, | ||||
|                                 _ => { | ||||
|                                     Some(handler.on_error(e.into())) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     }, | ||||
|                     Writing::Ready(ref mut encoder) => { | ||||
|                         trace!("Http1.Ready on_writable"); | ||||
|                         Some(handler.on_encode(&mut Encoder::h1(encoder, &mut self.transport))) | ||||
|                     }, | ||||
|                     Writing::Wait(..) => { | ||||
|                         trace!("Conn.on_writable Http1::Writing::Wait"); | ||||
|                         None | ||||
|                     } | ||||
|                     Writing::KeepAlive => { | ||||
|                         trace!("Conn.on_writable Http1::Writing::KeepAlive"); | ||||
|                         None | ||||
|                     } | ||||
|                     Writing::Closed => { | ||||
|                         trace!("on_writable Http1::Writing::Closed"); | ||||
|                         None | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             State::Closed => { | ||||
|                 trace!("on_writable State::Closed"); | ||||
|                 None | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         if let Some(next) = next { | ||||
|             state.update(next); | ||||
|         } | ||||
|         state | ||||
|     } | ||||
|  | ||||
|     fn can_read_more(&self) -> bool { | ||||
|         match self.state { | ||||
|             State::Init => false, | ||||
|             _ => !self.buf.is_empty() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn ready<F>(mut self, events: EventSet, scope: &mut Scope<F>) -> Option<(Self, Option<Duration>)> | ||||
|     where F: MessageHandlerFactory<K, T, Output=H> { | ||||
|         trace!("Conn::ready events='{:?}', blocked={:?}", events, self.transport.blocked()); | ||||
|  | ||||
|         if events.is_error() { | ||||
|             match self.transport.take_socket_error() { | ||||
|                 Ok(_) => { | ||||
|                     trace!("is_error, but not socket error"); | ||||
|                     // spurious? | ||||
|                 }, | ||||
|                 Err(e) => self.on_error(e.into()) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // if the user had an io interest, but the transport was blocked differently, | ||||
|         // the event needs to be translated to what the user was actually expecting. | ||||
|         // | ||||
|         // Example: | ||||
|         // - User asks for `Next::write(). | ||||
|         // - But transport is in the middle of renegotiating TLS, and is blocked on reading. | ||||
|         // - hyper should not wait on the `write` event, since epoll already | ||||
|         //   knows it is writable. We would just loop a whole bunch, and slow down. | ||||
|         // - So instead, hyper waits on the event needed to unblock the transport, `read`. | ||||
|         // - Once epoll detects the transport is readable, it will alert hyper | ||||
|         //   with a `readable` event. | ||||
|         // - hyper needs to translate that `readable` event back into a `write`, | ||||
|         //   since that is actually what the Handler wants. | ||||
|  | ||||
|         let events = if let Some(blocked) = self.transport.blocked() { | ||||
|             let interest = self.interest(); | ||||
|             trace!("translating blocked={:?}, interest={:?}", blocked, interest); | ||||
|             match (blocked, interest) { | ||||
|                 (Blocked::Read, Reg::Write) => EventSet::writable(), | ||||
|                 (Blocked::Write, Reg::Read) => EventSet::readable(), | ||||
|                 // otherwise, the transport was blocked on the same thing the user wanted | ||||
|                 _ => events | ||||
|             } | ||||
|         } else { | ||||
|             events | ||||
|         }; | ||||
|  | ||||
|         if events.is_readable() { | ||||
|             self.on_readable(scope); | ||||
|         } | ||||
|  | ||||
|         if events.is_writable() { | ||||
|             self.on_writable(scope); | ||||
|         } | ||||
|  | ||||
|         let events = match self.register() { | ||||
|             Reg::Read => EventSet::readable(), | ||||
|             Reg::Write => EventSet::writable(), | ||||
|             Reg::ReadWrite => EventSet::readable() | EventSet::writable(), | ||||
|             Reg::Wait => EventSet::none(), | ||||
|             Reg::Remove => { | ||||
|                 trace!("removing transport"); | ||||
|                 let _ = scope.deregister(&self.transport); | ||||
|                 self.on_remove(); | ||||
|                 return None; | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
|         if events.is_readable() && self.can_read_more() { | ||||
|             return self.ready(events, scope); | ||||
|         } | ||||
|  | ||||
|         trace!("scope.reregister({:?})", events); | ||||
|         match scope.reregister(&self.transport, events, PollOpt::level()) { | ||||
|             Ok(..) => { | ||||
|                 let timeout = self.state.timeout(); | ||||
|                 Some((self, timeout)) | ||||
|             }, | ||||
|             Err(e) => { | ||||
|                 error!("error reregistering: {:?}", e); | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn wakeup<F>(mut self, scope: &mut Scope<F>) -> Option<(Self, Option<Duration>)> | ||||
|     where F: MessageHandlerFactory<K, T, Output=H> { | ||||
|         loop { | ||||
|             match self.ctrl.1.try_recv() { | ||||
|                 Ok(next) => { | ||||
|                     trace!("woke up with {:?}", next); | ||||
|                     self.state.update(next); | ||||
|                 }, | ||||
|                 Err(_) => break | ||||
|             } | ||||
|         } | ||||
|         self.ready(EventSet::readable() | EventSet::writable(), scope) | ||||
|     } | ||||
|  | ||||
|     pub fn timeout<F>(mut self, scope: &mut Scope<F>) -> Option<(Self, Option<Duration>)> | ||||
|     where F: MessageHandlerFactory<K, T, Output=H> { | ||||
|         //TODO: check if this was a spurious timeout? | ||||
|         self.on_error(::Error::Timeout); | ||||
|         self.ready(EventSet::none(), scope) | ||||
|     } | ||||
|  | ||||
|     fn on_error(&mut self, err: ::Error) { | ||||
|         debug!("on_error err = {:?}", err); | ||||
|         trace!("on_error state = {:?}", self.state); | ||||
|         let next = match self.state { | ||||
|             State::Init => Next::remove(), | ||||
|             State::Http1(ref mut http1) => http1.handler.on_error(err), | ||||
|             State::Closed => Next::remove(), | ||||
|         }; | ||||
|         self.state.update(next); | ||||
|     } | ||||
|  | ||||
|     fn on_remove(self) { | ||||
|         debug!("on_remove"); | ||||
|         match self.state { | ||||
|             State::Init | State::Closed => (), | ||||
|             State::Http1(http1) => http1.handler.on_remove(self.transport), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn on_readable<F>(&mut self, scope: &mut Scope<F>) | ||||
|     where F: MessageHandlerFactory<K, T, Output=H> { | ||||
|         trace!("on_readable -> {:?}", self.state); | ||||
|         let state = mem::replace(&mut self.state, State::Closed); | ||||
|         self.state = self.read(scope, state); | ||||
|         trace!("on_readable <- {:?}", self.state); | ||||
|     } | ||||
|  | ||||
|     fn on_writable<F>(&mut self, scope: &mut Scope<F>) | ||||
|     where F: MessageHandlerFactory<K, T, Output=H> { | ||||
|         trace!("on_writable -> {:?}", self.state); | ||||
|         let state = mem::replace(&mut self.state, State::Closed); | ||||
|         self.state = self.write(scope, state); | ||||
|         trace!("on_writable <- {:?}", self.state); | ||||
|     } | ||||
| } | ||||
|  | ||||
| enum State<H: MessageHandler<T>, T: Transport> { | ||||
|     Init, | ||||
|     /// Http1 will only ever use a connection to send and receive a single | ||||
|     /// message at a time. Once a H1 status has been determined, we will either | ||||
|     /// be reading or writing an H1 message, and optionally multiple if | ||||
|     /// keep-alive is true. | ||||
|     Http1(Http1<H, T>), | ||||
|     /// Http2 allows multiplexing streams over a single connection. So even | ||||
|     /// when we've identified a certain message, we must always parse frame | ||||
|     /// head to determine if the incoming frame is part of a current message, | ||||
|     /// or a new one. This also means we could have multiple messages at once. | ||||
|     //Http2 {}, | ||||
|     Closed, | ||||
| } | ||||
|  | ||||
|  | ||||
| impl<H: MessageHandler<T>, T: Transport> State<H, T> { | ||||
|     fn timeout(&self) -> Option<Duration> { | ||||
|         match *self { | ||||
|             State::Init => None, | ||||
|             State::Http1(ref http1) => http1.timeout, | ||||
|             State::Closed => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<H: MessageHandler<T>, T: Transport> fmt::Debug for State<H, T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         match *self { | ||||
|             State::Init => f.write_str("Init"), | ||||
|             State::Http1(ref h1) => f.debug_tuple("Http1") | ||||
|                 .field(h1) | ||||
|                 .finish(), | ||||
|             State::Closed => f.write_str("Closed") | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<H: MessageHandler<T>, T: Transport> State<H, T> { | ||||
|     fn update(&mut self, next: Next) { | ||||
|         let timeout = next.timeout; | ||||
|         let state = mem::replace(self, State::Closed); | ||||
|         let new_state = match (state, next.interest) { | ||||
|             (_, Next_::Remove) => State::Closed, | ||||
|             (State::Closed, _) => State::Closed, | ||||
|             (State::Init, _) => State::Init, | ||||
|             (State::Http1(http1), Next_::End) => { | ||||
|                 let reading = match http1.reading { | ||||
|                     Reading::Body(ref decoder) if decoder.is_eof() => { | ||||
|                         if http1.keep_alive { | ||||
|                             Reading::KeepAlive | ||||
|                         } else { | ||||
|                             Reading::Closed | ||||
|                         } | ||||
|                     }, | ||||
|                     Reading::KeepAlive => http1.reading, | ||||
|                     _ => Reading::Closed, | ||||
|                 }; | ||||
|                 let writing = match http1.writing { | ||||
|                     Writing::Ready(ref encoder) if encoder.is_eof() => { | ||||
|                         if http1.keep_alive { | ||||
|                             Writing::KeepAlive | ||||
|                         } else { | ||||
|                             Writing::Closed | ||||
|                         } | ||||
|                     }, | ||||
|                     Writing::Ready(encoder) => { | ||||
|                         if encoder.is_eof() { | ||||
|                             if http1.keep_alive { | ||||
|                                 Writing::KeepAlive | ||||
|                             } else { | ||||
|                                 Writing::Closed | ||||
|                             } | ||||
|                         } else if let Some(buf) = encoder.end() { | ||||
|                             Writing::Chunk(Chunk { | ||||
|                                 buf: buf.bytes, | ||||
|                                 pos: buf.pos, | ||||
|                                 next: (h1::Encoder::length(0), Next::end()) | ||||
|                             }) | ||||
|                         } else { | ||||
|                             Writing::Closed | ||||
|                         } | ||||
|                     } | ||||
|                     Writing::Chunk(mut chunk) => { | ||||
|                         if chunk.is_written() { | ||||
|                             let encoder = chunk.next.0; | ||||
|                             //TODO: de-dupe this code and from  Writing::Ready | ||||
|                             if encoder.is_eof() { | ||||
|                                 if http1.keep_alive { | ||||
|                                     Writing::KeepAlive | ||||
|                                 } else { | ||||
|                                     Writing::Closed | ||||
|                                 } | ||||
|                             } else if let Some(buf) = encoder.end() { | ||||
|                                 Writing::Chunk(Chunk { | ||||
|                                     buf: buf.bytes, | ||||
|                                     pos: buf.pos, | ||||
|                                     next: (h1::Encoder::length(0), Next::end()) | ||||
|                                 }) | ||||
|                             } else { | ||||
|                                 Writing::Closed | ||||
|                             } | ||||
|                         } else { | ||||
|                             chunk.next.1 = next; | ||||
|                             Writing::Chunk(chunk) | ||||
|                         } | ||||
|                     }, | ||||
|                     _ => Writing::Closed, | ||||
|                 }; | ||||
|                 match (reading, writing) { | ||||
|                     (Reading::KeepAlive, Writing::KeepAlive) => State::Init, | ||||
|                     (reading, Writing::Chunk(chunk)) => { | ||||
|                         State::Http1(Http1 { | ||||
|                             reading: reading, | ||||
|                             writing: Writing::Chunk(chunk), | ||||
|                             .. http1 | ||||
|                         }) | ||||
|                     } | ||||
|                     _ => State::Closed | ||||
|                 } | ||||
|             }, | ||||
|             (State::Http1(mut http1), Next_::Read) => { | ||||
|                 http1.reading = match http1.reading { | ||||
|                     Reading::Init => Reading::Parse, | ||||
|                     Reading::Wait(decoder) => Reading::Body(decoder), | ||||
|                     same => same | ||||
|                 }; | ||||
|  | ||||
|                 http1.writing = match http1.writing { | ||||
|                     Writing::Ready(encoder) => if encoder.is_eof() { | ||||
|                         if http1.keep_alive { | ||||
|                             Writing::KeepAlive | ||||
|                         } else { | ||||
|                             Writing::Closed | ||||
|                         } | ||||
|                     } else { | ||||
|                         Writing::Wait(encoder) | ||||
|                     }, | ||||
|                     Writing::Chunk(chunk) => if chunk.is_written() { | ||||
|                         Writing::Wait(chunk.next.0) | ||||
|                     } else { | ||||
|                         Writing::Chunk(chunk) | ||||
|                     }, | ||||
|                     same => same | ||||
|                 }; | ||||
|  | ||||
|                 State::Http1(http1) | ||||
|             }, | ||||
|             (State::Http1(mut http1), Next_::Write) => { | ||||
|                 http1.writing = match http1.writing { | ||||
|                     Writing::Wait(encoder) => Writing::Ready(encoder), | ||||
|                     Writing::Init => Writing::Head, | ||||
|                     Writing::Chunk(chunk) => if chunk.is_written() { | ||||
|                         Writing::Ready(chunk.next.0) | ||||
|                     } else { | ||||
|                         Writing::Chunk(chunk) | ||||
|                     }, | ||||
|                     same => same | ||||
|                 }; | ||||
|  | ||||
|                 http1.reading = match http1.reading { | ||||
|                     Reading::Body(decoder) => if decoder.is_eof() { | ||||
|                         if http1.keep_alive { | ||||
|                             Reading::KeepAlive | ||||
|                         } else { | ||||
|                             Reading::Closed | ||||
|                         } | ||||
|                     } else { | ||||
|                         Reading::Wait(decoder) | ||||
|                     }, | ||||
|                     same => same | ||||
|                 }; | ||||
|                 State::Http1(http1) | ||||
|             }, | ||||
|             (State::Http1(mut http1), Next_::ReadWrite) => { | ||||
|                 http1.reading = match http1.reading { | ||||
|                     Reading::Init => Reading::Parse, | ||||
|                     Reading::Wait(decoder) => Reading::Body(decoder), | ||||
|                     same => same | ||||
|                 }; | ||||
|                 http1.writing = match http1.writing { | ||||
|                     Writing::Wait(encoder) => Writing::Ready(encoder), | ||||
|                     Writing::Init => Writing::Head, | ||||
|                     Writing::Chunk(chunk) => if chunk.is_written() { | ||||
|                         Writing::Ready(chunk.next.0) | ||||
|                     } else { | ||||
|                         Writing::Chunk(chunk) | ||||
|                     }, | ||||
|                     same => same | ||||
|                 }; | ||||
|                 State::Http1(http1) | ||||
|             }, | ||||
|             (State::Http1(mut http1), Next_::Wait) => { | ||||
|                 http1.reading = match http1.reading { | ||||
|                     Reading::Body(decoder) => Reading::Wait(decoder), | ||||
|                     same => same | ||||
|                 }; | ||||
|  | ||||
|                 http1.writing = match http1.writing { | ||||
|                     Writing::Ready(encoder) => Writing::Wait(encoder), | ||||
|                     Writing::Chunk(chunk) => if chunk.is_written() { | ||||
|                         Writing::Wait(chunk.next.0) | ||||
|                     } else { | ||||
|                         Writing::Chunk(chunk) | ||||
|                     }, | ||||
|                     same => same | ||||
|                 }; | ||||
|                 State::Http1(http1) | ||||
|             } | ||||
|         }; | ||||
|         let new_state = match new_state { | ||||
|             State::Http1(mut http1) => { | ||||
|                 http1.timeout = timeout; | ||||
|                 State::Http1(http1) | ||||
|             } | ||||
|             other => other | ||||
|         }; | ||||
|         mem::replace(self, new_state); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // These Reading and Writing stuff should probably get moved into h1/message.rs | ||||
|  | ||||
| struct Http1<H, T> { | ||||
|     handler: H, | ||||
|     reading: Reading, | ||||
|     writing: Writing, | ||||
|     keep_alive: bool, | ||||
|     timeout: Option<Duration>, | ||||
|     _marker: PhantomData<T>, | ||||
| } | ||||
|  | ||||
| impl<H, T> fmt::Debug for Http1<H, T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Http1") | ||||
|             .field("reading", &self.reading) | ||||
|             .field("writing", &self.writing) | ||||
|             .field("keep_alive", &self.keep_alive) | ||||
|             .field("timeout", &self.timeout) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum Reading { | ||||
|     Init, | ||||
|     Parse, | ||||
|     Body(h1::Decoder), | ||||
|     Wait(h1::Decoder), | ||||
|     KeepAlive, | ||||
|     Closed | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum Writing { | ||||
|     Init, | ||||
|     Head, | ||||
|     Chunk(Chunk) , | ||||
|     Ready(h1::Encoder), | ||||
|     Wait(h1::Encoder), | ||||
|     KeepAlive, | ||||
|     Closed | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct Chunk { | ||||
|     buf: Cow<'static, [u8]>, | ||||
|     pos: usize, | ||||
|     next: (h1::Encoder, Next), | ||||
| } | ||||
|  | ||||
| impl Chunk { | ||||
|     fn is_written(&self) -> bool { | ||||
|         self.pos >= self.buf.len() | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait MessageHandler<T: Transport> { | ||||
|     type Message: Http1Message; | ||||
|     fn on_incoming(&mut self, head: http::MessageHead<<Self::Message as Http1Message>::Incoming>) -> Next; | ||||
|     fn on_outgoing(&mut self, head: &mut http::MessageHead<<Self::Message as Http1Message>::Outgoing>) -> Next; | ||||
|     fn on_decode(&mut self, &mut http::Decoder<T>) -> Next; | ||||
|     fn on_encode(&mut self, &mut http::Encoder<T>) -> Next; | ||||
|     fn on_error(&mut self, err: ::Error) -> Next; | ||||
|  | ||||
|     fn on_remove(self, T) where Self: Sized; | ||||
| } | ||||
|  | ||||
| pub struct Seed<'a, K: Key + 'a>(&'a K, &'a channel::Sender<Next>); | ||||
|  | ||||
| impl<'a, K: Key + 'a> Seed<'a, K> { | ||||
|     pub fn control(&self) -> Control { | ||||
|         Control { | ||||
|             tx: self.1.clone(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn key(&self) -> &K { | ||||
|         &self.0 | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| pub trait MessageHandlerFactory<K: Key, T: Transport> { | ||||
|     type Output: MessageHandler<T>; | ||||
|  | ||||
|     fn create(&mut self, seed: Seed<K>) -> Self::Output; | ||||
| } | ||||
|  | ||||
| impl<F, K, H, T> MessageHandlerFactory<K, T> for F | ||||
| where F: FnMut(Seed<K>) -> H, | ||||
|       K: Key, | ||||
|       H: MessageHandler<T>, | ||||
|       T: Transport { | ||||
|     type Output = H; | ||||
|     fn create(&mut self, seed: Seed<K>) -> H { | ||||
|         self(seed) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait Key: Eq + Hash + Clone {} | ||||
| impl<T: Eq + Hash + Clone> Key for T {} | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     /* TODO: | ||||
|     test when the underlying Transport of a Conn is blocked on an action that | ||||
|     differs from the desired interest(). | ||||
|  | ||||
|     Ex: | ||||
|         transport.blocked() == Some(Blocked::Read) | ||||
|         self.interest() == Reg::Write | ||||
|  | ||||
|         Should call `scope.register(EventSet::read())`, not with write | ||||
|  | ||||
|     #[test] | ||||
|     fn test_conn_register_when_transport_blocked() { | ||||
|  | ||||
|     } | ||||
|     */ | ||||
| } | ||||
							
								
								
									
										1137
									
								
								src/http/h1.rs
									
									
									
									
									
								
							
							
						
						
									
										1137
									
								
								src/http/h1.rs
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										293
									
								
								src/http/h1/decode.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								src/http/h1/decode.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,293 @@ | ||||
| use std::cmp; | ||||
| use std::io::{self, Read}; | ||||
|  | ||||
| use self::Kind::{Length, Chunked, Eof}; | ||||
|  | ||||
| /// Decoders to handle different Transfer-Encodings. | ||||
| /// | ||||
| /// If a message body does not include a Transfer-Encoding, it *should* | ||||
| /// include a Content-Length header. | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Decoder { | ||||
|     kind: Kind, | ||||
| } | ||||
|  | ||||
| impl Decoder { | ||||
|     pub fn length(x: u64) -> Decoder { | ||||
|         Decoder { | ||||
|             kind: Kind::Length(x) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn chunked() -> Decoder { | ||||
|         Decoder { | ||||
|             kind: Kind::Chunked(None) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn eof() -> Decoder { | ||||
|         Decoder { | ||||
|             kind: Kind::Eof(false) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| enum Kind { | ||||
|     /// A Reader used when a Content-Length header is passed with a positive integer. | ||||
|     Length(u64), | ||||
|     /// A Reader used when Transfer-Encoding is `chunked`. | ||||
|     Chunked(Option<u64>), | ||||
|     /// A Reader used for responses that don't indicate a length or chunked. | ||||
|     /// | ||||
|     /// Note: This should only used for `Response`s. It is illegal for a | ||||
|     /// `Request` to be made with both `Content-Length` and | ||||
|     /// `Transfer-Encoding: chunked` missing, as explained from the spec: | ||||
|     /// | ||||
|     /// > If a Transfer-Encoding header field is present in a response and | ||||
|     /// > the chunked transfer coding is not the final encoding, the | ||||
|     /// > message body length is determined by reading the connection until | ||||
|     /// > it is closed by the server.  If a Transfer-Encoding header field | ||||
|     /// > is present in a request and the chunked transfer coding is not | ||||
|     /// > the final encoding, the message body length cannot be determined | ||||
|     /// > reliably; the server MUST respond with the 400 (Bad Request) | ||||
|     /// > status code and then close the connection. | ||||
|     Eof(bool), | ||||
| } | ||||
|  | ||||
| impl Decoder { | ||||
|     pub fn is_eof(&self) -> bool { | ||||
|         trace!("is_eof? {:?}", self); | ||||
|         match self.kind { | ||||
|             Length(0) | | ||||
|             Chunked(Some(0)) | | ||||
|             Eof(true) => true, | ||||
|             _ => false | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Decoder { | ||||
|     pub fn decode<R: Read>(&mut self, body: &mut R, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match self.kind { | ||||
|             Length(ref mut remaining) => { | ||||
|                 trace!("Sized read, remaining={:?}", remaining); | ||||
|                 if *remaining == 0 { | ||||
|                     Ok(0) | ||||
|                 } else { | ||||
|                     let to_read = cmp::min(*remaining as usize, buf.len()); | ||||
|                     let num = try!(body.read(&mut buf[..to_read])) as u64; | ||||
|                     trace!("Length read: {}", num); | ||||
|                     if num > *remaining { | ||||
|                         *remaining = 0; | ||||
|                     } else if num == 0 { | ||||
|                         return Err(io::Error::new(io::ErrorKind::Other, "early eof")); | ||||
|                     } else { | ||||
|                         *remaining -= num; | ||||
|                     } | ||||
|                     Ok(num as usize) | ||||
|                 } | ||||
|             }, | ||||
|             Chunked(ref mut opt_remaining) => { | ||||
|                 let mut rem = match *opt_remaining { | ||||
|                     Some(ref rem) => *rem, | ||||
|                     // None means we don't know the size of the next chunk | ||||
|                     None => try!(read_chunk_size(body)) | ||||
|                 }; | ||||
|                 trace!("Chunked read, remaining={:?}", rem); | ||||
|  | ||||
|                 if rem == 0 { | ||||
|                     *opt_remaining = Some(0); | ||||
|  | ||||
|                     // chunk of size 0 signals the end of the chunked stream | ||||
|                     // if the 0 digit was missing from the stream, it would | ||||
|                     // be an InvalidInput error instead. | ||||
|                     trace!("end of chunked"); | ||||
|                     return Ok(0) | ||||
|                 } | ||||
|  | ||||
|                 let to_read = cmp::min(rem as usize, buf.len()); | ||||
|                 let count = try!(body.read(&mut buf[..to_read])) as u64; | ||||
|  | ||||
|                 if count == 0 { | ||||
|                     *opt_remaining = Some(0); | ||||
|                     return Err(io::Error::new(io::ErrorKind::Other, "early eof")); | ||||
|                 } | ||||
|  | ||||
|                 rem -= count; | ||||
|                 *opt_remaining = if rem > 0 { | ||||
|                     Some(rem) | ||||
|                 } else { | ||||
|                     try!(eat(body, b"\r\n")); | ||||
|                     None | ||||
|                 }; | ||||
|                 Ok(count as usize) | ||||
|             }, | ||||
|             Eof(ref mut is_eof) => { | ||||
|                 match body.read(buf) { | ||||
|                     Ok(0) => { | ||||
|                         *is_eof = true; | ||||
|                         Ok(0) | ||||
|                     } | ||||
|                     other => other | ||||
|                 } | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn eat<R: Read>(rdr: &mut R, bytes: &[u8]) -> io::Result<()> { | ||||
|     let mut buf = [0]; | ||||
|     for &b in bytes.iter() { | ||||
|         match try!(rdr.read(&mut buf)) { | ||||
|             1 if buf[0] == b => (), | ||||
|             _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
|                                           "Invalid characters found")), | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| /// Chunked chunks start with 1*HEXDIGIT, indicating the size of the chunk. | ||||
| fn read_chunk_size<R: Read>(rdr: &mut R) -> io::Result<u64> { | ||||
|     macro_rules! byte ( | ||||
|         ($rdr:ident) => ({ | ||||
|             let mut buf = [0]; | ||||
|             match try!($rdr.read(&mut buf)) { | ||||
|                 1 => buf[0], | ||||
|                 _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
|                                                   "Invalid chunk size line")), | ||||
|  | ||||
|             } | ||||
|         }) | ||||
|     ); | ||||
|     let mut size = 0u64; | ||||
|     let radix = 16; | ||||
|     let mut in_ext = false; | ||||
|     let mut in_chunk_size = true; | ||||
|     loop { | ||||
|         match byte!(rdr) { | ||||
|             b@b'0'...b'9' if in_chunk_size => { | ||||
|                 size *= radix; | ||||
|                 size += (b - b'0') as u64; | ||||
|             }, | ||||
|             b@b'a'...b'f' if in_chunk_size => { | ||||
|                 size *= radix; | ||||
|                 size += (b + 10 - b'a') as u64; | ||||
|             }, | ||||
|             b@b'A'...b'F' if in_chunk_size => { | ||||
|                 size *= radix; | ||||
|                 size += (b + 10 - b'A') as u64; | ||||
|             }, | ||||
|             b'\r' => { | ||||
|                 match byte!(rdr) { | ||||
|                     b'\n' => break, | ||||
|                     _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
|                                                   "Invalid chunk size line")) | ||||
|  | ||||
|                 } | ||||
|             }, | ||||
|             // If we weren't in the extension yet, the ";" signals its start | ||||
|             b';' if !in_ext => { | ||||
|                 in_ext = true; | ||||
|                 in_chunk_size = false; | ||||
|             }, | ||||
|             // "Linear white space" is ignored between the chunk size and the | ||||
|             // extension separator token (";") due to the "implied *LWS rule". | ||||
|             b'\t' | b' ' if !in_ext & !in_chunk_size => {}, | ||||
|             // LWS can follow the chunk size, but no more digits can come | ||||
|             b'\t' | b' ' if in_chunk_size => in_chunk_size = false, | ||||
|             // We allow any arbitrary octet once we are in the extension, since | ||||
|             // they all get ignored anyway. According to the HTTP spec, valid | ||||
|             // extensions would have a more strict syntax: | ||||
|             //     (token ["=" (token | quoted-string)]) | ||||
|             // but we gain nothing by rejecting an otherwise valid chunk size. | ||||
|             _ext if in_ext => { | ||||
|                 //TODO: chunk extension byte; | ||||
|             }, | ||||
|             // Finally, if we aren't in the extension and we're reading any | ||||
|             // other octet, the chunk size line is invalid! | ||||
|             _ => { | ||||
|                 return Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
|                                          "Invalid chunk size line")); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     trace!("chunk size={:?}", size); | ||||
|     Ok(size) | ||||
| } | ||||
|  | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::error::Error; | ||||
|     use std::io; | ||||
|     use super::{Decoder, read_chunk_size}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_chunk_size() { | ||||
|         fn read(s: &str, result: u64) { | ||||
|             assert_eq!(read_chunk_size(&mut s.as_bytes()).unwrap(), result); | ||||
|         } | ||||
|  | ||||
|         fn read_err(s: &str) { | ||||
|             assert_eq!(read_chunk_size(&mut s.as_bytes()).unwrap_err().kind(), | ||||
|                 io::ErrorKind::InvalidInput); | ||||
|         } | ||||
|  | ||||
|         read("1\r\n", 1); | ||||
|         read("01\r\n", 1); | ||||
|         read("0\r\n", 0); | ||||
|         read("00\r\n", 0); | ||||
|         read("A\r\n", 10); | ||||
|         read("a\r\n", 10); | ||||
|         read("Ff\r\n", 255); | ||||
|         read("Ff   \r\n", 255); | ||||
|         // Missing LF or CRLF | ||||
|         read_err("F\rF"); | ||||
|         read_err("F"); | ||||
|         // Invalid hex digit | ||||
|         read_err("X\r\n"); | ||||
|         read_err("1X\r\n"); | ||||
|         read_err("-\r\n"); | ||||
|         read_err("-1\r\n"); | ||||
|         // Acceptable (if not fully valid) extensions do not influence the size | ||||
|         read("1;extension\r\n", 1); | ||||
|         read("a;ext name=value\r\n", 10); | ||||
|         read("1;extension;extension2\r\n", 1); | ||||
|         read("1;;;  ;\r\n", 1); | ||||
|         read("2; extension...\r\n", 2); | ||||
|         read("3   ; extension=123\r\n", 3); | ||||
|         read("3   ;\r\n", 3); | ||||
|         read("3   ;   \r\n", 3); | ||||
|         // Invalid extensions cause an error | ||||
|         read_err("1 invalid extension\r\n"); | ||||
|         read_err("1 A\r\n"); | ||||
|         read_err("1;no CRLF"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_sized_early_eof() { | ||||
|         let mut bytes = &b"foo bar"[..]; | ||||
|         let mut decoder = Decoder::length(10); | ||||
|         let mut buf = [0u8; 10]; | ||||
|         assert_eq!(decoder.decode(&mut bytes, &mut buf).unwrap(), 7); | ||||
|         let e = decoder.decode(&mut bytes, &mut buf).unwrap_err(); | ||||
|         assert_eq!(e.kind(), io::ErrorKind::Other); | ||||
|         assert_eq!(e.description(), "early eof"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_chunked_early_eof() { | ||||
|         let mut bytes = &b"\ | ||||
|             9\r\n\ | ||||
|             foo bar\ | ||||
|         "[..]; | ||||
|         let mut decoder = Decoder::chunked(); | ||||
|         let mut buf = [0u8; 10]; | ||||
|         assert_eq!(decoder.decode(&mut bytes, &mut buf).unwrap(), 7); | ||||
|         let e = decoder.decode(&mut bytes, &mut buf).unwrap_err(); | ||||
|         assert_eq!(e.kind(), io::ErrorKind::Other); | ||||
|         assert_eq!(e.description(), "early eof"); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										371
									
								
								src/http/h1/encode.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										371
									
								
								src/http/h1/encode.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,371 @@ | ||||
| use std::borrow::Cow; | ||||
| use std::cmp; | ||||
| use std::io::{self, Write}; | ||||
|  | ||||
| use http::internal::{AtomicWrite, WriteBuf}; | ||||
|  | ||||
| /// Encoders to handle different Transfer-Encodings. | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Encoder { | ||||
|     kind: Kind, | ||||
|     prefix: Prefix, //Option<WriteBuf<Vec<u8>>> | ||||
| } | ||||
|  | ||||
| #[derive(Debug, PartialEq, Clone)] | ||||
| enum Kind { | ||||
|     /// An Encoder for when Transfer-Encoding includes `chunked`. | ||||
|     Chunked(Chunked), | ||||
|     /// An Encoder for when Content-Length is set. | ||||
|     /// | ||||
|     /// Enforces that the body is not longer than the Content-Length header. | ||||
|     Length(u64), | ||||
| } | ||||
|  | ||||
| impl Encoder { | ||||
|     pub fn chunked() -> Encoder { | ||||
|         Encoder { | ||||
|             kind: Kind::Chunked(Chunked::Init), | ||||
|             prefix: Prefix(None) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn length(len: u64) -> Encoder { | ||||
|         Encoder { | ||||
|             kind: Kind::Length(len), | ||||
|             prefix: Prefix(None) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn prefix(&mut self, prefix: WriteBuf<Vec<u8>>) { | ||||
|         self.prefix.0 = Some(prefix); | ||||
|     } | ||||
|  | ||||
|     pub fn is_eof(&self) -> bool { | ||||
|         if self.prefix.0.is_some() { | ||||
|             return false; | ||||
|         } | ||||
|         match self.kind { | ||||
|             Kind::Length(0) | | ||||
|             Kind::Chunked(Chunked::End) => true, | ||||
|             _ => false | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn end(self) -> Option<WriteBuf<Cow<'static, [u8]>>> { | ||||
|         let trailer = self.trailer(); | ||||
|         let buf = self.prefix.0; | ||||
|  | ||||
|         match (buf, trailer) { | ||||
|             (Some(mut buf), Some(trailer)) => { | ||||
|                 buf.bytes.extend_from_slice(trailer); | ||||
|                 Some(WriteBuf { | ||||
|                     bytes: Cow::Owned(buf.bytes), | ||||
|                     pos: buf.pos, | ||||
|                 }) | ||||
|             }, | ||||
|             (Some(buf), None) => Some(WriteBuf { | ||||
|                 bytes: Cow::Owned(buf.bytes), | ||||
|                 pos: buf.pos | ||||
|             }), | ||||
|             (None, Some(trailer)) => { | ||||
|                 Some(WriteBuf { | ||||
|                     bytes: Cow::Borrowed(trailer), | ||||
|                     pos: 0, | ||||
|                 }) | ||||
|             }, | ||||
|             (None, None) => None | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn trailer(&self) -> Option<&'static [u8]> { | ||||
|         match self.kind { | ||||
|             Kind::Chunked(Chunked::Init) => { | ||||
|                 Some(b"0\r\n\r\n") | ||||
|             } | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn encode<W: AtomicWrite>(&mut self, w: &mut W, msg: &[u8]) -> io::Result<usize> { | ||||
|         match self.kind { | ||||
|             Kind::Chunked(ref mut chunked) => { | ||||
|                 chunked.encode(w, &mut self.prefix, msg) | ||||
|             }, | ||||
|             Kind::Length(ref mut remaining) => { | ||||
|                 let mut n = { | ||||
|                     let max = cmp::min(*remaining as usize, msg.len()); | ||||
|                     let slice = &msg[..max]; | ||||
|  | ||||
|                     let prefix = self.prefix.0.as_ref().map(|buf| &buf.bytes[buf.pos..]).unwrap_or(b""); | ||||
|  | ||||
|                     try!(w.write_atomic(&[prefix, slice])) | ||||
|                 }; | ||||
|  | ||||
|                 n = self.prefix.update(n); | ||||
|                 if n == 0 { | ||||
|                     return Err(io::Error::new(io::ErrorKind::WouldBlock, "would block")); | ||||
|                 } | ||||
|  | ||||
|                 *remaining -= n as u64; | ||||
|                 Ok(n) | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, PartialEq, Clone)] | ||||
| enum Chunked { | ||||
|     Init, | ||||
|     Size(ChunkSize), | ||||
|     SizeCr, | ||||
|     SizeLf, | ||||
|     Body(usize), | ||||
|     BodyCr, | ||||
|     BodyLf, | ||||
|     End, | ||||
| } | ||||
|  | ||||
| impl Chunked { | ||||
|     fn encode<W: AtomicWrite>(&mut self, w: &mut W, prefix: &mut Prefix, msg: &[u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             Chunked::Init => { | ||||
|                 let mut size = ChunkSize { | ||||
|                     bytes: [0; CHUNK_SIZE_MAX_BYTES], | ||||
|                     pos: 0, | ||||
|                     len: 0, | ||||
|                 }; | ||||
|                 trace!("chunked write, size = {:?}", msg.len()); | ||||
|                 write!(&mut size, "{:X}", msg.len()) | ||||
|                     .expect("CHUNK_SIZE_MAX_BYTES should fit any usize"); | ||||
|                 *self = Chunked::Size(size); | ||||
|             } | ||||
|             Chunked::End => return Ok(0), | ||||
|             _ => {} | ||||
|         } | ||||
|         let mut n = { | ||||
|             let pieces = match *self { | ||||
|                 Chunked::Init => unreachable!("Chunked::Init should have become Chunked::Size"), | ||||
|                 Chunked::Size(ref size) => [ | ||||
|                     prefix.0.as_ref().map(|buf| &buf.bytes[buf.pos..]).unwrap_or(b""), | ||||
|                     &size.bytes[size.pos.into() .. size.len.into()], | ||||
|                     &b"\r\n"[..], | ||||
|                     msg, | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::SizeCr => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b"\r\n"[..], | ||||
|                     msg, | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::SizeLf => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b"\n"[..], | ||||
|                     msg, | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::Body(pos) => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &msg[pos..], | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::BodyCr => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::BodyLf => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b"\n"[..], | ||||
|                 ], | ||||
|                 Chunked::End => unreachable!("Chunked::End shouldn't write more") | ||||
|             }; | ||||
|             try!(w.write_atomic(&pieces)) | ||||
|         }; | ||||
|  | ||||
|         if n > 0 { | ||||
|             n = prefix.update(n); | ||||
|         } | ||||
|         while n > 0 { | ||||
|             match *self { | ||||
|                 Chunked::Init => unreachable!("Chunked::Init should have become Chunked::Size"), | ||||
|                 Chunked::Size(mut size) => { | ||||
|                     n = size.update(n); | ||||
|                     if size.len == 0 { | ||||
|                         *self = Chunked::SizeCr; | ||||
|                     } else { | ||||
|                         *self = Chunked::Size(size); | ||||
|                     } | ||||
|                 }, | ||||
|                 Chunked::SizeCr => { | ||||
|                     *self = Chunked::SizeLf; | ||||
|                     n -= 1; | ||||
|                 } | ||||
|                 Chunked::SizeLf => { | ||||
|                     *self = Chunked::Body(0); | ||||
|                     n -= 1; | ||||
|                 } | ||||
|                 Chunked::Body(pos) => { | ||||
|                     let left = msg.len() - pos; | ||||
|                     if n >= left { | ||||
|                         *self = Chunked::BodyCr; | ||||
|                         n -= left; | ||||
|                     } else { | ||||
|                         *self = Chunked::Body(pos + n); | ||||
|                         n = 0; | ||||
|                     } | ||||
|                 } | ||||
|                 Chunked::BodyCr => { | ||||
|                     *self = Chunked::BodyLf; | ||||
|                     n -= 1; | ||||
|                 } | ||||
|                 Chunked::BodyLf => { | ||||
|                     assert!(n == 1); | ||||
|                     *self = if msg.len() == 0 { | ||||
|                         Chunked::End | ||||
|                     } else { | ||||
|                         Chunked::Init | ||||
|                     }; | ||||
|                     n = 0; | ||||
|                 }, | ||||
|                 Chunked::End => unreachable!("Chunked::End shouldn't have any to write") | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         match *self { | ||||
|             Chunked::Init | | ||||
|             Chunked::End => Ok(msg.len()), | ||||
|             _ => Err(io::Error::new(io::ErrorKind::WouldBlock, "chunked incomplete")) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(target_pointer_width = "32")] | ||||
| const USIZE_BYTES: usize = 4; | ||||
|  | ||||
| #[cfg(target_pointer_width = "64")] | ||||
| const USIZE_BYTES: usize = 8; | ||||
|  | ||||
| // each byte will become 2 hex | ||||
| const CHUNK_SIZE_MAX_BYTES: usize = USIZE_BYTES * 2; | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| struct ChunkSize { | ||||
|     bytes: [u8; CHUNK_SIZE_MAX_BYTES], | ||||
|     pos: u8, | ||||
|     len: u8, | ||||
| } | ||||
|  | ||||
| impl ChunkSize { | ||||
|     fn update(&mut self, n: usize) -> usize { | ||||
|         let diff = (self.len - self.pos).into(); | ||||
|         if n >= diff { | ||||
|             self.pos = 0; | ||||
|             self.len = 0; | ||||
|             n - diff | ||||
|         } else { | ||||
|             self.pos += n as u8; // just verified it was a small usize | ||||
|             0 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ::std::fmt::Debug for ChunkSize { | ||||
|     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||
|         f.debug_struct("ChunkSize") | ||||
|             .field("bytes", &&self.bytes[..self.len.into()]) | ||||
|             .field("pos", &self.pos) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ::std::cmp::PartialEq for ChunkSize { | ||||
|     fn eq(&self, other: &ChunkSize) -> bool { | ||||
|         self.len == other.len && | ||||
|             self.pos == other.pos && | ||||
|             (&self.bytes[..]) == (&other.bytes[..]) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl io::Write for ChunkSize { | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         let n = (&mut self.bytes[self.len.into() ..]).write(msg) | ||||
|             .expect("&mut [u8].write() cannot error"); | ||||
|         self.len += n as u8; // safe because bytes is never bigger than 256 | ||||
|         Ok(n) | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| struct Prefix(Option<WriteBuf<Vec<u8>>>); | ||||
|  | ||||
| impl Prefix { | ||||
|     fn update(&mut self, n: usize) -> usize { | ||||
|         if let Some(mut buf) = self.0.take() { | ||||
|             if buf.bytes.len() - buf.pos > n { | ||||
|                 buf.pos += n; | ||||
|                 self.0 = Some(buf); | ||||
|                 0 | ||||
|             } else { | ||||
|                 let nbuf = buf.bytes.len() - buf.pos; | ||||
|                 n - nbuf | ||||
|             } | ||||
|         } else { | ||||
|             n | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::Encoder; | ||||
|     use mock::{Async, Buf}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_write_chunked_sync() { | ||||
|         let mut dst = Buf::new(); | ||||
|         let mut encoder = Encoder::chunked(); | ||||
|  | ||||
|         encoder.encode(&mut dst, b"foo bar").unwrap(); | ||||
|         encoder.encode(&mut dst, b"baz quux herp").unwrap(); | ||||
|         encoder.encode(&mut dst, b"").unwrap(); | ||||
|         assert_eq!(&dst[..], &b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n"[..]); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_write_chunked_async() { | ||||
|         let mut dst = Async::new(Buf::new(), 7); | ||||
|         let mut encoder = Encoder::chunked(); | ||||
|  | ||||
|         assert!(encoder.encode(&mut dst, b"foo bar").is_err()); | ||||
|         dst.block_in(6); | ||||
|         assert_eq!(7, encoder.encode(&mut dst, b"foo bar").unwrap()); | ||||
|         dst.block_in(30); | ||||
|         assert_eq!(13, encoder.encode(&mut dst, b"baz quux herp").unwrap()); | ||||
|         encoder.encode(&mut dst, b"").unwrap(); | ||||
|         assert_eq!(&dst[..], &b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n"[..]); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_write_sized() { | ||||
|         let mut dst = Buf::new(); | ||||
|         let mut encoder = Encoder::length(8); | ||||
|         encoder.encode(&mut dst, b"foo bar").unwrap(); | ||||
|         assert_eq!(encoder.encode(&mut dst, b"baz").unwrap(), 1); | ||||
|  | ||||
|         assert_eq!(dst, b"foo barb"); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										136
									
								
								src/http/h1/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/http/h1/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| /* | ||||
| use std::fmt; | ||||
| use std::io::{self, Write}; | ||||
| use std::marker::PhantomData; | ||||
| use std::sync::mpsc; | ||||
|  | ||||
| use url::Url; | ||||
| use tick; | ||||
| use time::now_utc; | ||||
|  | ||||
| use header::{self, Headers}; | ||||
| use http::{self, conn}; | ||||
| use method::Method; | ||||
| use net::{Fresh, Streaming}; | ||||
| use status::StatusCode; | ||||
| use version::HttpVersion; | ||||
| */ | ||||
|  | ||||
| pub use self::decode::Decoder; | ||||
| pub use self::encode::Encoder; | ||||
|  | ||||
| pub use self::parse::parse; | ||||
|  | ||||
| mod decode; | ||||
| mod encode; | ||||
| mod parse; | ||||
|  | ||||
| /* | ||||
| fn should_have_response_body(method: &Method, status: u16) -> bool { | ||||
|     trace!("should_have_response_body({:?}, {})", method, status); | ||||
|     match (method, status) { | ||||
|         (&Method::Head, _) | | ||||
|         (_, 100...199) | | ||||
|         (_, 204) | | ||||
|         (_, 304) | | ||||
|         (&Method::Connect, 200...299) => false, | ||||
|         _ => true | ||||
|     } | ||||
| } | ||||
| */ | ||||
| /* | ||||
| const MAX_INVALID_RESPONSE_BYTES: usize = 1024 * 128; | ||||
| impl HttpMessage for Http11Message { | ||||
|  | ||||
|     fn get_incoming(&mut self) -> ::Result<ResponseHead> { | ||||
|         unimplemented!(); | ||||
|         /* | ||||
|         try!(self.flush_outgoing()); | ||||
|         let stream = match self.stream.take() { | ||||
|             Some(stream) => stream, | ||||
|             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 expected_no_content = stream.previous_response_expected_no_content(); | ||||
|         trace!("previous_response_expected_no_content = {}", expected_no_content); | ||||
|  | ||||
|         let mut stream = BufReader::new(stream); | ||||
|  | ||||
|         let mut invalid_bytes_read = 0; | ||||
|         let head; | ||||
|         loop { | ||||
|             head = match parse_response(&mut stream) { | ||||
|                 Ok(head) => head, | ||||
|                 Err(::Error::Version) | ||||
|                 if expected_no_content && invalid_bytes_read < MAX_INVALID_RESPONSE_BYTES => { | ||||
|                     trace!("expected_no_content, found content"); | ||||
|                     invalid_bytes_read += 1; | ||||
|                     stream.consume(1); | ||||
|                     continue; | ||||
|                 } | ||||
|                 Err(e) => { | ||||
|                     self.stream = Some(stream.into_inner()); | ||||
|                     return Err(e); | ||||
|                 } | ||||
|             }; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         let raw_status = head.subject; | ||||
|         let headers = head.headers; | ||||
|  | ||||
|         let method = self.method.take().unwrap_or(Method::Get); | ||||
|  | ||||
|         let is_empty = !should_have_response_body(&method, raw_status.0); | ||||
|         stream.get_mut().set_previous_response_expected_no_content(is_empty); | ||||
|         // According to https://tools.ietf.org/html/rfc7230#section-3.3.3 | ||||
|         // 1. HEAD reponses, and Status 1xx, 204, and 304 cannot have a body. | ||||
|         // 2. Status 2xx to a CONNECT cannot have a body. | ||||
|         // 3. Transfer-Encoding: chunked has a chunked body. | ||||
|         // 4. If multiple differing Content-Length headers or invalid, close connection. | ||||
|         // 5. Content-Length header has a sized body. | ||||
|         // 6. Not Client. | ||||
|         // 7. Read till EOF. | ||||
|         self.reader = Some(if is_empty { | ||||
|             SizedReader(stream, 0) | ||||
|         } else { | ||||
|              if let Some(&TransferEncoding(ref codings)) = headers.get() { | ||||
|                 if codings.last() == Some(&Chunked) { | ||||
|                     ChunkedReader(stream, None) | ||||
|                 } else { | ||||
|                     trace!("not chuncked. read till eof"); | ||||
|                     EofReader(stream) | ||||
|                 } | ||||
|             } else if let Some(&ContentLength(len)) =  headers.get() { | ||||
|                 SizedReader(stream, len) | ||||
|             } else if headers.has::<ContentLength>() { | ||||
|                 trace!("illegal Content-Length: {:?}", headers.get_raw("Content-Length")); | ||||
|                 return Err(Error::Header); | ||||
|             } else { | ||||
|                 trace!("neither Transfer-Encoding nor Content-Length"); | ||||
|                 EofReader(stream) | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         trace!("Http11Message.reader = {:?}", self.reader); | ||||
|  | ||||
|  | ||||
|         Ok(ResponseHead { | ||||
|             headers: headers, | ||||
|             raw_status: raw_status, | ||||
|             version: head.version, | ||||
|         }) | ||||
|         */ | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| */ | ||||
|  | ||||
|  | ||||
							
								
								
									
										246
									
								
								src/http/h1/parse.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								src/http/h1/parse.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,246 @@ | ||||
| use std::borrow::Cow; | ||||
| use std::io::Write; | ||||
|  | ||||
| use httparse; | ||||
|  | ||||
| use header::{self, Headers, ContentLength, TransferEncoding}; | ||||
| use http::{MessageHead, RawStatus, Http1Message, ParseResult, Next, ServerMessage, ClientMessage, Next_, RequestLine}; | ||||
| use http::h1::{Encoder, Decoder}; | ||||
| use method::Method; | ||||
| use status::StatusCode; | ||||
| use version::HttpVersion::{Http10, Http11}; | ||||
|  | ||||
| const MAX_HEADERS: usize = 100; | ||||
| const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific | ||||
|  | ||||
| pub fn parse<T: Http1Message<Incoming=I>, I>(buf: &[u8]) -> ParseResult<I> { | ||||
|     if buf.len() == 0 { | ||||
|         return Ok(None); | ||||
|     } | ||||
|     trace!("parse({:?})", buf); | ||||
|     <T as Http1Message>::parse(buf) | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| impl Http1Message for ServerMessage { | ||||
|     type Incoming = RequestLine; | ||||
|     type Outgoing = StatusCode; | ||||
|  | ||||
|     fn initial_interest() -> Next { | ||||
|         Next::new(Next_::Read) | ||||
|     } | ||||
|  | ||||
|     fn parse(buf: &[u8]) -> ParseResult<RequestLine> { | ||||
|         let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; | ||||
|         trace!("Request.parse([Header; {}], [u8; {}])", headers.len(), buf.len()); | ||||
|         let mut req = httparse::Request::new(&mut headers); | ||||
|         Ok(match try!(req.parse(buf)) { | ||||
|             httparse::Status::Complete(len) => { | ||||
|                 trace!("Request.parse Complete({})", len); | ||||
|                 Some((MessageHead { | ||||
|                     version: if req.version.unwrap() == 1 { Http11 } else { Http10 }, | ||||
|                     subject: RequestLine( | ||||
|                         try!(req.method.unwrap().parse()), | ||||
|                         try!(req.path.unwrap().parse()) | ||||
|                     ), | ||||
|                     headers: try!(Headers::from_raw(req.headers)) | ||||
|                 }, len)) | ||||
|             }, | ||||
|             httparse::Status::Partial => None | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     fn decoder(head: &MessageHead<Self::Incoming>) -> ::Result<Decoder> { | ||||
|         use ::header; | ||||
|         if let Some(&header::ContentLength(len)) = head.headers.get() { | ||||
|             Ok(Decoder::length(len)) | ||||
|         } else if head.headers.has::<header::TransferEncoding>() { | ||||
|             //TODO: check for Transfer-Encoding: chunked | ||||
|             Ok(Decoder::chunked()) | ||||
|         } else { | ||||
|             Ok(Decoder::length(0)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     fn encode(mut head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder { | ||||
|         use ::header; | ||||
|         trace!("writing head: {:?}", head); | ||||
|  | ||||
|         if !head.headers.has::<header::Date>() { | ||||
|             head.headers.set(header::Date(header::HttpDate(::time::now_utc()))); | ||||
|         } | ||||
|  | ||||
|         let mut is_chunked = true; | ||||
|         let mut body = Encoder::chunked(); | ||||
|         if let Some(cl) = head.headers.get::<header::ContentLength>() { | ||||
|             body = Encoder::length(**cl); | ||||
|             is_chunked = false | ||||
|         } | ||||
|  | ||||
|         if is_chunked { | ||||
|             let encodings = match head.headers.get_mut::<header::TransferEncoding>() { | ||||
|                 Some(&mut header::TransferEncoding(ref mut encodings)) => { | ||||
|                     if encodings.last() != Some(&header::Encoding::Chunked) { | ||||
|                         encodings.push(header::Encoding::Chunked); | ||||
|                     } | ||||
|                     false | ||||
|                 }, | ||||
|                 None => true | ||||
|             }; | ||||
|  | ||||
|             if encodings { | ||||
|                 head.headers.set(header::TransferEncoding(vec![header::Encoding::Chunked])); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         let init_cap = 30 + head.headers.len() * AVERAGE_HEADER_SIZE; | ||||
|         dst.reserve(init_cap); | ||||
|         debug!("writing {:#?}", head.headers); | ||||
|         let _ = write!(dst, "{} {}\r\n{}\r\n", head.version, head.subject, head.headers); | ||||
|  | ||||
|         body | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Http1Message for ClientMessage { | ||||
|     type Incoming = RawStatus; | ||||
|     type Outgoing = RequestLine; | ||||
|  | ||||
|  | ||||
|     fn initial_interest() -> Next { | ||||
|         Next::new(Next_::Write) | ||||
|     } | ||||
|  | ||||
|     fn parse(buf: &[u8]) -> ParseResult<RawStatus> { | ||||
|         let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; | ||||
|         trace!("Response.parse([Header; {}], [u8; {}])", headers.len(), buf.len()); | ||||
|         let mut res = httparse::Response::new(&mut headers); | ||||
|         Ok(match try!(res.parse(buf)) { | ||||
|             httparse::Status::Complete(len) => { | ||||
|                 trace!("Response.try_parse Complete({})", len); | ||||
|                 let code = res.code.unwrap(); | ||||
|                 let reason = match StatusCode::from_u16(code).canonical_reason() { | ||||
|                     Some(reason) if reason == res.reason.unwrap() => Cow::Borrowed(reason), | ||||
|                     _ => Cow::Owned(res.reason.unwrap().to_owned()) | ||||
|                 }; | ||||
|                 Some((MessageHead { | ||||
|                     version: if res.version.unwrap() == 1 { Http11 } else { Http10 }, | ||||
|                     subject: RawStatus(code, reason), | ||||
|                     headers: try!(Headers::from_raw(res.headers)) | ||||
|                 }, len)) | ||||
|             }, | ||||
|             httparse::Status::Partial => None | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     fn decoder(inc: &MessageHead<Self::Incoming>) -> ::Result<Decoder> { | ||||
|         use ::header; | ||||
|         // According to https://tools.ietf.org/html/rfc7230#section-3.3.3 | ||||
|         // 1. HEAD reponses, and Status 1xx, 204, and 304 cannot have a body. | ||||
|         // 2. Status 2xx to a CONNECT cannot have a body. | ||||
|         // | ||||
|         // First two steps taken care of before this method. | ||||
|         // | ||||
|         // 3. Transfer-Encoding: chunked has a chunked body. | ||||
|         // 4. If multiple differing Content-Length headers or invalid, close connection. | ||||
|         // 5. Content-Length header has a sized body. | ||||
|         // 6. Not Client. | ||||
|         // 7. Read till EOF. | ||||
|         if let Some(&header::TransferEncoding(ref codings)) = inc.headers.get() { | ||||
|             if codings.last() == Some(&header::Encoding::Chunked) { | ||||
|                 Ok(Decoder::chunked()) | ||||
|             } else { | ||||
|                 trace!("not chuncked. read till eof"); | ||||
|                 Ok(Decoder::eof()) | ||||
|             } | ||||
|         } else if let Some(&header::ContentLength(len)) = inc.headers.get() { | ||||
|             Ok(Decoder::length(len)) | ||||
|         } else if inc.headers.has::<header::ContentLength>() { | ||||
|             trace!("illegal Content-Length: {:?}", inc.headers.get_raw("Content-Length")); | ||||
|             Err(::Error::Header) | ||||
|         } else { | ||||
|             trace!("neither Transfer-Encoding nor Content-Length"); | ||||
|             Ok(Decoder::eof()) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn encode(mut head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder { | ||||
|         trace!("writing head: {:?}", head); | ||||
|  | ||||
|  | ||||
|         let mut body = Encoder::length(0); | ||||
|         let expects_no_body = match head.subject.0 { | ||||
|             Method::Head | Method::Get | Method::Connect => true, | ||||
|             _ => false | ||||
|         }; | ||||
|         let mut chunked = false; | ||||
|  | ||||
|         if let Some(con_len) = head.headers.get::<ContentLength>() { | ||||
|             body = Encoder::length(**con_len); | ||||
|         } else { | ||||
|             chunked = !expects_no_body; | ||||
|         } | ||||
|  | ||||
|         if chunked { | ||||
|             body = Encoder::chunked(); | ||||
|             let encodings = match head.headers.get_mut::<TransferEncoding>() { | ||||
|                 Some(encodings) => { | ||||
|                     //TODO: check if Chunked already exists | ||||
|                     encodings.push(header::Encoding::Chunked); | ||||
|                     true | ||||
|                 }, | ||||
|                 None => false | ||||
|             }; | ||||
|  | ||||
|             if !encodings { | ||||
|                 head.headers.set(TransferEncoding(vec![header::Encoding::Chunked])); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let init_cap = 30 + head.headers.len() * AVERAGE_HEADER_SIZE; | ||||
|         dst.reserve(init_cap); | ||||
|         debug!("writing {:#?}", head.headers); | ||||
|         let _ = write!(dst, "{} {}\r\n{}\r\n", head.subject, head.version, head.headers); | ||||
|  | ||||
|         body | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use http; | ||||
|     use super::{parse}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_request() { | ||||
|         let raw = b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"; | ||||
|         parse::<http::ServerMessage, _>(raw).unwrap(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_raw_status() { | ||||
|         let raw = b"HTTP/1.1 200 OK\r\n\r\n"; | ||||
|         let (res, _) = parse::<http::ClientMessage, _>(raw).unwrap().unwrap(); | ||||
|         assert_eq!(res.subject.1, "OK"); | ||||
|  | ||||
|         let raw = b"HTTP/1.1 200 Howdy\r\n\r\n"; | ||||
|         let (res, _) = parse::<http::ClientMessage, _>(raw).unwrap().unwrap(); | ||||
|         assert_eq!(res.subject.1, "Howdy"); | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "nightly")] | ||||
|     use test::Bencher; | ||||
|  | ||||
|     #[cfg(feature = "nightly")] | ||||
|     #[bench] | ||||
|     fn bench_parse_incoming(b: &mut Bencher) { | ||||
|         let raw = b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"; | ||||
|         b.iter(|| { | ||||
|             parse::<http::ServerMessage, _>(raw).unwrap() | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,133 +0,0 @@ | ||||
| //! Defines the `HttpMessage` trait that serves to encapsulate the operations of a single | ||||
| //! request-response cycle on any HTTP connection. | ||||
|  | ||||
| use std::any::{Any, TypeId}; | ||||
| use std::fmt::Debug; | ||||
| use std::io::{Read, Write}; | ||||
| use std::mem; | ||||
|  | ||||
| use std::io; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use typeable::Typeable; | ||||
|  | ||||
| use header::Headers; | ||||
| use http::RawStatus; | ||||
| use url::Url; | ||||
|  | ||||
| use method; | ||||
| use version; | ||||
| use traitobject; | ||||
|  | ||||
| /// The trait provides an API for creating new `HttpMessage`s depending on the underlying HTTP | ||||
| /// protocol. | ||||
| pub trait Protocol { | ||||
|     /// Creates a fresh `HttpMessage` bound to the given host, based on the given protocol scheme. | ||||
|     fn new_message(&self, host: &str, port: u16, scheme: &str) -> ::Result<Box<HttpMessage>>; | ||||
| } | ||||
|  | ||||
| /// Describes a request. | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct RequestHead { | ||||
|     /// The headers of the request | ||||
|     pub headers: Headers, | ||||
|     /// The method of the request | ||||
|     pub method: method::Method, | ||||
|     /// The URL of the request | ||||
|     pub url: Url, | ||||
| } | ||||
|  | ||||
| /// Describes a response. | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct ResponseHead { | ||||
|     /// The headers of the reponse | ||||
|     pub headers: Headers, | ||||
|     /// The raw status line of the response | ||||
|     pub raw_status: RawStatus, | ||||
|     /// The HTTP/2 version which generated the response | ||||
|     pub version: version::HttpVersion, | ||||
| } | ||||
|  | ||||
| /// The trait provides an API for sending an receiving HTTP messages. | ||||
| pub trait HttpMessage: Write + Read + Send + Any + Typeable + Debug { | ||||
|     /// Initiates a new outgoing request. | ||||
|     /// | ||||
|     /// Only the request's head is provided (in terms of the `RequestHead` struct). | ||||
|     /// | ||||
|     /// After this, the `HttpMessage` instance can be used as an `io::Write` in order to write the | ||||
|     /// body of the request. | ||||
|     fn set_outgoing(&mut self, head: RequestHead) -> ::Result<RequestHead>; | ||||
|     /// Obtains the incoming response and returns its head (i.e. the `ResponseHead` struct) | ||||
|     /// | ||||
|     /// After this, the `HttpMessage` instance can be used as an `io::Read` in order to read out | ||||
|     /// the response body. | ||||
|     fn get_incoming(&mut self) -> ::Result<ResponseHead>; | ||||
|     /// Set the read timeout duration for this message. | ||||
|     fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()>; | ||||
|     /// Set the write timeout duration for this message. | ||||
|     fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()>; | ||||
|     /// Closes the underlying HTTP connection. | ||||
|     fn close_connection(&mut self) -> ::Result<()>; | ||||
|     /// Returns whether the incoming message has a body. | ||||
|     fn has_body(&self) -> bool; | ||||
|     /// Called when the Client wishes to use a Proxy. | ||||
|     fn set_proxied(&mut self, val: bool) { | ||||
|         // default implementation so as to not be a breaking change. | ||||
|         warn!("default set_proxied({:?})", val); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HttpMessage { | ||||
|     unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T { | ||||
|         mem::transmute(traitobject::data(self)) | ||||
|     } | ||||
|  | ||||
|     unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T { | ||||
|         mem::transmute(traitobject::data_mut(self)) | ||||
|     } | ||||
|  | ||||
|     unsafe fn downcast_unchecked<T: 'static>(self: Box<HttpMessage>) -> Box<T>  { | ||||
|         let raw: *mut HttpMessage = mem::transmute(self); | ||||
|         mem::transmute(traitobject::data_mut(raw)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HttpMessage { | ||||
|     /// Is the underlying type in this trait object a T? | ||||
|     #[inline] | ||||
|     pub fn is<T: Any>(&self) -> bool { | ||||
|         (*self).get_type() == TypeId::of::<T>() | ||||
|     } | ||||
|  | ||||
|     /// If the underlying type is T, get a reference to the contained data. | ||||
|     #[inline] | ||||
|     pub fn downcast_ref<T: Any>(&self) -> Option<&T> { | ||||
|         if self.is::<T>() { | ||||
|             Some(unsafe { self.downcast_ref_unchecked() }) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// If the underlying type is T, get a mutable reference to the contained | ||||
|     /// data. | ||||
|     #[inline] | ||||
|     pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> { | ||||
|         if self.is::<T>() { | ||||
|             Some(unsafe { self.downcast_mut_unchecked() }) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// If the underlying type is T, extract it. | ||||
|     #[inline] | ||||
|     pub fn downcast<T: Any>(self: Box<HttpMessage>) | ||||
|             -> Result<Box<T>, Box<HttpMessage>> { | ||||
|         if self.is::<T>() { | ||||
|             Ok(unsafe { self.downcast_unchecked() }) | ||||
|         } else { | ||||
|             Err(self) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										331
									
								
								src/http/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										331
									
								
								src/http/mod.rs
									
									
									
									
									
								
							| @@ -1,25 +1,196 @@ | ||||
| //! Pieces pertaining to the HTTP message protocol. | ||||
| use std::borrow::Cow; | ||||
| use std::fmt; | ||||
| use std::io::{self, Read, Write}; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use header::Connection; | ||||
| use header::ConnectionOption::{KeepAlive, Close}; | ||||
| use header::Headers; | ||||
| use method::Method; | ||||
| use net::Transport; | ||||
| use status::StatusCode; | ||||
| use uri::RequestUri; | ||||
| use version::HttpVersion; | ||||
| use version::HttpVersion::{Http10, Http11}; | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||||
|  | ||||
| pub use self::message::{HttpMessage, RequestHead, ResponseHead, Protocol}; | ||||
| pub use self::conn::{Conn, MessageHandler, MessageHandlerFactory, Seed, Key}; | ||||
|  | ||||
| pub mod h1; | ||||
| pub mod h2; | ||||
| pub mod message; | ||||
| mod buffer; | ||||
| pub mod channel; | ||||
| mod conn; | ||||
| mod h1; | ||||
| //mod h2; | ||||
|  | ||||
| /// Wraps a `Transport` to provide HTTP decoding when reading. | ||||
| #[derive(Debug)] | ||||
| pub struct Decoder<'a, T: Read + 'a>(DecoderImpl<'a, T>); | ||||
|  | ||||
| /// Wraps a `Transport` to provide HTTP encoding when writing. | ||||
| #[derive(Debug)] | ||||
| pub struct Encoder<'a, T: Transport + 'a>(EncoderImpl<'a, T>); | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum DecoderImpl<'a, T: Read + 'a> { | ||||
|     H1(&'a mut h1::Decoder, Trans<'a, T>), | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum Trans<'a, T: Read + 'a> { | ||||
|     Port(&'a mut T), | ||||
|     Buf(self::buffer::BufReader<'a, T>) | ||||
| } | ||||
|  | ||||
| impl<'a, T: Read + 'a> Read for Trans<'a, T> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             Trans::Port(ref mut t) => t.read(buf), | ||||
|             Trans::Buf(ref mut b) => b.read(buf) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum EncoderImpl<'a, T: Transport + 'a> { | ||||
|     H1(&'a mut h1::Encoder, &'a mut T), | ||||
| } | ||||
|  | ||||
| impl<'a, T: Read> Decoder<'a, T> { | ||||
|     fn h1(decoder: &'a mut h1::Decoder, transport: Trans<'a, T>) -> Decoder<'a, T> { | ||||
|         Decoder(DecoderImpl::H1(decoder, transport)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, T: Transport> Encoder<'a, T> { | ||||
|     fn h1(encoder: &'a mut h1::Encoder, transport: &'a mut T) -> Encoder<'a, T> { | ||||
|         Encoder(EncoderImpl::H1(encoder, transport)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, T: Read> Read for Decoder<'a, T> { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match self.0 { | ||||
|             DecoderImpl::H1(ref mut decoder, ref mut transport) => { | ||||
|                 decoder.decode(transport, buf) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, T: Transport> Write for Encoder<'a, T> { | ||||
|     #[inline] | ||||
|     fn write(&mut self, data: &[u8]) -> io::Result<usize> { | ||||
|         if data.is_empty() { | ||||
|             return Ok(0); | ||||
|         } | ||||
|         match self.0 { | ||||
|             EncoderImpl::H1(ref mut encoder, ref mut transport) => { | ||||
|                 encoder.encode(*transport, data) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         match self.0 { | ||||
|             EncoderImpl::H1(_, ref mut transport) => { | ||||
|                 transport.flush() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Because privacy rules. Reasons. | ||||
| /// https://github.com/rust-lang/rust/issues/30905 | ||||
| mod internal { | ||||
|     use std::io::{self, Write}; | ||||
|  | ||||
|     #[derive(Debug, Clone)] | ||||
|     pub struct WriteBuf<T: AsRef<[u8]>> { | ||||
|         pub bytes: T, | ||||
|         pub pos: usize, | ||||
|     } | ||||
|  | ||||
|     pub trait AtomicWrite { | ||||
|         fn write_atomic(&mut self, data: &[&[u8]]) -> io::Result<usize>; | ||||
|     } | ||||
|  | ||||
|     #[cfg(not(windows))] | ||||
|     impl<T: Write + ::vecio::Writev> AtomicWrite for T { | ||||
|  | ||||
|         fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|             self.writev(bufs) | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     #[cfg(windows)] | ||||
|     impl<T: Write> AtomicWrite for T { | ||||
|         fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|             let vec = bufs.concat(); | ||||
|             self.write(&vec) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// An Incoming Message head. Includes request/status line, and headers. | ||||
| #[derive(Debug, Default)] | ||||
| pub struct MessageHead<S> { | ||||
|     /// HTTP version of the message. | ||||
|     pub version: HttpVersion, | ||||
|     /// Subject (request line or status line) of Incoming message. | ||||
|     pub subject: S, | ||||
|     /// Headers of the Incoming message. | ||||
|     pub headers: Headers | ||||
| } | ||||
|  | ||||
| /// An incoming request message. | ||||
| pub type RequestHead = MessageHead<RequestLine>; | ||||
|  | ||||
| #[derive(Debug, Default)] | ||||
| pub struct RequestLine(pub Method, pub RequestUri); | ||||
|  | ||||
| impl fmt::Display for RequestLine { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         write!(f, "{} {}", self.0, self.1) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// An incoming response message. | ||||
| pub type ResponseHead = MessageHead<RawStatus>; | ||||
|  | ||||
| impl<S> MessageHead<S> { | ||||
|     pub fn should_keep_alive(&self) -> bool { | ||||
|         should_keep_alive(self.version, &self.headers) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// The raw status code and reason-phrase. | ||||
| #[derive(Clone, PartialEq, Debug)] | ||||
| pub struct RawStatus(pub u16, pub Cow<'static, str>); | ||||
|  | ||||
| impl fmt::Display for RawStatus { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         write!(f, "{} {}", self.0, self.1) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<StatusCode> for RawStatus { | ||||
|     fn from(status: StatusCode) -> RawStatus { | ||||
|         RawStatus(status.to_u16(), Cow::Borrowed(status.canonical_reason().unwrap_or(""))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for RawStatus { | ||||
|     fn default() -> RawStatus { | ||||
|         RawStatus(200, Cow::Borrowed("OK")) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| impl Serialize for RawStatus { | ||||
|     fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { | ||||
| @@ -46,6 +217,158 @@ pub fn should_keep_alive(version: HttpVersion, headers: &Headers) -> bool { | ||||
|         _ => true | ||||
|     } | ||||
| } | ||||
| pub type ParseResult<T> = ::Result<Option<(MessageHead<T>, usize)>>; | ||||
|  | ||||
| pub fn parse<T: Http1Message<Incoming=I>, I>(rdr: &[u8]) -> ParseResult<I> { | ||||
|     h1::parse::<T, I>(rdr) | ||||
| } | ||||
|  | ||||
| // These 2 enums are not actually dead_code. They are used in the server and | ||||
| // and client modules, respectively. However, their being used as associated | ||||
| // types doesn't mark them as used, so the dead_code linter complains. | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| #[derive(Debug)] | ||||
| pub enum ServerMessage {} | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| #[derive(Debug)] | ||||
| pub enum ClientMessage {} | ||||
|  | ||||
| pub trait Http1Message { | ||||
|     type Incoming; | ||||
|     type Outgoing: Default; | ||||
|     //TODO: replace with associated const when stable | ||||
|     fn initial_interest() -> Next; | ||||
|     fn parse(bytes: &[u8]) -> ParseResult<Self::Incoming>; | ||||
|     fn decoder(head: &MessageHead<Self::Incoming>) -> ::Result<h1::Decoder>; | ||||
|     fn encode(head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> h1::Encoder; | ||||
|  | ||||
| } | ||||
|  | ||||
| /// Used to signal desired events when working with asynchronous IO. | ||||
| #[must_use] | ||||
| #[derive(Clone)] | ||||
| pub struct Next { | ||||
|     interest: Next_, | ||||
|     timeout: Option<Duration>, | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Next { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         try!(write!(f, "Next::{:?}", &self.interest)); | ||||
|         match self.timeout { | ||||
|             Some(ref d) => write!(f, "({:?})", d), | ||||
|             None => Ok(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy)] | ||||
| enum Next_ { | ||||
|     Read, | ||||
|     Write, | ||||
|     ReadWrite, | ||||
|     Wait, | ||||
|     End, | ||||
|     Remove, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy)] | ||||
| enum Reg { | ||||
|     Read, | ||||
|     Write, | ||||
|     ReadWrite, | ||||
|     Wait, | ||||
|     Remove | ||||
| } | ||||
|  | ||||
| /// A notifier to wakeup a socket after having used `Next::wait()` | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Control { | ||||
|     tx: self::channel::Sender<Next>, | ||||
| } | ||||
|  | ||||
| impl Control { | ||||
|     /// Wakeup a waiting socket to listen for a certain event. | ||||
|     pub fn ready(&self, next: Next) -> Result<(), ControlError> { | ||||
|         //TODO: assert!( next.interest != Next_::Wait ) ? | ||||
|         self.tx.send(next).map_err(|_| ControlError(())) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// An error occured trying to tell a Control it is ready. | ||||
| #[derive(Debug)] | ||||
| pub struct ControlError(()); | ||||
|  | ||||
| impl ::std::error::Error for ControlError { | ||||
|     fn description(&self) -> &str { | ||||
|         "Cannot wakeup event loop: loop is closed" | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Display for ControlError { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_str(::std::error::Error::description(self)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Next { | ||||
|     fn new(interest: Next_) -> Next { | ||||
|         Next { | ||||
|             interest: interest, | ||||
|             timeout: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn interest(&self) -> Reg { | ||||
|         match self.interest { | ||||
|             Next_::Read => Reg::Read, | ||||
|             Next_::Write => Reg::Write, | ||||
|             Next_::ReadWrite => Reg::ReadWrite, | ||||
|             Next_::Wait => Reg::Wait, | ||||
|             Next_::End => Reg::Remove, | ||||
|             Next_::Remove => Reg::Remove, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to read from the transport. | ||||
|     pub fn read() -> Next { | ||||
|         Next::new(Next_::Read) | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to write to the transport. | ||||
|     pub fn write() -> Next { | ||||
|         Next::new(Next_::Write) | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to read and write to the transport. | ||||
|     pub fn read_and_write() -> Next { | ||||
|         Next::new(Next_::ReadWrite) | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to end the current HTTP message. | ||||
|     pub fn end() -> Next { | ||||
|         Next::new(Next_::End) | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to abruptly remove the current transport from the | ||||
|     /// event loop. | ||||
|     pub fn remove() -> Next { | ||||
|         Next::new(Next_::Remove) | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to wait until some future time before acting again. | ||||
|     pub fn wait() -> Next { | ||||
|         Next::new(Next_::Wait) | ||||
|     } | ||||
|  | ||||
|     /// Signals a maximum duration to be waited for the desired event. | ||||
|     pub fn timeout(mut self, dur: Duration) -> Next { | ||||
|         self.timeout = Some(dur); | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_should_keep_alive() { | ||||
|   | ||||
							
								
								
									
										168
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										168
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| #![doc(html_root_url = "https://hyperium.github.io/hyper/")] | ||||
| #![cfg_attr(test, deny(missing_docs))] | ||||
| #![cfg_attr(test, deny(warnings))] | ||||
| #![deny(missing_docs)] | ||||
| #![deny(warnings)] | ||||
| #![deny(missing_debug_implementations)] | ||||
| #![cfg_attr(all(test, feature = "nightly"), feature(test))] | ||||
|  | ||||
| //! # Hyper | ||||
| @@ -9,125 +10,9 @@ | ||||
| //! is a low-level typesafe abstraction over raw HTTP, providing an elegant | ||||
| //! layer over "stringly-typed" HTTP. | ||||
| //! | ||||
| //! Hyper offers both a [Client](client/index.html) and a | ||||
| //! [Server](server/index.html) which can be used to drive complex web | ||||
| //! applications written entirely in Rust. | ||||
| //! | ||||
| //! ## Internal Design | ||||
| //! | ||||
| //! Hyper is designed as a relatively low-level wrapper over raw HTTP. It should | ||||
| //! allow the implementation of higher-level abstractions with as little pain as | ||||
| //! possible, and should not irrevocably hide any information from its users. | ||||
| //! | ||||
| //! ### Common Functionality | ||||
| //! | ||||
| //! Functionality and code shared between the Server and Client implementations | ||||
| //! can be found in `src` directly - this includes `NetworkStream`s, `Method`s, | ||||
| //! `StatusCode`, and so on. | ||||
| //! | ||||
| //! #### Methods | ||||
| //! | ||||
| //! Methods are represented as a single `enum` to remain as simple as possible. | ||||
| //! Extension Methods are represented as raw `String`s. A method's safety and | ||||
| //! idempotence can be accessed using the `safe` and `idempotent` methods. | ||||
| //! | ||||
| //! #### StatusCode | ||||
| //! | ||||
| //! Status codes are also represented as a single, exhaustive, `enum`. This | ||||
| //! representation is efficient, typesafe, and ergonomic as it allows the use of | ||||
| //! `match` to disambiguate known status codes. | ||||
| //! | ||||
| //! #### Headers | ||||
| //! | ||||
| //! Hyper's [header](header/index.html) representation is likely the most | ||||
| //! complex API exposed by Hyper. | ||||
| //! | ||||
| //! Hyper's headers are an abstraction over an internal `HashMap` and provides a | ||||
| //! typesafe API for interacting with headers that does not rely on the use of | ||||
| //! "string-typing." | ||||
| //! | ||||
| //! Each HTTP header in Hyper has an associated type and implementation of the | ||||
| //! `Header` trait, which defines an HTTP headers name as a string, how to parse | ||||
| //! that header, and how to format that header. | ||||
| //! | ||||
| //! Headers are then parsed from the string representation lazily when the typed | ||||
| //! representation of a header is requested and formatted back into their string | ||||
| //! representation when headers are written back to the client. | ||||
| //! | ||||
| //! #### NetworkStream and NetworkAcceptor | ||||
| //! | ||||
| //! These are found in `src/net.rs` and define the interface that acceptors and | ||||
| //! streams must fulfill for them to be used within Hyper. They are by and large | ||||
| //! internal tools and you should only need to mess around with them if you want to | ||||
| //! mock or replace `TcpStream` and `TcpAcceptor`. | ||||
| //! | ||||
| //! ### Server | ||||
| //! | ||||
| //! Server-specific functionality, such as `Request` and `Response` | ||||
| //! representations, are found in in `src/server`. | ||||
| //! | ||||
| //! #### Handler + Server | ||||
| //! | ||||
| //! A `Handler` in Hyper accepts a `Request` and `Response`. This is where | ||||
| //! user-code can handle each connection. The server accepts connections in a | ||||
| //! task pool with a customizable number of threads, and passes the Request / | ||||
| //! Response to the handler. | ||||
| //! | ||||
| //! #### Request | ||||
| //! | ||||
| //! An incoming HTTP Request is represented as a struct containing | ||||
| //! a `Reader` over a `NetworkStream`, which represents the body, headers, a remote | ||||
| //! address, an HTTP version, and a `Method` - relatively standard stuff. | ||||
| //! | ||||
| //! `Request` implements `Reader` itself, meaning that you can ergonomically get | ||||
| //! the body out of a `Request` using standard `Reader` methods and helpers. | ||||
| //! | ||||
| //! #### Response | ||||
| //! | ||||
| //! An outgoing HTTP Response is also represented as a struct containing a `Writer` | ||||
| //! over a `NetworkStream` which represents the Response body in addition to | ||||
| //! standard items such as the `StatusCode` and HTTP version. `Response`'s `Writer` | ||||
| //! implementation provides a streaming interface for sending data over to the | ||||
| //! client. | ||||
| //! | ||||
| //! One of the traditional problems with representing outgoing HTTP Responses is | ||||
| //! tracking the write-status of the Response - have we written the status-line, | ||||
| //! the headers, the body, etc.? Hyper tracks this information statically using the | ||||
| //! type system and prevents you, using the type system, from writing headers after | ||||
| //! you have started writing to the body or vice versa. | ||||
| //! | ||||
| //! Hyper does this through a phantom type parameter in the definition of Response, | ||||
| //! which tracks whether you are allowed to write to the headers or the body. This | ||||
| //! phantom type can have two values `Fresh` or `Streaming`, with `Fresh` | ||||
| //! indicating that you can write the headers and `Streaming` indicating that you | ||||
| //! may write to the body, but not the headers. | ||||
| //! | ||||
| //! ### Client | ||||
| //! | ||||
| //! Client-specific functionality, such as `Request` and `Response` | ||||
| //! representations, are found in `src/client`. | ||||
| //! | ||||
| //! #### Request | ||||
| //! | ||||
| //! An outgoing HTTP Request is represented as a struct containing a `Writer` over | ||||
| //! a `NetworkStream` which represents the Request body in addition to the standard | ||||
| //! information such as headers and the request method. | ||||
| //! | ||||
| //! Outgoing Requests track their write-status in almost exactly the same way as | ||||
| //! outgoing HTTP Responses do on the Server, so we will defer to the explanation | ||||
| //! in the documentation for server Response. | ||||
| //! | ||||
| //! Requests expose an efficient streaming interface instead of a builder pattern, | ||||
| //! but they also provide the needed interface for creating a builder pattern over | ||||
| //! the API exposed by core Hyper. | ||||
| //! | ||||
| //! #### Response | ||||
| //! | ||||
| //! Incoming HTTP Responses are represented as a struct containing a `Reader` over | ||||
| //! a `NetworkStream` and contain headers, a status, and an http version. They | ||||
| //! implement `Reader` and can be read to get the data out of a `Response`. | ||||
| //! | ||||
|  | ||||
| //! Hyper provides both a [Client](client/index.html) and a | ||||
| //! [Server](server/index.html), along with a | ||||
| //! [typed Headers system](header/index.html). | ||||
| extern crate rustc_serialize as serialize; | ||||
| extern crate time; | ||||
| #[macro_use] extern crate url; | ||||
| @@ -142,10 +27,11 @@ extern crate serde; | ||||
| extern crate cookie; | ||||
| extern crate unicase; | ||||
| extern crate httparse; | ||||
| extern crate num_cpus; | ||||
| extern crate rotor; | ||||
| extern crate spmc; | ||||
| extern crate traitobject; | ||||
| extern crate typeable; | ||||
| extern crate solicit; | ||||
| extern crate vecio; | ||||
|  | ||||
| #[macro_use] | ||||
| extern crate language_tags; | ||||
| @@ -163,35 +49,31 @@ extern crate test; | ||||
| pub use url::Url; | ||||
| pub use client::Client; | ||||
| pub use error::{Result, Error}; | ||||
| pub use method::Method::{Get, Head, Post, Delete}; | ||||
| pub use status::StatusCode::{Ok, BadRequest, NotFound}; | ||||
| pub use http::{Next, Encoder, Decoder, Control}; | ||||
| pub use header::Headers; | ||||
| pub use method::Method::{self, Get, Head, Post, Delete}; | ||||
| pub use status::StatusCode::{self, Ok, BadRequest, NotFound}; | ||||
| pub use server::Server; | ||||
| pub use uri::RequestUri; | ||||
| pub use version::HttpVersion; | ||||
| pub use language_tags::LanguageTag; | ||||
|  | ||||
| macro_rules! todo( | ||||
|     ($($arg:tt)*) => (if cfg!(not(ndebug)) { | ||||
|         trace!("TODO: {:?}", format_args!($($arg)*)) | ||||
|     }) | ||||
| ); | ||||
|  | ||||
| macro_rules! inspect( | ||||
|     ($name:expr, $value:expr) => ({ | ||||
|         let v = $value; | ||||
|         trace!("inspect: {:?} = {:?}", $name, v); | ||||
|         v | ||||
|     }) | ||||
| ); | ||||
| macro_rules! rotor_try { | ||||
|     ($e:expr) => ({ | ||||
|         match $e { | ||||
|             Ok(v) => v, | ||||
|             Err(e) => return ::rotor::Response::error(e.into()) | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| #[macro_use] | ||||
| mod mock; | ||||
| #[doc(hidden)] | ||||
| pub mod buffer; | ||||
| pub mod client; | ||||
| pub mod error; | ||||
| pub mod method; | ||||
| pub mod header; | ||||
| pub mod http; | ||||
| mod http; | ||||
| pub mod net; | ||||
| pub mod server; | ||||
| pub mod status; | ||||
| @@ -203,6 +85,7 @@ pub mod mime { | ||||
|     pub use mime_crate::*; | ||||
| } | ||||
|  | ||||
| /* | ||||
| #[allow(unconditional_recursion)] | ||||
| fn _assert_send<T: Send>() { | ||||
|     _assert_send::<Client>(); | ||||
| @@ -216,3 +99,4 @@ fn _assert_sync<T: Sync>() { | ||||
|     _assert_sync::<Client>(); | ||||
|     _assert_sync::<error::Error>(); | ||||
| } | ||||
| */ | ||||
|   | ||||
| @@ -128,6 +128,12 @@ impl fmt::Display for Method { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for Method { | ||||
|     fn default() -> Method { | ||||
|         Method::Get | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| impl Serialize for Method { | ||||
|     fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { | ||||
|   | ||||
							
								
								
									
										394
									
								
								src/mock.rs
									
									
									
									
									
								
							
							
						
						
									
										394
									
								
								src/mock.rs
									
									
									
									
									
								
							| @@ -1,327 +1,127 @@ | ||||
| use std::ascii::AsciiExt; | ||||
| use std::io::{self, Read, Write, Cursor}; | ||||
| use std::cell::RefCell; | ||||
| use std::net::{SocketAddr, Shutdown}; | ||||
| use std::sync::{Arc, Mutex}; | ||||
| use std::time::Duration; | ||||
| use std::cell::Cell; | ||||
| use std::cmp; | ||||
| use std::io::{self, Read, Write}; | ||||
|  | ||||
| use solicit::http::HttpScheme; | ||||
| use solicit::http::transport::TransportStream; | ||||
| use solicit::http::frame::{SettingsFrame, Frame}; | ||||
| use solicit::http::connection::{HttpConnection, EndStream, DataChunk}; | ||||
|  | ||||
| use header::Headers; | ||||
| use net::{NetworkStream, NetworkConnector, SslClient}; | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct MockStream { | ||||
|     pub read: Cursor<Vec<u8>>, | ||||
|     next_reads: Vec<Vec<u8>>, | ||||
|     pub write: Vec<u8>, | ||||
|     pub is_closed: bool, | ||||
|     pub error_on_write: bool, | ||||
|     pub error_on_read: bool, | ||||
|     pub read_timeout: Cell<Option<Duration>>, | ||||
|     pub write_timeout: Cell<Option<Duration>>, | ||||
| #[derive(Debug)] | ||||
| pub struct Buf { | ||||
|     vec: Vec<u8> | ||||
| } | ||||
|  | ||||
| impl PartialEq for MockStream { | ||||
|     fn eq(&self, other: &MockStream) -> bool { | ||||
|         self.read.get_ref() == other.read.get_ref() && self.write == other.write | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl MockStream { | ||||
|     pub fn new() -> MockStream { | ||||
|         MockStream::with_input(b"") | ||||
|     } | ||||
|  | ||||
|     pub fn with_input(input: &[u8]) -> MockStream { | ||||
|         MockStream::with_responses(vec![input]) | ||||
|     } | ||||
|  | ||||
|     pub fn with_responses(mut responses: Vec<&[u8]>) -> MockStream { | ||||
|         MockStream { | ||||
|             read: Cursor::new(responses.remove(0).to_vec()), | ||||
|             next_reads: responses.into_iter().map(|arr| arr.to_vec()).collect(), | ||||
|             write: vec![], | ||||
|             is_closed: false, | ||||
|             error_on_write: false, | ||||
|             error_on_read: false, | ||||
|             read_timeout: Cell::new(None), | ||||
|             write_timeout: Cell::new(None), | ||||
| impl Buf { | ||||
|     pub fn new() -> Buf { | ||||
|         Buf { | ||||
|             vec: vec![] | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Read for MockStream { | ||||
| impl ::std::ops::Deref for Buf { | ||||
|     type Target = [u8]; | ||||
|  | ||||
|     fn deref(&self) -> &[u8] { | ||||
|         &self.vec | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: AsRef<[u8]>> PartialEq<S> for Buf { | ||||
|     fn eq(&self, other: &S) -> bool { | ||||
|         self.vec == other.as_ref() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Write for Buf { | ||||
|     fn write(&mut self, data: &[u8]) -> io::Result<usize> { | ||||
|         self.vec.extend(data); | ||||
|         Ok(data.len()) | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Read for Buf { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         if self.error_on_read { | ||||
|             Err(io::Error::new(io::ErrorKind::Other, "mock error")) | ||||
|         (&*self.vec).read(buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ::vecio::Writev for Buf { | ||||
|     fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|         let cap = bufs.iter().map(|buf| buf.len()).fold(0, |total, next| total + next); | ||||
|         let mut vec = Vec::with_capacity(cap); | ||||
|         for &buf in bufs { | ||||
|             vec.extend(buf); | ||||
|         } | ||||
|  | ||||
|         self.write(&vec) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct Async<T> { | ||||
|     inner: T, | ||||
|     bytes_until_block: usize, | ||||
| } | ||||
|  | ||||
| impl<T> Async<T> { | ||||
|     pub fn new(inner: T, bytes: usize) -> Async<T> { | ||||
|         Async { | ||||
|             inner: inner, | ||||
|             bytes_until_block: bytes | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn block_in(&mut self, bytes: usize) { | ||||
|         self.bytes_until_block = bytes; | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Read> Read for Async<T> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         if self.bytes_until_block == 0 { | ||||
|             Err(io::Error::new(io::ErrorKind::WouldBlock, "mock block")) | ||||
|         } else { | ||||
|             match self.read.read(buf) { | ||||
|                 Ok(n) => { | ||||
|                     if self.read.position() as usize == self.read.get_ref().len() { | ||||
|                         if self.next_reads.len() > 0 { | ||||
|                             self.read = Cursor::new(self.next_reads.remove(0)); | ||||
|                         } | ||||
|                     } | ||||
|             let n = cmp::min(self.bytes_until_block, buf.len()); | ||||
|             let n = try!(self.inner.read(&mut buf[..n])); | ||||
|             self.bytes_until_block -= n; | ||||
|             Ok(n) | ||||
|                 }, | ||||
|                 r => r | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Write for MockStream { | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         if self.error_on_write { | ||||
|             Err(io::Error::new(io::ErrorKind::Other, "mock error")) | ||||
| impl<T: Write> Write for Async<T> { | ||||
|     fn write(&mut self, data: &[u8]) -> io::Result<usize> { | ||||
|         if self.bytes_until_block == 0 { | ||||
|             Err(io::Error::new(io::ErrorKind::WouldBlock, "mock block")) | ||||
|         } else { | ||||
|             Write::write(&mut self.write, msg) | ||||
|             let n = cmp::min(self.bytes_until_block, data.len()); | ||||
|             let n = try!(self.inner.write(&data[..n])); | ||||
|             self.bytes_until_block -= n; | ||||
|             Ok(n) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         Ok(()) | ||||
|         self.inner.flush() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl NetworkStream for MockStream { | ||||
|     fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         Ok("127.0.0.1:1337".parse().unwrap()) | ||||
| impl<T: Write> ::vecio::Writev for Async<T> { | ||||
|     fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|         let cap = bufs.iter().map(|buf| buf.len()).fold(0, |total, next| total + next); | ||||
|         let mut vec = Vec::with_capacity(cap); | ||||
|         for &buf in bufs { | ||||
|             vec.extend(buf); | ||||
|         } | ||||
|  | ||||
|     fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { | ||||
|         self.read_timeout.set(dur); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> { | ||||
|         self.write_timeout.set(dur); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn close(&mut self, _how: Shutdown) -> io::Result<()> { | ||||
|         self.is_closed = true; | ||||
|         Ok(()) | ||||
|         self.write(&vec) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A wrapper around a `MockStream` that allows one to clone it and keep an independent copy to the | ||||
| /// same underlying stream. | ||||
| #[derive(Clone)] | ||||
| pub struct CloneableMockStream { | ||||
|     pub inner: Arc<Mutex<MockStream>>, | ||||
| } | ||||
| impl ::std::ops::Deref for Async<Buf> { | ||||
|     type Target = [u8]; | ||||
|  | ||||
| impl Write for CloneableMockStream { | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         self.inner.lock().unwrap().write(msg) | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         self.inner.lock().unwrap().flush() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Read for CloneableMockStream { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         self.inner.lock().unwrap().read(buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl TransportStream for CloneableMockStream { | ||||
|     fn try_split(&self) -> Result<CloneableMockStream, io::Error> { | ||||
|         Ok(self.clone()) | ||||
|     } | ||||
|  | ||||
|     fn close(&mut self) -> Result<(), io::Error> { | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl NetworkStream for CloneableMockStream { | ||||
|     fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         self.inner.lock().unwrap().peer_addr() | ||||
|     } | ||||
|  | ||||
|     fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { | ||||
|         self.inner.lock().unwrap().set_read_timeout(dur) | ||||
|     } | ||||
|  | ||||
|     fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> { | ||||
|         self.inner.lock().unwrap().set_write_timeout(dur) | ||||
|     } | ||||
|  | ||||
|     fn close(&mut self, how: Shutdown) -> io::Result<()> { | ||||
|         NetworkStream::close(&mut *self.inner.lock().unwrap(), how) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl CloneableMockStream { | ||||
|     pub fn with_stream(stream: MockStream) -> CloneableMockStream { | ||||
|         CloneableMockStream { | ||||
|             inner: Arc::new(Mutex::new(stream)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct MockConnector; | ||||
|  | ||||
| impl NetworkConnector for MockConnector { | ||||
|     type Stream = MockStream; | ||||
|  | ||||
|     fn connect(&self, _host: &str, _port: u16, _scheme: &str) -> ::Result<MockStream> { | ||||
|         Ok(MockStream::new()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// new connectors must be created if you wish to intercept requests. | ||||
| macro_rules! mock_connector ( | ||||
|     ($name:ident { | ||||
|         $($url:expr => $res:expr)* | ||||
|     }) => ( | ||||
|  | ||||
|         struct $name; | ||||
|  | ||||
|         impl $crate::net::NetworkConnector for $name { | ||||
|             type Stream = ::mock::MockStream; | ||||
|             fn connect(&self, host: &str, port: u16, scheme: &str) | ||||
|                     -> $crate::Result<::mock::MockStream> { | ||||
|                 use std::collections::HashMap; | ||||
|                 debug!("MockStream::connect({:?}, {:?}, {:?})", host, port, scheme); | ||||
|                 let mut map = HashMap::new(); | ||||
|                 $(map.insert($url, $res);)* | ||||
|  | ||||
|  | ||||
|                 let key = format!("{}://{}", scheme, host); | ||||
|                 // ignore port for now | ||||
|                 match map.get(&*key) { | ||||
|                     Some(&res) => Ok($crate::mock::MockStream::with_input(res.as_bytes())), | ||||
|                     None => panic!("{:?} doesn't know url {}", stringify!($name), key) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     ); | ||||
|  | ||||
|     ($name:ident { $($response:expr),+ }) => ( | ||||
|         struct $name; | ||||
|  | ||||
|         impl $crate::net::NetworkConnector for $name { | ||||
|             type Stream = $crate::mock::MockStream; | ||||
|             fn connect(&self, _: &str, _: u16, _: &str) | ||||
|                     -> $crate::Result<$crate::mock::MockStream> { | ||||
|                 Ok($crate::mock::MockStream::with_responses(vec![ | ||||
|                     $($response),+ | ||||
|                 ])) | ||||
|             } | ||||
|         } | ||||
|     ); | ||||
| ); | ||||
|  | ||||
| impl TransportStream for MockStream { | ||||
|     fn try_split(&self) -> Result<MockStream, io::Error> { | ||||
|         Ok(self.clone()) | ||||
|     } | ||||
|  | ||||
|     fn close(&mut self) -> Result<(), io::Error> { | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl MockStream { | ||||
|     /// Creates a new `MockStream` that will return the response described by the parameters as an | ||||
|     /// HTTP/2 response. This will also include the correct server preface. | ||||
|     pub fn new_http2_response(status: &[u8], headers: &Headers, body: Option<Vec<u8>>) | ||||
|             -> MockStream { | ||||
|         let resp_bytes = build_http2_response(status, headers, body); | ||||
|         MockStream::with_input(&resp_bytes) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Builds up a sequence of bytes that represent a server's response based on the given parameters. | ||||
| pub fn build_http2_response(status: &[u8], headers: &Headers, body: Option<Vec<u8>>) -> Vec<u8> { | ||||
|     let mut conn = HttpConnection::new(MockStream::new(), MockStream::new(), HttpScheme::Http); | ||||
|     // Server preface first | ||||
|     conn.sender.write(&SettingsFrame::new().serialize()).unwrap(); | ||||
|  | ||||
|     let mut resp_headers: Vec<_> = headers.iter().map(|h| { | ||||
|         (h.name().to_ascii_lowercase().into_bytes(), h.value_string().into_bytes()) | ||||
|     }).collect(); | ||||
|     resp_headers.insert(0, (b":status".to_vec(), status.into())); | ||||
|  | ||||
|     let end = if body.is_none() { | ||||
|         EndStream::Yes | ||||
|     } else { | ||||
|         EndStream::No | ||||
|     }; | ||||
|     conn.send_headers(resp_headers, 1, end).unwrap(); | ||||
|     if body.is_some() { | ||||
|         let chunk = DataChunk::new_borrowed(&body.as_ref().unwrap()[..], 1, EndStream::Yes); | ||||
|         conn.send_data(chunk).unwrap(); | ||||
|     } | ||||
|  | ||||
|     conn.sender.write | ||||
| } | ||||
|  | ||||
| /// A mock connector that produces `MockStream`s that are set to return HTTP/2 responses. | ||||
| /// | ||||
| /// This means that the streams' payloads are fairly opaque byte sequences (as HTTP/2 is a binary | ||||
| /// protocol), which can be understood only be HTTP/2 clients. | ||||
| pub struct MockHttp2Connector { | ||||
|     /// The list of streams that the connector returns, in the given order. | ||||
|     pub streams: RefCell<Vec<CloneableMockStream>>, | ||||
| } | ||||
|  | ||||
| impl MockHttp2Connector { | ||||
|     /// Creates a new `MockHttp2Connector` with no streams. | ||||
|     pub fn new() -> MockHttp2Connector { | ||||
|         MockHttp2Connector { | ||||
|             streams: RefCell::new(Vec::new()), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Adds a new `CloneableMockStream` to the end of the connector's stream queue. | ||||
|     /// | ||||
|     /// Streams are returned in a FIFO manner. | ||||
|     pub fn add_stream(&mut self, stream: CloneableMockStream) { | ||||
|         self.streams.borrow_mut().push(stream); | ||||
|     } | ||||
|  | ||||
|     /// Adds a new response stream that will be placed to the end of the connector's stream queue. | ||||
|     /// | ||||
|     /// Returns a separate `CloneableMockStream` that allows the user to inspect what is written | ||||
|     /// into the original stream. | ||||
|     pub fn new_response_stream(&mut self, status: &[u8], headers: &Headers, body: Option<Vec<u8>>) | ||||
|             -> CloneableMockStream { | ||||
|         let stream = MockStream::new_http2_response(status, headers, body); | ||||
|         let stream = CloneableMockStream::with_stream(stream); | ||||
|         let ret = stream.clone(); | ||||
|         self.add_stream(stream); | ||||
|  | ||||
|         ret | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl NetworkConnector for MockHttp2Connector { | ||||
|     type Stream = CloneableMockStream; | ||||
|     #[inline] | ||||
|     fn connect(&self, _host: &str, _port: u16, _scheme: &str) | ||||
|             -> ::Result<CloneableMockStream> { | ||||
|         Ok(self.streams.borrow_mut().remove(0)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Default)] | ||||
| pub struct MockSsl; | ||||
|  | ||||
| impl<T: NetworkStream + Send + Clone> SslClient<T> for MockSsl { | ||||
|     type Stream = T; | ||||
|     fn wrap_client(&self, stream: T, _host: &str) -> ::Result<T> { | ||||
|         Ok(stream) | ||||
|     fn deref(&self) -> &[u8] { | ||||
|         &self.inner | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										984
									
								
								src/net.rs
									
									
									
									
									
								
							
							
						
						
									
										984
									
								
								src/net.rs
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,79 +0,0 @@ | ||||
| use std::sync::{Arc, mpsc}; | ||||
| use std::thread; | ||||
|  | ||||
| use net::NetworkListener; | ||||
|  | ||||
| pub struct ListenerPool<A: NetworkListener> { | ||||
|     acceptor: A | ||||
| } | ||||
|  | ||||
| impl<A: NetworkListener + Send + 'static> ListenerPool<A> { | ||||
|     /// Create a thread pool to manage the acceptor. | ||||
|     pub fn new(acceptor: A) -> ListenerPool<A> { | ||||
|         ListenerPool { acceptor: acceptor } | ||||
|     } | ||||
|  | ||||
|     /// Runs the acceptor pool. Blocks until the acceptors are closed. | ||||
|     /// | ||||
|     /// ## Panics | ||||
|     /// | ||||
|     /// Panics if threads == 0. | ||||
|     pub fn accept<F>(self, work: F, threads: usize) | ||||
|         where F: Fn(A::Stream) + Send + Sync + 'static { | ||||
|         assert!(threads != 0, "Can't accept on 0 threads."); | ||||
|  | ||||
|         let (super_tx, supervisor_rx) = mpsc::channel(); | ||||
|  | ||||
|         let work = Arc::new(work); | ||||
|  | ||||
|         // Begin work. | ||||
|         for _ in 0..threads { | ||||
|             spawn_with(super_tx.clone(), work.clone(), self.acceptor.clone()) | ||||
|         } | ||||
|  | ||||
|         // Monitor for panics. | ||||
|         // FIXME(reem): This won't ever exit since we still have a super_tx handle. | ||||
|         for _ in supervisor_rx.iter() { | ||||
|             spawn_with(super_tx.clone(), work.clone(), self.acceptor.clone()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn spawn_with<A, F>(supervisor: mpsc::Sender<()>, work: Arc<F>, mut acceptor: A) | ||||
| where A: NetworkListener + Send + 'static, | ||||
|       F: Fn(<A as NetworkListener>::Stream) + Send + Sync + 'static { | ||||
|     thread::spawn(move || { | ||||
|         let _sentinel = Sentinel::new(supervisor, ()); | ||||
|  | ||||
|         loop { | ||||
|             match acceptor.accept() { | ||||
|                 Ok(stream) => work(stream), | ||||
|                 Err(e) => { | ||||
|                     error!("Connection failed: {}", e); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| struct Sentinel<T: Send + 'static> { | ||||
|     value: Option<T>, | ||||
|     supervisor: mpsc::Sender<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Send + 'static> Sentinel<T> { | ||||
|     fn new(channel: mpsc::Sender<T>, data: T) -> Sentinel<T> { | ||||
|         Sentinel { | ||||
|             value: Some(data), | ||||
|             supervisor: channel, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Send + 'static> Drop for Sentinel<T> { | ||||
|     fn drop(&mut self) { | ||||
|         // Respawn ourselves | ||||
|         let _ = self.supervisor.send(self.value.take().unwrap()); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										58
									
								
								src/server/message.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/server/message.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| use std::marker::PhantomData; | ||||
|  | ||||
|  | ||||
| use http::{self, Next}; | ||||
| use net::Transport; | ||||
|  | ||||
| use super::{Handler, request, response}; | ||||
|  | ||||
| /// A MessageHandler for a Server. | ||||
| /// | ||||
| /// This should be really thin glue between http::MessageHandler and | ||||
| /// server::Handler, but largely just providing the proper types one | ||||
| /// would expect in a Server Handler. | ||||
| pub struct Message<H: Handler<T>, T: Transport> { | ||||
|     handler: H, | ||||
|     _marker: PhantomData<T> | ||||
| } | ||||
|  | ||||
| impl<H: Handler<T>, T: Transport> Message<H, T> { | ||||
|     pub fn new(handler: H) -> Message<H, T> { | ||||
|         Message { | ||||
|             handler: handler, | ||||
|             _marker: PhantomData, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<H: Handler<T>, T: Transport> http::MessageHandler<T> for Message<H, T> { | ||||
|     type Message = http::ServerMessage; | ||||
|  | ||||
|     fn on_incoming(&mut self, head: http::RequestHead) -> Next { | ||||
|         trace!("on_incoming {:?}", head); | ||||
|         let req = request::new(head); | ||||
|         self.handler.on_request(req) | ||||
|     } | ||||
|  | ||||
|     fn on_decode(&mut self, transport: &mut http::Decoder<T>) -> Next { | ||||
|         self.handler.on_request_readable(transport) | ||||
|     } | ||||
|  | ||||
|     fn on_outgoing(&mut self, head: &mut http::MessageHead<::status::StatusCode>) -> Next { | ||||
|         let mut res = response::new(head); | ||||
|         self.handler.on_response(&mut res) | ||||
|     } | ||||
|  | ||||
|     fn on_encode(&mut self, transport: &mut http::Encoder<T>) -> Next { | ||||
|         self.handler.on_response_writable(transport) | ||||
|     } | ||||
|  | ||||
|     fn on_error(&mut self, error: ::Error) -> Next { | ||||
|         self.handler.on_error(error) | ||||
|     } | ||||
|  | ||||
|     fn on_remove(self, transport: T) { | ||||
|         self.handler.on_remove(transport); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -1,510 +1,369 @@ | ||||
| //! HTTP Server | ||||
| //! | ||||
| //! # Server | ||||
| //! | ||||
| //! A `Server` is created to listen on port, parse HTTP requests, and hand | ||||
| //! them off to a `Handler`. By default, the Server will listen across multiple | ||||
| //! threads, but that can be configured to a single thread if preferred. | ||||
| //! | ||||
| //! # Handling requests | ||||
| //! | ||||
| //! You must pass a `Handler` to the Server that will handle requests. There is | ||||
| //! a default implementation for `fn`s and closures, allowing you pass one of | ||||
| //! those easily. | ||||
| //! | ||||
| //! | ||||
| //! ```no_run | ||||
| //! use hyper::server::{Server, Request, Response}; | ||||
| //! | ||||
| //! fn hello(req: Request, res: Response) { | ||||
| //!     // handle things here | ||||
| //! } | ||||
| //! | ||||
| //! Server::http("0.0.0.0:0").unwrap().handle(hello).unwrap(); | ||||
| //! ``` | ||||
| //! | ||||
| //! As with any trait, you can also define a struct and implement `Handler` | ||||
| //! directly on your own type, and pass that to the `Server` instead. | ||||
| //! | ||||
| //! ```no_run | ||||
| //! use std::sync::Mutex; | ||||
| //! use std::sync::mpsc::{channel, Sender}; | ||||
| //! use hyper::server::{Handler, Server, Request, Response}; | ||||
| //! | ||||
| //! struct SenderHandler { | ||||
| //!     sender: Mutex<Sender<&'static str>> | ||||
| //! } | ||||
| //! | ||||
| //! impl Handler for SenderHandler { | ||||
| //!     fn handle(&self, req: Request, res: Response) { | ||||
| //!         self.sender.lock().unwrap().send("start").unwrap(); | ||||
| //!     } | ||||
| //! } | ||||
| //! | ||||
| //! | ||||
| //! let (tx, rx) = channel(); | ||||
| //! Server::http("0.0.0.0:0").unwrap().handle(SenderHandler { | ||||
| //!     sender: Mutex::new(tx) | ||||
| //! }).unwrap(); | ||||
| //! ``` | ||||
| //! | ||||
| //! Since the `Server` will be listening on multiple threads, the `Handler` | ||||
| //! must implement `Sync`: any mutable state must be synchronized. | ||||
| //! | ||||
| //! ```no_run | ||||
| //! use std::sync::atomic::{AtomicUsize, Ordering}; | ||||
| //! use hyper::server::{Server, Request, Response}; | ||||
| //! | ||||
| //! let counter = AtomicUsize::new(0); | ||||
| //! Server::http("0.0.0.0:0").unwrap().handle(move |req: Request, res: Response| { | ||||
| //!     counter.fetch_add(1, Ordering::Relaxed); | ||||
| //! }).unwrap(); | ||||
| //! ``` | ||||
| //! | ||||
| //! # The `Request` and `Response` pair | ||||
| //! | ||||
| //! A `Handler` receives a pair of arguments, a `Request` and a `Response`. The | ||||
| //! `Request` includes access to the `method`, `uri`, and `headers` of the | ||||
| //! incoming HTTP request. It also implements `std::io::Read`, in order to | ||||
| //! read any body, such as with `POST` or `PUT` messages. | ||||
| //! | ||||
| //! Likewise, the `Response` includes ways to set the `status` and `headers`, | ||||
| //! and implements `std::io::Write` to allow writing the response body. | ||||
| //! | ||||
| //! ```no_run | ||||
| //! use std::io; | ||||
| //! use hyper::server::{Server, Request, Response}; | ||||
| //! use hyper::status::StatusCode; | ||||
| //! | ||||
| //! Server::http("0.0.0.0:0").unwrap().handle(|mut req: Request, mut res: Response| { | ||||
| //!     match req.method { | ||||
| //!         hyper::Post => { | ||||
| //!             io::copy(&mut req, &mut res.start().unwrap()).unwrap(); | ||||
| //!         }, | ||||
| //!         _ => *res.status_mut() = StatusCode::MethodNotAllowed | ||||
| //!     } | ||||
| //! }).unwrap(); | ||||
| //! ``` | ||||
| //! | ||||
| //! ## An aside: Write Status | ||||
| //! | ||||
| //! The `Response` uses a phantom type parameter to determine its write status. | ||||
| //! What does that mean? In short, it ensures you never write a body before | ||||
| //! adding all headers, and never add a header after writing some of the body. | ||||
| //! | ||||
| //! This is often done in most implementations by include a boolean property | ||||
| //! on the response, such as `headers_written`, checking that each time the | ||||
| //! body has something to write, so as to make sure the headers are sent once, | ||||
| //! and only once. But this has 2 downsides: | ||||
| //! | ||||
| //! 1. You are typically never notified that your late header is doing nothing. | ||||
| //! 2. There's a runtime cost to checking on every write. | ||||
| //! | ||||
| //! Instead, hyper handles this statically, or at compile-time. A | ||||
| //! `Response<Fresh>` includes a `headers_mut()` method, allowing you add more | ||||
| //! headers. It also does not implement `Write`, so you can't accidentally | ||||
| //! write early. Once the "head" of the response is correct, you can "send" it | ||||
| //! out by calling `start` on the `Response<Fresh>`. This will return a new | ||||
| //! `Response<Streaming>` object, that no longer has `headers_mut()`, but does | ||||
| //! implement `Write`. | ||||
| //! them off to a `Handler`. | ||||
| use std::fmt; | ||||
| use std::io::{self, ErrorKind, BufWriter, Write}; | ||||
| use std::net::{SocketAddr, ToSocketAddrs}; | ||||
| use std::thread::{self, JoinHandle}; | ||||
| use std::net::SocketAddr; | ||||
| use std::sync::Arc; | ||||
| use std::sync::atomic::{AtomicBool, Ordering}; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use num_cpus; | ||||
| use rotor::mio::{EventSet, PollOpt}; | ||||
| use rotor::{self, Scope}; | ||||
|  | ||||
| pub use self::request::Request; | ||||
| pub use self::response::Response; | ||||
|  | ||||
| pub use net::{Fresh, Streaming}; | ||||
| use http::{self, Next}; | ||||
| use net::{Accept, HttpListener, HttpsListener, SslServer, Transport}; | ||||
|  | ||||
| use Error; | ||||
| use buffer::BufReader; | ||||
| use header::{Headers, Expect, Connection}; | ||||
| use http; | ||||
| use method::Method; | ||||
| use net::{NetworkListener, NetworkStream, HttpListener, HttpsListener, Ssl}; | ||||
| use status::StatusCode; | ||||
| use uri::RequestUri; | ||||
| use version::HttpVersion::Http11; | ||||
|  | ||||
| use self::listener::ListenerPool; | ||||
| mod request; | ||||
| mod response; | ||||
| mod message; | ||||
|  | ||||
| pub mod request; | ||||
| pub mod response; | ||||
| /// A configured `Server` ready to run. | ||||
| pub struct ServerLoop<A, H> where A: Accept, H: HandlerFactory<A::Output> { | ||||
|     inner: Option<(rotor::Loop<ServerFsm<A, H>>, Context<H>)>, | ||||
| } | ||||
|  | ||||
| mod listener; | ||||
| impl<A: Accept, H: HandlerFactory<A::Output>> fmt::Debug for ServerLoop<A, H> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.pad("ServerLoop") | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A server can listen on a TCP socket. | ||||
| /// | ||||
| /// Once listening, it will create a `Request`/`Response` pair for each | ||||
| /// incoming connection, and hand them to the provided handler. | ||||
| /// A Server that can accept incoming network requests. | ||||
| #[derive(Debug)] | ||||
| pub struct Server<L = HttpListener> { | ||||
|     listener: L, | ||||
|     timeouts: Timeouts, | ||||
| pub struct Server<T: Accept> { | ||||
|     listener: T, | ||||
|     keep_alive: bool, | ||||
|     idle_timeout: Duration, | ||||
|     max_sockets: usize, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy, Debug)] | ||||
| struct Timeouts { | ||||
|     read: Option<Duration>, | ||||
|     write: Option<Duration>, | ||||
|     keep_alive: Option<Duration>, | ||||
| } | ||||
|  | ||||
| impl Default for Timeouts { | ||||
|     fn default() -> Timeouts { | ||||
|         Timeouts { | ||||
|             read: None, | ||||
|             write: None, | ||||
|             keep_alive: Some(Duration::from_secs(5)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| macro_rules! try_option( | ||||
|     ($e:expr) => {{ | ||||
|         match $e { | ||||
|             Some(v) => v, | ||||
|             None => return None | ||||
|         } | ||||
|     }} | ||||
| ); | ||||
|  | ||||
| impl<L: NetworkListener> Server<L> { | ||||
|     /// Creates a new server with the provided handler. | ||||
| impl<T> Server<T> where T: Accept, T::Output: Transport { | ||||
|     /// Creates a new server with the provided Listener. | ||||
|     #[inline] | ||||
|     pub fn new(listener: L) -> Server<L> { | ||||
|     pub fn new(listener: T) -> Server<T> { | ||||
|         Server { | ||||
|             listener: listener, | ||||
|             timeouts: Timeouts::default() | ||||
|             keep_alive: true, | ||||
|             idle_timeout: Duration::from_secs(10), | ||||
|             max_sockets: 4096, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Controls keep-alive for this server. | ||||
|     /// Enables or disables HTTP keep-alive. | ||||
|     /// | ||||
|     /// The timeout duration passed will be used to determine how long | ||||
|     /// to keep the connection alive before dropping it. | ||||
|     /// Default is true. | ||||
|     pub fn keep_alive(mut self, val: bool) -> Server<T> { | ||||
|         self.keep_alive = val; | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Sets how long an idle connection will be kept before closing. | ||||
|     /// | ||||
|     /// Passing `None` will disable keep-alive. | ||||
|     /// Default is 10 seconds. | ||||
|     pub fn idle_timeout(mut self, val: Duration) -> Server<T> { | ||||
|         self.idle_timeout = val; | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Sets the maximum open sockets for this Server. | ||||
|     /// | ||||
|     /// Default is enabled with a 5 second timeout. | ||||
|     #[inline] | ||||
|     pub fn keep_alive(&mut self, timeout: Option<Duration>) { | ||||
|         self.timeouts.keep_alive = timeout; | ||||
|     } | ||||
|  | ||||
|     /// Sets the read timeout for all Request reads. | ||||
|     pub fn set_read_timeout(&mut self, dur: Option<Duration>) { | ||||
|         self.timeouts.read = dur; | ||||
|     } | ||||
|  | ||||
|     /// Sets the write timeout for all Response writes. | ||||
|     pub fn set_write_timeout(&mut self, dur: Option<Duration>) { | ||||
|         self.timeouts.write = dur; | ||||
|     /// Default is 4096, but most servers can handle much more than this. | ||||
|     pub fn max_sockets(mut self, val: usize) -> Server<T> { | ||||
|         self.max_sockets = val; | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Server<HttpListener> { | ||||
|     /// Creates a new server that will handle `HttpStream`s. | ||||
|     pub fn http<To: ToSocketAddrs>(addr: To) -> ::Result<Server<HttpListener>> { | ||||
|         HttpListener::new(addr).map(Server::new) | ||||
| impl Server<HttpListener> { //<H: HandlerFactory<<HttpListener as Accept>::Output>> Server<HttpListener, H> { | ||||
|     /// Creates a new HTTP server config listening on the provided address. | ||||
|     pub fn http(addr: &SocketAddr) -> ::Result<Server<HttpListener>> { | ||||
|         use ::rotor::mio::tcp::TcpListener; | ||||
|         TcpListener::bind(addr) | ||||
|             .map(HttpListener) | ||||
|             .map(Server::new) | ||||
|             .map_err(From::from) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: Ssl + Clone + Send> Server<HttpsListener<S>> { | ||||
|     /// Creates a new server that will handle `HttpStream`s over SSL. | ||||
|  | ||||
| impl<S: SslServer> Server<HttpsListener<S>> { | ||||
|     /// Creates a new server config that will handle `HttpStream`s over SSL. | ||||
|     /// | ||||
|     /// You can use any SSL implementation, as long as implements `hyper::net::Ssl`. | ||||
|     pub fn https<A: ToSocketAddrs>(addr: A, ssl: S) -> ::Result<Server<HttpsListener<S>>> { | ||||
|         HttpsListener::new(addr, ssl).map(Server::new) | ||||
|     pub fn https(addr: &SocketAddr, ssl: S) -> ::Result<Server<HttpsListener<S>>> { | ||||
|         HttpsListener::new(addr, ssl) | ||||
|             .map(Server::new) | ||||
|             .map_err(From::from) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<L: NetworkListener + Send + 'static> Server<L> { | ||||
|  | ||||
| impl<A: Accept> Server<A> where A::Output: Transport  { | ||||
|     /// Binds to a socket and starts handling connections. | ||||
|     pub fn handle<H: Handler + 'static>(self, handler: H) -> ::Result<Listening> { | ||||
|         self.handle_threads(handler, num_cpus::get() * 5 / 4) | ||||
|     } | ||||
|     pub fn handle<H>(self, factory: H) -> ::Result<(Listening, ServerLoop<A, H>)> | ||||
|     where H: HandlerFactory<A::Output> { | ||||
|         let addr = try!(self.listener.local_addr()); | ||||
|         let shutdown = Arc::new(AtomicBool::new(false)); | ||||
|         let shutdown_rx = shutdown.clone(); | ||||
|  | ||||
|     /// Binds to a socket and starts handling connections with the provided | ||||
|     /// number of threads. | ||||
|     pub fn handle_threads<H: Handler + 'static>(self, handler: H, | ||||
|             threads: usize) -> ::Result<Listening> { | ||||
|         handle(self, handler, threads) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn handle<H, L>(mut server: Server<L>, handler: H, threads: usize) -> ::Result<Listening> | ||||
| where H: Handler + 'static, L: NetworkListener + Send + 'static { | ||||
|     let socket = try!(server.listener.local_addr()); | ||||
|  | ||||
|     debug!("threads = {:?}", threads); | ||||
|     let pool = ListenerPool::new(server.listener); | ||||
|     let worker = Worker::new(handler, server.timeouts); | ||||
|     let work = move |mut stream| worker.handle_connection(&mut stream); | ||||
|  | ||||
|     let guard = thread::spawn(move || pool.accept(work, threads)); | ||||
|  | ||||
|     Ok(Listening { | ||||
|         _guard: Some(guard), | ||||
|         socket: socket, | ||||
|     }) | ||||
| } | ||||
|  | ||||
| struct Worker<H: Handler + 'static> { | ||||
|     handler: H, | ||||
|     timeouts: Timeouts, | ||||
| } | ||||
|  | ||||
| impl<H: Handler + 'static> Worker<H> { | ||||
|     fn new(handler: H, timeouts: Timeouts) -> Worker<H> { | ||||
|         Worker { | ||||
|             handler: handler, | ||||
|             timeouts: timeouts, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn handle_connection<S>(&self, mut stream: &mut S) where S: NetworkStream + Clone { | ||||
|         debug!("Incoming stream"); | ||||
|  | ||||
|         self.handler.on_connection_start(); | ||||
|  | ||||
|         if let Err(e) = self.set_timeouts(&*stream) { | ||||
|             error!("set_timeouts error: {:?}", e); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let addr = match stream.peer_addr() { | ||||
|             Ok(addr) => addr, | ||||
|             Err(e) => { | ||||
|                 error!("Peer Name error: {:?}", e); | ||||
|                 return; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         // FIXME: Use Type ascription | ||||
|         let stream_clone: &mut NetworkStream = &mut stream.clone(); | ||||
|         let mut rdr = BufReader::new(stream_clone); | ||||
|         let mut wrt = BufWriter::new(stream); | ||||
|  | ||||
|         while self.keep_alive_loop(&mut rdr, &mut wrt, addr) { | ||||
|             if let Err(e) = self.set_read_timeout(*rdr.get_ref(), self.timeouts.keep_alive) { | ||||
|                 error!("set_read_timeout keep_alive {:?}", e); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         self.handler.on_connection_end(); | ||||
|  | ||||
|         debug!("keep_alive loop ending for {}", addr); | ||||
|     } | ||||
|  | ||||
|     fn set_timeouts(&self, s: &NetworkStream) -> io::Result<()> { | ||||
|         try!(self.set_read_timeout(s, self.timeouts.read)); | ||||
|         self.set_write_timeout(s, self.timeouts.write) | ||||
|     } | ||||
|  | ||||
|     fn set_write_timeout(&self, s: &NetworkStream, timeout: Option<Duration>) -> io::Result<()> { | ||||
|         s.set_write_timeout(timeout) | ||||
|     } | ||||
|  | ||||
|     fn set_read_timeout(&self, s: &NetworkStream, timeout: Option<Duration>) -> io::Result<()> { | ||||
|         s.set_read_timeout(timeout) | ||||
|     } | ||||
|  | ||||
|     fn keep_alive_loop<W: Write>(&self, mut rdr: &mut BufReader<&mut NetworkStream>, | ||||
|             wrt: &mut W, addr: SocketAddr) -> bool { | ||||
|         let req = match Request::new(rdr, addr) { | ||||
|             Ok(req) => req, | ||||
|             Err(Error::Io(ref e)) if e.kind() == ErrorKind::ConnectionAborted => { | ||||
|                 trace!("tcp closed, cancelling keep-alive loop"); | ||||
|                 return false; | ||||
|             } | ||||
|             Err(Error::Io(e)) => { | ||||
|                 debug!("ioerror in keepalive loop = {:?}", e); | ||||
|                 return false; | ||||
|             } | ||||
|             Err(e) => { | ||||
|                 //TODO: send a 400 response | ||||
|                 error!("request error = {:?}", e); | ||||
|                 return false; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         if !self.handle_expect(&req, wrt) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if let Err(e) = req.set_read_timeout(self.timeouts.read) { | ||||
|             error!("set_read_timeout {:?}", e); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         let mut keep_alive = self.timeouts.keep_alive.is_some() && | ||||
|             http::should_keep_alive(req.version, &req.headers); | ||||
|         let version = req.version; | ||||
|         let mut res_headers = Headers::new(); | ||||
|         if !keep_alive { | ||||
|             res_headers.set(Connection::close()); | ||||
|         } | ||||
|         let mut config = rotor::Config::new(); | ||||
|         config.slab_capacity(self.max_sockets); | ||||
|         config.mio().notify_capacity(self.max_sockets); | ||||
|         let keep_alive = self.keep_alive; | ||||
|         let mut loop_ = rotor::Loop::new(&config).unwrap(); | ||||
|         let mut notifier = None; | ||||
|         { | ||||
|             let mut res = Response::new(wrt, &mut res_headers); | ||||
|             res.version = version; | ||||
|             self.handler.handle(req, res); | ||||
|             let notifier = &mut notifier; | ||||
|             loop_.add_machine_with(move |scope| { | ||||
|                 *notifier = Some(scope.notifier()); | ||||
|                 rotor_try!(scope.register(&self.listener, EventSet::readable(), PollOpt::level())); | ||||
|                 rotor::Response::ok(ServerFsm::Listener::<A, H>(self.listener, shutdown_rx)) | ||||
|             }).unwrap(); | ||||
|         } | ||||
|         let notifier = notifier.expect("loop.add_machine failed"); | ||||
|  | ||||
|         let listening = Listening { | ||||
|             addr: addr, | ||||
|             shutdown: (shutdown, notifier), | ||||
|         }; | ||||
|         let server = ServerLoop { | ||||
|             inner: Some((loop_, Context { | ||||
|                 keep_alive: keep_alive, | ||||
|                 factory: factory | ||||
|             })) | ||||
|         }; | ||||
|         Ok((listening, server)) | ||||
|     } | ||||
| } | ||||
|  | ||||
|         // if the request was keep-alive, we need to check that the server agrees | ||||
|         // if it wasn't, then the server cannot force it to be true anyways | ||||
|         if keep_alive { | ||||
|             keep_alive = http::should_keep_alive(version, &res_headers); | ||||
|  | ||||
| impl<A: Accept, H: HandlerFactory<A::Output>> ServerLoop<A, H> { | ||||
|     /// Runs the server forever in this loop. | ||||
|     /// | ||||
|     /// This will block the current thread. | ||||
|     pub fn run(self) { | ||||
|         // drop will take care of it. | ||||
|     } | ||||
| } | ||||
|  | ||||
|         debug!("keep_alive = {:?} for {}", keep_alive, addr); | ||||
|         keep_alive | ||||
| impl<A: Accept, H: HandlerFactory<A::Output>> Drop for ServerLoop<A, H> { | ||||
|     fn drop(&mut self) { | ||||
|         self.inner.take().map(|(loop_, ctx)| { | ||||
|             let _ = loop_.run(ctx); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     fn handle_expect<W: Write>(&self, req: &Request, wrt: &mut W) -> bool { | ||||
|          if req.version == Http11 && req.headers.get() == Some(&Expect::Continue) { | ||||
|             let status = self.handler.check_continue((&req.method, &req.uri, &req.headers)); | ||||
|             match write!(wrt, "{} {}\r\n\r\n", Http11, status).and_then(|_| wrt.flush()) { | ||||
|                 Ok(..) => (), | ||||
| struct Context<F> { | ||||
|     keep_alive: bool, | ||||
|     factory: F, | ||||
| } | ||||
|  | ||||
| impl<F: HandlerFactory<T>, T: Transport> http::MessageHandlerFactory<(), T> for Context<F> { | ||||
|     type Output = message::Message<F::Output, T>; | ||||
|  | ||||
|     fn create(&mut self, seed: http::Seed<()>) -> Self::Output { | ||||
|         message::Message::new(self.factory.create(seed.control())) | ||||
|     } | ||||
| } | ||||
|  | ||||
| enum ServerFsm<A, H> | ||||
| where A: Accept, | ||||
|       A::Output: Transport, | ||||
|       H: HandlerFactory<A::Output> { | ||||
|     Listener(A, Arc<AtomicBool>), | ||||
|     Conn(http::Conn<(), A::Output, message::Message<H::Output, A::Output>>) | ||||
| } | ||||
|  | ||||
| impl<A, H> rotor::Machine for ServerFsm<A, H> | ||||
| where A: Accept, | ||||
|       A::Output: Transport, | ||||
|       H: HandlerFactory<A::Output> { | ||||
|     type Context = Context<H>; | ||||
|     type Seed = A::Output; | ||||
|  | ||||
|     fn create(seed: Self::Seed, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, rotor::Void> { | ||||
|         rotor_try!(scope.register(&seed, EventSet::readable(), PollOpt::level())); | ||||
|         rotor::Response::ok( | ||||
|             ServerFsm::Conn( | ||||
|                 http::Conn::new((), seed, scope.notifier()) | ||||
|                     .keep_alive(scope.keep_alive) | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     fn ready(self, events: EventSet, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ServerFsm::Listener(listener, rx) => { | ||||
|                 match listener.accept() { | ||||
|                     Ok(Some(conn)) => { | ||||
|                         rotor::Response::spawn(ServerFsm::Listener(listener, rx), conn) | ||||
|                     }, | ||||
|                     Ok(None) => rotor::Response::ok(ServerFsm::Listener(listener, rx)), | ||||
|                     Err(e) => { | ||||
|                     error!("error writing 100-continue: {:?}", e); | ||||
|                     return false; | ||||
|                         error!("listener accept error {}", e); | ||||
|                         // usually fine, just keep listening | ||||
|                         rotor::Response::ok(ServerFsm::Listener(listener, rx)) | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             ServerFsm::Conn(conn) => { | ||||
|                 match conn.ready(events, scope) { | ||||
|                     Some((conn, None)) => rotor::Response::ok(ServerFsm::Conn(conn)), | ||||
|                     Some((conn, Some(dur))) => { | ||||
|                         rotor::Response::ok(ServerFsm::Conn(conn)) | ||||
|                             .deadline(scope.now() + dur) | ||||
|                     } | ||||
|                     None => rotor::Response::done() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|             if status != StatusCode::Continue { | ||||
|                 debug!("non-100 status ({}) for Expect 100 request", status); | ||||
|                 return false; | ||||
|     fn spawned(self, _scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ServerFsm::Listener(listener, rx) => { | ||||
|                 match listener.accept() { | ||||
|                     Ok(Some(conn)) => { | ||||
|                         rotor::Response::spawn(ServerFsm::Listener(listener, rx), conn) | ||||
|                     }, | ||||
|                     Ok(None) => rotor::Response::ok(ServerFsm::Listener(listener, rx)), | ||||
|                     Err(e) => { | ||||
|                         error!("listener accept error {}", e); | ||||
|                         // usually fine, just keep listening | ||||
|                         rotor::Response::ok(ServerFsm::Listener(listener, rx)) | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             sock => rotor::Response::ok(sock) | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     fn timeout(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ServerFsm::Listener(..) => unreachable!("Listener cannot timeout"), | ||||
|             ServerFsm::Conn(conn) => { | ||||
|                 match conn.timeout(scope) { | ||||
|                     Some((conn, None)) => rotor::Response::ok(ServerFsm::Conn(conn)), | ||||
|                     Some((conn, Some(dur))) => { | ||||
|                         rotor::Response::ok(ServerFsm::Conn(conn)) | ||||
|                             .deadline(scope.now() + dur) | ||||
|                     } | ||||
|                     None => rotor::Response::done() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         true | ||||
|     fn wakeup(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ServerFsm::Listener(lst, shutdown) => { | ||||
|                 if shutdown.load(Ordering::Acquire) { | ||||
|                     let _ = scope.deregister(&lst); | ||||
|                     scope.shutdown_loop(); | ||||
|                     rotor::Response::done() | ||||
|                 } else { | ||||
|                     rotor::Response::ok(ServerFsm::Listener(lst, shutdown)) | ||||
|                 } | ||||
|             }, | ||||
|             ServerFsm::Conn(conn) => match conn.wakeup(scope) { | ||||
|                 Some((conn, None)) => rotor::Response::ok(ServerFsm::Conn(conn)), | ||||
|                 Some((conn, Some(dur))) => { | ||||
|                     rotor::Response::ok(ServerFsm::Conn(conn)) | ||||
|                         .deadline(scope.now() + dur) | ||||
|                 } | ||||
|                 None => rotor::Response::done() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A listening server, which can later be closed. | ||||
| /// A handle of the running server. | ||||
| pub struct Listening { | ||||
|     _guard: Option<JoinHandle<()>>, | ||||
|     /// The socket addresses that the server is bound to. | ||||
|     pub socket: SocketAddr, | ||||
|     addr: SocketAddr, | ||||
|     shutdown: (Arc<AtomicBool>, rotor::Notifier), | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Listening { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         write!(f, "Listening {{ socket: {:?} }}", self.socket) | ||||
|         f.debug_struct("Listening") | ||||
|             .field("addr", &self.addr) | ||||
|             .field("closed", &self.shutdown.0.load(Ordering::Relaxed)) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Drop for Listening { | ||||
|     fn drop(&mut self) { | ||||
|         let _ = self._guard.take().map(|g| g.join()); | ||||
| impl fmt::Display for Listening { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt::Display::fmt(&self.addr, f) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Listening { | ||||
|     /// Warning: This function doesn't work. The server remains listening after you called | ||||
|     /// it. See https://github.com/hyperium/hyper/issues/338 for more details. | ||||
|     /// | ||||
|     /// The address this server is listening on. | ||||
|     pub fn addr(&self) -> &SocketAddr { | ||||
|         &self.addr | ||||
|     } | ||||
|  | ||||
|     /// Stop the server from listening to its socket address. | ||||
|     pub fn close(&mut self) -> ::Result<()> { | ||||
|         let _ = self._guard.take(); | ||||
|         debug!("closing server"); | ||||
|         Ok(()) | ||||
|     pub fn close(self) { | ||||
|         debug!("closing server {}", self); | ||||
|         self.shutdown.0.store(true, Ordering::Release); | ||||
|         self.shutdown.1.wakeup().unwrap(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A handler that can handle incoming requests for a server. | ||||
| pub trait Handler: Sync + Send { | ||||
|     /// Receives a `Request`/`Response` pair, and should perform some action on them. | ||||
| /// A trait to react to server events that happen for each message. | ||||
| /// | ||||
|     /// This could reading from the request, and writing to the response. | ||||
|     fn handle<'a, 'k>(&'a self, Request<'a, 'k>, Response<'a, Fresh>); | ||||
| /// Each event handler returns it's desired `Next` action. | ||||
| pub trait Handler<T: Transport> { | ||||
|     /// This event occurs first, triggering when a `Request` has been parsed. | ||||
|     fn on_request(&mut self, request: Request) -> Next; | ||||
|     /// This event occurs each time the `Request` is ready to be read from. | ||||
|     fn on_request_readable(&mut self, request: &mut http::Decoder<T>) -> Next; | ||||
|     /// This event occurs after the first time this handled signals `Next::write()`. | ||||
|     fn on_response(&mut self, response: &mut Response) -> Next; | ||||
|     /// This event occurs each time the `Response` is ready to be written to. | ||||
|     fn on_response_writable(&mut self, response: &mut http::Encoder<T>) -> Next; | ||||
|  | ||||
|     /// Called when a Request includes a `Expect: 100-continue` header. | ||||
|     /// This event occurs whenever an `Error` occurs outside of the other events. | ||||
|     /// | ||||
|     /// By default, this will always immediately response with a `StatusCode::Continue`, | ||||
|     /// but can be overridden with custom behavior. | ||||
|     fn check_continue(&self, _: (&Method, &RequestUri, &Headers)) -> StatusCode { | ||||
|         StatusCode::Continue | ||||
|     /// This could IO errors while waiting for events, or a timeout, etc. | ||||
|     fn on_error(&mut self, err: ::Error) -> Next where Self: Sized { | ||||
|         debug!("default Handler.on_error({:?})", err); | ||||
|         http::Next::remove() | ||||
|     } | ||||
|  | ||||
|     /// This is run after a connection is received, on a per-connection basis (not a | ||||
|     /// per-request basis, as a connection with keep-alive may handle multiple | ||||
|     /// requests) | ||||
|     fn on_connection_start(&self) { } | ||||
|  | ||||
|     /// This is run before a connection is closed, on a per-connection basis (not a | ||||
|     /// per-request basis, as a connection with keep-alive may handle multiple | ||||
|     /// requests) | ||||
|     fn on_connection_end(&self) { } | ||||
|     /// This event occurs when this Handler has requested to remove the Transport. | ||||
|     fn on_remove(self, _transport: T) where Self: Sized { | ||||
|         debug!("default Handler.on_remove"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<F> Handler for F where F: Fn(Request, Response<Fresh>), F: Sync + Send { | ||||
|     fn handle<'a, 'k>(&'a self, req: Request<'a, 'k>, res: Response<'a, Fresh>) { | ||||
|         self(req, res) | ||||
|  | ||||
| /// Used to create a `Handler` when a new message is received by the server. | ||||
| pub trait HandlerFactory<T: Transport> { | ||||
|     /// The `Handler` to use for the incoming message. | ||||
|     type Output: Handler<T>; | ||||
|     /// Creates the associated `Handler`. | ||||
|     fn create(&mut self, ctrl: http::Control) -> Self::Output; | ||||
| } | ||||
|  | ||||
| impl<F, H, T> HandlerFactory<T> for F | ||||
| where F: FnMut(http::Control) -> H, H: Handler<T>, T: Transport { | ||||
|     type Output = H; | ||||
|     fn create(&mut self, ctrl: http::Control) -> H { | ||||
|         self(ctrl) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use header::Headers; | ||||
|     use method::Method; | ||||
|     use mock::MockStream; | ||||
|     use status::StatusCode; | ||||
|     use uri::RequestUri; | ||||
|  | ||||
|     use super::{Request, Response, Fresh, Handler, Worker}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_check_continue_default() { | ||||
|         let mut mock = MockStream::with_input(b"\ | ||||
|             POST /upload HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             Expect: 100-continue\r\n\ | ||||
|             Content-Length: 10\r\n\ | ||||
|             \r\n\ | ||||
|             1234567890\ | ||||
|         "); | ||||
|  | ||||
|         fn handle(_: Request, res: Response<Fresh>) { | ||||
|             res.start().unwrap().end().unwrap(); | ||||
|         } | ||||
|  | ||||
|         Worker::new(handle, Default::default()).handle_connection(&mut mock); | ||||
|         let cont = b"HTTP/1.1 100 Continue\r\n\r\n"; | ||||
|         assert_eq!(&mock.write[..cont.len()], cont); | ||||
|         let res = b"HTTP/1.1 200 OK\r\n"; | ||||
|         assert_eq!(&mock.write[cont.len()..cont.len() + res.len()], res); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_check_continue_reject() { | ||||
|         struct Reject; | ||||
|         impl Handler for Reject { | ||||
|             fn handle<'a, 'k>(&'a self, _: Request<'a, 'k>, res: Response<'a, Fresh>) { | ||||
|                 res.start().unwrap().end().unwrap(); | ||||
|             } | ||||
|  | ||||
|             fn check_continue(&self, _: (&Method, &RequestUri, &Headers)) -> StatusCode { | ||||
|                 StatusCode::ExpectationFailed | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let mut mock = MockStream::with_input(b"\ | ||||
|             POST /upload HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             Expect: 100-continue\r\n\ | ||||
|             Content-Length: 10\r\n\ | ||||
|             \r\n\ | ||||
|             1234567890\ | ||||
|         "); | ||||
|  | ||||
|         Worker::new(Reject, Default::default()).handle_connection(&mut mock); | ||||
|         assert_eq!(mock.write, &b"HTTP/1.1 417 Expectation Failed\r\n\r\n"[..]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,324 +2,75 @@ | ||||
| //! | ||||
| //! These are requests that a `hyper::Server` receives, and include its method, | ||||
| //! target URI, headers, and message body. | ||||
| use std::io::{self, Read}; | ||||
| use std::net::SocketAddr; | ||||
| use std::time::Duration; | ||||
| //use std::net::SocketAddr; | ||||
|  | ||||
| use buffer::BufReader; | ||||
| use net::NetworkStream; | ||||
| use version::{HttpVersion}; | ||||
| use version::HttpVersion; | ||||
| use method::Method; | ||||
| use header::{Headers, ContentLength, TransferEncoding}; | ||||
| use http::h1::{self, Incoming, HttpReader}; | ||||
| use http::h1::HttpReader::{SizedReader, ChunkedReader, EmptyReader}; | ||||
| use header::Headers; | ||||
| use http::{RequestHead, MessageHead, RequestLine}; | ||||
| use uri::RequestUri; | ||||
|  | ||||
| /// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`. | ||||
| pub struct Request<'a, 'b: 'a> { | ||||
|     /// The IP address of the remote connection. | ||||
|     pub remote_addr: SocketAddr, | ||||
|     /// The `Method`, such as `Get`, `Post`, etc. | ||||
|     pub method: Method, | ||||
|     /// The headers of the incoming request. | ||||
|     pub headers: Headers, | ||||
|     /// The target request-uri for this request. | ||||
|     pub uri: RequestUri, | ||||
|     /// The version of HTTP for this request. | ||||
|     pub version: HttpVersion, | ||||
|     body: HttpReader<&'a mut BufReader<&'b mut NetworkStream>> | ||||
| } | ||||
|  | ||||
|  | ||||
| impl<'a, 'b: 'a> Request<'a, 'b> { | ||||
|     /// Create a new Request, reading the StartLine and Headers so they are | ||||
|     /// immediately useful. | ||||
|     pub fn new(mut stream: &'a mut BufReader<&'b mut NetworkStream>, addr: SocketAddr) | ||||
|         -> ::Result<Request<'a, 'b>> { | ||||
|  | ||||
|         let Incoming { version, subject: (method, uri), headers } = try!(h1::parse_request(stream)); | ||||
| pub fn new(incoming: RequestHead) -> Request { | ||||
|     let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming; | ||||
|     debug!("Request Line: {:?} {:?} {:?}", method, uri, version); | ||||
|         debug!("{:?}", headers); | ||||
|     debug!("{:#?}", headers); | ||||
|  | ||||
|         let body = if headers.has::<ContentLength>() { | ||||
|             match headers.get::<ContentLength>() { | ||||
|                 Some(&ContentLength(len)) => SizedReader(stream, len), | ||||
|                 None => unreachable!() | ||||
|             } | ||||
|         } else if headers.has::<TransferEncoding>() { | ||||
|             todo!("check for Transfer-Encoding: chunked"); | ||||
|             ChunkedReader(stream, None) | ||||
|         } else { | ||||
|             EmptyReader(stream) | ||||
|         }; | ||||
|  | ||||
|         Ok(Request { | ||||
|             remote_addr: addr, | ||||
|     Request { | ||||
|         //remote_addr: addr, | ||||
|         method: method, | ||||
|         uri: uri, | ||||
|         headers: headers, | ||||
|         version: version, | ||||
|             body: body | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
|     /// Set the read timeout of the underlying NetworkStream. | ||||
|     #[inline] | ||||
|     pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { | ||||
|         self.body.get_ref().get_ref().set_read_timeout(timeout) | ||||
| /// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`. | ||||
| #[derive(Debug)] | ||||
| pub struct Request { | ||||
|     // The IP address of the remote connection. | ||||
|     //remote_addr: SocketAddr, | ||||
|     method: Method, | ||||
|     headers: Headers, | ||||
|     uri: RequestUri, | ||||
|     version: HttpVersion, | ||||
| } | ||||
|  | ||||
|     /// Get a reference to the underlying `NetworkStream`. | ||||
|     #[inline] | ||||
|     pub fn downcast_ref<T: NetworkStream>(&self) -> Option<&T> { | ||||
|         self.body.get_ref().get_ref().downcast_ref() | ||||
|     } | ||||
|  | ||||
|     /// Get a reference to the underlying Ssl stream, if connected | ||||
|     /// over HTTPS. | ||||
|     /// | ||||
|     /// # Example | ||||
|     /// | ||||
|     /// ```rust | ||||
|     /// # extern crate hyper; | ||||
|     /// # #[cfg(feature = "openssl")] | ||||
|     /// extern crate openssl; | ||||
|     /// # #[cfg(feature = "openssl")] | ||||
|     /// use openssl::ssl::SslStream; | ||||
|     /// use hyper::net::HttpStream; | ||||
|     /// # fn main() {} | ||||
|     /// # #[cfg(feature = "openssl")] | ||||
|     /// # fn doc_ssl(req: hyper::server::Request) { | ||||
|     /// let maybe_ssl = req.ssl::<SslStream<HttpStream>>(); | ||||
|     /// # } | ||||
|     /// ``` | ||||
| impl Request { | ||||
|     /// The `Method`, such as `Get`, `Post`, etc. | ||||
|     #[inline] | ||||
|     pub fn ssl<T: NetworkStream>(&self) -> Option<&T> { | ||||
|         use ::net::HttpsStream; | ||||
|         match self.downcast_ref() { | ||||
|             Some(&HttpsStream::Https(ref s)) => Some(s), | ||||
|     pub fn method(&self) -> &Method { &self.method } | ||||
|  | ||||
|     /// The headers of the incoming request. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &Headers { &self.headers } | ||||
|  | ||||
|     /// The target request-uri for this request. | ||||
|     #[inline] | ||||
|     pub fn uri(&self) -> &RequestUri { &self.uri } | ||||
|  | ||||
|     /// The version of HTTP for this request. | ||||
|     #[inline] | ||||
|     pub fn version(&self) -> &HttpVersion { &self.version } | ||||
|  | ||||
|     /* | ||||
|     /// The target path of this Request. | ||||
|     #[inline] | ||||
|     pub fn path(&self) -> Option<&str> { | ||||
|         match *self.uri { | ||||
|             RequestUri::AbsolutePath(ref s) => Some(s), | ||||
|             RequestUri::AbsoluteUri(ref url) => Some(&url[::url::Position::BeforePath..]), | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     /// Deconstruct a Request into its constituent parts. | ||||
|     /// Deconstruct this Request into its pieces. | ||||
|     /// | ||||
|     /// Modifying these pieces will have no effect on how hyper behaves. | ||||
|     #[inline] | ||||
|     pub fn deconstruct(self) -> (SocketAddr, Method, Headers, | ||||
|                                  RequestUri, HttpVersion, | ||||
|                                  HttpReader<&'a mut BufReader<&'b mut NetworkStream>>) { | ||||
|         (self.remote_addr, self.method, self.headers, | ||||
|          self.uri, self.version, self.body) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, 'b> Read for Request<'a, 'b> { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         self.body.read(buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use buffer::BufReader; | ||||
|     use header::{Host, TransferEncoding, Encoding}; | ||||
|     use net::NetworkStream; | ||||
|     use mock::MockStream; | ||||
|     use super::Request; | ||||
|  | ||||
|     use std::io::{self, Read}; | ||||
|     use std::net::SocketAddr; | ||||
|  | ||||
|     fn sock(s: &str) -> SocketAddr { | ||||
|         s.parse().unwrap() | ||||
|     } | ||||
|  | ||||
|     fn read_to_string(mut req: Request) -> io::Result<String> { | ||||
|         let mut s = String::new(); | ||||
|         try!(req.read_to_string(&mut s)); | ||||
|         Ok(s) | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_get_empty_body() { | ||||
|         let mut mock = MockStream::with_input(b"\ | ||||
|             GET / HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             \r\n\ | ||||
|             I'm a bad request.\r\n\ | ||||
|         "); | ||||
|  | ||||
|         // FIXME: Use Type ascription | ||||
|         let mock: &mut NetworkStream = &mut mock; | ||||
|         let mut stream = BufReader::new(mock); | ||||
|  | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         assert_eq!(read_to_string(req).unwrap(), "".to_owned()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_get_with_body() { | ||||
|         let mut mock = MockStream::with_input(b"\ | ||||
|             GET / HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             Content-Length: 19\r\n\ | ||||
|             \r\n\ | ||||
|             I'm a good request.\r\n\ | ||||
|         "); | ||||
|  | ||||
|         // FIXME: Use Type ascription | ||||
|         let mock: &mut NetworkStream = &mut mock; | ||||
|         let mut stream = BufReader::new(mock); | ||||
|  | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         assert_eq!(read_to_string(req).unwrap(), "I'm a good request.".to_owned()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_head_empty_body() { | ||||
|         let mut mock = MockStream::with_input(b"\ | ||||
|             HEAD / HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             \r\n\ | ||||
|             I'm a bad request.\r\n\ | ||||
|         "); | ||||
|  | ||||
|         // FIXME: Use Type ascription | ||||
|         let mock: &mut NetworkStream = &mut mock; | ||||
|         let mut stream = BufReader::new(mock); | ||||
|  | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         assert_eq!(read_to_string(req).unwrap(), "".to_owned()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_post_empty_body() { | ||||
|         let mut mock = MockStream::with_input(b"\ | ||||
|             POST / HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             \r\n\ | ||||
|             I'm a bad request.\r\n\ | ||||
|         "); | ||||
|  | ||||
|         // FIXME: Use Type ascription | ||||
|         let mock: &mut NetworkStream = &mut mock; | ||||
|         let mut stream = BufReader::new(mock); | ||||
|  | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         assert_eq!(read_to_string(req).unwrap(), "".to_owned()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_chunked_request() { | ||||
|         let mut mock = MockStream::with_input(b"\ | ||||
|             POST / HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             Transfer-Encoding: chunked\r\n\ | ||||
|             \r\n\ | ||||
|             1\r\n\ | ||||
|             q\r\n\ | ||||
|             2\r\n\ | ||||
|             we\r\n\ | ||||
|             2\r\n\ | ||||
|             rt\r\n\ | ||||
|             0\r\n\ | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         // FIXME: Use Type ascription | ||||
|         let mock: &mut NetworkStream = &mut mock; | ||||
|         let mut stream = BufReader::new(mock); | ||||
|  | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|  | ||||
|         // The headers are correct? | ||||
|         match req.headers.get::<Host>() { | ||||
|             Some(host) => { | ||||
|                 assert_eq!("example.domain", host.hostname); | ||||
|             }, | ||||
|             None => panic!("Host header expected!"), | ||||
|         }; | ||||
|         match req.headers.get::<TransferEncoding>() { | ||||
|             Some(encodings) => { | ||||
|                 assert_eq!(1, encodings.len()); | ||||
|                 assert_eq!(Encoding::Chunked, encodings[0]); | ||||
|             } | ||||
|             None => panic!("Transfer-Encoding: chunked expected!"), | ||||
|         }; | ||||
|         // The content is correctly read? | ||||
|         assert_eq!(read_to_string(req).unwrap(), "qwert".to_owned()); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a chunk size is not a valid radix-16 number, an error | ||||
|     /// is returned. | ||||
|     #[test] | ||||
|     fn test_invalid_chunk_size_not_hex_digit() { | ||||
|         let mut mock = MockStream::with_input(b"\ | ||||
|             POST / HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             Transfer-Encoding: chunked\r\n\ | ||||
|             \r\n\ | ||||
|             X\r\n\ | ||||
|             1\r\n\ | ||||
|             0\r\n\ | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         // FIXME: Use Type ascription | ||||
|         let mock: &mut NetworkStream = &mut mock; | ||||
|         let mut stream = BufReader::new(mock); | ||||
|  | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|  | ||||
|         assert!(read_to_string(req).is_err()); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a chunk size contains an invalid extension, an error is | ||||
|     /// returned. | ||||
|     #[test] | ||||
|     fn test_invalid_chunk_size_extension() { | ||||
|         let mut mock = MockStream::with_input(b"\ | ||||
|             POST / HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             Transfer-Encoding: chunked\r\n\ | ||||
|             \r\n\ | ||||
|             1 this is an invalid extension\r\n\ | ||||
|             1\r\n\ | ||||
|             0\r\n\ | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         // FIXME: Use Type ascription | ||||
|         let mock: &mut NetworkStream = &mut mock; | ||||
|         let mut stream = BufReader::new(mock); | ||||
|  | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|  | ||||
|         assert!(read_to_string(req).is_err()); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a valid extension that contains a digit is appended to | ||||
|     /// the chunk size, the chunk is correctly read. | ||||
|     #[test] | ||||
|     fn test_chunk_size_with_extension() { | ||||
|         let mut mock = MockStream::with_input(b"\ | ||||
|             POST / HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             Transfer-Encoding: chunked\r\n\ | ||||
|             \r\n\ | ||||
|             1;this is an extension with a digit 1\r\n\ | ||||
|             1\r\n\ | ||||
|             0\r\n\ | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         // FIXME: Use Type ascription | ||||
|         let mock: &mut NetworkStream = &mut mock; | ||||
|         let mut stream = BufReader::new(mock); | ||||
|  | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|  | ||||
|         assert_eq!(read_to_string(req).unwrap(), "1".to_owned()); | ||||
|     pub fn deconstruct(self) -> (Method, RequestUri, HttpVersion, Headers) { | ||||
|         (self.method, self.uri, self.version, self.headers) | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,431 +2,49 @@ | ||||
| //! | ||||
| //! These are responses sent by a `hyper::Server` to clients, after | ||||
| //! receiving a request. | ||||
| use std::any::{Any, TypeId}; | ||||
| use std::marker::PhantomData; | ||||
| use std::mem; | ||||
| use std::io::{self, Write}; | ||||
| use std::ptr; | ||||
| use std::thread; | ||||
|  | ||||
| use time::now_utc; | ||||
|  | ||||
| use header; | ||||
| use http::h1::{LINE_ENDING, HttpWriter}; | ||||
| use http::h1::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter, EmptyWriter}; | ||||
| use status; | ||||
| use net::{Fresh, Streaming}; | ||||
| use http; | ||||
| use status::StatusCode; | ||||
| use version; | ||||
|  | ||||
|  | ||||
| /// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`. | ||||
| /// | ||||
| /// The default `StatusCode` for a `Response` is `200 OK`. | ||||
| /// | ||||
| /// There is a `Drop` implementation for `Response` that will automatically | ||||
| /// write the head and flush the body, if the handler has not already done so, | ||||
| /// so that the server doesn't accidentally leave dangling requests. | ||||
| #[derive(Debug)] | ||||
| pub struct Response<'a, W: Any = Fresh> { | ||||
|     /// The HTTP version of this response. | ||||
|     pub version: version::HttpVersion, | ||||
|     // Stream the Response is writing to, not accessible through UnwrittenResponse | ||||
|     body: HttpWriter<&'a mut (Write + 'a)>, | ||||
|     // The status code for the request. | ||||
|     status: status::StatusCode, | ||||
|     // The outgoing headers on this response. | ||||
|     headers: &'a mut header::Headers, | ||||
|  | ||||
|     _writing: PhantomData<W> | ||||
| pub struct Response<'a> { | ||||
|     head: &'a mut http::MessageHead<StatusCode>, | ||||
| } | ||||
|  | ||||
| impl<'a, W: Any> Response<'a, W> { | ||||
|     /// The status of this response. | ||||
|     #[inline] | ||||
|     pub fn status(&self) -> status::StatusCode { self.status } | ||||
|  | ||||
| impl<'a> Response<'a> { | ||||
|     /// The headers of this response. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &header::Headers { &*self.headers } | ||||
|     pub fn headers(&self) -> &header::Headers { &self.head.headers } | ||||
|  | ||||
|     /// Construct a Response from its constituent parts. | ||||
|     /// The status of this response. | ||||
|     #[inline] | ||||
|     pub fn construct(version: version::HttpVersion, | ||||
|                      body: HttpWriter<&'a mut (Write + 'a)>, | ||||
|                      status: status::StatusCode, | ||||
|                      headers: &'a mut header::Headers) -> Response<'a, Fresh> { | ||||
|         Response { | ||||
|             status: status, | ||||
|             version: version, | ||||
|             body: body, | ||||
|             headers: headers, | ||||
|             _writing: PhantomData, | ||||
|         } | ||||
|     pub fn status(&self) -> &StatusCode { | ||||
|         &self.head.subject | ||||
|     } | ||||
|  | ||||
|     /// Deconstruct this Response into its constituent parts. | ||||
|     /// The HTTP version of this response. | ||||
|     #[inline] | ||||
|     pub fn deconstruct(self) -> (version::HttpVersion, HttpWriter<&'a mut (Write + 'a)>, | ||||
|                                  status::StatusCode, &'a mut header::Headers) { | ||||
|         unsafe { | ||||
|             let parts = ( | ||||
|                 self.version, | ||||
|                 ptr::read(&self.body), | ||||
|                 self.status, | ||||
|                 ptr::read(&self.headers) | ||||
|             ); | ||||
|             mem::forget(self); | ||||
|             parts | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn write_head(&mut self) -> io::Result<Body> { | ||||
|         debug!("writing head: {:?} {:?}", self.version, self.status); | ||||
|         try!(write!(&mut self.body, "{} {}\r\n", self.version, self.status)); | ||||
|  | ||||
|         if !self.headers.has::<header::Date>() { | ||||
|             self.headers.set(header::Date(header::HttpDate(now_utc()))); | ||||
|         } | ||||
|  | ||||
|         let body_type = match self.status { | ||||
|             status::StatusCode::NoContent | status::StatusCode::NotModified => Body::Empty, | ||||
|             c if c.class() == status::StatusClass::Informational => Body::Empty, | ||||
|             _ => if let Some(cl) = self.headers.get::<header::ContentLength>() { | ||||
|                 Body::Sized(**cl) | ||||
|             } else { | ||||
|                 Body::Chunked | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         // can't do in match above, thanks borrowck | ||||
|         if body_type == Body::Chunked { | ||||
|             let encodings = match self.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 { | ||||
|                 self.headers.set::<header::TransferEncoding>( | ||||
|                     header::TransferEncoding(vec![header::Encoding::Chunked])) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         debug!("headers [\n{:?}]", self.headers); | ||||
|         try!(write!(&mut self.body, "{}", self.headers)); | ||||
|         try!(write!(&mut self.body, "{}", LINE_ENDING)); | ||||
|  | ||||
|         Ok(body_type) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Response<'a, Fresh> { | ||||
|     /// Creates a new Response that can be used to write to a network stream. | ||||
|     #[inline] | ||||
|     pub fn new(stream: &'a mut (Write + 'a), headers: &'a mut header::Headers) -> | ||||
|             Response<'a, Fresh> { | ||||
|         Response { | ||||
|             status: status::StatusCode::Ok, | ||||
|             version: version::HttpVersion::Http11, | ||||
|             headers: headers, | ||||
|             body: ThroughWriter(stream), | ||||
|             _writing: PhantomData, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Writes the body and ends the response. | ||||
|     /// | ||||
|     /// This is a shortcut method for when you have a response with a fixed | ||||
|     /// size, and would only need a single `write` call normally. | ||||
|     /// | ||||
|     /// # Example | ||||
|     /// | ||||
|     /// ``` | ||||
|     /// # use hyper::server::Response; | ||||
|     /// fn handler(res: Response) { | ||||
|     ///     res.send(b"Hello World!").unwrap(); | ||||
|     /// } | ||||
|     /// ``` | ||||
|     /// | ||||
|     /// The above is the same, but shorter, than the longer: | ||||
|     /// | ||||
|     /// ``` | ||||
|     /// # use hyper::server::Response; | ||||
|     /// use std::io::Write; | ||||
|     /// use hyper::header::ContentLength; | ||||
|     /// fn handler(mut res: Response) { | ||||
|     ///     let body = b"Hello World!"; | ||||
|     ///     res.headers_mut().set(ContentLength(body.len() as u64)); | ||||
|     ///     let mut res = res.start().unwrap(); | ||||
|     ///     res.write_all(body).unwrap(); | ||||
|     /// } | ||||
|     /// ``` | ||||
|     #[inline] | ||||
|     pub fn send(mut self, body: &[u8]) -> io::Result<()> { | ||||
|         self.headers.set(header::ContentLength(body.len() as u64)); | ||||
|         let mut stream = try!(self.start()); | ||||
|         try!(stream.write_all(body)); | ||||
|         stream.end() | ||||
|     } | ||||
|  | ||||
|     /// Consume this Response<Fresh>, writing the Headers and Status and | ||||
|     /// creating a Response<Streaming> | ||||
|     pub fn start(mut self) -> io::Result<Response<'a, Streaming>> { | ||||
|         let body_type = try!(self.write_head()); | ||||
|         let (version, body, status, headers) = self.deconstruct(); | ||||
|         let stream = match body_type { | ||||
|             Body::Chunked => ChunkedWriter(body.into_inner()), | ||||
|             Body::Sized(len) => SizedWriter(body.into_inner(), len), | ||||
|             Body::Empty => EmptyWriter(body.into_inner()), | ||||
|         }; | ||||
|  | ||||
|         // "copy" to change the phantom type | ||||
|         Ok(Response { | ||||
|             version: version, | ||||
|             body: stream, | ||||
|             status: status, | ||||
|             headers: headers, | ||||
|             _writing: PhantomData, | ||||
|         }) | ||||
|     } | ||||
|     /// Get a mutable reference to the status. | ||||
|     #[inline] | ||||
|     pub fn status_mut(&mut self) -> &mut status::StatusCode { &mut self.status } | ||||
|     pub fn version(&self) -> &version::HttpVersion { &self.head.version } | ||||
|  | ||||
|     /// Get a mutable reference to the Headers. | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut header::Headers { self.headers } | ||||
| } | ||||
|     pub fn headers_mut(&mut self) -> &mut header::Headers { &mut self.head.headers } | ||||
|  | ||||
|  | ||||
| impl<'a> Response<'a, Streaming> { | ||||
|     /// Flushes all writing of a response to the client. | ||||
|     /// Get a mutable reference to the status. | ||||
|     #[inline] | ||||
|     pub fn end(self) -> io::Result<()> { | ||||
|         trace!("ending"); | ||||
|         let (_, body, _, _) = self.deconstruct(); | ||||
|         try!(body.end()); | ||||
|         Ok(()) | ||||
|     pub fn set_status(&mut self, status: StatusCode) { | ||||
|         self.head.subject = status; | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Write for Response<'a, Streaming> { | ||||
|     #[inline] | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         debug!("write {:?} bytes", msg.len()); | ||||
|         self.body.write(msg) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         self.body.flush() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(PartialEq)] | ||||
| enum Body { | ||||
|     Chunked, | ||||
|     Sized(u64), | ||||
|     Empty, | ||||
| } | ||||
|  | ||||
| impl<'a, T: Any> Drop for Response<'a, T> { | ||||
|     fn drop(&mut self) { | ||||
|         if TypeId::of::<T>() == TypeId::of::<Fresh>() { | ||||
|             if thread::panicking() { | ||||
|                 self.status = status::StatusCode::InternalServerError; | ||||
|             } | ||||
|  | ||||
|             let mut body = match self.write_head() { | ||||
|                 Ok(Body::Chunked) => ChunkedWriter(self.body.get_mut()), | ||||
|                 Ok(Body::Sized(len)) => SizedWriter(self.body.get_mut(), len), | ||||
|                 Ok(Body::Empty) => EmptyWriter(self.body.get_mut()), | ||||
|                 Err(e) => { | ||||
|                     debug!("error dropping request: {:?}", e); | ||||
|                     return; | ||||
|                 } | ||||
|             }; | ||||
|             end(&mut body); | ||||
|         } else { | ||||
|             end(&mut self.body); | ||||
|         }; | ||||
|  | ||||
|  | ||||
|         #[inline] | ||||
|         fn end<W: Write>(w: &mut W) { | ||||
|             match w.write(&[]) { | ||||
|                 Ok(_) => match w.flush() { | ||||
|                     Ok(_) => debug!("drop successful"), | ||||
|                     Err(e) => debug!("error dropping request: {:?}", e) | ||||
|                 }, | ||||
|                 Err(e) => debug!("error dropping request: {:?}", e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use header::Headers; | ||||
|     use mock::MockStream; | ||||
|     use super::Response; | ||||
|  | ||||
|     macro_rules! lines { | ||||
|         ($s:ident = $($line:pat),+) => ({ | ||||
|             let s = String::from_utf8($s.write).unwrap(); | ||||
|             let mut lines = s.split_terminator("\r\n"); | ||||
|  | ||||
|             $( | ||||
|                 match lines.next() { | ||||
|                     Some($line) => (), | ||||
|                     other => panic!("line mismatch: {:?} != {:?}", other, stringify!($line)) | ||||
|                 } | ||||
|             )+ | ||||
|  | ||||
|             assert_eq!(lines.next(), None); | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_fresh_start() { | ||||
|         let mut headers = Headers::new(); | ||||
|         let mut stream = MockStream::new(); | ||||
|         { | ||||
|             let res = Response::new(&mut stream, &mut headers); | ||||
|             res.start().unwrap().deconstruct(); | ||||
|         } | ||||
|  | ||||
|         lines! { stream = | ||||
|             "HTTP/1.1 200 OK", | ||||
|             _date, | ||||
|             _transfer_encoding, | ||||
|             "" | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_streaming_end() { | ||||
|         let mut headers = Headers::new(); | ||||
|         let mut stream = MockStream::new(); | ||||
|         { | ||||
|             let res = Response::new(&mut stream, &mut headers); | ||||
|             res.start().unwrap().end().unwrap(); | ||||
|         } | ||||
|  | ||||
|         lines! { stream = | ||||
|             "HTTP/1.1 200 OK", | ||||
|             _date, | ||||
|             _transfer_encoding, | ||||
|             "", | ||||
|             "0", | ||||
|             "" // empty zero body | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_fresh_drop() { | ||||
|         use status::StatusCode; | ||||
|         let mut headers = Headers::new(); | ||||
|         let mut stream = MockStream::new(); | ||||
|         { | ||||
|             let mut res = Response::new(&mut stream, &mut headers); | ||||
|             *res.status_mut() = StatusCode::NotFound; | ||||
|         } | ||||
|  | ||||
|         lines! { stream = | ||||
|             "HTTP/1.1 404 Not Found", | ||||
|             _date, | ||||
|             _transfer_encoding, | ||||
|             "", | ||||
|             "0", | ||||
|             "" // empty zero body | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // x86 windows msvc does not support unwinding | ||||
|     // See https://github.com/rust-lang/rust/issues/25869 | ||||
|     #[cfg(not(all(windows, target_arch="x86", target_env="msvc")))] | ||||
|     #[test] | ||||
|     fn test_fresh_drop_panicing() { | ||||
|         use std::thread; | ||||
|         use std::sync::{Arc, Mutex}; | ||||
|  | ||||
|         use status::StatusCode; | ||||
|  | ||||
|         let stream = MockStream::new(); | ||||
|         let stream = Arc::new(Mutex::new(stream)); | ||||
|         let inner_stream = stream.clone(); | ||||
|         let join_handle = thread::spawn(move || { | ||||
|             let mut headers = Headers::new(); | ||||
|             let mut stream = inner_stream.lock().unwrap(); | ||||
|             let mut res = Response::new(&mut *stream, &mut headers); | ||||
|             *res.status_mut() = StatusCode::NotFound; | ||||
|  | ||||
|             panic!("inside") | ||||
|         }); | ||||
|  | ||||
|         assert!(join_handle.join().is_err()); | ||||
|  | ||||
|         let stream = match stream.lock() { | ||||
|             Err(poisoned) => poisoned.into_inner().clone(), | ||||
|             Ok(_) => unreachable!() | ||||
|         }; | ||||
|  | ||||
|         lines! { stream = | ||||
|             "HTTP/1.1 500 Internal Server Error", | ||||
|             _date, | ||||
|             _transfer_encoding, | ||||
|             "", | ||||
|             "0", | ||||
|             "" // empty zero body | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #[test] | ||||
|     fn test_streaming_drop() { | ||||
|         use std::io::Write; | ||||
|         use status::StatusCode; | ||||
|         let mut headers = Headers::new(); | ||||
|         let mut stream = MockStream::new(); | ||||
|         { | ||||
|             let mut res = Response::new(&mut stream, &mut headers); | ||||
|             *res.status_mut() = StatusCode::NotFound; | ||||
|             let mut stream = res.start().unwrap(); | ||||
|             stream.write_all(b"foo").unwrap(); | ||||
|         } | ||||
|  | ||||
|         lines! { stream = | ||||
|             "HTTP/1.1 404 Not Found", | ||||
|             _date, | ||||
|             _transfer_encoding, | ||||
|             "", | ||||
|             "3", | ||||
|             "foo", | ||||
|             "0", | ||||
|             "" // empty zero body | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_no_content() { | ||||
|         use status::StatusCode; | ||||
|         let mut headers = Headers::new(); | ||||
|         let mut stream = MockStream::new(); | ||||
|         { | ||||
|             let mut res = Response::new(&mut stream, &mut headers); | ||||
|             *res.status_mut() = StatusCode::NoContent; | ||||
|             res.start().unwrap(); | ||||
|         } | ||||
|  | ||||
|         lines! { stream = | ||||
|             "HTTP/1.1 204 No Content", | ||||
|             _date, | ||||
|             "" | ||||
|         } | ||||
| /// Creates a new Response that can be used to write to a network stream. | ||||
| pub fn new<'a>(head: &'a mut http::MessageHead<StatusCode>) -> Response<'a> { | ||||
|     Response { | ||||
|         head: head | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -547,6 +547,12 @@ impl Ord for StatusCode { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for StatusCode { | ||||
|     fn default() -> StatusCode { | ||||
|         StatusCode::Ok | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// The class of an HTTP `status-code`. | ||||
| /// | ||||
| /// [RFC 7231, section 6 (Response Status Codes)](https://tools.ietf.org/html/rfc7231#section-6): | ||||
|   | ||||
							
								
								
									
										10
									
								
								src/uri.rs
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/uri.rs
									
									
									
									
									
								
							| @@ -21,7 +21,7 @@ use Error; | ||||
| /// >                / authority-form | ||||
| /// >                / asterisk-form | ||||
| /// > ``` | ||||
| #[derive(Debug, PartialEq, Clone)] | ||||
| #[derive(Debug, PartialEq, Eq, Hash, Clone)] | ||||
| pub enum RequestUri { | ||||
|     /// The most common request target, an absolute path and optional query. | ||||
|     /// | ||||
| @@ -50,6 +50,12 @@ pub enum RequestUri { | ||||
|     Star, | ||||
| } | ||||
|  | ||||
| impl Default for RequestUri { | ||||
|     fn default() -> RequestUri { | ||||
|         RequestUri::Star | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl FromStr for RequestUri { | ||||
|     type Err = Error; | ||||
|  | ||||
| @@ -67,7 +73,7 @@ impl FromStr for RequestUri { | ||||
|             let mut temp = "http://".to_owned(); | ||||
|             temp.push_str(s); | ||||
|             try!(Url::parse(&temp[..])); | ||||
|             todo!("compare vs u.authority()"); | ||||
|             //TODO: compare vs u.authority()? | ||||
|             Ok(RequestUri::Authority(s.to_owned())) | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
| //! the `HttpVersion` enum. | ||||
| use std::fmt; | ||||
|  | ||||
| use self::HttpVersion::{Http09, Http10, Http11, Http20}; | ||||
| use self::HttpVersion::{Http09, Http10, Http11, H2, H2c}; | ||||
|  | ||||
| /// Represents a version of the HTTP spec. | ||||
| #[derive(PartialEq, PartialOrd, Copy, Clone, Eq, Ord, Hash, Debug)] | ||||
| @@ -15,8 +15,10 @@ pub enum HttpVersion { | ||||
|     Http10, | ||||
|     /// `HTTP/1.1` | ||||
|     Http11, | ||||
|     /// `HTTP/2.0` | ||||
|     Http20 | ||||
|     /// `HTTP/2.0` over TLS | ||||
|     H2, | ||||
|     /// `HTTP/2.0` over cleartext | ||||
|     H2c, | ||||
| } | ||||
|  | ||||
| impl fmt::Display for HttpVersion { | ||||
| @@ -25,7 +27,14 @@ impl fmt::Display for HttpVersion { | ||||
|             Http09 => "HTTP/0.9", | ||||
|             Http10 => "HTTP/1.0", | ||||
|             Http11 => "HTTP/1.1", | ||||
|             Http20 => "HTTP/2.0", | ||||
|             H2 => "h2", | ||||
|             H2c => "h2c", | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for HttpVersion { | ||||
|     fn default() -> HttpVersion { | ||||
|         Http11 | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										205
									
								
								tests/client.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								tests/client.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | ||||
| #![deny(warnings)] | ||||
| extern crate hyper; | ||||
|  | ||||
| use std::io::{self, Read, Write}; | ||||
| use std::net::TcpListener; | ||||
| use std::sync::mpsc; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use hyper::client::{Handler, Request, Response, HttpConnector}; | ||||
| use hyper::header; | ||||
| use hyper::{Method, StatusCode, Next, Encoder, Decoder}; | ||||
| use hyper::net::HttpStream; | ||||
|  | ||||
| fn s(bytes: &[u8]) -> &str { | ||||
|     ::std::str::from_utf8(bytes.as_ref()).unwrap() | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct TestHandler { | ||||
|     opts: Opts, | ||||
|     tx: mpsc::Sender<Msg> | ||||
| } | ||||
|  | ||||
| impl TestHandler { | ||||
|     fn new(opts: Opts) -> (TestHandler, mpsc::Receiver<Msg>) { | ||||
|         let (tx, rx) = mpsc::channel(); | ||||
|         (TestHandler { | ||||
|             opts: opts, | ||||
|             tx: tx | ||||
|         }, rx) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum Msg { | ||||
|     Head(Response), | ||||
|     Chunk(Vec<u8>), | ||||
|     Error(hyper::Error), | ||||
| } | ||||
|  | ||||
| fn read(opts: &Opts) -> Next { | ||||
|     if let Some(timeout) = opts.read_timeout { | ||||
|         Next::read().timeout(timeout) | ||||
|     } else { | ||||
|         Next::read() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Handler<HttpStream> for TestHandler { | ||||
|     fn on_request(&mut self, req: &mut Request) -> Next { | ||||
|         req.set_method(self.opts.method.clone()); | ||||
|         read(&self.opts) | ||||
|     } | ||||
|  | ||||
|     fn on_request_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next { | ||||
|         read(&self.opts) | ||||
|     } | ||||
|  | ||||
|     fn on_response(&mut self, res: Response) -> Next { | ||||
|         use hyper::header; | ||||
|         // server responses can include a body until eof, if not size is specified | ||||
|         let mut has_body = true; | ||||
|         if let Some(len) = res.headers().get::<header::ContentLength>() { | ||||
|             if **len == 0 { | ||||
|                 has_body = false; | ||||
|             } | ||||
|         } | ||||
|         self.tx.send(Msg::Head(res)).unwrap(); | ||||
|         if has_body { | ||||
|             read(&self.opts) | ||||
|         } else { | ||||
|             Next::end() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn on_response_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next { | ||||
|         let mut v = vec![0; 512]; | ||||
|         match decoder.read(&mut v) { | ||||
|             Ok(n) => { | ||||
|                 v.truncate(n); | ||||
|                 self.tx.send(Msg::Chunk(v)).unwrap(); | ||||
|                 if n == 0 { | ||||
|                     Next::end() | ||||
|                 } else { | ||||
|                     read(&self.opts) | ||||
|                 } | ||||
|             }, | ||||
|             Err(e) => match e.kind() { | ||||
|                 io::ErrorKind::WouldBlock => read(&self.opts), | ||||
|                 _ => panic!("io read error: {:?}", e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn on_error(&mut self, err: hyper::Error) -> Next { | ||||
|         self.tx.send(Msg::Error(err)).unwrap(); | ||||
|         Next::remove() | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct Client { | ||||
|     client: Option<hyper::Client<TestHandler>>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct Opts { | ||||
|     method: Method, | ||||
|     read_timeout: Option<Duration>, | ||||
| } | ||||
|  | ||||
| impl Default for Opts { | ||||
|     fn default() -> Opts { | ||||
|         Opts { | ||||
|             method: Method::Get, | ||||
|             read_timeout: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn opts() -> Opts { | ||||
|     Opts::default() | ||||
| } | ||||
|  | ||||
| impl Opts { | ||||
|     fn method(mut self, method: Method) -> Opts { | ||||
|         self.method = method; | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     fn read_timeout(mut self, timeout: Duration) -> Opts { | ||||
|         self.read_timeout = Some(timeout); | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Client { | ||||
|     fn request<U>(&self, url: U, opts: Opts) -> mpsc::Receiver<Msg> | ||||
|     where U: AsRef<str> { | ||||
|         let (handler, rx) = TestHandler::new(opts); | ||||
|         self.client.as_ref().unwrap() | ||||
|             .request(url.as_ref().parse().unwrap(), handler).unwrap(); | ||||
|         rx | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Drop for Client { | ||||
|     fn drop(&mut self) { | ||||
|         self.client.take().map(|c| c.close()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn client() -> Client { | ||||
|     let c = hyper::Client::<TestHandler>::configure() | ||||
|         .connector(HttpConnector::default()) | ||||
|         .build().unwrap(); | ||||
|     Client { | ||||
|         client: Some(c), | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| #[test] | ||||
| fn client_get() { | ||||
|     let server = TcpListener::bind("127.0.0.1:0").unwrap(); | ||||
|     let addr = server.local_addr().unwrap(); | ||||
|     let client = client(); | ||||
|     let res = client.request(format!("http://{}/", addr), opts().method(Method::Get)); | ||||
|  | ||||
|     let mut inc = server.accept().unwrap().0; | ||||
|     inc.set_read_timeout(Some(Duration::from_secs(5))).unwrap(); | ||||
|     inc.set_write_timeout(Some(Duration::from_secs(5))).unwrap(); | ||||
|     let mut buf = [0; 4096]; | ||||
|     let n = inc.read(&mut buf).unwrap(); | ||||
|     let expected = format!("GET / HTTP/1.1\r\nHost: {}\r\n\r\n", addr); | ||||
|     assert_eq!(s(&buf[..n]), expected); | ||||
|  | ||||
|     inc.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").unwrap(); | ||||
|  | ||||
|     if let Msg::Head(head) = res.recv().unwrap() { | ||||
|         assert_eq!(head.status(), &StatusCode::Ok); | ||||
|         assert_eq!(head.headers().get(), Some(&header::ContentLength(0))); | ||||
|     } else { | ||||
|         panic!("we lost the head!"); | ||||
|     } | ||||
|     //drop(inc); | ||||
|  | ||||
|     assert!(res.recv().is_err()); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn client_read_timeout() { | ||||
|     let server = TcpListener::bind("127.0.0.1:0").unwrap(); | ||||
|     let addr = server.local_addr().unwrap(); | ||||
|     let client = client(); | ||||
|     let res = client.request(format!("http://{}/", addr), opts().read_timeout(Duration::from_secs(3))); | ||||
|  | ||||
|     let mut inc = server.accept().unwrap().0; | ||||
|     let mut buf = [0; 4096]; | ||||
|     inc.read(&mut buf).unwrap(); | ||||
|  | ||||
|     match res.recv() { | ||||
|         Ok(Msg::Error(hyper::Error::Timeout)) => (), | ||||
|         other => panic!("expected timeout, actual: {:?}", other) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										379
									
								
								tests/server.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										379
									
								
								tests/server.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,379 @@ | ||||
| #![deny(warnings)] | ||||
| extern crate hyper; | ||||
|  | ||||
| use std::net::{TcpStream, SocketAddr}; | ||||
| use std::io::{self, Read, Write}; | ||||
| use std::sync::mpsc; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use hyper::{Next, Encoder, Decoder}; | ||||
| use hyper::net::HttpStream; | ||||
| use hyper::server::{Server, Handler, Request, Response}; | ||||
|  | ||||
| struct Serve { | ||||
|     listening: Option<hyper::server::Listening>, | ||||
|     msg_rx: mpsc::Receiver<Msg>, | ||||
|     reply_tx: mpsc::Sender<Reply>, | ||||
| } | ||||
|  | ||||
| impl Serve { | ||||
|     fn addr(&self) -> &SocketAddr { | ||||
|         self.listening.as_ref().unwrap().addr() | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     fn head(&self) -> Request { | ||||
|         unimplemented!() | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     fn body(&self) -> Vec<u8> { | ||||
|         let mut buf = vec![]; | ||||
|         while let Ok(Msg::Chunk(msg)) = self.msg_rx.try_recv() { | ||||
|             buf.extend(&msg); | ||||
|         } | ||||
|         buf | ||||
|     } | ||||
|  | ||||
|     fn reply(&self) -> ReplyBuilder { | ||||
|         ReplyBuilder { | ||||
|             tx: &self.reply_tx | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct ReplyBuilder<'a> { | ||||
|     tx: &'a mpsc::Sender<Reply>, | ||||
| } | ||||
|  | ||||
| impl<'a> ReplyBuilder<'a> { | ||||
|     fn status(self, status: hyper::StatusCode) -> Self { | ||||
|         self.tx.send(Reply::Status(status)).unwrap(); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     fn header<H: hyper::header::Header>(self, header: H) -> Self { | ||||
|         let mut headers = hyper::Headers::new(); | ||||
|         headers.set(header); | ||||
|         self.tx.send(Reply::Headers(headers)).unwrap(); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     fn body<T: AsRef<[u8]>>(self, body: T) { | ||||
|         self.tx.send(Reply::Body(body.as_ref().into())).unwrap(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Drop for Serve { | ||||
|     fn drop(&mut self) { | ||||
|         self.listening.take().unwrap().close(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct TestHandler { | ||||
|     tx: mpsc::Sender<Msg>, | ||||
|     rx: mpsc::Receiver<Reply>, | ||||
|     peeked: Option<Vec<u8>>, | ||||
|     timeout: Option<Duration>, | ||||
| } | ||||
|  | ||||
| enum Reply { | ||||
|     Status(hyper::StatusCode), | ||||
|     Headers(hyper::Headers), | ||||
|     Body(Vec<u8>), | ||||
| } | ||||
|  | ||||
| enum Msg { | ||||
|     //Head(Request), | ||||
|     Chunk(Vec<u8>), | ||||
| } | ||||
|  | ||||
| impl TestHandler { | ||||
|     fn next(&self, next: Next) -> Next { | ||||
|         if let Some(dur) = self.timeout { | ||||
|             next.timeout(dur) | ||||
|         } else { | ||||
|             next | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Handler<HttpStream> for TestHandler { | ||||
|     fn on_request(&mut self, _req: Request) -> Next { | ||||
|         //self.tx.send(Msg::Head(req)).unwrap(); | ||||
|         self.next(Next::read()) | ||||
|     } | ||||
|  | ||||
|     fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next { | ||||
|         let mut vec = vec![0; 1024]; | ||||
|         match decoder.read(&mut vec) { | ||||
|             Ok(0) => { | ||||
|                 self.next(Next::write()) | ||||
|             } | ||||
|             Ok(n) => { | ||||
|                 vec.truncate(n); | ||||
|                 self.tx.send(Msg::Chunk(vec)).unwrap(); | ||||
|                 self.next(Next::read()) | ||||
|             } | ||||
|             Err(e) => match e.kind() { | ||||
|                 io::ErrorKind::WouldBlock => self.next(Next::read()), | ||||
|                 _ => panic!("test error: {}", e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn on_response(&mut self, res: &mut Response) -> Next { | ||||
|         loop { | ||||
|             match self.rx.try_recv() { | ||||
|                 Ok(Reply::Status(s)) => { | ||||
|                     res.set_status(s); | ||||
|                 }, | ||||
|                 Ok(Reply::Headers(headers)) => { | ||||
|                     use std::iter::Extend; | ||||
|                     res.headers_mut().extend(headers.iter()); | ||||
|                 }, | ||||
|                 Ok(Reply::Body(body)) => { | ||||
|                     self.peeked = Some(body); | ||||
|                 }, | ||||
|                 Err(..) => { | ||||
|                     return if self.peeked.is_some() { | ||||
|                         self.next(Next::write()) | ||||
|                     } else { | ||||
|                         self.next(Next::end()) | ||||
|                     }; | ||||
|                 }, | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next { | ||||
|         match self.peeked { | ||||
|             Some(ref body) => { | ||||
|                 encoder.write(body).unwrap(); | ||||
|                 self.next(Next::end()) | ||||
|             }, | ||||
|             None => self.next(Next::end()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn serve() -> Serve { | ||||
|     serve_with_timeout(None) | ||||
| } | ||||
|  | ||||
| fn serve_with_timeout(dur: Option<Duration>) -> Serve { | ||||
|     use std::thread; | ||||
|  | ||||
|     let (msg_tx, msg_rx) = mpsc::channel(); | ||||
|     let (reply_tx, reply_rx) = mpsc::channel(); | ||||
|     let mut reply_rx = Some(reply_rx); | ||||
|     let (listening, server) = Server::http(&"127.0.0.1:0".parse().unwrap()).unwrap() | ||||
|         .handle(move |_| TestHandler { | ||||
|             tx: msg_tx.clone(), | ||||
|             timeout: dur, | ||||
|             rx: reply_rx.take().unwrap(), | ||||
|             peeked: None, | ||||
|         }).unwrap(); | ||||
|  | ||||
|  | ||||
|     let thread_name = format!("test-server-{}: {:?}", listening.addr(), dur); | ||||
|     thread::Builder::new().name(thread_name).spawn(move || { | ||||
|         server.run(); | ||||
|     }).unwrap(); | ||||
|  | ||||
|     Serve { | ||||
|         listening: Some(listening), | ||||
|         msg_rx: msg_rx, | ||||
|         reply_tx: reply_tx, | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn server_get_should_ignore_body() { | ||||
|     let server = serve(); | ||||
|  | ||||
|     let mut req = TcpStream::connect(server.addr()).unwrap(); | ||||
|     req.write_all(b"\ | ||||
|         GET / HTTP/1.1\r\n\ | ||||
|         Host: example.domain\r\n\ | ||||
|         \r\n\ | ||||
|         I shouldn't be read.\r\n\ | ||||
|     ").unwrap(); | ||||
|     req.read(&mut [0; 256]).unwrap(); | ||||
|  | ||||
|     assert_eq!(server.body(), b""); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn server_get_with_body() { | ||||
|     let server = serve(); | ||||
|     let mut req = TcpStream::connect(server.addr()).unwrap(); | ||||
|     req.write_all(b"\ | ||||
|         GET / HTTP/1.1\r\n\ | ||||
|         Host: example.domain\r\n\ | ||||
|         Content-Length: 19\r\n\ | ||||
|         \r\n\ | ||||
|         I'm a good request.\r\n\ | ||||
|     ").unwrap(); | ||||
|     req.read(&mut [0; 256]).unwrap(); | ||||
|  | ||||
|     // note: doesnt include trailing \r\n, cause Content-Length wasn't 21 | ||||
|     assert_eq!(server.body(), b"I'm a good request."); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn server_get_fixed_response() { | ||||
|     let foo_bar = b"foo bar baz"; | ||||
|     let server = serve(); | ||||
|     server.reply() | ||||
|         .status(hyper::Ok) | ||||
|         .header(hyper::header::ContentLength(foo_bar.len() as u64)) | ||||
|         .body(foo_bar); | ||||
|     let mut req = TcpStream::connect(server.addr()).unwrap(); | ||||
|     req.write_all(b"\ | ||||
|         GET / HTTP/1.1\r\n\ | ||||
|         Host: example.domain\r\n\ | ||||
|         Connection: close\r\n | ||||
|         \r\n\ | ||||
|     ").unwrap(); | ||||
|     let mut body = String::new(); | ||||
|     req.read_to_string(&mut body).unwrap(); | ||||
|     let n = body.find("\r\n\r\n").unwrap() + 4; | ||||
|  | ||||
|     assert_eq!(&body[n..], "foo bar baz"); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn server_get_chunked_response() { | ||||
|     let foo_bar = b"foo bar baz"; | ||||
|     let server = serve(); | ||||
|     server.reply() | ||||
|         .status(hyper::Ok) | ||||
|         .header(hyper::header::TransferEncoding::chunked()) | ||||
|         .body(foo_bar); | ||||
|     let mut req = TcpStream::connect(server.addr()).unwrap(); | ||||
|     req.write_all(b"\ | ||||
|         GET / HTTP/1.1\r\n\ | ||||
|         Host: example.domain\r\n\ | ||||
|         Connection: close\r\n | ||||
|         \r\n\ | ||||
|     ").unwrap(); | ||||
|     let mut body = String::new(); | ||||
|     req.read_to_string(&mut body).unwrap(); | ||||
|     let n = body.find("\r\n\r\n").unwrap() + 4; | ||||
|  | ||||
|     assert_eq!(&body[n..], "B\r\nfoo bar baz\r\n0\r\n\r\n"); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn server_post_with_chunked_body() { | ||||
|     let server = serve(); | ||||
|     let mut req = TcpStream::connect(server.addr()).unwrap(); | ||||
|     req.write_all(b"\ | ||||
|         POST / HTTP/1.1\r\n\ | ||||
|         Host: example.domain\r\n\ | ||||
|         Transfer-Encoding: chunked\r\n\ | ||||
|         \r\n\ | ||||
|         1\r\n\ | ||||
|         q\r\n\ | ||||
|         2\r\n\ | ||||
|         we\r\n\ | ||||
|         2\r\n\ | ||||
|         rt\r\n\ | ||||
|         0\r\n\ | ||||
|         \r\n | ||||
|     ").unwrap(); | ||||
|     req.read(&mut [0; 256]).unwrap(); | ||||
|  | ||||
|     assert_eq!(server.body(), b"qwert"); | ||||
| } | ||||
|  | ||||
| /* | ||||
| #[test] | ||||
| fn server_empty_response() { | ||||
|     let server = serve(); | ||||
|     server.reply() | ||||
|         .status(hyper::Ok); | ||||
|     let mut req = TcpStream::connect(server.addr()).unwrap(); | ||||
|     req.write_all(b"\ | ||||
|         GET / HTTP/1.1\r\n\ | ||||
|         Host: example.domain\r\n\ | ||||
|         Connection: close\r\n | ||||
|         \r\n\ | ||||
|     ").unwrap(); | ||||
|  | ||||
|     let mut response = String::new(); | ||||
|     req.read_to_string(&mut response).unwrap(); | ||||
|  | ||||
|     assert_eq!(response, "foo"); | ||||
|     assert!(!response.contains("Transfer-Encoding: chunked\r\n")); | ||||
|  | ||||
|     let mut lines = response.lines(); | ||||
|     assert_eq!(lines.next(), Some("HTTP/1.1 200 OK")); | ||||
|  | ||||
|     let mut lines = lines.skip_while(|line| !line.is_empty()); | ||||
|     assert_eq!(lines.next(), Some("")); | ||||
|     assert_eq!(lines.next(), None); | ||||
| } | ||||
| */ | ||||
|  | ||||
| #[test] | ||||
| fn server_empty_response_chunked() { | ||||
|     let server = serve(); | ||||
|     server.reply() | ||||
|         .status(hyper::Ok) | ||||
|         .body(""); | ||||
|     let mut req = TcpStream::connect(server.addr()).unwrap(); | ||||
|     req.write_all(b"\ | ||||
|         GET / HTTP/1.1\r\n\ | ||||
|         Host: example.domain\r\n\ | ||||
|         Connection: close\r\n | ||||
|         \r\n\ | ||||
|     ").unwrap(); | ||||
|  | ||||
|     let mut response = String::new(); | ||||
|     req.read_to_string(&mut response).unwrap(); | ||||
|  | ||||
|     assert!(response.contains("Transfer-Encoding: chunked\r\n")); | ||||
|  | ||||
|     let mut lines = response.lines(); | ||||
|     assert_eq!(lines.next(), Some("HTTP/1.1 200 OK")); | ||||
|  | ||||
|     let mut lines = lines.skip_while(|line| !line.is_empty()); | ||||
|     assert_eq!(lines.next(), Some("")); | ||||
|     // 0\r\n\r\n | ||||
|     assert_eq!(lines.next(), Some("0")); | ||||
|     assert_eq!(lines.next(), Some("")); | ||||
|     assert_eq!(lines.next(), None); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn server_empty_response_chunked_without_calling_write() { | ||||
|     let server = serve(); | ||||
|     server.reply() | ||||
|         .status(hyper::Ok) | ||||
|         .header(hyper::header::TransferEncoding::chunked()); | ||||
|     let mut req = TcpStream::connect(server.addr()).unwrap(); | ||||
|     req.write_all(b"\ | ||||
|         GET / HTTP/1.1\r\n\ | ||||
|         Host: example.domain\r\n\ | ||||
|         Connection: close\r\n | ||||
|         \r\n\ | ||||
|     ").unwrap(); | ||||
|  | ||||
|     let mut response = String::new(); | ||||
|     req.read_to_string(&mut response).unwrap(); | ||||
|  | ||||
|     assert!(response.contains("Transfer-Encoding: chunked\r\n")); | ||||
|  | ||||
|     let mut lines = response.lines(); | ||||
|     assert_eq!(lines.next(), Some("HTTP/1.1 200 OK")); | ||||
|  | ||||
|     let mut lines = lines.skip_while(|line| !line.is_empty()); | ||||
|     assert_eq!(lines.next(), Some("")); | ||||
|     // 0\r\n\r\n | ||||
|     assert_eq!(lines.next(), Some("0")); | ||||
|     assert_eq!(lines.next(), Some("")); | ||||
|     assert_eq!(lines.next(), None); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user