Merge pull request #354 from hyperium/new-io
feat(hyper): switch to std::io, std::net, and std::path.
This commit is contained in:
		
							
								
								
									
										21
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								README.md
									
									
									
									
									
								
							| @@ -27,12 +27,13 @@ Hello World Server: | ||||
| ```rust | ||||
| extern crate hyper; | ||||
|  | ||||
| use hyper::status::StatusCode; | ||||
| use hyper::server::Server; | ||||
| use hyper::server::request::Request; | ||||
| use hyper::server::response::Response; | ||||
| use std::io::Write; | ||||
| use std::net::IpAddr; | ||||
|  | ||||
| use hyper::Server; | ||||
| use hyper::server::Request; | ||||
| use hyper::server::Response; | ||||
| use hyper::net::Fresh; | ||||
| use hyper::IpAddr::Ipv4Addr; | ||||
|  | ||||
| fn hello(_: Request, mut res: Response<Fresh>) { | ||||
|     let mut res = res.start().unwrap(); | ||||
| @@ -41,8 +42,7 @@ fn hello(_: Request, mut res: Response<Fresh>) { | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
|     let server = Server::http(Ipv4Addr(127, 0, 0, 1), 1337); | ||||
|     server.listen(hello).unwrap(); | ||||
|     Server::http(hello).listen(IpAddr::new_v4(127, 0, 0, 1), 3000).unwrap(); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| @@ -51,7 +51,9 @@ Client: | ||||
| ```rust | ||||
| extern crate hyper; | ||||
|  | ||||
| use hyper::client::Client; | ||||
| use std::io::Read; | ||||
|  | ||||
| use hyper::Client; | ||||
| use hyper::header::Connection; | ||||
| use hyper::header::ConnectionOption; | ||||
|  | ||||
| @@ -67,7 +69,8 @@ fn main() { | ||||
|         .send().unwrap(); | ||||
|  | ||||
|     // Read the Response. | ||||
|     let body = res.read_to_string().unwrap(); | ||||
|     let mut body = String::new(); | ||||
|     res.read_to_string(&mut body).unwrap(); | ||||
|  | ||||
|     println!("Response: {}", body); | ||||
| } | ||||
|   | ||||
| @@ -1,33 +1,53 @@ | ||||
| #![feature(core, old_io, test)] | ||||
| #![feature(collections, io, net, test)] | ||||
| extern crate hyper; | ||||
|  | ||||
| extern crate test; | ||||
|  | ||||
| use std::fmt; | ||||
| use std::old_io::net::ip::Ipv4Addr; | ||||
| use hyper::server::{Request, Response, Server}; | ||||
| use hyper::header::Headers; | ||||
| use hyper::Client; | ||||
| use std::io::{self, Read, Write, Cursor}; | ||||
| use std::net::SocketAddr; | ||||
|  | ||||
| fn listen() -> hyper::server::Listening { | ||||
|     let server = Server::http(Ipv4Addr(127, 0, 0, 1), 0); | ||||
|     server.listen(handle).unwrap() | ||||
| use hyper::net; | ||||
|  | ||||
| static README: &'static [u8] = include_bytes!("../README.md"); | ||||
|  | ||||
| struct MockStream { | ||||
|     read: Cursor<Vec<u8>> | ||||
| } | ||||
|  | ||||
| macro_rules! try_return( | ||||
|     ($e:expr) => {{ | ||||
|         match $e { | ||||
|             Ok(v) => v, | ||||
|             Err(..) => return | ||||
| 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.push_all(README); | ||||
|         MockStream { | ||||
|             read: Cursor::new(res) | ||||
|         } | ||||
|     }} | ||||
| ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn handle(_r: Request, res: Response) { | ||||
|     static BODY: &'static [u8] = b"Benchmarking hyper vs others!"; | ||||
|     let mut res = try_return!(res.start()); | ||||
|     try_return!(res.write_all(BODY)); | ||||
|     try_return!(res.end()); | ||||
| 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)] | ||||
| @@ -48,17 +68,36 @@ impl hyper::header::HeaderFormat for Foo { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[bench] | ||||
| fn bench_hyper(b: &mut test::Bencher) { | ||||
|     let mut listening = listen(); | ||||
|     let s = format!("http://{}/", listening.socket); | ||||
|     let url = s.as_slice(); | ||||
|     let mut client = Client::new(); | ||||
|     let mut headers = Headers::new(); | ||||
|     headers.set(Foo); | ||||
|     b.iter(|| { | ||||
|         client.get(url).header(Foo).send().unwrap().read_to_string().unwrap(); | ||||
|     }); | ||||
|     listening.close().unwrap() | ||||
| impl net::NetworkStream for MockStream { | ||||
|     fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         Ok("127.0.0.1:1337".parse().unwrap()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct MockConnector; | ||||
|  | ||||
| impl net::NetworkConnector for MockConnector { | ||||
|     type Stream = MockStream; | ||||
|     fn connect(&mut self, _: &str, _: u16, _: &str) -> io::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(), &mut MockConnector | ||||
|         ).unwrap(); | ||||
|         req.headers_mut().set(Foo); | ||||
|  | ||||
|         let mut s = String::new(); | ||||
|         req | ||||
|             .start().unwrap() | ||||
|             .send().unwrap() | ||||
|             .read_to_string(&mut s).unwrap() | ||||
|     }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,98 +0,0 @@ | ||||
| #![feature(collections, old_io, test)] | ||||
| extern crate hyper; | ||||
|  | ||||
| extern crate test; | ||||
|  | ||||
| use std::fmt; | ||||
| use std::old_io::{IoResult, MemReader}; | ||||
| use std::old_io::net::ip::SocketAddr; | ||||
|  | ||||
| use hyper::net; | ||||
|  | ||||
| static README: &'static [u8] = include_bytes!("../README.md"); | ||||
|  | ||||
|  | ||||
| struct MockStream { | ||||
|     read: MemReader, | ||||
| } | ||||
|  | ||||
| impl Clone for MockStream { | ||||
|     fn clone(&self) -> MockStream { | ||||
|         MockStream::new() | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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.push_all(README); | ||||
|         MockStream { | ||||
|             read: MemReader::new(res), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Reader for MockStream { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||||
|         self.read.read(buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Writer for MockStream { | ||||
|     fn write_all(&mut self, _msg: &[u8]) -> IoResult<()> { | ||||
|         // we're mocking, what do we care. | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| struct Foo; | ||||
|  | ||||
| impl hyper::header::Header for Foo { | ||||
|     fn header_name() -> &'static str { | ||||
|         "x-foo" | ||||
|     } | ||||
|     fn parse_header(_: &[Vec<u8>]) -> Option<Foo> { | ||||
|         None | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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_name(&mut self) -> IoResult<SocketAddr> { | ||||
|         Ok("127.0.0.1:1337".parse().unwrap()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct MockConnector; | ||||
|  | ||||
| impl net::NetworkConnector for MockConnector { | ||||
|     type Stream = MockStream; | ||||
|     fn connect(&mut self, _: &str, _: u16, _: &str) -> IoResult<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(), &mut MockConnector | ||||
|         ).unwrap(); | ||||
|         req.headers_mut().set(Foo); | ||||
|  | ||||
|         req | ||||
|             .start().unwrap() | ||||
|             .send().unwrap() | ||||
|             .read_to_string().unwrap() | ||||
|     }); | ||||
| } | ||||
|  | ||||
| @@ -1,9 +1,10 @@ | ||||
| #![feature(old_io, test)] | ||||
| #![feature(io, net, test)] | ||||
| extern crate hyper; | ||||
| extern crate test; | ||||
|  | ||||
| use test::Bencher; | ||||
| use std::old_io::net::ip::Ipv4Addr; | ||||
| use std::io::{Read, Write}; | ||||
| use std::net::IpAddr; | ||||
|  | ||||
| use hyper::method::Method::Get; | ||||
| use hyper::server::{Request, Response}; | ||||
| @@ -12,7 +13,8 @@ static PHRASE: &'static [u8] = b"Benchmarking hyper vs others!"; | ||||
|  | ||||
| fn request(url: hyper::Url) { | ||||
|     let req = hyper::client::Request::new(Get, url).unwrap(); | ||||
|     req.start().unwrap().send().unwrap().read_to_string().unwrap(); | ||||
|     let mut s = String::new(); | ||||
|     req.start().unwrap().send().unwrap().read_to_string(&mut s).unwrap(); | ||||
| } | ||||
|  | ||||
| fn hyper_handle(_: Request, res: Response) { | ||||
| @@ -23,8 +25,8 @@ fn hyper_handle(_: Request, res: Response) { | ||||
|  | ||||
| #[bench] | ||||
| fn bench_hyper(b: &mut Bencher) { | ||||
|     let server = hyper::Server::http(Ipv4Addr(127, 0, 0, 1), 0); | ||||
|     let mut listener = server.listen(hyper_handle).unwrap(); | ||||
|     let server = hyper::Server::http(hyper_handle); | ||||
|     let mut listener = server.listen(IpAddr::new_v4(127, 0, 0, 1), 0).unwrap(); | ||||
|  | ||||
|     let url = hyper::Url::parse(&*format!("http://{}", listener.socket)).unwrap(); | ||||
|     b.iter(|| request(url.clone())); | ||||
|   | ||||
| @@ -1,9 +1,7 @@ | ||||
| #![feature(env, old_io)] | ||||
| #![feature(env)] | ||||
| extern crate hyper; | ||||
|  | ||||
| use std::env; | ||||
| use std::old_io::stdout; | ||||
| use std::old_io::util::copy; | ||||
|  | ||||
| use hyper::Client; | ||||
|  | ||||
| @@ -18,16 +16,12 @@ fn main() { | ||||
|  | ||||
|     let mut client = Client::new(); | ||||
|  | ||||
|     let mut res = match client.get(&*url).send() { | ||||
|     let res = match client.get(&*url).send() { | ||||
|         Ok(res) => res, | ||||
|         Err(err) => panic!("Failed to connect: {:?}", err) | ||||
|     }; | ||||
|  | ||||
|     println!("Response: {}", res.status); | ||||
|     println!("Headers:\n{}", res.headers); | ||||
|     match copy(&mut res, &mut stdout()) { | ||||
|         Ok(..) => (), | ||||
|         Err(e) => panic!("Stream failure: {:?}", e) | ||||
|     }; | ||||
|  | ||||
|     //TODO: add copy back when std::stdio impls std::io::Write. | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| #![feature(old_io)] | ||||
| #![feature(io, net)] | ||||
| extern crate hyper; | ||||
|  | ||||
| use std::old_io::net::ip::Ipv4Addr; | ||||
| use std::io::Write; | ||||
| use std::net::IpAddr; | ||||
| use hyper::server::{Request, Response}; | ||||
|  | ||||
| static PHRASE: &'static [u8] = b"Hello World!"; | ||||
| @@ -13,7 +14,7 @@ fn hello(_: Request, res: Response) { | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
|     let _listening = hyper::Server::http(Ipv4Addr(127, 0, 0, 1), 3000) | ||||
|         .listen(hello).unwrap(); | ||||
|     let _listening = hyper::Server::http(hello) | ||||
|         .listen(IpAddr::new_v4(127, 0, 0, 1), 3000).unwrap(); | ||||
|     println!("Listening on http://127.0.0.1:3000"); | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| #![feature(old_io)] | ||||
| #![feature(io, net)] | ||||
| extern crate hyper; | ||||
| #[macro_use] extern crate log; | ||||
|  | ||||
| use std::old_io::util::copy; | ||||
| use std::old_io::net::ip::Ipv4Addr; | ||||
| use std::io::{Write, copy}; | ||||
| use std::net::IpAddr; | ||||
|  | ||||
| use hyper::{Get, Post}; | ||||
| use hyper::header::ContentLength; | ||||
| @@ -50,7 +50,7 @@ fn echo(mut req: Request, mut res: Response) { | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
|     let server = Server::http(Ipv4Addr(127, 0, 0, 1), 1337); | ||||
|     let _guard = server.listen(echo).unwrap(); | ||||
|     let server = Server::http(echo); | ||||
|     let _guard = server.listen(IpAddr::new_v4(127, 0, 0, 1), 1337).unwrap(); | ||||
|     println!("Listening on http://127.0.0.1:1337"); | ||||
| } | ||||
|   | ||||
| @@ -18,8 +18,7 @@ | ||||
| //! to the `status`, the `headers`, and the response body via the `Writer` | ||||
| //! trait. | ||||
| use std::default::Default; | ||||
| use std::old_io::IoResult; | ||||
| use std::old_io::util::copy; | ||||
| use std::io::{self, copy, Read}; | ||||
| use std::iter::Extend; | ||||
|  | ||||
| use url::UrlParser; | ||||
| @@ -30,7 +29,7 @@ use header::{ContentLength, Location}; | ||||
| use method::Method; | ||||
| use net::{NetworkConnector, HttpConnector, ContextVerifier}; | ||||
| use status::StatusClass::Redirection; | ||||
| use {Url, Port, HttpResult}; | ||||
| use {Url, HttpResult}; | ||||
| use HttpError::HttpUriError; | ||||
|  | ||||
| pub use self::request::Request; | ||||
| @@ -238,9 +237,9 @@ pub trait IntoBody<'a> { | ||||
| /// The target enum for the IntoBody trait. | ||||
| pub enum Body<'a> { | ||||
|     /// A Reader does not necessarily know it's size, so it is chunked. | ||||
|     ChunkedBody(&'a mut (Reader + 'a)), | ||||
|     ChunkedBody(&'a mut (Read + 'a)), | ||||
|     /// For Readers that can know their size, like a `File`. | ||||
|     SizedBody(&'a mut (Reader + 'a), u64), | ||||
|     SizedBody(&'a mut (Read + 'a), u64), | ||||
|     /// A String has a size, and uses Content-Length. | ||||
|     BufBody(&'a [u8] , usize), | ||||
| } | ||||
| @@ -255,13 +254,13 @@ impl<'a> Body<'a> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Reader for Body<'a> { | ||||
| impl<'a> Read for Body<'a> { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             Body::ChunkedBody(ref mut r) => r.read(buf), | ||||
|             Body::SizedBody(ref mut r, _) => r.read(buf), | ||||
|             Body::BufBody(ref mut r, _) => r.read(buf), | ||||
|             Body::BufBody(ref mut r, _) => Read::read(r, buf), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -288,7 +287,7 @@ impl<'a> IntoBody<'a> for &'a str { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, R: Reader> IntoBody<'a> for &'a mut R { | ||||
| impl<'a, R: Read> IntoBody<'a> for &'a mut R { | ||||
|     #[inline] | ||||
|     fn into_body(self) -> Body<'a> { | ||||
|         Body::ChunkedBody(self) | ||||
| @@ -337,7 +336,7 @@ impl Default for RedirectPolicy { | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn get_host_and_port(url: &Url) -> HttpResult<(String, Port)> { | ||||
| fn get_host_and_port(url: &Url) -> HttpResult<(String, u16)> { | ||||
|     let host = match url.serialize_host() { | ||||
|         Some(host) => host, | ||||
|         None => return Err(HttpUriError(UrlError::EmptyHost)) | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| //! Client Requests | ||||
| use std::old_io::{BufferedWriter, IoResult}; | ||||
| use std::marker::PhantomData; | ||||
| use std::io::{self, Write, BufWriter}; | ||||
|  | ||||
| use url::Url; | ||||
|  | ||||
| @@ -23,7 +23,7 @@ pub struct Request<W> { | ||||
|     /// The HTTP version of this request. | ||||
|     pub version: version::HttpVersion, | ||||
|  | ||||
|     body: HttpWriter<BufferedWriter<Box<NetworkStream + Send>>>, | ||||
|     body: HttpWriter<BufWriter<Box<NetworkStream + Send>>>, | ||||
|     headers: Headers, | ||||
|     method: method::Method, | ||||
|  | ||||
| @@ -59,7 +59,7 @@ impl Request<Fresh> { | ||||
|         let (host, port) = try!(get_host_and_port(&url)); | ||||
|  | ||||
|         let stream = try!(connector.connect(&*host, port, &*url.scheme)); | ||||
|         let stream = ThroughWriter(BufferedWriter::new(box stream as Box<NetworkStream + Send>)); | ||||
|         let stream = ThroughWriter(BufWriter::new(box stream as Box<NetworkStream + Send>)); | ||||
|  | ||||
|         let mut headers = Headers::new(); | ||||
|         headers.set(Host { | ||||
| @@ -96,7 +96,7 @@ impl Request<Fresh> { | ||||
|             Method::Get | Method::Head => { | ||||
|                 debug!("headers [\n{:?}]", self.headers); | ||||
|                 try!(write!(&mut self.body, "{}{}", self.headers, LINE_ENDING)); | ||||
|                 EmptyWriter(self.body.unwrap()) | ||||
|                 EmptyWriter(self.body.into_inner()) | ||||
|             }, | ||||
|             _ => { | ||||
|                 let mut chunked = true; | ||||
| @@ -131,9 +131,9 @@ impl Request<Fresh> { | ||||
|                 try!(write!(&mut self.body, "{}{}", self.headers, LINE_ENDING)); | ||||
|  | ||||
|                 if chunked { | ||||
|                     ChunkedWriter(self.body.unwrap()) | ||||
|                     ChunkedWriter(self.body.into_inner()) | ||||
|                 } else { | ||||
|                     SizedWriter(self.body.unwrap(), len) | ||||
|                     SizedWriter(self.body.into_inner(), len) | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
| @@ -158,19 +158,19 @@ impl Request<Streaming> { | ||||
|     /// | ||||
|     /// Consumes the Request. | ||||
|     pub fn send(self) -> HttpResult<Response> { | ||||
|         let raw = try!(self.body.end()).into_inner(); | ||||
|         let raw = try!(self.body.end()).into_inner().unwrap(); // end() already flushes | ||||
|         Response::new(raw) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Writer for Request<Streaming> { | ||||
| impl Write for Request<Streaming> { | ||||
|     #[inline] | ||||
|     fn write_all(&mut self, msg: &[u8]) -> IoResult<()> { | ||||
|         self.body.write_all(msg) | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         self.body.write(msg) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> IoResult<()> { | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         self.body.flush() | ||||
|     } | ||||
| } | ||||
| @@ -191,8 +191,8 @@ mod tests { | ||||
|         ).unwrap(); | ||||
|         let req = req.start().unwrap(); | ||||
|         let stream = *req.body.end().unwrap() | ||||
|             .into_inner().downcast::<MockStream>().ok().unwrap(); | ||||
|         let bytes = stream.write.into_inner(); | ||||
|             .into_inner().unwrap().downcast::<MockStream>().ok().unwrap(); | ||||
|         let bytes = stream.write; | ||||
|         let s = from_utf8(&bytes[..]).unwrap(); | ||||
|         assert!(!s.contains("Content-Length:")); | ||||
|         assert!(!s.contains("Transfer-Encoding:")); | ||||
| @@ -205,8 +205,8 @@ mod tests { | ||||
|         ).unwrap(); | ||||
|         let req = req.start().unwrap(); | ||||
|         let stream = *req.body.end().unwrap() | ||||
|             .into_inner().downcast::<MockStream>().ok().unwrap(); | ||||
|         let bytes = stream.write.into_inner(); | ||||
|             .into_inner().unwrap().downcast::<MockStream>().ok().unwrap(); | ||||
|         let bytes = stream.write; | ||||
|         let s = from_utf8(&bytes[..]).unwrap(); | ||||
|         assert!(!s.contains("Content-Length:")); | ||||
|         assert!(!s.contains("Transfer-Encoding:")); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| //! Client Responses | ||||
| use std::io::{self, Read, BufReader}; | ||||
| use std::num::FromPrimitive; | ||||
| use std::old_io::{BufferedReader, IoResult}; | ||||
| use std::marker::PhantomData; | ||||
|  | ||||
| use header; | ||||
| @@ -23,7 +23,7 @@ pub struct Response<S = HttpStream> { | ||||
|     /// The HTTP version of this response from the server. | ||||
|     pub version: version::HttpVersion, | ||||
|     status_raw: RawStatus, | ||||
|     body: HttpReader<BufferedReader<Box<NetworkStream + Send>>>, | ||||
|     body: HttpReader<BufReader<Box<NetworkStream + Send>>>, | ||||
|  | ||||
|     _marker: PhantomData<S>, | ||||
| } | ||||
| @@ -35,7 +35,7 @@ impl Response { | ||||
|  | ||||
|     /// Creates a new response from a server. | ||||
|     pub fn new(stream: Box<NetworkStream + Send>) -> HttpResult<Response> { | ||||
|         let mut stream = BufferedReader::new(stream); | ||||
|         let mut stream = BufReader::new(stream); | ||||
|         let (version, raw_status) = try!(read_status_line(&mut stream)); | ||||
|         let status = match FromPrimitive::from_u16(raw_status.0) { | ||||
|             Some(status) => status, | ||||
| @@ -89,13 +89,13 @@ impl Response { | ||||
|  | ||||
|     /// Consumes the Request to return the NetworkStream underneath. | ||||
|     pub fn into_inner(self) -> Box<NetworkStream + Send> { | ||||
|         self.body.unwrap().into_inner() | ||||
|         self.body.into_inner().into_inner() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Reader for Response { | ||||
| impl Read for Response { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         self.body.read(buf) | ||||
|     } | ||||
| } | ||||
| @@ -104,7 +104,7 @@ impl Reader for Response { | ||||
| mod tests { | ||||
|     use std::borrow::Cow::Borrowed; | ||||
|     use std::boxed::BoxAny; | ||||
|     use std::old_io::BufferedReader; | ||||
|     use std::io::{self, Read, BufReader}; | ||||
|     use std::marker::PhantomData; | ||||
|  | ||||
|     use header::Headers; | ||||
| @@ -119,14 +119,20 @@ mod tests { | ||||
|  | ||||
|     use super::Response; | ||||
|  | ||||
|     fn read_to_string(mut r: Response) -> io::Result<String> { | ||||
|         let mut s = String::new(); | ||||
|         try!(r.read_to_string(&mut s)); | ||||
|         Ok(s) | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #[test] | ||||
|     fn test_unwrap() { | ||||
|     fn test_into_inner() { | ||||
|         let res = Response { | ||||
|             status: status::StatusCode::Ok, | ||||
|             headers: Headers::new(), | ||||
|             version: version::HttpVersion::Http11, | ||||
|             body: EofReader(BufferedReader::new(box MockStream::new() as Box<NetworkStream + Send>)), | ||||
|             body: EofReader(BufReader::new(box MockStream::new() as Box<NetworkStream + Send>)), | ||||
|             status_raw: RawStatus(200, Borrowed("OK")), | ||||
|             _marker: PhantomData, | ||||
|         }; | ||||
| @@ -152,7 +158,7 @@ mod tests { | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let mut res = Response::new(box stream).unwrap(); | ||||
|         let res = Response::new(box stream).unwrap(); | ||||
|  | ||||
|         // The status line is correct? | ||||
|         assert_eq!(res.status, status::StatusCode::Ok); | ||||
| @@ -166,8 +172,7 @@ mod tests { | ||||
|             None => panic!("Transfer-Encoding: chunked expected!"), | ||||
|         }; | ||||
|         // The body is correct? | ||||
|         let body = res.read_to_string().unwrap(); | ||||
|         assert_eq!("qwert", body); | ||||
|         assert_eq!(read_to_string(res), Ok("qwert".to_string())); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a chunk size is not a valid radix-16 number, an error | ||||
| @@ -184,9 +189,9 @@ mod tests { | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let mut res = Response::new(box stream).unwrap(); | ||||
|         let res = Response::new(box stream).unwrap(); | ||||
|  | ||||
|         assert!(res.read_to_string().is_err()); | ||||
|         assert!(read_to_string(res).is_err()); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a chunk size contains an invalid extension, an error is | ||||
| @@ -203,9 +208,9 @@ mod tests { | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let mut res = Response::new(box stream).unwrap(); | ||||
|         let res = Response::new(box stream).unwrap(); | ||||
|  | ||||
|         assert!(res.read_to_string().is_err()); | ||||
|         assert!(read_to_string(res).is_err()); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a valid extension that contains a digit is appended to | ||||
| @@ -222,8 +227,8 @@ mod tests { | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let mut res = Response::new(box stream).unwrap(); | ||||
|         let res = Response::new(box stream).unwrap(); | ||||
|  | ||||
|         assert_eq!("1", res.read_to_string().unwrap()) | ||||
|         assert_eq!(read_to_string(res), Ok("1".to_string())); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| use header::{self, QualityItem}; | ||||
| use header::QualityItem; | ||||
| use std::str::FromStr; | ||||
| use std::fmt; | ||||
|  | ||||
| @@ -49,7 +49,6 @@ impl_list_header!(AcceptLanguage, | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use header::{Header, qitem, Quality, QualityItem}; | ||||
|  | ||||
|     use super::*; | ||||
|  | ||||
|     #[test] | ||||
|   | ||||
| @@ -142,14 +142,9 @@ impl FromStr for Basic { | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::old_io::MemReader; | ||||
|     use super::{Authorization, Basic}; | ||||
|     use super::super::super::{Headers}; | ||||
|  | ||||
|     fn mem(s: &str) -> MemReader { | ||||
|         MemReader::new(s.as_bytes().to_vec()) | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_raw_auth() { | ||||
|         let mut headers = Headers::new(); | ||||
| @@ -159,7 +154,7 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn test_raw_auth_parse() { | ||||
|         let headers = Headers::from_raw(&mut mem("Authorization: foo bar baz\r\n\r\n")).unwrap(); | ||||
|         let headers = Headers::from_raw(&mut b"Authorization: foo bar baz\r\n\r\n").unwrap(); | ||||
|         assert_eq!(&headers.get::<Authorization<String>>().unwrap().0[..], "foo bar baz"); | ||||
|     } | ||||
|  | ||||
| @@ -179,7 +174,7 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn test_basic_auth_parse() { | ||||
|         let headers = Headers::from_raw(&mut mem("Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n\r\n")).unwrap(); | ||||
|         let headers = Headers::from_raw(&mut b"Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n\r\n").unwrap(); | ||||
|         let auth = headers.get::<Authorization<Basic>>().unwrap(); | ||||
|         assert_eq!(&auth.0.username[..], "Aladdin"); | ||||
|         assert_eq!(auth.0.password, Some("open sesame".to_string())); | ||||
| @@ -187,7 +182,7 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn test_basic_auth_parse_no_password() { | ||||
|         let headers = Headers::from_raw(&mut mem("Authorization: Basic QWxhZGRpbjo=\r\n\r\n")).unwrap(); | ||||
|         let headers = Headers::from_raw(&mut b"Authorization: Basic QWxhZGRpbjo=\r\n\r\n").unwrap(); | ||||
|         let auth = headers.get::<Authorization<Basic>>().unwrap(); | ||||
|         assert_eq!(auth.0.username.as_slice(), "Aladdin"); | ||||
|         assert_eq!(auth.0.password, Some("".to_string())); | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| use header::{Header, HeaderFormat}; | ||||
| use Port; | ||||
| use std::fmt; | ||||
| use header::parsing::from_one_raw_str; | ||||
|  | ||||
| @@ -15,7 +14,7 @@ pub struct Host { | ||||
|     /// The hostname, such a example.domain. | ||||
|     pub hostname: String, | ||||
|     /// An optional port number. | ||||
|     pub port: Option<Port> | ||||
|     pub port: Option<u16> | ||||
| } | ||||
|  | ||||
| impl Header for Host { | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
| use std::any::{Any, TypeId}; | ||||
| use std::borrow::Cow::{Borrowed, Owned}; | ||||
| use std::fmt; | ||||
| use std::io::Read; | ||||
| use std::raw::TraitObject; | ||||
| use std::str::from_utf8; | ||||
| use std::collections::HashMap; | ||||
| @@ -132,7 +133,7 @@ impl Headers { | ||||
|     } | ||||
|  | ||||
|     #[doc(hidden)] | ||||
|     pub fn from_raw<R: Reader>(rdr: &mut R) -> HttpResult<Headers> { | ||||
|     pub fn from_raw<R: Read>(rdr: &mut R) -> HttpResult<Headers> { | ||||
|         let mut headers = Headers::new(); | ||||
|         let mut count = 0u32; | ||||
|         loop { | ||||
| @@ -534,7 +535,6 @@ impl<'a, H: HeaderFormat> fmt::Debug for HeaderFormatter<'a, H> { | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::old_io::MemReader; | ||||
|     use std::fmt; | ||||
|     use mime::Mime; | ||||
|     use mime::TopLevel::Text; | ||||
| @@ -544,13 +544,9 @@ mod tests { | ||||
|  | ||||
|     use test::Bencher; | ||||
|  | ||||
|     fn mem(s: &str) -> MemReader { | ||||
|         MemReader::new(s.as_bytes().to_vec()) | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_from_raw() { | ||||
|         let headers = Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap(); | ||||
|         let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap(); | ||||
|         assert_eq!(headers.get(), Some(&ContentLength(10))); | ||||
|     } | ||||
|  | ||||
| @@ -603,21 +599,21 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn test_different_structs_for_same_header() { | ||||
|         let headers = Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap(); | ||||
|         let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap(); | ||||
|         let ContentLength(_) = *headers.get::<ContentLength>().unwrap(); | ||||
|         assert!(headers.get::<CrazyLength>().is_none()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_trailing_whitespace() { | ||||
|         let headers = Headers::from_raw(&mut mem("Content-Length: 10   \r\n\r\n")).unwrap(); | ||||
|         let headers = Headers::from_raw(&mut b"Content-Length: 10   \r\n\r\n").unwrap(); | ||||
|         let ContentLength(_) = *headers.get::<ContentLength>().unwrap(); | ||||
|         assert!(headers.get::<CrazyLength>().is_none()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_multiple_reads() { | ||||
|         let headers = Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap(); | ||||
|         let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap(); | ||||
|         let ContentLength(one) = *headers.get::<ContentLength>().unwrap(); | ||||
|         let ContentLength(two) = *headers.get::<ContentLength>().unwrap(); | ||||
|         assert_eq!(one, two); | ||||
| @@ -625,14 +621,14 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn test_different_reads() { | ||||
|         let headers = Headers::from_raw(&mut mem("Content-Length: 10\r\nContent-Type: text/plain\r\n\r\n")).unwrap(); | ||||
|         let headers = Headers::from_raw(&mut b"Content-Length: 10\r\nContent-Type: text/plain\r\n\r\n").unwrap(); | ||||
|         let ContentLength(_) = *headers.get::<ContentLength>().unwrap(); | ||||
|         let ContentType(_) = *headers.get::<ContentType>().unwrap(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_get_mutable() { | ||||
|         let mut headers = Headers::from_raw(&mut mem("Content-Length: 10\r\nContent-Type: text/plain\r\n\r\n")).unwrap(); | ||||
|         let mut headers = Headers::from_raw(&mut b"Content-Length: 10\r\nContent-Type: text/plain\r\n\r\n").unwrap(); | ||||
|         *headers.get_mut::<ContentLength>().unwrap() = ContentLength(20); | ||||
|         assert_eq!(*headers.get::<ContentLength>().unwrap(), ContentLength(20)); | ||||
|     } | ||||
| @@ -653,7 +649,7 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn test_headers_show_raw() { | ||||
|         let headers = Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap(); | ||||
|         let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap(); | ||||
|         let s = headers.to_string(); | ||||
|         assert_eq!(s, "Content-Length: 10\r\n"); | ||||
|     } | ||||
| @@ -720,7 +716,7 @@ mod tests { | ||||
|  | ||||
|     #[bench] | ||||
|     fn bench_headers_from_raw(b: &mut Bencher) { | ||||
|         b.iter(|| Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap()) | ||||
|         b.iter(|| Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap()) | ||||
|     } | ||||
|  | ||||
|     #[bench] | ||||
|   | ||||
							
								
								
									
										351
									
								
								src/http.rs
									
									
									
									
									
								
							
							
						
						
									
										351
									
								
								src/http.rs
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
| use std::borrow::Cow::{self, Borrowed, Owned}; | ||||
| use std::borrow::IntoCow; | ||||
| use std::cmp::min; | ||||
| use std::old_io::{self, Reader, IoResult, BufWriter}; | ||||
| use std::io::{self, Read, Write, Cursor}; | ||||
| use std::num::from_u16; | ||||
| use std::str; | ||||
|  | ||||
| @@ -14,8 +14,8 @@ use status::StatusCode; | ||||
| use uri; | ||||
| use uri::RequestUri::{AbsolutePath, AbsoluteUri, Authority, Star}; | ||||
| use version::HttpVersion; | ||||
| use version::HttpVersion::{Http09, Http10, Http11, Http20}; | ||||
| use HttpError::{HttpHeaderError, HttpIoError, HttpMethodError, HttpStatusError, | ||||
| use version::HttpVersion::{Http09, Http10, Http11}; | ||||
| use HttpError::{HttpHeaderError, HttpMethodError, HttpStatusError, | ||||
|                 HttpUriError, HttpVersionError}; | ||||
| use HttpResult; | ||||
|  | ||||
| @@ -52,10 +52,10 @@ pub enum HttpReader<R> { | ||||
|     EmptyReader(R), | ||||
| } | ||||
|  | ||||
| impl<R: Reader> HttpReader<R> { | ||||
| impl<R: Read> HttpReader<R> { | ||||
|  | ||||
|     /// Unwraps this HttpReader and returns the underlying Reader. | ||||
|     pub fn unwrap(self) -> R { | ||||
|     pub fn into_inner(self) -> R { | ||||
|         match self { | ||||
|             SizedReader(r, _) => r, | ||||
|             ChunkedReader(r, _) => r, | ||||
| @@ -65,13 +65,13 @@ impl<R: Reader> HttpReader<R> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<R: Reader> Reader for HttpReader<R> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||||
| impl<R: Read> Read for HttpReader<R> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             SizedReader(ref mut body, ref mut remaining) => { | ||||
|                 debug!("Sized read, remaining={:?}", remaining); | ||||
|                 if *remaining == 0 { | ||||
|                     Err(old_io::standard_error(old_io::EndOfFile)) | ||||
|                     Ok(0) | ||||
|                 } else { | ||||
|                     let num = try!(body.read(buf)) as u64; | ||||
|                     if num > *remaining { | ||||
| @@ -97,7 +97,7 @@ impl<R: Reader> Reader for HttpReader<R> { | ||||
|                     // if the 0 digit was missing from the stream, it would | ||||
|                     // be an InvalidInput error instead. | ||||
|                     debug!("end of chunked"); | ||||
|                     return Err(old_io::standard_error(old_io::EndOfFile)); | ||||
|                     return Ok(0) | ||||
|                 } | ||||
|  | ||||
|                 let to_read = min(rem as usize, buf.len()); | ||||
| @@ -115,29 +115,44 @@ impl<R: Reader> Reader for HttpReader<R> { | ||||
|             EofReader(ref mut body) => { | ||||
|                 body.read(buf) | ||||
|             }, | ||||
|             EmptyReader(_) => Err(old_io::standard_error(old_io::EndOfFile)) | ||||
|             EmptyReader(_) => Ok(0) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn eat<R: Reader>(rdr: &mut R, bytes: &[u8]) -> IoResult<()> { | ||||
| fn eat<R: Read>(rdr: &mut R, bytes: &[u8]) -> io::Result<()> { | ||||
|     let mut buf = [0]; | ||||
|     for &b in bytes.iter() { | ||||
|         match try!(rdr.read_byte()) { | ||||
|             byte if byte == b => (), | ||||
|             _ => return Err(old_io::standard_error(old_io::InvalidInput)) | ||||
|         match try!(rdr.read(&mut buf)) { | ||||
|             1 if buf[0] == b => (), | ||||
|             _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
|                                           "Invalid characters found", | ||||
|                                            None)) | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| /// Chunked chunks start with 1*HEXDIGIT, indicating the size of the chunk. | ||||
| fn read_chunk_size<R: Reader>(rdr: &mut R) -> IoResult<u64> { | ||||
| 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", | ||||
|                                                    None)), | ||||
|  | ||||
|             } | ||||
|         }) | ||||
|     ); | ||||
|     let mut size = 0u64; | ||||
|     let radix = 16; | ||||
|     let mut in_ext = false; | ||||
|     let mut in_chunk_size = true; | ||||
|     loop { | ||||
|         match try!(rdr.read_byte()) { | ||||
|         match byte!(rdr) { | ||||
|             b@b'0'...b'9' if in_chunk_size => { | ||||
|                 size *= radix; | ||||
|                 size += (b - b'0') as u64; | ||||
| @@ -151,9 +166,12 @@ fn read_chunk_size<R: Reader>(rdr: &mut R) -> IoResult<u64> { | ||||
|                 size += (b + 10 - b'A') as u64; | ||||
|             }, | ||||
|             CR => { | ||||
|                 match try!(rdr.read_byte()) { | ||||
|                 match byte!(rdr) { | ||||
|                     LF => break, | ||||
|                     _ => return Err(old_io::standard_error(old_io::InvalidInput)) | ||||
|                     _ => return Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
|                                                   "Invalid chunk size line", | ||||
|                                                    None)) | ||||
|  | ||||
|                 } | ||||
|             }, | ||||
|             // If we weren't in the extension yet, the ";" signals its start | ||||
| @@ -177,7 +195,9 @@ fn read_chunk_size<R: Reader>(rdr: &mut R) -> IoResult<u64> { | ||||
|             // Finally, if we aren't in the extension and we're reading any | ||||
|             // other octet, the chunk size line is invalid! | ||||
|             _ => { | ||||
|                 return Err(old_io::standard_error(old_io::InvalidInput)); | ||||
|                 return Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
|                                          "Invalid chunk size line", | ||||
|                                          None)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -186,7 +206,7 @@ fn read_chunk_size<R: Reader>(rdr: &mut R) -> IoResult<u64> { | ||||
| } | ||||
|  | ||||
| /// Writers to handle different Transfer-Encodings. | ||||
| pub enum HttpWriter<W: Writer> { | ||||
| pub enum HttpWriter<W: Write> { | ||||
|     /// A no-op Writer, used initially before Transfer-Encoding is determined. | ||||
|     ThroughWriter(W), | ||||
|     /// A Writer for when Transfer-Encoding includes `chunked`. | ||||
| @@ -199,10 +219,10 @@ pub enum HttpWriter<W: Writer> { | ||||
|     EmptyWriter(W), | ||||
| } | ||||
|  | ||||
| impl<W: Writer> HttpWriter<W> { | ||||
| impl<W: Write> HttpWriter<W> { | ||||
|     /// Unwraps the HttpWriter and returns the underlying Writer. | ||||
|     #[inline] | ||||
|     pub fn unwrap(self) -> W { | ||||
|     pub fn into_inner(self) -> W { | ||||
|         match self { | ||||
|             ThroughWriter(w) => w, | ||||
|             ChunkedWriter(w) => w, | ||||
| @@ -241,24 +261,25 @@ impl<W: Writer> HttpWriter<W> { | ||||
|     /// A final `write_all()` is called with an empty message, and then flushed. | ||||
|     /// The ChunkedWriter variant will use this to write the 0-sized last-chunk. | ||||
|     #[inline] | ||||
|     pub fn end(mut self) -> IoResult<W> { | ||||
|         try!(self.write_all(&[])); | ||||
|     pub fn end(mut self) -> io::Result<W> { | ||||
|         try!(self.write(&[])); | ||||
|         try!(self.flush()); | ||||
|         Ok(self.unwrap()) | ||||
|         Ok(self.into_inner()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<W: Writer> Writer for HttpWriter<W> { | ||||
| impl<W: Write> Write for HttpWriter<W> { | ||||
|     #[inline] | ||||
|     fn write_all(&mut self, msg: &[u8]) -> IoResult<()> { | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             ThroughWriter(ref mut w) => w.write_all(msg), | ||||
|             ThroughWriter(ref mut w) => w.write(msg), | ||||
|             ChunkedWriter(ref mut w) => { | ||||
|                 let chunk_size = msg.len(); | ||||
|                 debug!("chunked write, size = {:?}", chunk_size); | ||||
|                 try!(write!(w, "{:X}{}", chunk_size, LINE_ENDING)); | ||||
|                 try!(w.write_all(msg)); | ||||
|                 w.write_str(LINE_ENDING) | ||||
|                 try!(w.write_all(LINE_ENDING.as_bytes())); | ||||
|                 Ok(msg.len()) | ||||
|             }, | ||||
|             SizedWriter(ref mut w, ref mut remaining) => { | ||||
|                 let len = msg.len() as u64; | ||||
| @@ -266,29 +287,24 @@ impl<W: Writer> Writer for HttpWriter<W> { | ||||
|                     let len = *remaining; | ||||
|                     *remaining = 0; | ||||
|                     try!(w.write_all(&msg[..len as usize])); | ||||
|                     Err(old_io::standard_error(old_io::ShortWrite(len as usize))) | ||||
|                     Ok(len as usize) | ||||
|                 } else { | ||||
|                     *remaining -= len; | ||||
|                     w.write_all(msg) | ||||
|                     try!(w.write_all(msg)); | ||||
|                     Ok(len as usize) | ||||
|                 } | ||||
|             }, | ||||
|             EmptyWriter(..) => { | ||||
|                 let bytes = msg.len(); | ||||
|                 if bytes == 0 { | ||||
|                     Ok(()) | ||||
|                 } else { | ||||
|                     Err(old_io::IoError { | ||||
|                         kind: old_io::ShortWrite(bytes), | ||||
|                         desc: "EmptyWriter cannot write any bytes", | ||||
|                         detail: Some("Cannot include a body with this kind of message".to_string()) | ||||
|                     }) | ||||
|                 if msg.len() != 0 { | ||||
|                     error!("Cannot include a body with this kind of message"); | ||||
|                 } | ||||
|                 Ok(0) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> IoResult<()> { | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         match *self { | ||||
|             ThroughWriter(ref mut w) => w.flush(), | ||||
|             ChunkedWriter(ref mut w) => w.flush(), | ||||
| @@ -345,24 +361,36 @@ pub fn is_token(b: u8) -> bool { | ||||
| /// otherwise returns any error encountered reading the stream. | ||||
| /// | ||||
| /// The remaining contents of `buf` are left untouched. | ||||
| fn read_token_until_space<R: Reader>(stream: &mut R, buf: &mut [u8]) -> HttpResult<bool> { | ||||
|     use std::old_io::BufWriter; | ||||
|     let mut bufwrt = BufWriter::new(buf); | ||||
| fn read_method_token_until_space<R: Read>(stream: &mut R, buf: &mut [u8]) -> HttpResult<bool> { | ||||
|     macro_rules! byte ( | ||||
|         ($rdr:ident) => ({ | ||||
|             let mut slot = [0]; | ||||
|             match try!($rdr.read(&mut slot)) { | ||||
|                 1 => slot[0], | ||||
|                 _ => return Err(HttpMethodError), | ||||
|             } | ||||
|         }) | ||||
|     ); | ||||
|  | ||||
|     let mut cursor = Cursor::new(buf); | ||||
|  | ||||
|     loop { | ||||
|         let byte = try!(stream.read_byte()); | ||||
|         let b = byte!(stream); | ||||
|  | ||||
|         if byte == SP { | ||||
|         if b == SP { | ||||
|             break; | ||||
|         } else if !is_token(byte) { | ||||
|         } else if !is_token(b) { | ||||
|             return Err(HttpMethodError); | ||||
|         // Read to end but there's still more | ||||
|         } else if bufwrt.write_u8(byte).is_err() { | ||||
|             return Ok(false); | ||||
|         } else { | ||||
|             match cursor.write(&[b]) { | ||||
|                 Ok(1) => (), | ||||
|                 _ => return Ok(false) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if bufwrt.tell().unwrap() == 0 { | ||||
|     if cursor.position() == 0 { | ||||
|         return Err(HttpMethodError); | ||||
|     } | ||||
|  | ||||
| @@ -372,10 +400,10 @@ fn read_token_until_space<R: Reader>(stream: &mut R, buf: &mut [u8]) -> HttpResu | ||||
| /// Read a `Method` from a raw stream, such as `GET`. | ||||
| /// ### Note: | ||||
| /// Extension methods are only parsed to 16 characters. | ||||
| pub fn read_method<R: Reader>(stream: &mut R) -> HttpResult<method::Method> { | ||||
| pub fn read_method<R: Read>(stream: &mut R) -> HttpResult<method::Method> { | ||||
|     let mut buf = [SP; 16]; | ||||
|  | ||||
|     if !try!(read_token_until_space(stream, &mut buf)) { | ||||
|     if !try!(read_method_token_until_space(stream, &mut buf)) { | ||||
|         return Err(HttpMethodError); | ||||
|     } | ||||
|  | ||||
| @@ -404,20 +432,29 @@ pub fn read_method<R: Reader>(stream: &mut R) -> HttpResult<method::Method> { | ||||
| } | ||||
|  | ||||
| /// Read a `RequestUri` from a raw stream. | ||||
| pub fn read_uri<R: Reader>(stream: &mut R) -> HttpResult<uri::RequestUri> { | ||||
|     let mut b = try!(stream.read_byte()); | ||||
| pub fn read_uri<R: Read>(stream: &mut R) -> HttpResult<uri::RequestUri> { | ||||
|     macro_rules! byte ( | ||||
|         ($rdr:ident) => ({ | ||||
|             let mut buf = [0]; | ||||
|             match try!($rdr.read(&mut buf)) { | ||||
|                 1 => buf[0], | ||||
|                 _ => return Err(HttpUriError(UrlError::InvalidCharacter)), | ||||
|             } | ||||
|         }) | ||||
|     ); | ||||
|     let mut b = byte!(stream); | ||||
|     while b == SP { | ||||
|         b = try!(stream.read_byte()); | ||||
|         b = byte!(stream); | ||||
|     } | ||||
|  | ||||
|     let mut s = String::new(); | ||||
|     if b == STAR { | ||||
|         try!(expect(stream.read_byte(), SP)); | ||||
|         try!(expect(byte!(stream), SP)); | ||||
|         return Ok(Star) | ||||
|     } else { | ||||
|         s.push(b as char); | ||||
|         loop { | ||||
|             match try!(stream.read_byte()) { | ||||
|             match byte!(stream) { | ||||
|                 SP => { | ||||
|                     break; | ||||
|                 }, | ||||
| @@ -448,32 +485,37 @@ pub fn read_uri<R: Reader>(stream: &mut R) -> HttpResult<uri::RequestUri> { | ||||
|  | ||||
|  | ||||
| /// Read the `HttpVersion` from a raw stream, such as `HTTP/1.1`. | ||||
| pub fn read_http_version<R: Reader>(stream: &mut R) -> HttpResult<HttpVersion> { | ||||
|     try!(expect(stream.read_byte(), b'H')); | ||||
|     try!(expect(stream.read_byte(), b'T')); | ||||
|     try!(expect(stream.read_byte(), b'T')); | ||||
|     try!(expect(stream.read_byte(), b'P')); | ||||
|     try!(expect(stream.read_byte(), b'/')); | ||||
| pub fn read_http_version<R: Read>(stream: &mut R) -> HttpResult<HttpVersion> { | ||||
|     macro_rules! byte ( | ||||
|         ($rdr:ident) => ({ | ||||
|             let mut buf = [0]; | ||||
|             match try!($rdr.read(&mut buf)) { | ||||
|                 1 => buf[0], | ||||
|                 _ => return Err(HttpVersionError), | ||||
|             } | ||||
|         }) | ||||
|     ); | ||||
|  | ||||
|     match try!(stream.read_byte()) { | ||||
|     try!(expect(byte!(stream), b'H')); | ||||
|     try!(expect(byte!(stream), b'T')); | ||||
|     try!(expect(byte!(stream), b'T')); | ||||
|     try!(expect(byte!(stream), b'P')); | ||||
|     try!(expect(byte!(stream), b'/')); | ||||
|  | ||||
|     match byte!(stream) { | ||||
|         b'0' => { | ||||
|             try!(expect(stream.read_byte(), b'.')); | ||||
|             try!(expect(stream.read_byte(), b'9')); | ||||
|             try!(expect(byte!(stream), b'.')); | ||||
|             try!(expect(byte!(stream), b'9')); | ||||
|             Ok(Http09) | ||||
|         }, | ||||
|         b'1' => { | ||||
|             try!(expect(stream.read_byte(), b'.')); | ||||
|             match try!(stream.read_byte()) { | ||||
|             try!(expect(byte!(stream), b'.')); | ||||
|             match byte!(stream) { | ||||
|                 b'0' => Ok(Http10), | ||||
|                 b'1' => Ok(Http11), | ||||
|                 _ => Err(HttpVersionError) | ||||
|             } | ||||
|         }, | ||||
|         b'2' => { | ||||
|             try!(expect(stream.read_byte(), b'.')); | ||||
|             try!(expect(stream.read_byte(), b'0')); | ||||
|             Ok(Http20) | ||||
|         }, | ||||
|         _ => Err(HttpVersionError) | ||||
|     } | ||||
| } | ||||
| @@ -507,14 +549,24 @@ pub type RawHeaderLine = (String, Vec<u8>); | ||||
| /// >                ; obsolete line folding | ||||
| /// >                ; see Section 3.2.4 | ||||
| /// > ``` | ||||
| pub fn read_header<R: Reader>(stream: &mut R) -> HttpResult<Option<RawHeaderLine>> { | ||||
| pub fn read_header<R: Read>(stream: &mut R) -> HttpResult<Option<RawHeaderLine>> { | ||||
|     macro_rules! byte ( | ||||
|         ($rdr:ident) => ({ | ||||
|             let mut buf = [0]; | ||||
|             match try!($rdr.read(&mut buf)) { | ||||
|                 1 => buf[0], | ||||
|                 _ => return Err(HttpHeaderError), | ||||
|             } | ||||
|         }) | ||||
|     ); | ||||
|  | ||||
|     let mut name = String::new(); | ||||
|     let mut value = vec![]; | ||||
|  | ||||
|     loop { | ||||
|         match try!(stream.read_byte()) { | ||||
|         match byte!(stream) { | ||||
|             CR if name.len() == 0 => { | ||||
|                 match try!(stream.read_byte()) { | ||||
|                 match byte!(stream) { | ||||
|                     LF => return Ok(None), | ||||
|                     _ => return Err(HttpHeaderError) | ||||
|                 } | ||||
| @@ -534,7 +586,7 @@ pub fn read_header<R: Reader>(stream: &mut R) -> HttpResult<Option<RawHeaderLine | ||||
|  | ||||
|     todo!("handle obs-folding (gross!)"); | ||||
|     loop { | ||||
|         match try!(stream.read_byte()) { | ||||
|         match byte!(stream) { | ||||
|             CR => break, | ||||
|             LF => return Err(HttpHeaderError), | ||||
|             b' ' if ows => {}, | ||||
| @@ -549,7 +601,7 @@ pub fn read_header<R: Reader>(stream: &mut R) -> HttpResult<Option<RawHeaderLine | ||||
|     let real_len = value.len() - value.iter().rev().take_while(|&&x| b' ' == x).count(); | ||||
|     value.truncate(real_len); | ||||
|  | ||||
|     match try!(stream.read_byte()) { | ||||
|     match byte!(stream) { | ||||
|         LF => Ok(Some((name, value))), | ||||
|         _ => Err(HttpHeaderError) | ||||
|     } | ||||
| @@ -560,7 +612,17 @@ pub fn read_header<R: Reader>(stream: &mut R) -> HttpResult<Option<RawHeaderLine | ||||
| pub type RequestLine = (method::Method, uri::RequestUri, HttpVersion); | ||||
|  | ||||
| /// Read the `RequestLine`, such as `GET / HTTP/1.1`. | ||||
| pub fn read_request_line<R: Reader>(stream: &mut R) -> HttpResult<RequestLine> { | ||||
| pub fn read_request_line<R: Read>(stream: &mut R) -> HttpResult<RequestLine> { | ||||
|     macro_rules! byte ( | ||||
|         ($rdr:ident) => ({ | ||||
|             let mut buf = [0]; | ||||
|             match try!($rdr.read(&mut buf)) { | ||||
|                 1 => buf[0], | ||||
|                 _ => return Err(HttpVersionError), | ||||
|             } | ||||
|         }) | ||||
|     ); | ||||
|  | ||||
|     debug!("read request line"); | ||||
|     let method = try!(read_method(stream)); | ||||
|     debug!("method = {:?}", method); | ||||
| @@ -569,10 +631,10 @@ pub fn read_request_line<R: Reader>(stream: &mut R) -> HttpResult<RequestLine> { | ||||
|     let version = try!(read_http_version(stream)); | ||||
|     debug!("version = {:?}", version); | ||||
|  | ||||
|     if try!(stream.read_byte()) != CR { | ||||
|     if byte!(stream) != CR { | ||||
|         return Err(HttpVersionError); | ||||
|     } | ||||
|     if try!(stream.read_byte()) != LF { | ||||
|     if byte!(stream) != LF { | ||||
|         return Err(HttpVersionError); | ||||
|     } | ||||
|  | ||||
| @@ -606,9 +668,19 @@ impl Clone for RawStatus { | ||||
| /// > status-code    = 3DIGIT | ||||
| /// > reason-phrase  = *( HTAB / SP / VCHAR / obs-text ) | ||||
| /// >``` | ||||
| pub fn read_status_line<R: Reader>(stream: &mut R) -> HttpResult<StatusLine> { | ||||
| pub fn read_status_line<R: Read>(stream: &mut R) -> HttpResult<StatusLine> { | ||||
|     macro_rules! byte ( | ||||
|         ($rdr:ident) => ({ | ||||
|             let mut buf = [0]; | ||||
|             match try!($rdr.read(&mut buf)) { | ||||
|                 1 => buf[0], | ||||
|                 _ => return Err(HttpVersionError), | ||||
|             } | ||||
|         }) | ||||
|     ); | ||||
|  | ||||
|     let version = try!(read_http_version(stream)); | ||||
|     if try!(stream.read_byte()) != SP { | ||||
|     if byte!(stream) != SP { | ||||
|         return Err(HttpVersionError); | ||||
|     } | ||||
|     let code = try!(read_status(stream)); | ||||
| @@ -617,11 +689,21 @@ pub fn read_status_line<R: Reader>(stream: &mut R) -> HttpResult<StatusLine> { | ||||
| } | ||||
|  | ||||
| /// Read the StatusCode from a stream. | ||||
| pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<RawStatus> { | ||||
| pub fn read_status<R: Read>(stream: &mut R) -> HttpResult<RawStatus> { | ||||
|     macro_rules! byte ( | ||||
|         ($rdr:ident) => ({ | ||||
|             let mut buf = [0]; | ||||
|             match try!($rdr.read(&mut buf)) { | ||||
|                 1 => buf[0], | ||||
|                 _ => return Err(HttpStatusError), | ||||
|             } | ||||
|         }) | ||||
|     ); | ||||
|  | ||||
|     let code = [ | ||||
|         try!(stream.read_byte()), | ||||
|         try!(stream.read_byte()), | ||||
|         try!(stream.read_byte()), | ||||
|         byte!(stream), | ||||
|         byte!(stream), | ||||
|         byte!(stream), | ||||
|     ]; | ||||
|  | ||||
|     let code = match str::from_utf8(code.as_slice()).ok().and_then(|x| x.parse().ok()) { | ||||
| @@ -629,27 +711,25 @@ pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<RawStatus> { | ||||
|         None => return Err(HttpStatusError) | ||||
|     }; | ||||
|  | ||||
|     match try!(stream.read_byte()) { | ||||
|     match byte!(stream) { | ||||
|         b' ' => (), | ||||
|         _ => return Err(HttpStatusError) | ||||
|     } | ||||
|  | ||||
|     let mut buf = [b' '; 32]; | ||||
|  | ||||
|     let mut buf = [SP; 32]; | ||||
|     let mut cursor = Cursor::new(&mut buf[..]); | ||||
|     { | ||||
|         let mut bufwrt = BufWriter::new(&mut buf); | ||||
|         'read: loop { | ||||
|             match try!(stream.read_byte()) { | ||||
|                 CR => match try!(stream.read_byte()) { | ||||
|             match byte!(stream) { | ||||
|                 CR => match byte!(stream) { | ||||
|                     LF => break, | ||||
|                     _ => return Err(HttpStatusError) | ||||
|                 }, | ||||
|                 b => match bufwrt.write_u8(b) { | ||||
|                     Ok(_) => (), | ||||
|                     Err(_) => { | ||||
|                 b => match cursor.write(&[b]) { | ||||
|                     Ok(0) | Err(_) => { | ||||
|                         for _ in 0u8..128 { | ||||
|                             match try!(stream.read_byte()) { | ||||
|                                 CR => match try!(stream.read_byte()) { | ||||
|                             match byte!(stream) { | ||||
|                                 CR => match byte!(stream) { | ||||
|                                     LF => break 'read, | ||||
|                                     _ => return Err(HttpStatusError) | ||||
|                                 }, | ||||
| @@ -658,12 +738,13 @@ pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<RawStatus> { | ||||
|                         } | ||||
|                         return Err(HttpStatusError) | ||||
|                     } | ||||
|                     Ok(_) => (), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let reason = match str::from_utf8(&buf[..]) { | ||||
|     let reason = match str::from_utf8(cursor.into_inner()) { | ||||
|         Ok(s) => s.trim(), | ||||
|         Err(_) => return Err(HttpStatusError) | ||||
|     }; | ||||
| @@ -686,39 +767,34 @@ pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<RawStatus> { | ||||
| } | ||||
|  | ||||
| #[inline] | ||||
| fn expect(r: IoResult<u8>, expected: u8) -> HttpResult<()> { | ||||
|     match r { | ||||
|         Ok(b) if b == expected => Ok(()), | ||||
|         Ok(_) => Err(HttpVersionError), | ||||
|         Err(e) => Err(HttpIoError(e)) | ||||
| fn expect(actual: u8, expected: u8) -> HttpResult<()> { | ||||
|     if actual == expected { | ||||
|         Ok(()) | ||||
|     } else { | ||||
|         Err(HttpVersionError) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::old_io::{self, MemReader, MemWriter, IoResult}; | ||||
|     use std::io::{self, Write}; | ||||
|     use std::borrow::Cow::{Borrowed, Owned}; | ||||
|     use test::Bencher; | ||||
|     use uri::RequestUri; | ||||
|     use uri::RequestUri::{Star, AbsoluteUri, AbsolutePath, Authority}; | ||||
|     use method; | ||||
|     use version::HttpVersion; | ||||
|     use version::HttpVersion::{Http10, Http11, Http20}; | ||||
|     use version::HttpVersion::{Http10, Http11}; | ||||
|     use HttpError::{HttpVersionError, HttpMethodError}; | ||||
|     use HttpResult; | ||||
|     use url::Url; | ||||
|  | ||||
|     use super::{read_method, read_uri, read_http_version, read_header, | ||||
|                 RawHeaderLine, read_status, RawStatus, read_chunk_size}; | ||||
|  | ||||
|     fn mem(s: &str) -> MemReader { | ||||
|         MemReader::new(s.as_bytes().to_vec()) | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_method() { | ||||
|         fn read(s: &str, result: HttpResult<method::Method>) { | ||||
|             assert_eq!(read_method(&mut mem(s)), result); | ||||
|             assert_eq!(read_method(&mut s.as_bytes()), result); | ||||
|         } | ||||
|  | ||||
|         read("GET /", Ok(method::Method::Get)); | ||||
| @@ -737,7 +813,7 @@ mod tests { | ||||
|     #[test] | ||||
|     fn test_read_uri() { | ||||
|         fn read(s: &str, result: HttpResult<RequestUri>) { | ||||
|             assert_eq!(read_uri(&mut mem(s)), result); | ||||
|             assert_eq!(read_uri(&mut s.as_bytes()), result); | ||||
|         } | ||||
|  | ||||
|         read("* ", Ok(Star)); | ||||
| @@ -749,12 +825,11 @@ mod tests { | ||||
|     #[test] | ||||
|     fn test_read_http_version() { | ||||
|         fn read(s: &str, result: HttpResult<HttpVersion>) { | ||||
|             assert_eq!(read_http_version(&mut mem(s)), result); | ||||
|             assert_eq!(read_http_version(&mut s.as_bytes()), result); | ||||
|         } | ||||
|  | ||||
|         read("HTTP/1.0", Ok(Http10)); | ||||
|         read("HTTP/1.1", Ok(Http11)); | ||||
|         read("HTTP/2.0", Ok(Http20)); | ||||
|         read("HTP/2.0", Err(HttpVersionError)); | ||||
|         read("HTTP.2.0", Err(HttpVersionError)); | ||||
|         read("HTTP 2.0", Err(HttpVersionError)); | ||||
| @@ -764,11 +839,11 @@ mod tests { | ||||
|     #[test] | ||||
|     fn test_read_status() { | ||||
|         fn read(s: &str, result: HttpResult<RawStatus>) { | ||||
|             assert_eq!(read_status(&mut mem(s)), result); | ||||
|             assert_eq!(read_status(&mut s.as_bytes()), result); | ||||
|         } | ||||
|  | ||||
|         fn read_ignore_string(s: &str, result: HttpResult<RawStatus>) { | ||||
|             match (read_status(&mut mem(s)), result) { | ||||
|             match (read_status(&mut s.as_bytes()), result) { | ||||
|                 (Ok(RawStatus(ref c1, _)), Ok(RawStatus(ref c2, _))) => { | ||||
|                     assert_eq!(c1, c2); | ||||
|                 }, | ||||
| @@ -788,7 +863,7 @@ mod tests { | ||||
|     #[test] | ||||
|     fn test_read_header() { | ||||
|         fn read(s: &str, result: HttpResult<Option<RawHeaderLine>>) { | ||||
|             assert_eq!(read_header(&mut mem(s)), result); | ||||
|             assert_eq!(read_header(&mut s.as_bytes()), result); | ||||
|         } | ||||
|  | ||||
|         read("Host: rust-lang.org\r\n", Ok(Some(("Host".to_string(), | ||||
| @@ -798,10 +873,10 @@ mod tests { | ||||
|     #[test] | ||||
|     fn test_write_chunked() { | ||||
|         use std::str::from_utf8; | ||||
|         let mut w = super::HttpWriter::ChunkedWriter(MemWriter::new()); | ||||
|         let mut w = super::HttpWriter::ChunkedWriter(Vec::new()); | ||||
|         w.write_all(b"foo bar").unwrap(); | ||||
|         w.write_all(b"baz quux herp").unwrap(); | ||||
|         let buf = w.end().unwrap().into_inner(); | ||||
|         let buf = w.end().unwrap(); | ||||
|         let s = from_utf8(buf.as_slice()).unwrap(); | ||||
|         assert_eq!(s, "7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n"); | ||||
|     } | ||||
| @@ -809,19 +884,23 @@ mod tests { | ||||
|     #[test] | ||||
|     fn test_write_sized() { | ||||
|         use std::str::from_utf8; | ||||
|         let mut w = super::HttpWriter::SizedWriter(MemWriter::new(), 8); | ||||
|         let mut w = super::HttpWriter::SizedWriter(Vec::new(), 8); | ||||
|         w.write_all(b"foo bar").unwrap(); | ||||
|         assert_eq!(w.write_all(b"baz"), Err(old_io::standard_error(old_io::ShortWrite(1)))); | ||||
|         assert_eq!(w.write(b"baz"), Ok(1)); | ||||
|  | ||||
|         let buf = w.end().unwrap().into_inner(); | ||||
|         let buf = w.end().unwrap(); | ||||
|         let s = from_utf8(buf.as_slice()).unwrap(); | ||||
|         assert_eq!(s, "foo barb"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_chunk_size() { | ||||
|         fn read(s: &str, result: IoResult<u64>) { | ||||
|             assert_eq!(read_chunk_size(&mut mem(s)), result); | ||||
|         fn read(s: &str, result: io::Result<u64>) { | ||||
|             assert_eq!(read_chunk_size(&mut s.as_bytes()), 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", Ok(1)); | ||||
| @@ -833,13 +912,13 @@ mod tests { | ||||
|         read("Ff\r\n", Ok(255)); | ||||
|         read("Ff   \r\n", Ok(255)); | ||||
|         // Missing LF or CRLF | ||||
|         read("F\rF", Err(old_io::standard_error(old_io::InvalidInput))); | ||||
|         read("F", Err(old_io::standard_error(old_io::EndOfFile))); | ||||
|         read_err("F\rF"); | ||||
|         read_err("F"); | ||||
|         // Invalid hex digit | ||||
|         read("X\r\n", Err(old_io::standard_error(old_io::InvalidInput))); | ||||
|         read("1X\r\n", Err(old_io::standard_error(old_io::InvalidInput))); | ||||
|         read("-\r\n", Err(old_io::standard_error(old_io::InvalidInput))); | ||||
|         read("-1\r\n", Err(old_io::standard_error(old_io::InvalidInput))); | ||||
|         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", Ok(1)); | ||||
|         read("a;ext name=value\r\n", Ok(10)); | ||||
| @@ -850,21 +929,21 @@ mod tests { | ||||
|         read("3   ;\r\n", Ok(3)); | ||||
|         read("3   ;   \r\n", Ok(3)); | ||||
|         // Invalid extensions cause an error | ||||
|         read("1 invalid extension\r\n", Err(old_io::standard_error(old_io::InvalidInput))); | ||||
|         read("1 A\r\n", Err(old_io::standard_error(old_io::InvalidInput))); | ||||
|         read("1;no CRLF", Err(old_io::standard_error(old_io::EndOfFile))); | ||||
|         read_err("1 invalid extension\r\n"); | ||||
|         read_err("1 A\r\n"); | ||||
|         read_err("1;no CRLF"); | ||||
|     } | ||||
|  | ||||
|     #[bench] | ||||
|     fn bench_read_method(b: &mut Bencher) { | ||||
|         b.bytes = b"CONNECT ".len() as u64; | ||||
|         b.iter(|| assert_eq!(read_method(&mut mem("CONNECT ")), Ok(method::Method::Connect))); | ||||
|         b.iter(|| assert_eq!(read_method(&mut b"CONNECT "), Ok(method::Method::Connect))); | ||||
|     } | ||||
|  | ||||
|     #[bench] | ||||
|     fn bench_read_status(b: &mut Bencher) { | ||||
|         b.bytes = b"404 Not Found\r\n".len() as u64; | ||||
|         b.iter(|| assert_eq!(read_status(&mut mem("404 Not Found\r\n")), Ok(RawStatus(404, Borrowed("Not Found"))))); | ||||
|         b.iter(|| assert_eq!(read_status(&mut b"404 Not Found\r\n"), Ok(RawStatus(404, Borrowed("Not Found"))))); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										10
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| #![feature(core, collections, io, old_io, os, old_path, | ||||
| #![feature(core, collections, io, net, os, path, | ||||
|            std_misc, box_syntax, unsafe_destructor)] | ||||
| #![deny(missing_docs)] | ||||
| #![cfg_attr(test, deny(missing_docs))] | ||||
| #![cfg_attr(test, deny(warnings))] | ||||
| #![cfg_attr(test, feature(alloc, test))] | ||||
|  | ||||
| @@ -140,7 +140,7 @@ extern crate log; | ||||
| #[cfg(test)] | ||||
| extern crate test; | ||||
|  | ||||
| pub use std::old_io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr, Port}; | ||||
|  | ||||
| pub use mimewrapper::mime; | ||||
| pub use url::Url; | ||||
| pub use client::Client; | ||||
| @@ -150,7 +150,7 @@ pub use server::Server; | ||||
|  | ||||
| use std::error::{Error, FromError}; | ||||
| use std::fmt; | ||||
| use std::old_io::IoError; | ||||
| use std::io::Error as IoError; | ||||
|  | ||||
| use self::HttpError::{HttpMethodError, HttpUriError, HttpVersionError, | ||||
|                       HttpHeaderError, HttpStatusError, HttpIoError}; | ||||
| @@ -164,7 +164,7 @@ macro_rules! todo( | ||||
| macro_rules! inspect( | ||||
|     ($name:expr, $value:expr) => ({ | ||||
|         let v = $value; | ||||
|         debug!("inspect: {:?} = {:?}", $name, v); | ||||
|         trace!("inspect: {:?} = {:?}", $name, v); | ||||
|         v | ||||
|     }) | ||||
| ); | ||||
|   | ||||
							
								
								
									
										61
									
								
								src/mock.rs
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								src/mock.rs
									
									
									
									
									
								
							| @@ -1,67 +1,69 @@ | ||||
| use std::fmt; | ||||
| use std::old_io::{IoResult, MemReader, MemWriter}; | ||||
| use std::old_io::net::ip::SocketAddr; | ||||
| use std::io::{self, Read, Write, Cursor}; | ||||
| use std::net::SocketAddr; | ||||
|  | ||||
| use net::{NetworkStream, NetworkConnector}; | ||||
|  | ||||
| pub struct MockStream { | ||||
|     pub read: MemReader, | ||||
|     pub write: MemWriter, | ||||
|     pub read: Cursor<Vec<u8>>, | ||||
|     pub write: Vec<u8>, | ||||
| } | ||||
|  | ||||
| impl Clone for MockStream { | ||||
|     fn clone(&self) -> MockStream { | ||||
|         MockStream { | ||||
|             read: MemReader::new(self.read.get_ref().to_vec()), | ||||
|             write: MemWriter::from_vec(self.write.get_ref().to_vec()), | ||||
|             read: Cursor::new(self.read.get_ref().clone()), | ||||
|             write: self.write.clone() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl PartialEq for MockStream { | ||||
|     fn eq(&self, other: &MockStream) -> bool { | ||||
|         self.read.get_ref() == other.read.get_ref() && | ||||
|             self.write.get_ref() == other.write.get_ref() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for MockStream { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         write!(f, "MockStream {{ read: {:?}, write: {:?} }}", | ||||
|                self.read.get_ref(), self.write.get_ref()) | ||||
|         write!(f, "MockStream {{ read: {:?}, write: {:?} }}", self.read.get_ref(), self.write) | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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 { | ||||
|             read: MemReader::new(vec![]), | ||||
|             write: MemWriter::new(), | ||||
|             read: Cursor::new(vec![]), | ||||
|             write: vec![], | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn with_input(input: &[u8]) -> MockStream { | ||||
|         MockStream { | ||||
|             read: MemReader::new(input.to_vec()), | ||||
|             write: MemWriter::new(), | ||||
|             read: Cursor::new(input.to_vec()), | ||||
|             write: vec![] | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl Reader for MockStream { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||||
|  | ||||
| impl Read for MockStream { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         self.read.read(buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Writer for MockStream { | ||||
|     fn write_all(&mut self, msg: &[u8]) -> IoResult<()> { | ||||
|         self.write.write_all(msg) | ||||
| impl Write for MockStream { | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         Write::write(&mut self.write, msg) | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl NetworkStream for MockStream { | ||||
|     fn peer_name(&mut self) -> IoResult<SocketAddr> { | ||||
|     fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         Ok("127.0.0.1:1337".parse().unwrap()) | ||||
|     } | ||||
| } | ||||
| @@ -71,7 +73,7 @@ pub struct MockConnector; | ||||
| impl NetworkConnector for MockConnector { | ||||
|     type Stream = MockStream; | ||||
|  | ||||
|     fn connect(&mut self, _host: &str, _port: u16, _scheme: &str) -> IoResult<MockStream> { | ||||
|     fn connect(&mut self, _host: &str, _port: u16, _scheme: &str) -> io::Result<MockStream> { | ||||
|         Ok(MockStream::new()) | ||||
|     } | ||||
| } | ||||
| @@ -86,8 +88,9 @@ macro_rules! mock_connector ( | ||||
|  | ||||
|         impl ::net::NetworkConnector for $name { | ||||
|             type Stream = ::mock::MockStream; | ||||
|             fn connect(&mut self, host: &str, port: u16, scheme: &str) -> ::std::old_io::IoResult<::mock::MockStream> { | ||||
|             fn connect(&mut self, host: &str, port: u16, scheme: &str) -> ::std::io::Result<::mock::MockStream> { | ||||
|                 use std::collections::HashMap; | ||||
|                 use std::io::Cursor; | ||||
|                 debug!("MockStream::connect({:?}, {:?}, {:?})", host, port, scheme); | ||||
|                 let mut map = HashMap::new(); | ||||
|                 $(map.insert($url, $res);)* | ||||
| @@ -97,8 +100,8 @@ macro_rules! mock_connector ( | ||||
|                 // ignore port for now | ||||
|                 match map.get(&*key) { | ||||
|                     Some(res) => Ok(::mock::MockStream { | ||||
|                         write: ::std::old_io::MemWriter::new(), | ||||
|                         read: ::std::old_io::MemReader::new(res.to_string().into_bytes()) | ||||
|                         write: vec![], | ||||
|                         read: Cursor::new(res.to_string().into_bytes()), | ||||
|                     }), | ||||
|                     None => panic!("{:?} doesn't know url {}", stringify!($name), key) | ||||
|                 } | ||||
|   | ||||
							
								
								
									
										229
									
								
								src/net.rs
									
									
									
									
									
								
							
							
						
						
									
										229
									
								
								src/net.rs
									
									
									
									
									
								
							| @@ -1,11 +1,10 @@ | ||||
| //! A collection of traits abstracting over Listeners and Streams. | ||||
| use std::any::{Any, TypeId}; | ||||
| use std::fmt; | ||||
| use std::old_io::{IoResult, IoError, ConnectionAborted, InvalidInput, OtherIoError, | ||||
|               Stream, Listener, Acceptor}; | ||||
| use std::old_io::net::ip::{SocketAddr, ToSocketAddr, Port}; | ||||
| use std::old_io::net::tcp::{TcpStream, TcpListener, TcpAcceptor}; | ||||
| use std::io::{self, Read, Write}; | ||||
| use std::net::{SocketAddr, ToSocketAddrs, TcpStream, TcpListener}; | ||||
| use std::mem; | ||||
| use std::path::Path; | ||||
| use std::raw::{self, TraitObject}; | ||||
| use std::sync::Arc; | ||||
|  | ||||
| @@ -24,34 +23,26 @@ macro_rules! try_some { | ||||
| } | ||||
|  | ||||
| /// The write-status indicating headers have not been written. | ||||
| #[allow(missing_copy_implementations)] | ||||
| pub struct Fresh; | ||||
| pub enum Fresh {} | ||||
|  | ||||
| /// The write-status indicating headers have been written. | ||||
| #[allow(missing_copy_implementations)] | ||||
| pub struct Streaming; | ||||
| pub enum Streaming {} | ||||
|  | ||||
| /// An abstraction to listen for connections on a certain port. | ||||
| pub trait NetworkListener { | ||||
|     /// Type of Acceptor | ||||
|     type Acceptor: NetworkAcceptor; | ||||
|     /// Listens on a socket. | ||||
|     fn listen<To: ToSocketAddr>(&mut self, addr: To) -> IoResult<Self::Acceptor>; | ||||
| } | ||||
|  | ||||
| /// An abstraction to receive `NetworkStream`s. | ||||
| pub trait NetworkAcceptor: Clone + Send { | ||||
|     /// Type of Stream to receive | ||||
| pub trait NetworkListener: Clone { | ||||
|     /// The stream produced for each connection. | ||||
|     type Stream: NetworkStream + Send + Clone; | ||||
|     /// Listens on a socket. | ||||
|     //fn listen<To: ToSocketAddrs>(&mut self, addr: To) -> io::Result<Self::Acceptor>; | ||||
|  | ||||
|     /// Returns an iterator of streams. | ||||
|     fn accept(&mut self) -> IoResult<Self::Stream>; | ||||
|     fn accept(&mut self) -> io::Result<Self::Stream>; | ||||
|  | ||||
|     /// Get the address this Listener ended up listening on. | ||||
|     fn socket_name(&self) -> IoResult<SocketAddr>; | ||||
|     fn socket_addr(&mut self) -> io::Result<SocketAddr>; | ||||
|  | ||||
|     /// Closes the Acceptor, so no more incoming connections will be handled. | ||||
|     fn close(&mut self) -> IoResult<()>; | ||||
| //    fn close(&mut self) -> io::Result<()>; | ||||
|  | ||||
|     /// Returns an iterator over incoming connections. | ||||
|     fn incoming(&mut self) -> NetworkConnections<Self> { | ||||
| @@ -60,20 +51,20 @@ pub trait NetworkAcceptor: Clone + Send { | ||||
| } | ||||
|  | ||||
| /// An iterator wrapper over a NetworkAcceptor. | ||||
| pub struct NetworkConnections<'a, N: NetworkAcceptor + 'a>(&'a mut N); | ||||
| pub struct NetworkConnections<'a, N: NetworkListener + 'a>(&'a mut N); | ||||
|  | ||||
| impl<'a, N: NetworkAcceptor> Iterator for NetworkConnections<'a, N> { | ||||
|     type Item = IoResult<N::Stream>; | ||||
|     fn next(&mut self) -> Option<IoResult<N::Stream>> { | ||||
| impl<'a, N: NetworkListener + 'a> Iterator for NetworkConnections<'a, N> { | ||||
|     type Item = io::Result<N::Stream>; | ||||
|     fn next(&mut self) -> Option<io::Result<N::Stream>> { | ||||
|         Some(self.0.accept()) | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| /// An abstraction over streams that a Server can utilize. | ||||
| pub trait NetworkStream: Stream + Any + StreamClone + Send { | ||||
| pub trait NetworkStream: Read + Write + Any + StreamClone + Send { | ||||
|     /// Get the remote address of the underlying connection. | ||||
|     fn peer_name(&mut self) -> IoResult<SocketAddr>; | ||||
|     fn peer_addr(&mut self) -> io::Result<SocketAddr>; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -94,7 +85,7 @@ pub trait NetworkConnector { | ||||
|     /// Type of Stream to create | ||||
|     type Stream: NetworkStream + Send; | ||||
|     /// Connect to a remote address. | ||||
|     fn connect(&mut self, host: &str, port: Port, scheme: &str) -> IoResult<Self::Stream>; | ||||
|     fn connect(&mut self, host: &str, port: u16, scheme: &str) -> io::Result<Self::Stream>; | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Box<NetworkStream + Send> { | ||||
| @@ -108,32 +99,6 @@ impl Clone for Box<NetworkStream + Send> { | ||||
|     fn clone(&self) -> Box<NetworkStream + Send> { self.clone_box() } | ||||
| } | ||||
|  | ||||
| impl Reader for Box<NetworkStream + Send> { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { (**self).read(buf) } | ||||
| } | ||||
|  | ||||
| impl Writer for Box<NetworkStream + Send> { | ||||
|     #[inline] | ||||
|     fn write_all(&mut self, msg: &[u8]) -> IoResult<()> { (**self).write_all(msg) } | ||||
|  | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> IoResult<()> { (**self).flush() } | ||||
| } | ||||
|  | ||||
| impl<'a> Reader for &'a mut NetworkStream { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { (**self).read(buf) } | ||||
| } | ||||
|  | ||||
| impl<'a> Writer for &'a mut NetworkStream { | ||||
|     #[inline] | ||||
|     fn write_all(&mut self, msg: &[u8]) -> IoResult<()> { (**self).write_all(msg) } | ||||
|  | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> IoResult<()> { (**self).flush() } | ||||
| } | ||||
|  | ||||
| impl UnsafeAnyExt for NetworkStream { | ||||
|     unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T { | ||||
|         mem::transmute(mem::transmute::<&NetworkStream, | ||||
| @@ -191,24 +156,31 @@ impl NetworkStream { | ||||
| } | ||||
|  | ||||
| /// A `NetworkListener` for `HttpStream`s. | ||||
| #[allow(missing_copy_implementations)] | ||||
| pub enum HttpListener { | ||||
|     /// Http variant. | ||||
|     Http, | ||||
|     Http(TcpListener), | ||||
|     /// Https variant. The two paths point to the certificate and key PEM files, in that order. | ||||
|     Https(Path, Path), | ||||
|     Https(TcpListener, Arc<SslContext>) | ||||
| } | ||||
|  | ||||
| impl NetworkListener for HttpListener { | ||||
|     type Acceptor = HttpAcceptor; | ||||
| impl Clone for HttpListener { | ||||
|     fn clone(&self) -> HttpListener { | ||||
|         match *self { | ||||
|             HttpListener::Http(ref tcp) => HttpListener::Http(tcp.try_clone().unwrap()), | ||||
|             HttpListener::Https(ref tcp, ref ssl) => HttpListener::Https(tcp.try_clone().unwrap(), ssl.clone()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     #[inline] | ||||
|     fn listen<To: ToSocketAddr>(&mut self, addr: To) -> IoResult<HttpAcceptor> { | ||||
|         let mut tcp = try!(TcpListener::bind(addr)); | ||||
|         let addr = try!(tcp.socket_name()); | ||||
|         Ok(match *self { | ||||
|             HttpListener::Http => HttpAcceptor::Http(try!(tcp.listen()), addr), | ||||
|             HttpListener::Https(ref cert, ref key) => { | ||||
| impl HttpListener { | ||||
|  | ||||
|     /// Start listening to an address over HTTP. | ||||
|     pub fn http<To: ToSocketAddrs>(addr: &To) -> io::Result<HttpListener> { | ||||
|         Ok(HttpListener::Http(try!(TcpListener::bind(addr)))) | ||||
|     } | ||||
|  | ||||
|     /// Start listening to an address over HTTPS. | ||||
|     pub fn https<To: ToSocketAddrs>(addr: &To, cert: &Path, key: &Path) -> io::Result<HttpListener> { | ||||
|         let mut ssl_context = try!(SslContext::new(Sslv23).map_err(lift_ssl_error)); | ||||
|         try_some!(ssl_context.set_cipher_list("DEFAULT").map(lift_ssl_error)); | ||||
|         try_some!(ssl_context.set_certificate_file( | ||||
| @@ -216,38 +188,25 @@ impl NetworkListener for HttpListener { | ||||
|         try_some!(ssl_context.set_private_key_file( | ||||
|                 key, X509FileType::PEM).map(lift_ssl_error)); | ||||
|         ssl_context.set_verify(SslVerifyNone, None); | ||||
|                 HttpAcceptor::Https(try!(tcp.listen()), addr, Arc::new(ssl_context)) | ||||
|             } | ||||
|         }) | ||||
|         Ok(HttpListener::Https(try!(TcpListener::bind(addr)), Arc::new(ssl_context))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A `NetworkAcceptor` for `HttpStream`s. | ||||
| #[derive(Clone)] | ||||
| pub enum HttpAcceptor { | ||||
|     /// Http variant. | ||||
|     Http(TcpAcceptor, SocketAddr), | ||||
|     /// Https variant. | ||||
|     Https(TcpAcceptor, SocketAddr, Arc<SslContext>), | ||||
| } | ||||
|  | ||||
| impl NetworkAcceptor for HttpAcceptor { | ||||
| impl NetworkListener for HttpListener { | ||||
|     type Stream = HttpStream; | ||||
|  | ||||
|     #[inline] | ||||
|     fn accept(&mut self) -> IoResult<HttpStream> { | ||||
|     fn accept(&mut self) -> io::Result<HttpStream> { | ||||
|         Ok(match *self { | ||||
|             HttpAcceptor::Http(ref mut tcp, _) => HttpStream::Http(try!(tcp.accept())), | ||||
|             HttpAcceptor::Https(ref mut tcp, _, ref ssl_context) => { | ||||
|                 let stream = try!(tcp.accept()); | ||||
|                 match SslStream::<TcpStream>::new_server(&**ssl_context, stream) { | ||||
|             HttpListener::Http(ref mut tcp) => HttpStream::Http(CloneTcpStream(try!(tcp.accept()).0)), | ||||
|             HttpListener::Https(ref mut tcp, ref ssl_context) => { | ||||
|                 let stream = CloneTcpStream(try!(tcp.accept()).0); | ||||
|                 match SslStream::new_server(&**ssl_context, stream) { | ||||
|                     Ok(ssl_stream) => HttpStream::Https(ssl_stream), | ||||
|                     Err(StreamError(ref e)) => { | ||||
|                         return Err(IoError { | ||||
|                             kind: ConnectionAborted, | ||||
|                             desc: "SSL Handshake Interrupted", | ||||
|                             detail: Some(e.desc.to_string()) | ||||
|                         }); | ||||
|                         return Err(io::Error::new(io::ErrorKind::ConnectionAborted, | ||||
|                                                 "SSL Handshake Interrupted", | ||||
|                                                 Some(e.to_string()))); | ||||
|                     }, | ||||
|                     Err(e) => return Err(lift_ssl_error(e)) | ||||
|                 } | ||||
| @@ -256,19 +215,39 @@ impl NetworkAcceptor for HttpAcceptor { | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn close(&mut self) -> IoResult<()> { | ||||
|     fn socket_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         match *self { | ||||
|             HttpAcceptor::Http(ref mut tcp, _) => tcp.close_accept(), | ||||
|             HttpAcceptor::Https(ref mut tcp, _, _) => tcp.close_accept(), | ||||
|             HttpListener::Http(ref mut tcp) => tcp.socket_addr(), | ||||
|             HttpListener::Https(ref mut tcp, _) => tcp.socket_addr(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[doc(hidden)] | ||||
| pub struct CloneTcpStream(TcpStream); | ||||
|  | ||||
| impl Clone for CloneTcpStream{ | ||||
|     #[inline] | ||||
|     fn socket_name(&self) -> IoResult<SocketAddr> { | ||||
|         match *self { | ||||
|             HttpAcceptor::Http(_, addr) => Ok(addr), | ||||
|             HttpAcceptor::Https(_, addr, _) => Ok(addr), | ||||
|     fn clone(&self) -> CloneTcpStream { | ||||
|         CloneTcpStream(self.0.try_clone().unwrap()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Read for CloneTcpStream { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         self.0.read(buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Write for CloneTcpStream { | ||||
|     #[inline] | ||||
|     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | ||||
|         self.0.write(buf) | ||||
|     } | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         self.0.flush() | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -276,14 +255,14 @@ impl NetworkAcceptor for HttpAcceptor { | ||||
| #[derive(Clone)] | ||||
| pub enum HttpStream { | ||||
|     /// A stream over the HTTP protocol. | ||||
|     Http(TcpStream), | ||||
|     Http(CloneTcpStream), | ||||
|     /// A stream over the HTTP protocol, protected by SSL. | ||||
|     Https(SslStream<TcpStream>), | ||||
|     Https(SslStream<CloneTcpStream>), | ||||
| } | ||||
|  | ||||
| impl Reader for HttpStream { | ||||
| impl Read for HttpStream { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             HttpStream::Http(ref mut inner) => inner.read(buf), | ||||
|             HttpStream::Https(ref mut inner) => inner.read(buf) | ||||
| @@ -291,16 +270,16 @@ impl Reader for HttpStream { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Writer for HttpStream { | ||||
| impl Write for HttpStream { | ||||
|     #[inline] | ||||
|     fn write_all(&mut self, msg: &[u8]) -> IoResult<()> { | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             HttpStream::Http(ref mut inner) => inner.write_all(msg), | ||||
|             HttpStream::Https(ref mut inner) => inner.write_all(msg) | ||||
|             HttpStream::Http(ref mut inner) => inner.write(msg), | ||||
|             HttpStream::Https(ref mut inner) => inner.write(msg) | ||||
|         } | ||||
|     } | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> IoResult<()> { | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         match *self { | ||||
|             HttpStream::Http(ref mut inner) => inner.flush(), | ||||
|             HttpStream::Https(ref mut inner) => inner.flush(), | ||||
| @@ -309,10 +288,10 @@ impl Writer for HttpStream { | ||||
| } | ||||
|  | ||||
| impl NetworkStream for HttpStream { | ||||
|     fn peer_name(&mut self) -> IoResult<SocketAddr> { | ||||
|     fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         match *self { | ||||
|             HttpStream::Http(ref mut inner) => inner.peer_name(), | ||||
|             HttpStream::Https(ref mut inner) => inner.get_mut().peer_name() | ||||
|             HttpStream::Http(ref mut inner) => inner.0.peer_addr(), | ||||
|             HttpStream::Https(ref mut inner) => inner.get_mut().0.peer_addr() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -327,16 +306,16 @@ pub type ContextVerifier<'v> = Box<FnMut(&mut SslContext) -> ()+'v>; | ||||
| impl<'v> NetworkConnector for HttpConnector<'v> { | ||||
|     type Stream = HttpStream; | ||||
|  | ||||
|     fn connect(&mut self, host: &str, port: Port, scheme: &str) -> IoResult<HttpStream> { | ||||
|         let addr = (host, port); | ||||
|     fn connect(&mut self, host: &str, port: u16, scheme: &str) -> io::Result<HttpStream> { | ||||
|         let addr = &(host, port); | ||||
|         match scheme { | ||||
|             "http" => { | ||||
|                 debug!("http scheme"); | ||||
|                 Ok(HttpStream::Http(try!(TcpStream::connect(addr)))) | ||||
|                 Ok(HttpStream::Http(CloneTcpStream(try!(TcpStream::connect(addr))))) | ||||
|             }, | ||||
|             "https" => { | ||||
|                 debug!("https scheme"); | ||||
|                 let stream = try!(TcpStream::connect(addr)); | ||||
|                 let stream = CloneTcpStream(try!(TcpStream::connect(addr))); | ||||
|                 let mut context = try!(SslContext::new(Sslv23).map_err(lift_ssl_error)); | ||||
|                 if let Some(ref mut verifier) = self.0 { | ||||
|                     verifier(&mut context); | ||||
| @@ -347,32 +326,26 @@ impl<'v> NetworkConnector for HttpConnector<'v> { | ||||
|                 Ok(HttpStream::Https(stream)) | ||||
|             }, | ||||
|             _ => { | ||||
|                 Err(IoError { | ||||
|                     kind: InvalidInput, | ||||
|                     desc: "Invalid scheme for Http", | ||||
|                     detail: None | ||||
|                 }) | ||||
|                 Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
|                                 "Invalid scheme for Http", | ||||
|                                 None)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn lift_ssl_error(ssl: SslError) -> IoError { | ||||
| fn lift_ssl_error(ssl: SslError) -> io::Error { | ||||
|     debug!("lift_ssl_error: {:?}", ssl); | ||||
|     match ssl { | ||||
|         StreamError(err) => err, | ||||
|         SslSessionClosed => IoError { | ||||
|             kind: ConnectionAborted, | ||||
|             desc: "SSL Connection Closed", | ||||
|             detail: None | ||||
|         }, | ||||
|         SslSessionClosed => io::Error::new(io::ErrorKind::ConnectionAborted, | ||||
|                                          "SSL Connection Closed", | ||||
|                                          None), | ||||
|         // Unfortunately throw this away. No way to support this | ||||
|         // detail without a better Error abstraction. | ||||
|         OpenSslErrors(errs) => IoError { | ||||
|             kind: OtherIoError, | ||||
|             desc: "Error in OpenSSL", | ||||
|             detail: Some(format!("{:?}", errs)) | ||||
|         } | ||||
|         OpenSslErrors(errs) => io::Error::new(io::ErrorKind::Other, | ||||
|                                          "Error in OpenSSL", | ||||
|                                          Some(format!("{:?}", errs))) | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,16 +1,16 @@ | ||||
| use std::thread::{self, JoinGuard}; | ||||
| use std::sync::mpsc; | ||||
| use std::collections::VecMap; | ||||
| use net::NetworkAcceptor; | ||||
| use net::NetworkListener; | ||||
| 
 | ||||
| pub struct AcceptorPool<A: NetworkAcceptor> { | ||||
| pub struct ListenerPool<A: NetworkListener> { | ||||
|     acceptor: A | ||||
| } | ||||
| 
 | ||||
| impl<'a, A: NetworkAcceptor + 'a> AcceptorPool<A> { | ||||
| impl<'a, A: NetworkListener + Send + 'a> ListenerPool<A> { | ||||
|     /// Create a thread pool to manage the acceptor.
 | ||||
|     pub fn new(acceptor: A) -> AcceptorPool<A> { | ||||
|         AcceptorPool { acceptor: acceptor } | ||||
|     pub fn new(acceptor: A) -> ListenerPool<A> { | ||||
|         ListenerPool { acceptor: acceptor } | ||||
|     } | ||||
| 
 | ||||
|     /// Runs the acceptor pool. Blocks until the acceptors are closed.
 | ||||
| @@ -44,23 +44,16 @@ impl<'a, A: NetworkAcceptor + 'a> AcceptorPool<A> { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn spawn_with<'a, A, F>(supervisor: mpsc::Sender<usize>, work: &'a F, mut acceptor: A, id: usize) -> JoinGuard<'a, ()> | ||||
| where A: NetworkAcceptor + 'a, | ||||
|       F: Fn(<A as NetworkAcceptor>::Stream) + Send + Sync + 'a { | ||||
|     use std::old_io::EndOfFile; | ||||
| fn spawn_with<'a, A, F>(supervisor: mpsc::Sender<usize>, work: &'a F, mut acceptor: A, id: usize) -> thread::JoinGuard<'a, ()> | ||||
| where A: NetworkListener + Send + 'a, | ||||
|       F: Fn(<A as NetworkListener>::Stream) + Send + Sync + 'a { | ||||
| 
 | ||||
|     thread::scoped(move || { | ||||
|         let sentinel = Sentinel::new(supervisor, id); | ||||
|         let _sentinel = Sentinel::new(supervisor, id); | ||||
| 
 | ||||
|         loop { | ||||
|             match acceptor.accept() { | ||||
|                 Ok(stream) => work(stream), | ||||
|                 Err(ref e) if e.kind == EndOfFile => { | ||||
|                     debug!("Server closed."); | ||||
|                     sentinel.cancel(); | ||||
|                     return; | ||||
|                 }, | ||||
| 
 | ||||
|                 Err(e) => { | ||||
|                     error!("Connection failed: {}", e); | ||||
|                 } | ||||
| @@ -72,7 +65,7 @@ where A: NetworkAcceptor + 'a, | ||||
| struct Sentinel<T: Send> { | ||||
|     value: Option<T>, | ||||
|     supervisor: mpsc::Sender<T>, | ||||
|     active: bool | ||||
|     //active: bool
 | ||||
| } | ||||
| 
 | ||||
| impl<T: Send> Sentinel<T> { | ||||
| @@ -80,18 +73,18 @@ impl<T: Send> Sentinel<T> { | ||||
|         Sentinel { | ||||
|             value: Some(data), | ||||
|             supervisor: channel, | ||||
|             active: true | ||||
|             //active: true
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn cancel(mut self) { self.active = false; } | ||||
|     //fn cancel(mut self) { self.active = false; }
 | ||||
| } | ||||
| 
 | ||||
| #[unsafe_destructor] | ||||
| impl<T: Send + 'static> Drop for Sentinel<T> { | ||||
|     fn drop(&mut self) { | ||||
|         // If we were cancelled, get out of here.
 | ||||
|         if !self.active { return; } | ||||
|         //if !self.active { return; }
 | ||||
| 
 | ||||
|         // Respawn ourselves
 | ||||
|         let _ = self.supervisor.send(self.value.take().unwrap()); | ||||
| @@ -1,7 +1,9 @@ | ||||
| //! HTTP Server | ||||
| use std::old_io::{Listener, BufferedReader, BufferedWriter}; | ||||
| use std::old_io::net::ip::{IpAddr, Port, SocketAddr}; | ||||
| use std::io::{BufReader, BufWriter}; | ||||
| use std::marker::PhantomData; | ||||
| use std::net::{IpAddr, SocketAddr}; | ||||
| use std::os; | ||||
| use std::path::Path; | ||||
| use std::thread::{self, JoinGuard}; | ||||
|  | ||||
| pub use self::request::Request; | ||||
| @@ -13,25 +15,24 @@ use HttpError::HttpIoError; | ||||
| use {HttpResult}; | ||||
| use header::Connection; | ||||
| use header::ConnectionOption::{Close, KeepAlive}; | ||||
| use net::{NetworkListener, NetworkStream, NetworkAcceptor, | ||||
|           HttpAcceptor, HttpListener}; | ||||
| use net::{NetworkListener, NetworkStream, HttpListener}; | ||||
| use version::HttpVersion::{Http10, Http11}; | ||||
|  | ||||
| use self::acceptor::AcceptorPool; | ||||
| use self::listener::ListenerPool; | ||||
|  | ||||
| pub mod request; | ||||
| pub mod response; | ||||
|  | ||||
| mod acceptor; | ||||
| mod listener; | ||||
|  | ||||
| /// 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. | ||||
| pub struct Server<L = HttpListener> { | ||||
|     ip: IpAddr, | ||||
|     port: Port, | ||||
|     listener: L, | ||||
| pub struct Server<'a, H: Handler, L = HttpListener> { | ||||
|     handler: H, | ||||
|     ssl: Option<(&'a Path, &'a Path)>, | ||||
|     _marker: PhantomData<L> | ||||
| } | ||||
|  | ||||
| macro_rules! try_option( | ||||
| @@ -43,38 +44,59 @@ macro_rules! try_option( | ||||
|     }} | ||||
| ); | ||||
|  | ||||
| impl Server<HttpListener> { | ||||
|     /// Creates a new server that will handle `HttpStream`s. | ||||
|     pub fn http(ip: IpAddr, port: Port) -> Server { | ||||
|         Server::with_listener(ip, port, HttpListener::Http) | ||||
| impl<'a, H: Handler, L: NetworkListener> Server<'a, H, L> { | ||||
|     pub fn new(handler: H) -> Server<'a, H, L> { | ||||
|         Server { | ||||
|             handler: handler, | ||||
|             ssl: None, | ||||
|             _marker: PhantomData | ||||
|         } | ||||
|     /// Creates a new server that will handler `HttpStreams`s using a TLS connection. | ||||
|     pub fn https(ip: IpAddr, port: Port, cert: Path, key: Path) -> Server { | ||||
|         Server::with_listener(ip, port, HttpListener::Https(cert, key)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl< | ||||
| L: NetworkListener<Acceptor=A> + Send, | ||||
| A: NetworkAcceptor<Stream=S> + Send + 'static, | ||||
| S: NetworkStream + Clone + Send> Server<L> { | ||||
| impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> { | ||||
|     /// Creates a new server that will handle `HttpStream`s. | ||||
|     pub fn with_listener(ip: IpAddr, port: Port, listener: L) -> Server<L> { | ||||
|         Server { | ||||
|             ip: ip, | ||||
|             port: port, | ||||
|             listener: listener, | ||||
|     pub fn http(handler: H) -> Server<'a, H, HttpListener> { | ||||
|         Server::new(handler) | ||||
|     } | ||||
|     /// Creates a new server that will handler `HttpStreams`s using a TLS connection. | ||||
|     pub fn https(handler: H, cert: &'a Path, key: &'a Path) -> Server<'a, H, HttpListener> { | ||||
|         Server { | ||||
|             handler: handler, | ||||
|             ssl: Some((cert, key)), | ||||
|             _marker: PhantomData | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> { | ||||
|     /// Binds to a socket, and starts handling connections using a task pool. | ||||
|     pub fn listen_threads(self, ip: IpAddr, port: u16, threads: usize) -> HttpResult<Listening> { | ||||
|         let addr = &(ip, port); | ||||
|         let listener = try!(match self.ssl { | ||||
|             Some((cert, key)) => HttpListener::https(addr, cert, key), | ||||
|             None => HttpListener::http(addr) | ||||
|         }); | ||||
|         self.with_listener(listener, threads) | ||||
|     } | ||||
|  | ||||
|     /// Binds to a socket, and starts handling connections using a task pool. | ||||
|     pub fn listen_threads<H: Handler + 'static>(mut self, handler: H, threads: usize) -> HttpResult<Listening<L::Acceptor>> { | ||||
|         debug!("binding to {:?}:{:?}", self.ip, self.port); | ||||
|         let acceptor = try!(self.listener.listen((self.ip, self.port))); | ||||
|         let socket = try!(acceptor.socket_name()); | ||||
|     /// Binds to a socket and starts handling connections. | ||||
|     pub fn listen(self, ip: IpAddr, port: u16) -> HttpResult<Listening> { | ||||
|         self.listen_threads(ip, port, os::num_cpus() * 5 / 4) | ||||
|     } | ||||
| } | ||||
| impl< | ||||
| 'a, | ||||
| H: Handler + 'static, | ||||
| L: NetworkListener<Stream=S> + Send + 'static, | ||||
| S: NetworkStream + Clone + Send> Server<'a, H, L> { | ||||
|     /// Creates a new server that will handle `HttpStream`s. | ||||
|     pub fn with_listener(self, mut listener: L, threads: usize) -> HttpResult<Listening> { | ||||
|         let socket = try!(listener.socket_addr()); | ||||
|         let handler = self.handler; | ||||
|  | ||||
|         debug!("threads = {:?}", threads); | ||||
|         let pool = AcceptorPool::new(acceptor.clone()); | ||||
|         let pool = ListenerPool::new(listener.clone()); | ||||
|         let work = move |stream| handle_connection(stream, &handler); | ||||
|  | ||||
|         let guard = thread::scoped(move || pool.accept(work, threads)); | ||||
| @@ -82,21 +104,15 @@ S: NetworkStream + Clone + Send> Server<L> { | ||||
|         Ok(Listening { | ||||
|             _guard: guard, | ||||
|             socket: socket, | ||||
|             acceptor: acceptor | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// Binds to a socket and starts handling connections. | ||||
|     pub fn listen<H: Handler + 'static>(self, handler: H) -> HttpResult<Listening<L::Acceptor>> { | ||||
|         self.listen_threads(handler, os::num_cpus() * 5 / 4) | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| fn handle_connection<S, H>(mut stream: S, handler: &H) | ||||
| where S: NetworkStream + Clone, H: Handler { | ||||
|     debug!("Incoming stream"); | ||||
|     let addr = match stream.peer_name() { | ||||
|     let addr = match stream.peer_addr() { | ||||
|         Ok(addr) => addr, | ||||
|         Err(e) => { | ||||
|             error!("Peer Name error: {:?}", e); | ||||
| @@ -104,8 +120,8 @@ where S: NetworkStream + Clone, H: Handler { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let mut rdr = BufferedReader::new(stream.clone()); | ||||
|     let mut wrt = BufferedWriter::new(stream); | ||||
|     let mut rdr = BufReader::new(stream.clone()); | ||||
|     let mut wrt = BufWriter::new(stream); | ||||
|  | ||||
|     let mut keep_alive = true; | ||||
|     while keep_alive { | ||||
| @@ -135,18 +151,17 @@ where S: NetworkStream + Clone, H: Handler { | ||||
| } | ||||
|  | ||||
| /// A listening server, which can later be closed. | ||||
| pub struct Listening<A = HttpAcceptor> { | ||||
|     acceptor: A, | ||||
| pub struct Listening { | ||||
|     _guard: JoinGuard<'static, ()>, | ||||
|     /// The socket addresses that the server is bound to. | ||||
|     pub socket: SocketAddr, | ||||
| } | ||||
|  | ||||
| impl<A: NetworkAcceptor> Listening<A> { | ||||
| impl Listening { | ||||
|     /// Stop the server from listening to its socket address. | ||||
|     pub fn close(&mut self) -> HttpResult<()> { | ||||
|         debug!("closing server"); | ||||
|         try!(self.acceptor.close()); | ||||
|         //try!(self.acceptor.close()); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
| //! | ||||
| //! These are requests that a `hyper::Server` receives, and include its method, | ||||
| //! target URI, headers, and message body. | ||||
| use std::old_io::IoResult; | ||||
| use std::old_io::net::ip::SocketAddr; | ||||
| use std::io::{self, Read}; | ||||
| use std::net::SocketAddr; | ||||
|  | ||||
| use {HttpResult}; | ||||
| use version::{HttpVersion}; | ||||
| @@ -26,14 +26,14 @@ pub struct Request<'a> { | ||||
|     pub uri: RequestUri, | ||||
|     /// The version of HTTP for this request. | ||||
|     pub version: HttpVersion, | ||||
|     body: HttpReader<&'a mut (Reader + 'a)> | ||||
|     body: HttpReader<&'a mut (Read + 'a)> | ||||
| } | ||||
|  | ||||
|  | ||||
| impl<'a> Request<'a> { | ||||
|     /// Create a new Request, reading the StartLine and Headers so they are | ||||
|     /// immediately useful. | ||||
|     pub fn new(mut stream: &'a mut (Reader + 'a), addr: SocketAddr) -> HttpResult<Request<'a>> { | ||||
|     pub fn new(mut stream: &'a mut (Read + 'a), addr: SocketAddr) -> HttpResult<Request<'a>> { | ||||
|         let (method, uri, version) = try!(read_request_line(&mut stream)); | ||||
|         debug!("Request Line: {:?} {:?} {:?}", method, uri, version); | ||||
|         let headers = try!(Headers::from_raw(&mut stream)); | ||||
| @@ -66,14 +66,14 @@ impl<'a> Request<'a> { | ||||
|     /// Deconstruct a Request into its constituent parts. | ||||
|     pub fn deconstruct(self) -> (SocketAddr, Method, Headers, | ||||
|                                  RequestUri, HttpVersion, | ||||
|                                  HttpReader<&'a mut (Reader + 'a)>,) { | ||||
|                                  HttpReader<&'a mut (Read + 'a)>,) { | ||||
|         (self.remote_addr, self.method, self.headers, | ||||
|          self.uri, self.version, self.body) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Reader for Request<'a> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||||
| impl<'a> Read for Request<'a> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         self.body.read(buf) | ||||
|     } | ||||
| } | ||||
| @@ -84,12 +84,19 @@ mod tests { | ||||
|     use mock::MockStream; | ||||
|     use super::Request; | ||||
|  | ||||
|     use std::old_io::net::ip::SocketAddr; | ||||
|     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 stream = MockStream::with_input(b"\ | ||||
| @@ -99,8 +106,8 @@ mod tests { | ||||
|             I'm a bad request.\r\n\ | ||||
|         "); | ||||
|  | ||||
|         let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         assert_eq!(req.read_to_string(), Ok("".to_string())); | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         assert_eq!(read_to_string(req), Ok("".to_string())); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -112,8 +119,8 @@ mod tests { | ||||
|             I'm a bad request.\r\n\ | ||||
|         "); | ||||
|  | ||||
|         let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         assert_eq!(req.read_to_string(), Ok("".to_string())); | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         assert_eq!(read_to_string(req), Ok("".to_string())); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -125,8 +132,8 @@ mod tests { | ||||
|             I'm a bad request.\r\n\ | ||||
|         "); | ||||
|  | ||||
|         let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         assert_eq!(req.read_to_string(), Ok("".to_string())); | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         assert_eq!(read_to_string(req), Ok("".to_string())); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -146,7 +153,7 @@ mod tests { | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|  | ||||
|         // The headers are correct? | ||||
|         match req.headers.get::<Host>() { | ||||
| @@ -163,8 +170,7 @@ mod tests { | ||||
|             None => panic!("Transfer-Encoding: chunked expected!"), | ||||
|         }; | ||||
|         // The content is correctly read? | ||||
|         let body = req.read_to_string().unwrap(); | ||||
|         assert_eq!("qwert", body); | ||||
|         assert_eq!(read_to_string(req), Ok("qwert".to_string())); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a chunk size is not a valid radix-16 number, an error | ||||
| @@ -182,9 +188,9 @@ mod tests { | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|  | ||||
|         assert!(req.read_to_string().is_err()); | ||||
|         assert!(read_to_string(req).is_err()); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a chunk size contains an invalid extension, an error is | ||||
| @@ -202,9 +208,9 @@ mod tests { | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|  | ||||
|         assert!(req.read_to_string().is_err()); | ||||
|         assert!(read_to_string(req).is_err()); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a valid extension that contains a digit is appended to | ||||
| @@ -222,9 +228,9 @@ mod tests { | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|         let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap(); | ||||
|  | ||||
|         assert_eq!("1", req.read_to_string().unwrap()) | ||||
|         assert_eq!(read_to_string(req), Ok("1".to_string())); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
| //! | ||||
| //! These are responses sent by a `hyper::Server` to clients, after | ||||
| //! receiving a request. | ||||
| use std::old_io::IoResult; | ||||
| use std::marker::PhantomData; | ||||
| use std::io::{self, Write}; | ||||
|  | ||||
| use time::now_utc; | ||||
|  | ||||
| @@ -19,7 +19,7 @@ pub struct Response<'a, W = 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 (Writer + 'a)>, | ||||
|     body: HttpWriter<&'a mut (Write + 'a)>, | ||||
|     // The status code for the request. | ||||
|     status: status::StatusCode, | ||||
|     // The outgoing headers on this response. | ||||
| @@ -38,7 +38,7 @@ impl<'a, W> Response<'a, W> { | ||||
|  | ||||
|     /// Construct a Response from its constituent parts. | ||||
|     pub fn construct(version: version::HttpVersion, | ||||
|                      body: HttpWriter<&'a mut (Writer + 'a)>, | ||||
|                      body: HttpWriter<&'a mut (Write + 'a)>, | ||||
|                      status: status::StatusCode, | ||||
|                      headers: header::Headers) -> Response<'a, Fresh> { | ||||
|         Response { | ||||
| @@ -51,7 +51,7 @@ impl<'a, W> Response<'a, W> { | ||||
|     } | ||||
|  | ||||
|     /// Deconstruct this Response into its constituent parts. | ||||
|     pub fn deconstruct(self) -> (version::HttpVersion, HttpWriter<&'a mut (Writer + 'a)>, | ||||
|     pub fn deconstruct(self) -> (version::HttpVersion, HttpWriter<&'a mut (Write + 'a)>, | ||||
|                                  status::StatusCode, header::Headers) { | ||||
|         (self.version, self.body, self.status, self.headers) | ||||
|     } | ||||
| @@ -59,7 +59,7 @@ impl<'a, W> Response<'a, W> { | ||||
|  | ||||
| impl<'a> Response<'a, Fresh> { | ||||
|     /// Creates a new Response that can be used to write to a network stream. | ||||
|     pub fn new(stream: &'a mut (Writer + 'a)) -> Response<'a, Fresh> { | ||||
|     pub fn new(stream: &'a mut (Write + 'a)) -> Response<'a, Fresh> { | ||||
|         Response { | ||||
|             status: status::StatusCode::Ok, | ||||
|             version: version::HttpVersion::Http11, | ||||
| @@ -70,7 +70,7 @@ impl<'a> Response<'a, Fresh> { | ||||
|     } | ||||
|  | ||||
|     /// Consume this Response<Fresh>, writing the Headers and Status and creating a Response<Streaming> | ||||
|     pub fn start(mut self) -> IoResult<Response<'a, Streaming>> { | ||||
|     pub fn start(mut self) -> io::Result<Response<'a, Streaming>> { | ||||
|         debug!("writing head: {:?} {:?}", self.version, self.status); | ||||
|         try!(write!(&mut self.body, "{} {}{}{}", self.version, self.status, CR as char, LF as char)); | ||||
|  | ||||
| @@ -110,13 +110,12 @@ impl<'a> Response<'a, Fresh> { | ||||
|  | ||||
|         debug!("headers [\n{:?}]", self.headers); | ||||
|         try!(write!(&mut self.body, "{}", self.headers)); | ||||
|  | ||||
|         try!(self.body.write_str(LINE_ENDING)); | ||||
|         try!(write!(&mut self.body, "{}", LINE_ENDING)); | ||||
|  | ||||
|         let stream = if chunked { | ||||
|             ChunkedWriter(self.body.unwrap()) | ||||
|             ChunkedWriter(self.body.into_inner()) | ||||
|         } else { | ||||
|             SizedWriter(self.body.unwrap(), len) | ||||
|             SizedWriter(self.body.into_inner(), len) | ||||
|         }; | ||||
|  | ||||
|         // "copy" to change the phantom type | ||||
| @@ -139,20 +138,20 @@ impl<'a> Response<'a, Fresh> { | ||||
|  | ||||
| impl<'a> Response<'a, Streaming> { | ||||
|     /// Flushes all writing of a response to the client. | ||||
|     pub fn end(self) -> IoResult<()> { | ||||
|     pub fn end(self) -> io::Result<()> { | ||||
|         debug!("ending"); | ||||
|         try!(self.body.end()); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> Writer for Response<'a, Streaming> { | ||||
|     fn write_all(&mut self, msg: &[u8]) -> IoResult<()> { | ||||
| impl<'a> Write for Response<'a, Streaming> { | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         debug!("write {:?} bytes", msg.len()); | ||||
|         self.body.write_all(msg) | ||||
|         self.body.write(msg) | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> IoResult<()> { | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         self.body.flush() | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user