feat(lib): redesign API to use Futures and Tokio
There are many changes involved with this, but let's just talk about
user-facing changes.
- Creating a `Client` and `Server` now needs a Tokio `Core` event loop
to attach to.
- `Request` and `Response` both no longer implement the
`std::io::{Read,Write}` traits, but instead represent their bodies as a
`futures::Stream` of items, where each item is a `Chunk`.
- The `Client.request` method now takes a `Request`, instead of being
used as a builder, and returns a `Future` that resolves to `Response`.
- The `Handler` trait for servers is no more, and instead the Tokio
`Service` trait is used. This allows interoperability with generic
middleware.
BREAKING CHANGE: A big sweeping set of breaking changes.
			
			
This commit is contained in:
		| @@ -1,59 +1,59 @@ | ||||
| use std::collections::hash_map::{HashMap, Entry}; | ||||
| use std::hash::Hash; | ||||
| use std::fmt; | ||||
| use std::io; | ||||
| use std::net::SocketAddr; | ||||
| //use std::net::SocketAddr; | ||||
|  | ||||
| use rotor::mio::tcp::TcpStream; | ||||
| use futures::{Future, Poll, Async}; | ||||
| use tokio::io::Io; | ||||
| use tokio::reactor::Handle; | ||||
| use tokio::net::{TcpStream, TcpStreamNew}; | ||||
| use tokio_service::Service; | ||||
| use url::Url; | ||||
|  | ||||
| use net::{HttpStream, HttpsStream, Transport, SslClient}; | ||||
| use super::dns::Dns; | ||||
| use super::Registration; | ||||
| use super::dns; | ||||
|  | ||||
| /// A connector creates a Transport to a remote address.. | ||||
| pub trait Connect { | ||||
|     /// Type of Transport to create | ||||
|     type Output: Transport; | ||||
|     /// The key used to determine if an existing socket can be used. | ||||
|     type Key: Eq + Hash + Clone + fmt::Debug; | ||||
|     /// Returns the key based off the Url. | ||||
|     fn key(&self, &Url) -> Option<Self::Key>; | ||||
| /// A connector creates an Io to a remote address.. | ||||
| /// | ||||
| /// This trait is not implemented directly, and only exists to make | ||||
| /// the intent clearer. A connector should implement `Service` with | ||||
| /// `Request=Url` and `Response: Io` instead. | ||||
| pub trait Connect: Service<Request=Url, Error=io::Error> + 'static { | ||||
|     /// The connected Io Stream. | ||||
|     type Output: Io + 'static; | ||||
|     /// A Future that will resolve to the connected Stream. | ||||
|     type Future: Future<Item=Self::Output, Error=io::Error> + 'static; | ||||
|     /// Connect to a remote address. | ||||
|     fn connect(&mut self, &Url) -> io::Result<Self::Key>; | ||||
|     /// Returns a connected socket and associated host. | ||||
|     fn connected(&mut self) -> Option<(Self::Key, io::Result<Self::Output>)>; | ||||
|     #[doc(hidden)] | ||||
|     /// Configure number of dns workers to use. | ||||
|     fn dns_workers(&mut self, usize); | ||||
|     #[doc(hidden)] | ||||
|     fn register(&mut self, Registration); | ||||
|     fn connect(&self, Url) -> <Self as Connect>::Future; | ||||
| } | ||||
|  | ||||
| /// A connector for the `http` scheme. | ||||
| pub struct HttpConnector { | ||||
|     dns: Option<Dns>, | ||||
|     threads: usize, | ||||
|     resolving: HashMap<String, Vec<(&'static str, String, u16)>>, | ||||
| } | ||||
| impl<T> Connect for T | ||||
| where T: Service<Request=Url, Error=io::Error> + 'static, | ||||
|       T::Response: Io, | ||||
|       T::Future: Future<Error=io::Error>, | ||||
| { | ||||
|     type Output = T::Response; | ||||
|     type Future = T::Future; | ||||
|  | ||||
| impl HttpConnector { | ||||
|     /// Set the number of resolver threads. | ||||
|     /// | ||||
|     /// Default is 4. | ||||
|     pub fn threads(mut self, threads: usize) -> HttpConnector { | ||||
|         debug_assert!(self.dns.is_none(), "setting threads after Dns is created does nothing"); | ||||
|         self.threads = threads; | ||||
|         self | ||||
|     fn connect(&self, url: Url) -> <Self as Connect>::Future { | ||||
|         self.call(url) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for HttpConnector { | ||||
|     fn default() -> HttpConnector { | ||||
| /// A connector for the `http` scheme. | ||||
| #[derive(Clone)] | ||||
| pub struct HttpConnector { | ||||
|     dns: dns::Dns, | ||||
|     handle: Handle, | ||||
| } | ||||
|  | ||||
| impl HttpConnector { | ||||
|  | ||||
|     /// Construct a new HttpConnector. | ||||
|     /// | ||||
|     /// Takes number of DNS worker threads. | ||||
|     pub fn new(threads: usize, handle: &Handle) -> HttpConnector { | ||||
|         HttpConnector { | ||||
|             dns: None, | ||||
|             threads: 4, | ||||
|             resolving: HashMap::new(), | ||||
|             dns: dns::Dns::new(threads), | ||||
|             handle: handle.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -61,79 +61,115 @@ impl Default for HttpConnector { | ||||
| impl fmt::Debug for HttpConnector { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("HttpConnector") | ||||
|             .field("threads", &self.threads) | ||||
|             .field("resolving", &self.resolving) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Connect for HttpConnector { | ||||
|     type Output = HttpStream; | ||||
|     type Key = (&'static str, String, u16); | ||||
| impl Service for HttpConnector { | ||||
|     type Request = Url; | ||||
|     type Response = TcpStream; | ||||
|     type Error = io::Error; | ||||
|     type Future = HttpConnecting; | ||||
|  | ||||
|     fn dns_workers(&mut self, count: usize) { | ||||
|         self.threads = count; | ||||
|     } | ||||
|  | ||||
|     fn key(&self, url: &Url) -> Option<Self::Key> { | ||||
|         if url.scheme() == "http" { | ||||
|             Some(( | ||||
|                 "http", | ||||
|                 url.host_str().expect("http scheme must have host").to_owned(), | ||||
|                 url.port().unwrap_or(80), | ||||
|             )) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn connect(&mut self, url: &Url) -> io::Result<Self::Key> { | ||||
|     fn call(&self, url: Url) -> Self::Future { | ||||
|         debug!("Http::connect({:?})", url); | ||||
|         if let Some(key) = self.key(url) { | ||||
|             let host = url.host_str().expect("http scheme must have a host"); | ||||
|             self.dns.as_ref().expect("dns workers lost").resolve(host); | ||||
|             self.resolving.entry(host.to_owned()).or_insert_with(Vec::new).push(key.clone()); | ||||
|             Ok(key) | ||||
|         } else { | ||||
|             Err(io::Error::new(io::ErrorKind::InvalidInput, "scheme must be http")) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn connected(&mut self) -> Option<(Self::Key, io::Result<HttpStream>)> { | ||||
|         let (host, addrs) = match self.dns.as_ref().expect("dns workers lost").resolved() { | ||||
|             Ok(res) => res, | ||||
|             Err(_) => return None | ||||
|         let host = match url.host_str() { | ||||
|             Some(s) => s, | ||||
|             None => return HttpConnecting { | ||||
|                 state: State::Error(Some(io::Error::new(io::ErrorKind::InvalidInput, "invalid url"))), | ||||
|                 handle: self.handle.clone(), | ||||
|             }, | ||||
|         }; | ||||
|         //TODO: try all addrs | ||||
|         let addr = addrs.and_then(|mut addrs| Ok(addrs.next().unwrap())); | ||||
|         debug!("Http::resolved <- ({:?}, {:?})", host, addr); | ||||
|         if let Entry::Occupied(mut entry) = self.resolving.entry(host) { | ||||
|             let resolved = entry.get_mut().remove(0); | ||||
|             if entry.get().is_empty() { | ||||
|                 entry.remove(); | ||||
|             } | ||||
|             let port = resolved.2; | ||||
|             Some((resolved, addr.and_then(|addr| TcpStream::connect(&SocketAddr::new(addr, port)) | ||||
|                                                             .map(HttpStream)) | ||||
|                 )) | ||||
|         } else { | ||||
|             trace!("^--  resolved but not in hashmap?"); | ||||
|             None | ||||
|         let port = url.port_or_known_default().unwrap_or(80); | ||||
|  | ||||
|         HttpConnecting { | ||||
|             state: State::Resolving(self.dns.resolve(host.into(), port)), | ||||
|             handle: self.handle.clone(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn register(&mut self, reg: Registration) { | ||||
|         self.dns = Some(Dns::new(reg.notify, self.threads)); | ||||
| } | ||||
|  | ||||
| /// A Future representing work to connect to a URL. | ||||
| pub struct HttpConnecting { | ||||
|     state: State, | ||||
|     handle: Handle, | ||||
| } | ||||
|  | ||||
| enum State { | ||||
|     Resolving(dns::Query), | ||||
|     Connecting(ConnectingTcp), | ||||
|     Error(Option<io::Error>), | ||||
| } | ||||
|  | ||||
| impl Future for HttpConnecting { | ||||
|     type Item = TcpStream; | ||||
|     type Error = io::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         loop { | ||||
|             let state; | ||||
|             match self.state { | ||||
|                 State::Resolving(ref mut query) => { | ||||
|                     match try!(query.poll()) { | ||||
|                         Async::NotReady => return Ok(Async::NotReady), | ||||
|                         Async::Ready(addrs) => { | ||||
|                             state = State::Connecting(ConnectingTcp { | ||||
|                                 addrs: addrs, | ||||
|                                 current: None, | ||||
|                             }) | ||||
|                         } | ||||
|                     }; | ||||
|                 }, | ||||
|                 State::Connecting(ref mut c) => return c.poll(&self.handle).map_err(From::from), | ||||
|                 State::Error(ref mut e) => return Err(e.take().expect("polled more than once")), | ||||
|             } | ||||
|             self.state = state; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A connector that can protect HTTP streams using SSL. | ||||
| #[derive(Debug, Default)] | ||||
| pub struct HttpsConnector<S: SslClient> { | ||||
|     http: HttpConnector, | ||||
|     ssl: S | ||||
| impl fmt::Debug for HttpConnecting { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.pad("HttpConnecting") | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct ConnectingTcp { | ||||
|     addrs: dns::IpAddrs, | ||||
|     current: Option<TcpStreamNew>, | ||||
| } | ||||
|  | ||||
| impl ConnectingTcp { | ||||
|     // not a Future, since passing a &Handle to poll | ||||
|     fn poll(&mut self, handle: &Handle) -> Poll<TcpStream, io::Error> { | ||||
|         let mut err = None; | ||||
|         loop { | ||||
|             if let Some(ref mut current) = self.current { | ||||
|                 match current.poll() { | ||||
|                     Ok(ok) => return Ok(ok), | ||||
|                     Err(e) => { | ||||
|                         trace!("connect error {:?}", e); | ||||
|                         err = Some(e); | ||||
|                         if let Some(addr) = self.addrs.next() { | ||||
|                             debug!("connecting to {:?}", addr); | ||||
|                             *current = TcpStream::connect(&addr, handle); | ||||
|                             continue; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } else if let Some(addr) = self.addrs.next() { | ||||
|                 debug!("connecting to {:?}", addr); | ||||
|                 self.current = Some(TcpStream::connect(&addr, handle)); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             return Err(err.take().expect("missing connect error")); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
| impl<S: SslClient> HttpsConnector<S> { | ||||
|     /// Create a new connector using the provided SSL implementation. | ||||
|     pub fn new(s: S) -> HttpsConnector<S> { | ||||
| @@ -143,80 +179,22 @@ impl<S: SslClient> HttpsConnector<S> { | ||||
|         } | ||||
|     } | ||||
| } | ||||
| */ | ||||
|  | ||||
| impl<S: SslClient> Connect for HttpsConnector<S> { | ||||
|     type Output = HttpsStream<S::Stream>; | ||||
|     type Key = (&'static str, String, u16); | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::io; | ||||
|     use tokio::reactor::Core; | ||||
|     use url::Url; | ||||
|     use super::{Connect, HttpConnector}; | ||||
|  | ||||
|     fn dns_workers(&mut self, count: usize) { | ||||
|         self.http.dns_workers(count) | ||||
|     #[test] | ||||
|     fn test_non_http_url() { | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let url = Url::parse("file:///home/sean/foo.txt").unwrap(); | ||||
|         let connector = HttpConnector::new(1, &core.handle()); | ||||
|  | ||||
|         assert_eq!(core.run(connector.connect(url)).unwrap_err().kind(), io::ErrorKind::InvalidInput); | ||||
|     } | ||||
|  | ||||
|     fn key(&self, url: &Url) -> Option<Self::Key> { | ||||
|         let scheme = match url.scheme() { | ||||
|             "http" => "http", | ||||
|             "https" => "https", | ||||
|             _ => return None | ||||
|         }; | ||||
|         Some(( | ||||
|             scheme, | ||||
|             url.host_str().expect("http scheme must have host").to_owned(), | ||||
|             url.port_or_known_default().expect("http scheme must have a port"), | ||||
|         )) | ||||
|     } | ||||
|  | ||||
|     fn connect(&mut self, url: &Url) -> io::Result<Self::Key> { | ||||
|         debug!("Https::connect({:?})", url); | ||||
|         if let Some(key) = self.key(url) { | ||||
|             let host = url.host_str().expect("http scheme must have a host"); | ||||
|             self.http.dns.as_ref().expect("dns workers lost").resolve(host); | ||||
|             self.http.resolving.entry(host.to_owned()).or_insert_with(Vec::new).push(key.clone()); | ||||
|             Ok(key) | ||||
|         } else { | ||||
|             Err(io::Error::new(io::ErrorKind::InvalidInput, "scheme must be http or https")) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn connected(&mut self) -> Option<(Self::Key, io::Result<Self::Output>)> { | ||||
|         self.http.connected().map(|(key, res)| { | ||||
|             let res = res.and_then(|http| { | ||||
|                 if key.0 == "https" { | ||||
|                     self.ssl.wrap_client(http, &key.1) | ||||
|                         .map(HttpsStream::Https) | ||||
|                         .map_err(|e| match e { | ||||
|                             ::Error::Io(e) => e, | ||||
|                             e => io::Error::new(io::ErrorKind::Other, e) | ||||
|                         }) | ||||
|                 } else { | ||||
|                     Ok(HttpsStream::Http(http)) | ||||
|                 } | ||||
|             }); | ||||
|             (key, res) | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     fn register(&mut self, reg: Registration) { | ||||
|         self.http.register(reg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(not(any(feature = "openssl", feature = "security-framework")))] | ||||
| #[doc(hidden)] | ||||
| pub type DefaultConnector = HttpConnector; | ||||
|  | ||||
| #[cfg(all(feature = "openssl", not(feature = "security-framework")))] | ||||
| #[doc(hidden)] | ||||
| pub type DefaultConnector = HttpsConnector<::net::Openssl>; | ||||
|  | ||||
| #[cfg(feature = "security-framework")] | ||||
| #[doc(hidden)] | ||||
| pub type DefaultConnector = HttpsConnector<::net::SecureTransportClient>; | ||||
|  | ||||
| #[doc(hidden)] | ||||
| pub type DefaultTransport = <DefaultConnector as Connect>::Output; | ||||
|  | ||||
| fn _assert_defaults() { | ||||
|     fn _assert<T, U>() where T: Connect<Output=U>, U: Transport {} | ||||
|  | ||||
|     _assert::<DefaultConnector, DefaultTransport>(); | ||||
| } | ||||
|   | ||||
| @@ -1,96 +1,53 @@ | ||||
| use std::io; | ||||
| use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; | ||||
| use std::thread; | ||||
| use std::net::{SocketAddr, ToSocketAddrs}; | ||||
| use std::vec; | ||||
|  | ||||
| use ::spmc; | ||||
|  | ||||
| use http::channel; | ||||
| use ::futures::{Future, Poll}; | ||||
| use ::futures_cpupool::{CpuPool, CpuFuture}; | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct Dns { | ||||
|     tx: spmc::Sender<String>, | ||||
|     rx: channel::Receiver<Answer>, | ||||
|     pool: CpuPool, | ||||
| } | ||||
|  | ||||
| pub type Answer = (String, io::Result<IpAddrs>); | ||||
| impl Dns { | ||||
|     pub fn new(threads: usize) -> Dns { | ||||
|         Dns { | ||||
|             pool: CpuPool::new(threads) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn resolve(&self, host: String, port: u16) -> Query { | ||||
|         Query(self.pool.spawn_fn(move || work(host, port))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Query(CpuFuture<IpAddrs, io::Error>); | ||||
|  | ||||
| impl Future for Query { | ||||
|     type Item = IpAddrs; | ||||
|     type Error = io::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         self.0.poll() | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct IpAddrs { | ||||
|     iter: vec::IntoIter<SocketAddr>, | ||||
| } | ||||
|  | ||||
| impl Iterator for IpAddrs { | ||||
|     type Item = IpAddr; | ||||
|     type Item = SocketAddr; | ||||
|     #[inline] | ||||
|     fn next(&mut self) -> Option<IpAddr> { | ||||
|         self.iter.next().map(|addr| addr.ip()) | ||||
|     fn next(&mut self) -> Option<SocketAddr> { | ||||
|         self.iter.next() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Dns { | ||||
|     pub fn new(notify: (channel::Sender<Answer>, channel::Receiver<Answer>), threads: usize) -> Dns { | ||||
|         let (tx, rx) = spmc::channel(); | ||||
|         for _ in 0..threads { | ||||
|             work(rx.clone(), notify.0.clone()); | ||||
|         } | ||||
|         Dns { | ||||
|             tx: tx, | ||||
|             rx: notify.1, | ||||
|         } | ||||
|     } | ||||
| pub type Answer = io::Result<IpAddrs>; | ||||
|  | ||||
|     pub fn resolve<T: Into<String>>(&self, hostname: T) { | ||||
|         self.tx.send(hostname.into()).expect("DNS workers all died unexpectedly"); | ||||
|     } | ||||
|  | ||||
|     pub fn resolved(&self) -> Result<Answer, channel::TryRecvError> { | ||||
|         self.rx.try_recv() | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn work(rx: spmc::Receiver<String>, notify: channel::Sender<Answer>) { | ||||
|     thread::Builder::new().name(String::from("hyper-dns")).spawn(move || { | ||||
|         let mut worker = Worker::new(rx, notify); | ||||
|         let rx = worker.rx.as_ref().expect("Worker lost rx"); | ||||
|         let notify = worker.notify.as_ref().expect("Worker lost notify"); | ||||
|         while let Ok(host) = rx.recv() { | ||||
|             debug!("resolve {:?}", host); | ||||
|             let res = match (&*host, 80).to_socket_addrs().map(|i| IpAddrs{ iter: i }) { | ||||
|                 Ok(addrs) => (host, Ok(addrs)), | ||||
|                 Err(e) => (host, Err(e)) | ||||
|             }; | ||||
|  | ||||
|             if let Err(_) = notify.send(res) { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         worker.shutdown = true; | ||||
|     }).expect("spawn dns thread"); | ||||
| } | ||||
|  | ||||
| struct Worker { | ||||
|     rx: Option<spmc::Receiver<String>>, | ||||
|     notify: Option<channel::Sender<Answer>>, | ||||
|     shutdown: bool, | ||||
| } | ||||
|  | ||||
| impl Worker { | ||||
|     fn new(rx: spmc::Receiver<String>, notify: channel::Sender<Answer>) -> Worker { | ||||
|         Worker { | ||||
|             rx: Some(rx), | ||||
|             notify: Some(notify), | ||||
|             shutdown: false, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Drop for Worker { | ||||
|     fn drop(&mut self) { | ||||
|         if !self.shutdown { | ||||
|             trace!("Worker.drop panicked, restarting"); | ||||
|             work(self.rx.take().expect("Worker lost rx"), | ||||
|                 self.notify.take().expect("Worker lost notify")); | ||||
|         } else { | ||||
|             trace!("Worker.drop shutdown, closing"); | ||||
|         } | ||||
|     } | ||||
| fn work(hostname: String, port: u16) -> Answer { | ||||
|     debug!("resolve {:?}:{:?}", hostname, port); | ||||
|     (&*hostname, port).to_socket_addrs().map(|i| IpAddrs { iter: i }) | ||||
| } | ||||
|   | ||||
| @@ -3,51 +3,49 @@ | ||||
| //! The HTTP `Client` uses asynchronous IO, and utilizes the `Handler` trait | ||||
| //! to convey when IO events are available for a given request. | ||||
|  | ||||
| use std::collections::{VecDeque, HashMap}; | ||||
| use std::cell::RefCell; | ||||
| use std::fmt; | ||||
| use std::io; | ||||
| use std::marker::PhantomData; | ||||
| use std::sync::mpsc; | ||||
| use std::thread; | ||||
| use std::rc::Rc; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use rotor::{self, Scope, EventSet, PollOpt}; | ||||
| use futures::{Poll, Async, Future}; | ||||
| use relay; | ||||
| use tokio::io::Io; | ||||
| use tokio::reactor::Handle; | ||||
| use tokio_proto::BindClient; | ||||
| use tokio_proto::streaming::Message; | ||||
| use tokio_proto::streaming::pipeline::ClientProto; | ||||
| use tokio_proto::util::client_proxy::ClientProxy; | ||||
| pub use tokio_service::Service; | ||||
|  | ||||
| use header::Host; | ||||
| use http::{self, Next, RequestHead, ReadyResult}; | ||||
| use net::Transport; | ||||
| use header::{Headers, Host}; | ||||
| use http::{self, TokioBody}; | ||||
| use method::Method; | ||||
| use self::pool::{Pool, Pooled}; | ||||
| use uri::RequestUri; | ||||
| use {Url}; | ||||
|  | ||||
| pub use self::connect::{Connect, DefaultConnector, HttpConnector, HttpsConnector, DefaultTransport}; | ||||
| pub use self::connect::{HttpConnector, Connect}; | ||||
| pub use self::request::Request; | ||||
| pub use self::response::Response; | ||||
|  | ||||
| mod connect; | ||||
| mod dns; | ||||
| mod pool; | ||||
| mod request; | ||||
| mod response; | ||||
|  | ||||
| /// A Client to make outgoing HTTP requests. | ||||
| pub struct Client<H> { | ||||
|     tx: http::channel::Sender<Notify<H>>, | ||||
| // If the Connector is clone, then the Client can be clone easily. | ||||
| #[derive(Clone)] | ||||
| pub struct Client<C> { | ||||
|     connector: C, | ||||
|     handle: Handle, | ||||
|     pool: Pool<TokioClient>, | ||||
| } | ||||
|  | ||||
| impl<H> Clone for Client<H> { | ||||
|     fn clone(&self) -> Client<H> { | ||||
|         Client { | ||||
|             tx: self.tx.clone() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<H> fmt::Debug for Client<H> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.pad("Client") | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<H> Client<H> { | ||||
| impl Client<HttpConnector> { | ||||
|     /// Configure a Client. | ||||
|     /// | ||||
|     /// # Example | ||||
| @@ -56,116 +54,218 @@ impl<H> Client<H> { | ||||
|     /// # use hyper::Client; | ||||
|     /// let client = Client::configure() | ||||
|     ///     .keep_alive(true) | ||||
|     ///     .max_sockets(10_000) | ||||
|     ///     .build().unwrap(); | ||||
|     /// ``` | ||||
|     #[inline] | ||||
|     pub fn configure() -> Config<DefaultConnector> { | ||||
|     pub fn configure() -> Config<UseDefaultConnector> { | ||||
|         Config::default() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<H: Handler<<DefaultConnector as Connect>::Output>> Client<H> { | ||||
| impl Client<HttpConnector> { | ||||
|     /// Create a new Client with the default config. | ||||
|     #[inline] | ||||
|     pub fn new() -> ::Result<Client<H>> { | ||||
|         Client::<H>::configure().build() | ||||
|     pub fn new(handle: &Handle) -> Client<HttpConnector> { | ||||
|         Client::configure().build(handle) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<H: Send> Client<H> { | ||||
| impl<C: Connect> Client<C> { | ||||
|     /// Create a new client with a specific connector. | ||||
|     fn configured<T, C>(config: Config<C>) -> ::Result<Client<H>> | ||||
|     where H: Handler<T>, | ||||
|           T: Transport, | ||||
|           C: Connect<Output=T> + Send + 'static { | ||||
|         let mut rotor_config = rotor::Config::new(); | ||||
|         rotor_config.slab_capacity(config.max_sockets); | ||||
|         rotor_config.mio().notify_capacity(config.max_sockets); | ||||
|         let keep_alive = config.keep_alive; | ||||
|         let connect_timeout = config.connect_timeout; | ||||
|         let mut loop_ = try!(rotor::Loop::new(&rotor_config)); | ||||
|         let mut notifier = None; | ||||
|         let mut connector = config.connector; | ||||
|         connector.dns_workers(config.dns_workers); | ||||
|         { | ||||
|             let not = &mut notifier; | ||||
|             loop_.add_machine_with(move |scope| { | ||||
|                 let (tx, rx) = http::channel::new(scope.notifier()); | ||||
|                 let (dns_tx, dns_rx) = http::channel::share(&tx); | ||||
|                 *not = Some(tx); | ||||
|                 connector.register(Registration { | ||||
|                     notify: (dns_tx, dns_rx), | ||||
|                 }); | ||||
|                 rotor::Response::ok(ClientFsm::Connector(connector, rx)) | ||||
|             }).unwrap(); | ||||
|     #[inline] | ||||
|     fn configured(config: Config<C>, handle: &Handle) -> Client<C> { | ||||
|         Client { | ||||
|             connector: config.connector, | ||||
|             handle: handle.clone(), | ||||
|             pool: Pool::new(config.keep_alive, config.keep_alive_timeout), | ||||
|         } | ||||
|  | ||||
|         let notifier = notifier.expect("loop.add_machine_with failed"); | ||||
|         let _handle = try!(thread::Builder::new().name("hyper-client".to_owned()).spawn(move || { | ||||
|             loop_.run(Context { | ||||
|                 connect_timeout: connect_timeout, | ||||
|                 keep_alive: keep_alive, | ||||
|                 idle_conns: HashMap::new(), | ||||
|                 queue: HashMap::new(), | ||||
|                 awaiting_slot: VecDeque::new(), | ||||
|             }).unwrap() | ||||
|         })); | ||||
|  | ||||
|         Ok(Client { | ||||
|             //handle: Some(handle), | ||||
|             tx: notifier, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// Build a new request using this Client. | ||||
|     /// | ||||
|     /// ## Error | ||||
|     /// | ||||
|     /// If the event loop thread has died, or the queue is full, a `ClientError` | ||||
|     /// will be returned. | ||||
|     pub fn request(&self, url: Url, handler: H) -> Result<(), ClientError<H>> { | ||||
|         self.tx.send(Notify::Connect(url, handler)).map_err(|e| { | ||||
|             match e.0 { | ||||
|                 Some(Notify::Connect(url, handler)) => ClientError(Some((url, handler))), | ||||
|                 _ => ClientError(None) | ||||
|     /// Send a GET Request using this Client. | ||||
|     #[inline] | ||||
|     pub fn get(&self, url: Url) -> FutureResponse { | ||||
|         self.request(Request::new(Method::Get, url)) | ||||
|     } | ||||
|  | ||||
|     /// Send a constructed Request using this Client. | ||||
|     #[inline] | ||||
|     pub fn request(&self, req: Request) -> FutureResponse { | ||||
|         self.call(req) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A `Future` that will resolve to an HTTP Response. | ||||
| pub struct FutureResponse(Box<Future<Item=Response, Error=::Error> + 'static>); | ||||
|  | ||||
| impl fmt::Debug for FutureResponse { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.pad("Future<Response>") | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Future for FutureResponse { | ||||
|     type Item = Response; | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         self.0.poll() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<C: Connect> Service for Client<C> { | ||||
|     type Request = Request; | ||||
|     type Response = Response; | ||||
|     type Error = ::Error; | ||||
|     type Future = FutureResponse; | ||||
|  | ||||
|     fn call(&self, req: Request) -> Self::Future { | ||||
|         let url = req.url().clone(); | ||||
|  | ||||
|         let (mut head, body) = request::split(req); | ||||
|         let mut headers = Headers::new(); | ||||
|         headers.set(Host { | ||||
|             hostname: url.host_str().unwrap().to_owned(), | ||||
|             port: url.port().or(None), | ||||
|         }); | ||||
|         headers.extend(head.headers.iter()); | ||||
|         head.subject.1 = RequestUri::AbsolutePath { | ||||
|             path: url.path().to_owned(), | ||||
|             query: url.query().map(ToOwned::to_owned), | ||||
|         }; | ||||
|         head.headers = headers; | ||||
|  | ||||
|         let checkout = self.pool.checkout(&url[..::url::Position::BeforePath]); | ||||
|         let connect = { | ||||
|             let handle = self.handle.clone(); | ||||
|             let pool = self.pool.clone(); | ||||
|             let pool_key = Rc::new(url[..::url::Position::BeforePath].to_owned()); | ||||
|             self.connector.connect(url) | ||||
|                 .map(move |io| { | ||||
|                     let (tx, rx) = relay::channel(); | ||||
|                     let client = HttpClient { | ||||
|                         client_rx: RefCell::new(Some(rx)), | ||||
|                     }.bind_client(&handle, io); | ||||
|                     let pooled = pool.pooled(pool_key, client); | ||||
|                     tx.complete(pooled.clone()); | ||||
|                     pooled | ||||
|                 }) | ||||
|         }; | ||||
|  | ||||
|         let race = checkout.select(connect) | ||||
|             .map(|(client, _work)| client) | ||||
|             .map_err(|(e, _work)| { | ||||
|                 // the Pool Checkout cannot error, so the only error | ||||
|                 // is from the Connector | ||||
|                 // XXX: should wait on the Checkout? Problem is | ||||
|                 // that if the connector is failing, it may be that we | ||||
|                 // never had a pooled stream at all | ||||
|                 e.into() | ||||
|             }); | ||||
|         let req = race.and_then(move |client| { | ||||
|             let msg = match body { | ||||
|                 Some(body) => { | ||||
|                     Message::WithBody(head, body.into()) | ||||
|                 }, | ||||
|                 None => Message::WithoutBody(head), | ||||
|             }; | ||||
|             client.call(msg) | ||||
|         }); | ||||
|         FutureResponse(Box::new(req.map(|msg| { | ||||
|             match msg { | ||||
|                 Message::WithoutBody(head) => response::new(head, None), | ||||
|                 Message::WithBody(head, body) => response::new(head, Some(body.into())), | ||||
|             } | ||||
|         }) | ||||
|         }))) | ||||
|     } | ||||
|  | ||||
|     /// Close the Client loop. | ||||
|     pub fn close(self) { | ||||
|         // Most errors mean that the Receivers are already dead, which would | ||||
|         // imply the EventLoop panicked. | ||||
|         let _ = self.tx.send(Notify::Shutdown); | ||||
| } | ||||
|  | ||||
| impl<C> fmt::Debug for Client<C> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.pad("Client") | ||||
|     } | ||||
| } | ||||
|  | ||||
| type TokioClient = ClientProxy<Message<http::RequestHead, TokioBody>, Message<http::ResponseHead, TokioBody>, ::Error>; | ||||
|  | ||||
| struct HttpClient { | ||||
|     client_rx: RefCell<Option<relay::Receiver<Pooled<TokioClient>>>>, | ||||
| } | ||||
|  | ||||
| impl<T: Io + 'static> ClientProto<T> for HttpClient { | ||||
|     type Request = http::RequestHead; | ||||
|     type RequestBody = http::Chunk; | ||||
|     type Response = http::ResponseHead; | ||||
|     type ResponseBody = http::Chunk; | ||||
|     type Error = ::Error; | ||||
|     type Transport = http::Conn<T, http::ClientTransaction, Pooled<TokioClient>>; | ||||
|     type BindTransport = BindingClient<T>; | ||||
|  | ||||
|     fn bind_transport(&self, io: T) -> Self::BindTransport { | ||||
|         BindingClient { | ||||
|             rx: self.client_rx.borrow_mut().take().expect("client_rx was lost"), | ||||
|             io: Some(io), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct BindingClient<T> { | ||||
|     rx: relay::Receiver<Pooled<TokioClient>>, | ||||
|     io: Option<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Io + 'static> Future for BindingClient<T> { | ||||
|     type Item = http::Conn<T, http::ClientTransaction, Pooled<TokioClient>>; | ||||
|     type Error = io::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         match self.rx.poll() { | ||||
|             Ok(Async::Ready(client)) => Ok(Async::Ready( | ||||
|                     http::Conn::new(self.io.take().expect("binding client io lost"), client) | ||||
|             )), | ||||
|             Ok(Async::NotReady) => Ok(Async::NotReady), | ||||
|             Err(_canceled) => unreachable!(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Configuration for a Client | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Config<C> { | ||||
|     connect_timeout: Duration, | ||||
|     //connect_timeout: Duration, | ||||
|     connector: C, | ||||
|     keep_alive: bool, | ||||
|     keep_alive_timeout: Option<Duration>, | ||||
|     //TODO: make use of max_idle config | ||||
|     max_idle: usize, | ||||
|     max_sockets: usize, | ||||
|     dns_workers: usize, | ||||
| } | ||||
|  | ||||
| impl<C> Config<C> where C: Connect + Send + 'static { | ||||
| /// Phantom type used to signal that `Config` should create a `HttpConnector`. | ||||
| #[derive(Debug, Clone, Copy)] | ||||
| pub struct UseDefaultConnector(()); | ||||
|  | ||||
| impl Config<UseDefaultConnector> { | ||||
|     fn default() -> Config<UseDefaultConnector> { | ||||
|         Config { | ||||
|             //connect_timeout: Duration::from_secs(10), | ||||
|             connector: UseDefaultConnector(()), | ||||
|             keep_alive: true, | ||||
|             keep_alive_timeout: Some(Duration::from_secs(90)), | ||||
|             max_idle: 5, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<C> Config<C> { | ||||
|     /// Set the `Connect` type to be used. | ||||
|     #[inline] | ||||
|     pub fn connector<CC: Connect>(self, val: CC) -> Config<CC> { | ||||
|         Config { | ||||
|             connect_timeout: self.connect_timeout, | ||||
|             //connect_timeout: self.connect_timeout, | ||||
|             connector: val, | ||||
|             keep_alive: self.keep_alive, | ||||
|             keep_alive_timeout: Some(Duration::from_secs(60 * 2)), | ||||
|             keep_alive_timeout: self.keep_alive_timeout, | ||||
|             max_idle: self.max_idle, | ||||
|             max_sockets: self.max_sockets, | ||||
|             dns_workers: self.dns_workers, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -189,15 +289,7 @@ impl<C> Config<C> where C: Connect + Send + 'static { | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Set the max table size allocated for holding on to live sockets. | ||||
|     /// | ||||
|     /// Default is 1024. | ||||
|     #[inline] | ||||
|     pub fn max_sockets(mut self, val: usize) -> Config<C> { | ||||
|         self.max_sockets = val; | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     /// Set the timeout for connecting to a URL. | ||||
|     /// | ||||
|     /// Default is 10 seconds. | ||||
| @@ -206,584 +298,25 @@ impl<C> Config<C> where C: Connect + Send + 'static { | ||||
|         self.connect_timeout = val; | ||||
|         self | ||||
|     } | ||||
|     */ | ||||
| } | ||||
|  | ||||
|     /// Set number of Dns workers to use for this client | ||||
|     /// | ||||
|     /// Default is 4 | ||||
|     #[inline] | ||||
|     pub fn dns_workers(mut self, workers: usize) -> Config<C> { | ||||
|         self.dns_workers = workers; | ||||
|         self | ||||
|     } | ||||
|  | ||||
| impl<C: Connect> Config<C> { | ||||
|     /// Construct the Client with this configuration. | ||||
|     #[inline] | ||||
|     pub fn build<H: Handler<C::Output>>(self) -> ::Result<Client<H>> { | ||||
|         Client::configured(self) | ||||
|     pub fn build(self, handle: &Handle) -> Client<C> { | ||||
|         Client::configured(self, handle) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for Config<DefaultConnector> { | ||||
|     fn default() -> Config<DefaultConnector> { | ||||
|         Config { | ||||
|             connect_timeout: Duration::from_secs(10), | ||||
|             connector: DefaultConnector::default(), | ||||
|             keep_alive: true, | ||||
|             keep_alive_timeout: Some(Duration::from_secs(60 * 2)), | ||||
|             max_idle: 5, | ||||
|             max_sockets: 1024, | ||||
|             dns_workers: 4, | ||||
|         } | ||||
| impl Config<UseDefaultConnector> { | ||||
|     /// Construct the Client with this configuration. | ||||
|     #[inline] | ||||
|     pub fn build(self, handle: &Handle) -> Client<HttpConnector> { | ||||
|         self.connector(HttpConnector::new(4, handle)).build(handle) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// An error that can occur when trying to queue a request. | ||||
| #[derive(Debug)] | ||||
| pub struct ClientError<H>(Option<(Url, H)>); | ||||
|  | ||||
| impl<H> ClientError<H> { | ||||
|     /// If the event loop was down, the `Url` and `Handler` can be recovered | ||||
|     /// from this method. | ||||
|     pub fn recover(self) -> Option<(Url, H)> { | ||||
|         self.0 | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<H: fmt::Debug + ::std::any::Any> ::std::error::Error for ClientError<H> { | ||||
|     fn description(&self) -> &str { | ||||
|         "Cannot queue request" | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<H> fmt::Display for ClientError<H> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_str("Cannot queue request") | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A trait to react to client events that happen for each message. | ||||
| /// | ||||
| /// Each event handler returns it's desired `Next` action. | ||||
| pub trait Handler<T: Transport>: Send + 'static { | ||||
|     /// This event occurs first, triggering when a `Request` head can be written.. | ||||
|     fn on_request(&mut self, request: &mut Request) -> http::Next; | ||||
|     /// This event occurs each time the `Request` is ready to be written to. | ||||
|     fn on_request_writable(&mut self, request: &mut http::Encoder<T>) -> http::Next; | ||||
|     /// This event occurs after the first time this handler signals `Next::read()`, | ||||
|     /// and a Response has been parsed. | ||||
|     fn on_response(&mut self, response: Response) -> http::Next; | ||||
|     /// This event occurs each time the `Response` is ready to be read from. | ||||
|     fn on_response_readable(&mut self, response: &mut http::Decoder<T>) -> http::Next; | ||||
|  | ||||
|     /// This event occurs whenever an `Error` occurs outside of the other events. | ||||
|     /// | ||||
|     /// This could IO errors while waiting for events, or a timeout, etc. | ||||
|     fn on_error(&mut self, err: ::Error) -> http::Next { | ||||
|         debug!("default Handler.on_error({:?})", err); | ||||
|         http::Next::remove() | ||||
|     } | ||||
|  | ||||
|     /// This event occurs when this Handler has requested to remove the Transport. | ||||
|     fn on_remove(self, _transport: T) where Self: Sized { | ||||
|         debug!("default Handler.on_remove"); | ||||
|     } | ||||
|  | ||||
|     /// Receive a `Control` to manage waiting for this request. | ||||
|     fn on_control(&mut self, _: http::Control) { | ||||
|         debug!("default Handler.on_control()"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct Message<H: Handler<T>, T: Transport> { | ||||
|     handler: H, | ||||
|     url: Option<Url>, | ||||
|     _marker: PhantomData<T>, | ||||
| } | ||||
|  | ||||
| impl<H: Handler<T>, T: Transport> http::MessageHandler<T> for Message<H, T> { | ||||
|     type Message = http::ClientMessage; | ||||
|  | ||||
|     fn on_outgoing(&mut self, head: &mut RequestHead) -> Next { | ||||
|         let url = self.url.take().expect("Message.url is missing"); | ||||
|         if let Some(host) = url.host_str() { | ||||
|             head.headers.set(Host { | ||||
|                 hostname: host.to_owned(), | ||||
|                 port: url.port(), | ||||
|             }); | ||||
|         } | ||||
|         head.subject.1 = RequestUri::AbsolutePath { | ||||
|             path: url.path().to_owned(), | ||||
|             query: url.query().map(|q| q.to_owned()), | ||||
|         }; | ||||
|         let mut req = self::request::new(head); | ||||
|         self.handler.on_request(&mut req) | ||||
|     } | ||||
|  | ||||
|     fn on_encode(&mut self, transport: &mut http::Encoder<T>) -> Next { | ||||
|         self.handler.on_request_writable(transport) | ||||
|     } | ||||
|  | ||||
|     fn on_incoming(&mut self, head: http::ResponseHead, _: &T) -> Next { | ||||
|         trace!("on_incoming {:?}", head); | ||||
|         let resp = response::new(head); | ||||
|         self.handler.on_response(resp) | ||||
|     } | ||||
|  | ||||
|     fn on_decode(&mut self, transport: &mut http::Decoder<T>) -> Next { | ||||
|         self.handler.on_response_readable(transport) | ||||
|     } | ||||
|  | ||||
|     fn on_error(&mut self, error: ::Error) -> Next { | ||||
|         self.handler.on_error(error) | ||||
|     } | ||||
|  | ||||
|     fn on_remove(self, transport: T) { | ||||
|         self.handler.on_remove(transport); | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct Context<K, H, C: Connect> { | ||||
|     connect_timeout: Duration, | ||||
|     keep_alive: bool, | ||||
|     idle_conns: HashMap<K, VecDeque<http::Control>>, | ||||
|     queue: HashMap<K, VecDeque<Queued<H>>>, | ||||
|     awaiting_slot: VecDeque<(C::Key, C::Output)>, | ||||
| } | ||||
|  | ||||
| /// Macro for advancing state of a ClientFsm::Socket | ||||
| /// | ||||
| /// This was previously a method on Context, but due to eviction needs, this | ||||
| /// block now needs access to the registration APIs on rotor::Scope. | ||||
| macro_rules! conn_response { | ||||
|     ($scope:expr, $conn:expr, $time:expr) => {{ | ||||
|         match $conn { | ||||
|             Some((conn, timeout)) => { | ||||
|                 //TODO: HTTP2: a connection doesn't need to be idle to be used for a second stream | ||||
|                 if conn.is_idle() { | ||||
|                     $scope.idle_conns.entry(conn.key().clone()).or_insert_with(VecDeque::new) | ||||
|                         .push_back(conn.control()); | ||||
|                 } | ||||
|                 match timeout { | ||||
|                     Some(dur) => rotor::Response::ok(ClientFsm::Socket(conn)) | ||||
|                         .deadline($time + dur), | ||||
|                     None => rotor::Response::ok(ClientFsm::Socket(conn)), | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             None => { | ||||
|                 if let Some((key, socket)) = $scope.awaiting_slot.pop_front() { | ||||
|                     rotor_try!($scope.register(&socket, EventSet::writable() | EventSet::hup(), PollOpt::level())); | ||||
|                     rotor::Response::ok(ClientFsm::Connecting((key, socket))) | ||||
|                 } else { | ||||
|                     rotor::Response::done() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }} | ||||
| } | ||||
|  | ||||
| impl<K: http::Key, H, C: Connect> Context<K, H, C> { | ||||
|     fn pop_queue(&mut self, key: &K) -> Option<Queued<H>> { | ||||
|         let mut should_remove = false; | ||||
|         let queued = { | ||||
|             self.queue.get_mut(key).and_then(|vec| { | ||||
|                 let queued = vec.pop_front(); | ||||
|                 if vec.is_empty() { | ||||
|                     should_remove = true; | ||||
|                 } | ||||
|                 queued | ||||
|             }) | ||||
|         }; | ||||
|         if should_remove { | ||||
|             self.queue.remove(key); | ||||
|         } | ||||
|  | ||||
|         queued | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<K, H, T, C> http::MessageHandlerFactory<K, T> for Context<K, H, C> | ||||
|     where K: http::Key, | ||||
|           H: Handler<T>, | ||||
|           T: Transport, | ||||
|           C: Connect | ||||
| { | ||||
|     type Output = Message<H, T>; | ||||
|  | ||||
|     fn create(&mut self, seed: http::Seed<K>) -> Option<Self::Output> { | ||||
|         let key = seed.key(); | ||||
|         self.pop_queue(key).map(|queued| { | ||||
|             let (url, mut handler) = (queued.url, queued.handler); | ||||
|             handler.on_control(seed.control()); | ||||
|  | ||||
|             Message { | ||||
|                 handler: handler, | ||||
|                 url: Some(url), | ||||
|                 _marker: PhantomData, | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     fn keep_alive_interest(&self) -> Next { | ||||
|         Next::wait() | ||||
|     } | ||||
| } | ||||
|  | ||||
| enum Notify<T> { | ||||
|     Connect(Url, T), | ||||
|     Shutdown, | ||||
| } | ||||
|  | ||||
| enum ClientFsm<C, H> | ||||
| where C: Connect, | ||||
|       C::Output: Transport, | ||||
|       H: Handler<C::Output> { | ||||
|     Connector(C, http::channel::Receiver<Notify<H>>), | ||||
|     Connecting((C::Key, C::Output)), | ||||
|     Socket(http::Conn<C::Key, C::Output, Message<H, C::Output>>) | ||||
| } | ||||
|  | ||||
| unsafe impl<C, H> Send for ClientFsm<C, H> | ||||
| where | ||||
|     C: Connect + Send, | ||||
|     //C::Key, // Key doesn't need to be Send | ||||
|     C::Output: Transport, // Tranport doesn't need to be Send | ||||
|     H: Handler<C::Output> + Send | ||||
| {} | ||||
|  | ||||
| impl<C, H> rotor::Machine for ClientFsm<C, H> | ||||
| where C: Connect, | ||||
|       C::Key: fmt::Debug, | ||||
|       C::Output: Transport, | ||||
|       H: Handler<C::Output> { | ||||
|     type Context = Context<C::Key, H, C>; | ||||
|     type Seed = (C::Key, C::Output); | ||||
|  | ||||
|     fn create(seed: Self::Seed, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, rotor::Void> { | ||||
|         rotor_try!(scope.register(&seed.1, EventSet::writable() | EventSet::hup(), PollOpt::level())); | ||||
|         rotor::Response::ok(ClientFsm::Connecting(seed)) | ||||
|     } | ||||
|  | ||||
|     fn ready(self, events: EventSet, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ClientFsm::Socket(conn) => { | ||||
|                 let mut conn = Some(conn); | ||||
|                 loop { | ||||
|                     match conn.take().unwrap().ready(events, scope) { | ||||
|                         ReadyResult::Done(res) => { | ||||
|                             let now = scope.now(); | ||||
|                             return conn_response!(scope, res, now); | ||||
|                         }, | ||||
|                         ReadyResult::Continue(c) => conn = Some(c), | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             ClientFsm::Connecting(mut seed) => { | ||||
|                 if events.is_error() || events.is_hup() { | ||||
|                     if let Some(err) = seed.1.take_socket_error().err() { | ||||
|                         debug!("error while connecting: {:?}", err); | ||||
|                         scope.pop_queue(&seed.0).map(move |mut queued| queued.handler.on_error(::Error::Io(err))); | ||||
|                     } else { | ||||
|                         trace!("connecting is_error, but no socket error"); | ||||
|                     } | ||||
|  | ||||
|                     rotor::Response::done() | ||||
|                 } else if events.is_writable() { | ||||
|                     if scope.queue.contains_key(&seed.0) { | ||||
|                         trace!("connected and writable {:?}", seed.0); | ||||
|                         rotor::Response::ok( | ||||
|                             ClientFsm::Socket( | ||||
|                                 http::Conn::new( | ||||
|                                     seed.0, | ||||
|                                     seed.1, | ||||
|                                     Next::write().timeout(scope.connect_timeout), | ||||
|                                     scope.notifier(), | ||||
|                                     scope.now() | ||||
|                                 ).keep_alive(scope.keep_alive) | ||||
|                             ) | ||||
|                         ) | ||||
|                     } else { | ||||
|                         trace!("connected, but queued handler is gone: {:?}", seed.0); // probably took too long connecting | ||||
|                         rotor::Response::done() | ||||
|                     } | ||||
|                 } else { | ||||
|                     // spurious? | ||||
|                     rotor::Response::ok(ClientFsm::Connecting(seed)) | ||||
|                 } | ||||
|             } | ||||
|             ClientFsm::Connector(..) => { | ||||
|                 unreachable!("Connector can never be ready") | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn spawned(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ClientFsm::Connector(..) => self.connect(scope), | ||||
|             other => rotor::Response::ok(other) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn spawn_error( | ||||
|         self, | ||||
|         scope: &mut Scope<Self::Context>, | ||||
|         error: rotor::SpawnError<Self::Seed> | ||||
|     ) -> rotor::Response<Self, Self::Seed> { | ||||
|         // see if there's an idle connections that can be terminated. If yes, put this seed on a | ||||
|         // list waiting for empty slot. | ||||
|         if let rotor::SpawnError::NoSlabSpace((key, socket)) = error { | ||||
|             if let Some(mut queued) = scope.pop_queue(&key) { | ||||
|                 trace!("attempting to remove an idle socket"); | ||||
|                 // Remove an idle connection. Any connection. Just make some space | ||||
|                 // for the new request. | ||||
|                 let mut remove_keys = Vec::new(); | ||||
|                 let mut found_idle = false; | ||||
|  | ||||
|                 // Check all idle connections regardless of origin | ||||
|                 for (key, idle) in scope.idle_conns.iter_mut() { | ||||
|                     // Pop from the front since those are lease recently used | ||||
|                     while let Some(ctrl) = idle.pop_front() { | ||||
|                         // Signal connection to close. An err here means the | ||||
|                         // socket is already dead can should be tossed. | ||||
|                         if ctrl.ready(Next::remove()).is_ok() { | ||||
|                             found_idle = true; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     // This list is empty, mark it for removal | ||||
|                     if idle.is_empty() { | ||||
|                         remove_keys.push(key.to_owned()); | ||||
|                     } | ||||
|  | ||||
|                     // if found, stop looking for an idle connection. | ||||
|                     if found_idle { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 trace!("idle conns: {:?}", scope.idle_conns); | ||||
|  | ||||
|                 // Remove empty idle lists. | ||||
|                 for key in &remove_keys { | ||||
|                     scope.idle_conns.remove(&key); | ||||
|                 } | ||||
|  | ||||
|                 if found_idle { | ||||
|                     // A socket should be evicted soon; put it on a queue to | ||||
|                     // consume newly freed slot. Also need to put the Queued<H> | ||||
|                     // back onto front of queue. | ||||
|                     scope.awaiting_slot.push_back((key.clone(), socket)); | ||||
|                     scope.queue | ||||
|                         .entry(key) | ||||
|                         .or_insert_with(VecDeque::new) | ||||
|                         .push_back(queued); | ||||
|                 } else { | ||||
|                     // Couldn't evict a socket, just run the error handler. | ||||
|                     debug!("Error spawning state machine; slab full and no sockets idle"); | ||||
|                     let _ = queued.handler.on_error(::Error::Full); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         self.connect(scope) | ||||
|     } | ||||
|  | ||||
|     fn timeout(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         trace!("timeout now = {:?}", scope.now()); | ||||
|         match self { | ||||
|             ClientFsm::Connector(..) => { | ||||
|                 let now = scope.now(); | ||||
|                 let mut empty_keys = Vec::new(); | ||||
|                 { | ||||
|                     for (key, mut vec) in &mut scope.queue { | ||||
|                         while !vec.is_empty() && vec[0].deadline <= now { | ||||
|                             vec.pop_front() | ||||
|                                .map(|mut queued| queued.handler.on_error(::Error::Timeout)); | ||||
|                         } | ||||
|                         if vec.is_empty() { | ||||
|                             empty_keys.push(key.clone()); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 for key in &empty_keys { | ||||
|                     scope.queue.remove(key); | ||||
|                 } | ||||
|                 match self.deadline(scope) { | ||||
|                     Some(deadline) => { | ||||
|                         rotor::Response::ok(self).deadline(deadline) | ||||
|                     }, | ||||
|                     None => rotor::Response::ok(self) | ||||
|                 } | ||||
|             } | ||||
|             ClientFsm::Connecting(..) => unreachable!(), | ||||
|             ClientFsm::Socket(conn) => { | ||||
|                 let res = conn.timeout(scope); | ||||
|                 let now = scope.now(); | ||||
|                 conn_response!(scope, res, now) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn wakeup(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ClientFsm::Connector(..) => { | ||||
|                 self.connect(scope) | ||||
|             }, | ||||
|             ClientFsm::Socket(conn) => { | ||||
|                 let res = conn.wakeup(scope); | ||||
|                 let now = scope.now(); | ||||
|                 conn_response!(scope, res, now) | ||||
|             }, | ||||
|             ClientFsm::Connecting(..) => unreachable!("connecting sockets should not be woken up") | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<C, H> ClientFsm<C, H> | ||||
| where C: Connect, | ||||
|       C::Key: fmt::Debug, | ||||
|       C::Output: Transport, | ||||
|       H: Handler<C::Output> { | ||||
|     fn connect(self, scope: &mut rotor::Scope<<Self as rotor::Machine>::Context>) -> rotor::Response<Self, <Self as rotor::Machine>::Seed> { | ||||
|         match self { | ||||
|             ClientFsm::Connector(mut connector, rx) => { | ||||
|                 if let Some((key, res)) = connector.connected() { | ||||
|                     match res { | ||||
|                         Ok(socket) => { | ||||
|                             trace!("connecting {:?}", key); | ||||
|                             return rotor::Response::spawn(ClientFsm::Connector(connector, rx), (key, socket)); | ||||
|                         }, | ||||
|                         Err(e) => { | ||||
|                             trace!("connect error = {:?}", e); | ||||
|                             scope.pop_queue(&key).map(|mut queued| queued.handler.on_error(::Error::Io(e))); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 loop { | ||||
|                     match rx.try_recv() { | ||||
|                         Ok(Notify::Connect(url, mut handler)) => { | ||||
|                             // check pool for sockets to this domain | ||||
|                             if let Some(key) = connector.key(&url) { | ||||
|                                 let mut remove_idle = false; | ||||
|                                 let mut woke_up = false; | ||||
|                                 if let Some(mut idle) = scope.idle_conns.get_mut(&key) { | ||||
|                                     // Pop from back since those are most recently used. Connections | ||||
|                                     // at the front are allowed to expire. | ||||
|                                     while let Some(ctrl) = idle.pop_back() { | ||||
|                                         // err means the socket has since died | ||||
|                                         if ctrl.ready(Next::write()).is_ok() { | ||||
|                                             woke_up = true; | ||||
|                                             break; | ||||
|                                         } | ||||
|                                     } | ||||
|                                     remove_idle = idle.is_empty(); | ||||
|                                 } | ||||
|                                 if remove_idle { | ||||
|                                     scope.idle_conns.remove(&key); | ||||
|                                 } | ||||
|  | ||||
|                                 if woke_up { | ||||
|                                     trace!("woke up idle conn for '{}'", url); | ||||
|                                     let deadline = scope.now() + scope.connect_timeout; | ||||
|                                     scope.queue | ||||
|                                         .entry(key) | ||||
|                                         .or_insert_with(VecDeque::new) | ||||
|                                         .push_back(Queued { | ||||
|                                             deadline: deadline, | ||||
|                                             handler: handler, | ||||
|                                             url: url | ||||
|                                         }); | ||||
|                                     continue; | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 // this connector cannot handle this url anyways | ||||
|                                 let _ = handler.on_error(io::Error::new(io::ErrorKind::InvalidInput, "invalid url for connector").into()); | ||||
|                                 continue; | ||||
|                             } | ||||
|                             // no exist connection, call connector | ||||
|                             match connector.connect(&url) { | ||||
|                                 Ok(key) => { | ||||
|                                     let deadline = scope.now() + scope.connect_timeout; | ||||
|                                     scope.queue | ||||
|                                         .entry(key) | ||||
|                                         .or_insert_with(VecDeque::new) | ||||
|                                         .push_back(Queued { | ||||
|                                             deadline: deadline, | ||||
|                                             handler: handler, | ||||
|                                             url: url | ||||
|                                         }); | ||||
|                                 } | ||||
|                                 Err(e) => { | ||||
|                                     let _todo = handler.on_error(e.into()); | ||||
|                                     trace!("Connect error, next={:?}", _todo); | ||||
|                                     continue; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         Ok(Notify::Shutdown) => { | ||||
|                             scope.shutdown_loop(); | ||||
|                             return rotor::Response::done() | ||||
|                         }, | ||||
|                         Err(mpsc::TryRecvError::Disconnected) => { | ||||
|                             // if there is no way to send additional requests, | ||||
|                             // what more can the loop do? i suppose we should | ||||
|                             // shutdown. | ||||
|                             scope.shutdown_loop(); | ||||
|                             return rotor::Response::done() | ||||
|                         } | ||||
|                         Err(mpsc::TryRecvError::Empty) => { | ||||
|                             // spurious wakeup or loop is done | ||||
|                             let fsm = ClientFsm::Connector(connector, rx); | ||||
|                             return match fsm.deadline(scope) { | ||||
|                                 Some(deadline) => { | ||||
|                                     rotor::Response::ok(fsm).deadline(deadline) | ||||
|                                 }, | ||||
|                                 None => rotor::Response::ok(fsm) | ||||
|                             }; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             other => rotor::Response::ok(other) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn deadline(&self, scope: &mut rotor::Scope<<Self as rotor::Machine>::Context>) -> Option<rotor::Time> { | ||||
|         match *self { | ||||
|             ClientFsm::Connector(..) => { | ||||
|                 let mut earliest = None; | ||||
|                 for vec in scope.queue.values() { | ||||
|                     for queued in vec { | ||||
|                         match earliest { | ||||
|                             Some(ref mut earliest) => { | ||||
|                                 if queued.deadline < *earliest { | ||||
|                                     *earliest = queued.deadline; | ||||
|                                 } | ||||
|                             } | ||||
|                             None => earliest = Some(queued.deadline) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 trace!("deadline = {:?}, now = {:?}", earliest, scope.now()); | ||||
|                 earliest | ||||
|             } | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct Queued<H> { | ||||
|     deadline: rotor::Time, | ||||
|     handler: H, | ||||
|     url: Url, | ||||
| } | ||||
|  | ||||
| #[doc(hidden)] | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct Registration { | ||||
|     notify: (http::channel::Sender<self::dns::Answer>, http::channel::Receiver<self::dns::Answer>), | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|   | ||||
							
								
								
									
										353
									
								
								src/client/pool.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								src/client/pool.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,353 @@ | ||||
| use std::cell::{Cell, RefCell}; | ||||
| use std::collections::{HashMap, VecDeque}; | ||||
| use std::fmt; | ||||
| use std::io; | ||||
| use std::ops::{Deref, DerefMut, BitAndAssign}; | ||||
| use std::rc::Rc; | ||||
| use std::time::{Duration, Instant}; | ||||
|  | ||||
| use futures::{Future, Async, Poll}; | ||||
| use relay; | ||||
|  | ||||
| use http::{KeepAlive, KA}; | ||||
|  | ||||
| pub struct Pool<T> { | ||||
|     inner: Rc<RefCell<PoolInner<T>>>, | ||||
| } | ||||
|  | ||||
| struct PoolInner<T> { | ||||
|     enabled: bool, | ||||
|     idle: HashMap<Rc<String>, Vec<Entry<T>>>, | ||||
|     parked: HashMap<Rc<String>, VecDeque<relay::Sender<Entry<T>>>>, | ||||
|     timeout: Option<Duration>, | ||||
| } | ||||
|  | ||||
| impl<T: Clone> Pool<T> { | ||||
|     pub fn new(enabled: bool, timeout: Option<Duration>) -> Pool<T> { | ||||
|         Pool { | ||||
|             inner: Rc::new(RefCell::new(PoolInner { | ||||
|                 enabled: enabled, | ||||
|                 idle: HashMap::new(), | ||||
|                 parked: HashMap::new(), | ||||
|                 timeout: timeout, | ||||
|             })), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn checkout(&self, key: &str) -> Checkout<T> { | ||||
|         Checkout { | ||||
|             key: Rc::new(key.to_owned()), | ||||
|             pool: self.clone(), | ||||
|             parked: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn put(&mut self, key: Rc<String>, entry: Entry<T>) { | ||||
|         trace!("Pool::put {:?}", key); | ||||
|         let mut remove_parked = false; | ||||
|         let tx = self.inner.borrow_mut().parked.get_mut(&key).and_then(|parked| { | ||||
|             let mut ret = None; | ||||
|             while let Some(tx) = parked.pop_front() { | ||||
|                 if !tx.is_canceled() { | ||||
|                     ret = Some(tx); | ||||
|                     break; | ||||
|                 } | ||||
|                 trace!("Pool::put removing canceled parked {:?}", key); | ||||
|             } | ||||
|             remove_parked = parked.is_empty(); | ||||
|             ret | ||||
|         }); | ||||
|         if remove_parked { | ||||
|             self.inner.borrow_mut().parked.remove(&key); | ||||
|         } | ||||
|  | ||||
|         if let Some(tx) = tx { | ||||
|             trace!("Pool::put found parked {:?}", key); | ||||
|             tx.complete(entry); | ||||
|         } else { | ||||
|             self.inner.borrow_mut() | ||||
|                 .idle.entry(key) | ||||
|                 .or_insert(Vec::new()) | ||||
|                 .push(entry); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn pooled(&self, key: Rc<String>, value: T) -> Pooled<T> { | ||||
|         trace!("Pool::pooled {:?}", key); | ||||
|         Pooled { | ||||
|             entry: Entry { | ||||
|                 value: value, | ||||
|                 is_reused: false, | ||||
|                 status: Rc::new(Cell::new(KA::Busy)), | ||||
|             }, | ||||
|             key: key, | ||||
|             pool: self.clone(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn is_enabled(&self) -> bool { | ||||
|         self.inner.borrow().enabled | ||||
|     } | ||||
|  | ||||
|     fn reuse(&self, key: Rc<String>, mut entry: Entry<T>) -> Pooled<T> { | ||||
|         trace!("Pool::reuse {:?}", key); | ||||
|         entry.is_reused = true; | ||||
|         entry.status.set(KA::Busy); | ||||
|         Pooled { | ||||
|             entry: entry, | ||||
|             key: key, | ||||
|             pool: self.clone(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn park(&mut self, key: Rc<String>, tx: relay::Sender<Entry<T>>) { | ||||
|         trace!("Pool::park {:?}", key); | ||||
|         self.inner.borrow_mut() | ||||
|             .parked.entry(key) | ||||
|             .or_insert(VecDeque::new()) | ||||
|             .push_back(tx); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Clone for Pool<T> { | ||||
|     fn clone(&self) -> Pool<T> { | ||||
|         Pool { | ||||
|             inner: self.inner.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct Pooled<T> { | ||||
|     entry: Entry<T>, | ||||
|     key: Rc<String>, | ||||
|     pool: Pool<T>, | ||||
| } | ||||
|  | ||||
| impl<T> Deref for Pooled<T> { | ||||
|     type Target = T; | ||||
|     fn deref(&self) -> &T { | ||||
|         &self.entry.value | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> DerefMut for Pooled<T> { | ||||
|     fn deref_mut(&mut self) -> &mut T { | ||||
|         &mut self.entry.value | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Clone> KeepAlive for Pooled<T> { | ||||
|     fn busy(&mut self) { | ||||
|         self.entry.status.set(KA::Busy); | ||||
|     } | ||||
|  | ||||
|     fn disable(&mut self) { | ||||
|         self.entry.status.set(KA::Disabled); | ||||
|     } | ||||
|  | ||||
|     fn idle(&mut self) { | ||||
|         let previous = self.status(); | ||||
|         self.entry.status.set(KA::Idle(Instant::now())); | ||||
|         if let KA::Idle(..) = previous { | ||||
|             trace!("Pooled::idle already idle"); | ||||
|             return; | ||||
|         } | ||||
|         self.entry.is_reused = true; | ||||
|         if self.pool.is_enabled() { | ||||
|             self.pool.put(self.key.clone(), self.entry.clone()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn status(&self) -> KA { | ||||
|         self.entry.status.get() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> fmt::Debug for Pooled<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Pooled") | ||||
|             .field("status", &self.entry.status.get()) | ||||
|             .field("key", &self.key) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Clone> BitAndAssign<bool> for Pooled<T> { | ||||
|     fn bitand_assign(&mut self, enabled: bool) { | ||||
|         if !enabled { | ||||
|             self.disable(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| struct Entry<T> { | ||||
|     value: T, | ||||
|     is_reused: bool, | ||||
|     status: Rc<Cell<KA>>, | ||||
| } | ||||
|  | ||||
| pub struct Checkout<T> { | ||||
|     key: Rc<String>, | ||||
|     pool: Pool<T>, | ||||
|     parked: Option<relay::Receiver<Entry<T>>>, | ||||
| } | ||||
|  | ||||
| impl<T: Clone> Future for Checkout<T> { | ||||
|     type Item = Pooled<T>; | ||||
|     type Error = io::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||
|         trace!("Checkout::poll"); | ||||
|         let mut drop_parked = false; | ||||
|         if let Some(ref mut rx) = self.parked { | ||||
|             match rx.poll() { | ||||
|                 Ok(Async::Ready(entry)) => { | ||||
|                     trace!("Checkout::poll found client in relay for {:?}", self.key); | ||||
|                     return Ok(Async::Ready(self.pool.reuse(self.key.clone(), entry))); | ||||
|                 }, | ||||
|                 Ok(Async::NotReady) => (), | ||||
|                 Err(_canceled) => drop_parked = true, | ||||
|             } | ||||
|         } | ||||
|         if drop_parked { | ||||
|             self.parked.take(); | ||||
|         } | ||||
|         let expiration = Expiration::new(self.pool.inner.borrow().timeout); | ||||
|         let key = &self.key; | ||||
|         trace!("Checkout::poll url = {:?}, expiration = {:?}", key, expiration.0); | ||||
|         let mut should_remove = false; | ||||
|         let entry = self.pool.inner.borrow_mut().idle.get_mut(key).and_then(|list| { | ||||
|             trace!("Checkout::poll key found {:?}", key); | ||||
|             while let Some(entry) = list.pop() { | ||||
|                 match entry.status.get() { | ||||
|                     KA::Idle(idle_at) if !expiration.expires(idle_at) => { | ||||
|                         trace!("Checkout::poll found idle client for {:?}", key); | ||||
|                         should_remove = list.is_empty(); | ||||
|                         return Some(entry); | ||||
|                     }, | ||||
|                     _ => { | ||||
|                         trace!("Checkout::poll removing unacceptable pooled {:?}", key); | ||||
|                         // every other case the Entry should just be dropped | ||||
|                         // 1. Idle but expired | ||||
|                         // 2. Busy (something else somehow took it?) | ||||
|                         // 3. Disabled don't reuse of course | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             should_remove = true; | ||||
|             None | ||||
|         }); | ||||
|  | ||||
|         if should_remove { | ||||
|             self.pool.inner.borrow_mut().idle.remove(key); | ||||
|         } | ||||
|         match entry { | ||||
|             Some(entry) => Ok(Async::Ready(self.pool.reuse(self.key.clone(), entry))), | ||||
|             None => { | ||||
|                 if self.parked.is_none() { | ||||
|                     let (tx, mut rx) = relay::channel(); | ||||
|                     let _ = rx.poll(); // park this task | ||||
|                     self.pool.park(self.key.clone(), tx); | ||||
|                     self.parked = Some(rx); | ||||
|                 } | ||||
|                 Ok(Async::NotReady) | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct Expiration(Option<Instant>); | ||||
|  | ||||
| impl Expiration { | ||||
|     fn new(dur: Option<Duration>) -> Expiration { | ||||
|         Expiration(dur.map(|dur| Instant::now() - dur)) | ||||
|     } | ||||
|  | ||||
|     fn expires(&self, instant: Instant) -> bool { | ||||
|         match self.0 { | ||||
|             Some(expire) => expire > instant, | ||||
|             None => false, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::rc::Rc; | ||||
|     use std::time::Duration; | ||||
|     use futures::{Async, Future}; | ||||
|     use http::KeepAlive; | ||||
|     use super::Pool; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_pool_checkout_smoke() { | ||||
|         let pool = Pool::new(true, Some(Duration::from_secs(5))); | ||||
|         let key = Rc::new("foo".to_string()); | ||||
|         let mut pooled = pool.pooled(key.clone(), 41); | ||||
|         pooled.idle(); | ||||
|  | ||||
|         match pool.checkout(&key).poll().unwrap() { | ||||
|             Async::Ready(pooled) => assert_eq!(*pooled, 41), | ||||
|             _ => panic!("not ready"), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_pool_checkout_returns_none_if_expired() { | ||||
|         ::futures::lazy(|| { | ||||
|             let pool = Pool::new(true, Some(Duration::from_secs(1))); | ||||
|             let key = Rc::new("foo".to_string()); | ||||
|             let mut pooled = pool.pooled(key.clone(), 41); | ||||
|             pooled.idle(); | ||||
|             ::std::thread::sleep(pool.inner.borrow().timeout.unwrap()); | ||||
|             assert!(pool.checkout(&key).poll().unwrap().is_not_ready()); | ||||
|             ::futures::future::ok::<(), ()>(()) | ||||
|         }).wait().unwrap(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_pool_removes_expired() { | ||||
|         let pool = Pool::new(true, Some(Duration::from_secs(1))); | ||||
|         let key = Rc::new("foo".to_string()); | ||||
|  | ||||
|         let mut pooled1 = pool.pooled(key.clone(), 41); | ||||
|         pooled1.idle(); | ||||
|         let mut pooled2 = pool.pooled(key.clone(), 5); | ||||
|         pooled2.idle(); | ||||
|         let mut pooled3 = pool.pooled(key.clone(), 99); | ||||
|         pooled3.idle(); | ||||
|  | ||||
|  | ||||
|         assert_eq!(pool.inner.borrow().idle.get(&key).map(|entries| entries.len()), Some(3)); | ||||
|         ::std::thread::sleep(pool.inner.borrow().timeout.unwrap()); | ||||
|  | ||||
|         pooled1.idle(); | ||||
|         pooled2.idle(); // idle after sleep, not expired | ||||
|         pool.checkout(&key).poll().unwrap(); | ||||
|         assert_eq!(pool.inner.borrow().idle.get(&key).map(|entries| entries.len()), Some(1)); | ||||
|         pool.checkout(&key).poll().unwrap(); | ||||
|         assert!(pool.inner.borrow().idle.get(&key).is_none()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_pool_checkout_task_unparked() { | ||||
|         let pool = Pool::new(true, Some(Duration::from_secs(10))); | ||||
|         let key = Rc::new("foo".to_string()); | ||||
|         let pooled1 = pool.pooled(key.clone(), 41); | ||||
|  | ||||
|         let mut pooled = pooled1.clone(); | ||||
|         let checkout = pool.checkout(&key).join(::futures::lazy(move || { | ||||
|             // the checkout future will park first, | ||||
|             // and then this lazy future will be polled, which will insert | ||||
|             // the pooled back into the pool | ||||
|             // | ||||
|             // this test makes sure that doing so will unpark the checkout | ||||
|             pooled.idle(); | ||||
|             Ok(()) | ||||
|         })).map(|(entry, _)| entry); | ||||
|         assert_eq!(*checkout.wait().unwrap(), *pooled1); | ||||
|     } | ||||
| } | ||||
| @@ -1,55 +1,90 @@ | ||||
| //! Client Requests | ||||
| use std::fmt; | ||||
|  | ||||
| use Url; | ||||
|  | ||||
| use header::Headers; | ||||
| use http::RequestHead; | ||||
| use http::{Body, RequestHead}; | ||||
| use method::Method; | ||||
| use uri::RequestUri; | ||||
| use version::HttpVersion; | ||||
|  | ||||
|  | ||||
|  | ||||
| /// A client request to a remote server. | ||||
| #[derive(Debug)] | ||||
| pub struct Request<'a> { | ||||
|     head: &'a mut RequestHead | ||||
| pub struct Request { | ||||
|     method: Method, | ||||
|     url: Url, | ||||
|     version: HttpVersion, | ||||
|     headers: Headers, | ||||
|     body: Option<Body>, | ||||
| } | ||||
|  | ||||
| impl<'a> Request<'a> { | ||||
| impl Request { | ||||
|     /// Construct a new Request. | ||||
|     #[inline] | ||||
|     pub fn new(method: Method, url: Url) -> Request { | ||||
|         Request { | ||||
|             method: method, | ||||
|             url: url, | ||||
|             version: HttpVersion::default(), | ||||
|             headers: Headers::new(), | ||||
|             body: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Read the Request Url. | ||||
|     #[inline] | ||||
|     pub fn uri(&self) -> &RequestUri { &self.head.subject.1 } | ||||
|     pub fn url(&self) -> &Url { &self.url } | ||||
|  | ||||
|     /// Readthe Request Version. | ||||
|     #[inline] | ||||
|     pub fn version(&self) -> &HttpVersion { &self.head.version } | ||||
|     pub fn version(&self) -> &HttpVersion { &self.version } | ||||
|  | ||||
|     /// Read the Request headers. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &Headers { &self.head.headers } | ||||
|     pub fn headers(&self) -> &Headers { &self.headers } | ||||
|  | ||||
|     /// Read the Request method. | ||||
|     #[inline] | ||||
|     pub fn method(&self) -> &Method { &self.head.subject.0 } | ||||
|     pub fn method(&self) -> &Method { &self.method } | ||||
|  | ||||
|     /// Set the Method of this request. | ||||
|     #[inline] | ||||
|     pub fn set_method(&mut self, method: Method) { self.head.subject.0 = method; } | ||||
|     pub fn set_method(&mut self, method: Method) { self.method = method; } | ||||
|  | ||||
|     /// Get a mutable reference to the Request headers. | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut Headers { &mut self.head.headers } | ||||
|     pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers } | ||||
|  | ||||
|     /// Set the `RequestUri` of this request. | ||||
|     /// Set the `Url` of this request. | ||||
|     #[inline] | ||||
|     pub fn set_uri(&mut self, uri: RequestUri) { self.head.subject.1 = uri; } | ||||
|     pub fn set_url(&mut self, url: Url) { self.url = url; } | ||||
|  | ||||
|     /// Set the `HttpVersion` of this request. | ||||
|     #[inline] | ||||
|     pub fn set_version(&mut self, version: HttpVersion) { self.head.version = version; } | ||||
|     pub fn set_version(&mut self, version: HttpVersion) { self.version = version; } | ||||
|  | ||||
|     /// Set the body of the request. | ||||
|     #[inline] | ||||
|     pub fn set_body<T: Into<Body>>(&mut self, body: T) { self.body = Some(body.into()); } | ||||
| } | ||||
|  | ||||
| pub fn new(head: &mut RequestHead) -> Request { | ||||
|     Request { head: head } | ||||
| impl fmt::Debug for Request { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Request") | ||||
|             .field("method", &self.method) | ||||
|             .field("url", &self.url) | ||||
|             .field("version", &self.version) | ||||
|             .field("headers", &self.headers) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn split(req: Request) -> (RequestHead, Option<Body>) { | ||||
|     let head = RequestHead { | ||||
|         subject: ::http::RequestLine(req.method, RequestUri::AbsoluteUri(req.url)), | ||||
|         headers: req.headers, | ||||
|         version: req.version, | ||||
|     }; | ||||
|     (head, req.body) | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| //! Client Responses | ||||
| use std::fmt; | ||||
|  | ||||
| use header; | ||||
| //use net::NetworkStream; | ||||
| use http::{self, RawStatus}; | ||||
| use http::{self, RawStatus, Body}; | ||||
| use status; | ||||
| use version; | ||||
|  | ||||
| pub fn new(incoming: http::ResponseHead) -> Response { | ||||
| pub fn new(incoming: http::ResponseHead, body: Option<Body>) -> Response { | ||||
|     trace!("Response::new"); | ||||
|     let status = status::StatusCode::from_u16(incoming.subject.0); | ||||
|     debug!("version={:?}, status={:?}", incoming.version, status); | ||||
| @@ -16,17 +16,18 @@ pub fn new(incoming: http::ResponseHead) -> Response { | ||||
|         version: incoming.version, | ||||
|         headers: incoming.headers, | ||||
|         status_raw: incoming.subject, | ||||
|         body: body, | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| /// A response for a client request to a remote server. | ||||
| #[derive(Debug)] | ||||
| pub struct Response { | ||||
|     status: status::StatusCode, | ||||
|     headers: header::Headers, | ||||
|     version: version::HttpVersion, | ||||
|     status_raw: RawStatus, | ||||
|     body: Option<Body>, | ||||
| } | ||||
|  | ||||
| impl Response { | ||||
| @@ -42,170 +43,23 @@ impl Response { | ||||
|     #[inline] | ||||
|     pub fn status_raw(&self) -> &RawStatus { &self.status_raw } | ||||
|  | ||||
|     /// Get the final URL of this response. | ||||
|     #[inline] | ||||
|     //pub fn url(&self) -> &Url { &self.url } | ||||
|  | ||||
|     /// Get the HTTP version of this response from the server. | ||||
|     #[inline] | ||||
|     pub fn version(&self) -> &version::HttpVersion { &self.version } | ||||
| } | ||||
|  | ||||
| /* | ||||
| impl Drop for Response { | ||||
|     fn drop(&mut self) { | ||||
|         // if not drained, theres old bits in the Reader. we can't reuse this, | ||||
|         // since those old bits would end up in new Responses | ||||
|         // | ||||
|         // otherwise, the response has been drained. we should check that the | ||||
|         // server has agreed to keep the connection open | ||||
|         let is_drained = !self.message.has_body(); | ||||
|         trace!("Response.drop is_drained={}", is_drained); | ||||
|         if !(is_drained && http::should_keep_alive(self.version, &self.headers)) { | ||||
|             trace!("Response.drop closing connection"); | ||||
|             if let Err(e) = self.message.close_connection() { | ||||
|                 error!("Response.drop error closing connection: {}", e); | ||||
|             } | ||||
|         } | ||||
|     /// Take the `Body` of this response. | ||||
|     #[inline] | ||||
|     pub fn body(mut self) -> Body { | ||||
|         self.body.take().unwrap_or(Body::empty()) | ||||
|     } | ||||
| } | ||||
| */ | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     /* | ||||
|     use std::io::{self, Read}; | ||||
|  | ||||
|     use url::Url; | ||||
|  | ||||
|     use header::TransferEncoding; | ||||
|     use header::Encoding; | ||||
|     use http::HttpMessage; | ||||
|     use mock::MockStream; | ||||
|     use status; | ||||
|     use version; | ||||
|     use http::h1::Http11Message; | ||||
|  | ||||
|     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) | ||||
| impl fmt::Debug for Response { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Response") | ||||
|             .field("status", &self.status) | ||||
|             .field("version", &self.version) | ||||
|             .field("headers", &self.headers) | ||||
|             .finish() | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #[test] | ||||
|     fn test_into_inner() { | ||||
|         let message: Box<HttpMessage> = Box::new( | ||||
|             Http11Message::with_stream(Box::new(MockStream::new()))); | ||||
|         let message = message.downcast::<Http11Message>().ok().unwrap(); | ||||
|         let b = message.into_inner().downcast::<MockStream>().ok().unwrap(); | ||||
|         assert_eq!(b, Box::new(MockStream::new())); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_chunked_response() { | ||||
|         let stream = MockStream::with_input(b"\ | ||||
|             HTTP/1.1 200 OK\r\n\ | ||||
|             Transfer-Encoding: chunked\r\n\ | ||||
|             \r\n\ | ||||
|             1\r\n\ | ||||
|             q\r\n\ | ||||
|             2\r\n\ | ||||
|             we\r\n\ | ||||
|             2\r\n\ | ||||
|             rt\r\n\ | ||||
|             0\r\n\ | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let url = Url::parse("http://hyper.rs").unwrap(); | ||||
|         let res = Response::new(url, Box::new(stream)).unwrap(); | ||||
|  | ||||
|         // The status line is correct? | ||||
|         assert_eq!(res.status, status::StatusCode::Ok); | ||||
|         assert_eq!(res.version, version::HttpVersion::Http11); | ||||
|         // The header is correct? | ||||
|         match res.headers.get::<TransferEncoding>() { | ||||
|             Some(encodings) => { | ||||
|                 assert_eq!(1, encodings.len()); | ||||
|                 assert_eq!(Encoding::Chunked, encodings[0]); | ||||
|             }, | ||||
|             None => panic!("Transfer-Encoding: chunked expected!"), | ||||
|         }; | ||||
|         // The body is correct? | ||||
|         assert_eq!(read_to_string(res).unwrap(), "qwert".to_owned()); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a chunk size is not a valid radix-16 number, an error | ||||
|     /// is returned. | ||||
|     #[test] | ||||
|     fn test_invalid_chunk_size_not_hex_digit() { | ||||
|         let stream = MockStream::with_input(b"\ | ||||
|             HTTP/1.1 200 OK\r\n\ | ||||
|             Transfer-Encoding: chunked\r\n\ | ||||
|             \r\n\ | ||||
|             X\r\n\ | ||||
|             1\r\n\ | ||||
|             0\r\n\ | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let url = Url::parse("http://hyper.rs").unwrap(); | ||||
|         let res = Response::new(url, Box::new(stream)).unwrap(); | ||||
|  | ||||
|         assert!(read_to_string(res).is_err()); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a chunk size contains an invalid extension, an error is | ||||
|     /// returned. | ||||
|     #[test] | ||||
|     fn test_invalid_chunk_size_extension() { | ||||
|         let stream = MockStream::with_input(b"\ | ||||
|             HTTP/1.1 200 OK\r\n\ | ||||
|             Transfer-Encoding: chunked\r\n\ | ||||
|             \r\n\ | ||||
|             1 this is an invalid extension\r\n\ | ||||
|             1\r\n\ | ||||
|             0\r\n\ | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let url = Url::parse("http://hyper.rs").unwrap(); | ||||
|         let res = Response::new(url, Box::new(stream)).unwrap(); | ||||
|  | ||||
|         assert!(read_to_string(res).is_err()); | ||||
|     } | ||||
|  | ||||
|     /// Tests that when a valid extension that contains a digit is appended to | ||||
|     /// the chunk size, the chunk is correctly read. | ||||
|     #[test] | ||||
|     fn test_chunk_size_with_extension() { | ||||
|         let stream = MockStream::with_input(b"\ | ||||
|             HTTP/1.1 200 OK\r\n\ | ||||
|             Transfer-Encoding: chunked\r\n\ | ||||
|             \r\n\ | ||||
|             1;this is an extension with a digit 1\r\n\ | ||||
|             1\r\n\ | ||||
|             0\r\n\ | ||||
|             \r\n" | ||||
|         ); | ||||
|  | ||||
|         let url = Url::parse("http://hyper.rs").unwrap(); | ||||
|         let res = Response::new(url, Box::new(stream)).unwrap(); | ||||
|  | ||||
|         assert_eq!(read_to_string(res).unwrap(), "1".to_owned()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_error_closes() { | ||||
|         let url = Url::parse("http://hyper.rs").unwrap(); | ||||
|         let stream = MockStream::with_input(b"\ | ||||
|             definitely not http | ||||
|         "); | ||||
|  | ||||
|         assert!(Response::new(url, Box::new(stream)).is_err()); | ||||
|     } | ||||
|     */ | ||||
| } | ||||
|   | ||||
							
								
								
									
										28
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								src/error.rs
									
									
									
									
									
								
							| @@ -8,9 +8,6 @@ use std::string::FromUtf8Error; | ||||
| use httparse; | ||||
| use url; | ||||
|  | ||||
| #[cfg(feature = "openssl")] | ||||
| use openssl::ssl::error::SslError; | ||||
|  | ||||
| use self::Error::{ | ||||
|     Method, | ||||
|     Uri, | ||||
| @@ -19,7 +16,6 @@ use self::Error::{ | ||||
|     Status, | ||||
|     Timeout, | ||||
|     Io, | ||||
|     Ssl, | ||||
|     TooLarge, | ||||
|     Incomplete, | ||||
|     Utf8 | ||||
| @@ -49,12 +45,8 @@ pub enum Error { | ||||
|     Status, | ||||
|     /// A timeout occurred waiting for an IO event. | ||||
|     Timeout, | ||||
|     /// Event loop is full and cannot process request | ||||
|     Full, | ||||
|     /// An `io::Error` that occurred while trying to read or write to a network stream. | ||||
|     Io(IoError), | ||||
|     /// An error from a SSL library. | ||||
|     Ssl(Box<StdError + Send + Sync>), | ||||
|     /// Parsing a field as string failed | ||||
|     Utf8(Utf8Error), | ||||
|  | ||||
| @@ -76,7 +68,6 @@ impl fmt::Display for Error { | ||||
|         match *self { | ||||
|             Uri(ref e) => fmt::Display::fmt(e, f), | ||||
|             Io(ref e) => fmt::Display::fmt(e, f), | ||||
|             Ssl(ref e) => fmt::Display::fmt(e, f), | ||||
|             Utf8(ref e) => fmt::Display::fmt(e, f), | ||||
|             ref e => f.write_str(e.description()), | ||||
|         } | ||||
| @@ -93,10 +84,8 @@ impl StdError for Error { | ||||
|             Status => "Invalid Status provided", | ||||
|             Incomplete => "Message is incomplete", | ||||
|             Timeout => "Timeout", | ||||
|             Error::Full => "Event loop is full", | ||||
|             Uri(ref e) => e.description(), | ||||
|             Io(ref e) => e.description(), | ||||
|             Ssl(ref e) => e.description(), | ||||
|             Utf8(ref e) => e.description(), | ||||
|             Error::__Nonexhaustive(ref void) =>  match *void {} | ||||
|         } | ||||
| @@ -105,8 +94,9 @@ impl StdError for Error { | ||||
|     fn cause(&self) -> Option<&StdError> { | ||||
|         match *self { | ||||
|             Io(ref error) => Some(error), | ||||
|             Ssl(ref error) => Some(&**error), | ||||
|             Uri(ref error) => Some(error), | ||||
|             Utf8(ref error) => Some(error), | ||||
|             Error::__Nonexhaustive(ref void) =>  match *void {}, | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| @@ -124,16 +114,6 @@ impl From<url::ParseError> for Error { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "openssl")] | ||||
| impl From<SslError> for Error { | ||||
|     fn from(err: SslError) -> Error { | ||||
|         match err { | ||||
|             SslError::StreamError(err) => Io(err), | ||||
|             err => Ssl(Box::new(err)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Utf8Error> for Error { | ||||
|     fn from(err: Utf8Error) -> Error { | ||||
|         Utf8(err) | ||||
| @@ -181,9 +161,9 @@ mod tests { | ||||
|         ($from:expr => $error:pat) => { | ||||
|             match Error::from($from) { | ||||
|                 e @ $error => { | ||||
|                     assert!(e.description().len() > 5); | ||||
|                     assert!(e.description().len() >= 5); | ||||
|                 } , | ||||
|                 _ => panic!("{:?}", $from) | ||||
|                 e => panic!("{:?}", e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ header! { | ||||
|     /// # Examples | ||||
|     /// ``` | ||||
|     /// use hyper::header::{Headers, AccessControlAllowMethods}; | ||||
|     /// use hyper::method::Method; | ||||
|     /// use hyper::Method; | ||||
|     /// | ||||
|     /// let mut headers = Headers::new(); | ||||
|     /// headers.set( | ||||
| @@ -28,7 +28,7 @@ header! { | ||||
|     /// ``` | ||||
|     /// ``` | ||||
|     /// use hyper::header::{Headers, AccessControlAllowMethods}; | ||||
|     /// use hyper::method::Method; | ||||
|     /// use hyper::Method; | ||||
|     /// | ||||
|     /// let mut headers = Headers::new(); | ||||
|     /// headers.set( | ||||
|   | ||||
| @@ -17,7 +17,7 @@ header! { | ||||
|     /// # Examples | ||||
|     /// ``` | ||||
|     /// use hyper::header::{Headers, AccessControlRequestMethod}; | ||||
|     /// use hyper::method::Method; | ||||
|     /// use hyper::Method; | ||||
|     ///  | ||||
|     /// let mut headers = Headers::new(); | ||||
|     /// headers.set(AccessControlRequestMethod(Method::Get)); | ||||
|   | ||||
| @@ -21,7 +21,7 @@ header! { | ||||
|     /// # Examples | ||||
|     /// ``` | ||||
|     /// use hyper::header::{Headers, Allow}; | ||||
|     /// use hyper::method::Method; | ||||
|     /// use hyper::Method; | ||||
|     /// | ||||
|     /// let mut headers = Headers::new(); | ||||
|     /// headers.set( | ||||
| @@ -30,7 +30,7 @@ header! { | ||||
|     /// ``` | ||||
|     /// ``` | ||||
|     /// use hyper::header::{Headers, Allow}; | ||||
|     /// use hyper::method::Method; | ||||
|     /// use hyper::Method; | ||||
|     /// | ||||
|     /// let mut headers = Headers::new(); | ||||
|     /// headers.set( | ||||
|   | ||||
| @@ -72,7 +72,6 @@ impl fmt::Display for ContentLength { | ||||
| } | ||||
|  | ||||
| __hyper__deref!(ContentLength => u64); | ||||
| __hyper_generate_header_serialization!(ContentLength); | ||||
|  | ||||
| __hyper__tm!(ContentLength, tests { | ||||
|     // Testcase from RFC | ||||
|   | ||||
| @@ -182,31 +182,6 @@ macro_rules! test_header { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[doc(hidden)] | ||||
| #[macro_export] | ||||
| macro_rules! __hyper_generate_header_serialization { | ||||
|     ($id:ident) => { | ||||
|         #[cfg(feature = "serde-serialization")] | ||||
|         impl ::serde::Serialize for $id { | ||||
|             fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> | ||||
|                             where S: ::serde::Serializer { | ||||
|                 format!("{}", self).serialize(serializer) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #[cfg(feature = "serde-serialization")] | ||||
|         impl ::serde::Deserialize for $id { | ||||
|             fn deserialize<D>(deserializer: &mut D) -> Result<$id, D::Error> | ||||
|                               where D: ::serde::Deserializer { | ||||
|                 let string_representation: String = | ||||
|                     try!(::serde::Deserialize::deserialize(deserializer)); | ||||
|                 let raw = string_representation.into_bytes().into(); | ||||
|                 Ok($crate::header::Header::parse_header(&raw).unwrap()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! header { | ||||
|     // $a:meta: Attributes associated with the header item (usually docs) | ||||
| @@ -238,8 +213,6 @@ macro_rules! header { | ||||
|                 self.fmt_header(f) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         __hyper_generate_header_serialization!($id); | ||||
|     }; | ||||
|     // List header, one or more items | ||||
|     ($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)+) => { | ||||
| @@ -265,7 +238,6 @@ macro_rules! header { | ||||
|                 self.fmt_header(f) | ||||
|             } | ||||
|         } | ||||
|         __hyper_generate_header_serialization!($id); | ||||
|     }; | ||||
|     // Single value header | ||||
|     ($(#[$a:meta])*($id:ident, $n:expr) => [$value:ty]) => { | ||||
| @@ -290,7 +262,6 @@ macro_rules! header { | ||||
|                 ::std::fmt::Display::fmt(&**self, f) | ||||
|             } | ||||
|         } | ||||
|         __hyper_generate_header_serialization!($id); | ||||
|     }; | ||||
|     // List header, one or more items with "*" option | ||||
|     ($(#[$a:meta])*($id:ident, $n:expr) => {Any / ($item:ty)+}) => { | ||||
| @@ -330,7 +301,6 @@ macro_rules! header { | ||||
|                 self.fmt_header(f) | ||||
|             } | ||||
|         } | ||||
|         __hyper_generate_header_serialization!($id); | ||||
|     }; | ||||
|  | ||||
|     // optional test module | ||||
| @@ -421,4 +391,4 @@ mod transfer_encoding; | ||||
| mod upgrade; | ||||
| mod user_agent; | ||||
| mod vary; | ||||
| mod warning; | ||||
| mod warning; | ||||
|   | ||||
| @@ -85,11 +85,6 @@ use unicase::UniCase; | ||||
|  | ||||
| use self::internals::{Item, VecMap, Entry}; | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| use serde::de; | ||||
|  | ||||
| pub use self::shared::*; | ||||
| pub use self::common::*; | ||||
| pub use self::raw::Raw; | ||||
| @@ -437,44 +432,6 @@ impl fmt::Debug for Headers { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| impl Serialize for Headers { | ||||
|     fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> | ||||
|         where S: Serializer | ||||
|     { | ||||
|         let mut state = try!(serializer.serialize_map(Some(self.len()))); | ||||
|         for header in self.iter() { | ||||
|             try!(serializer.serialize_map_key(&mut state, header.name())); | ||||
|             try!(serializer.serialize_map_value(&mut state, header.value_string())); | ||||
|         } | ||||
|         serializer.serialize_map_end(state) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| impl Deserialize for Headers { | ||||
|     fn deserialize<D>(deserializer: &mut D) -> Result<Headers, D::Error> where D: Deserializer { | ||||
|         struct HeadersVisitor; | ||||
|  | ||||
|         impl de::Visitor for HeadersVisitor { | ||||
|             type Value = Headers; | ||||
|  | ||||
|             fn visit_map<V>(&mut self, mut visitor: V) -> Result<Headers, V::Error> | ||||
|                             where V: de::MapVisitor { | ||||
|                 let mut result = Headers::new(); | ||||
|                 while let Some((key, value)) = try!(visitor.visit()) { | ||||
|                     let (key, value): (String, String) = (key, value); | ||||
|                     result.set_raw(key, vec![value.into_bytes()]); | ||||
|                 } | ||||
|                 try!(visitor.end()); | ||||
|                 Ok(result) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         deserializer.deserialize_map(HeadersVisitor) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// An `Iterator` over the fields in a `Headers` map. | ||||
| #[allow(missing_debug_implementations)] | ||||
| pub struct HeadersItems<'a> { | ||||
|   | ||||
							
								
								
									
										97
									
								
								src/http/body.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/http/body.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| use std::convert::From; | ||||
| use std::sync::Arc; | ||||
|  | ||||
| use tokio_proto; | ||||
| use http::Chunk; | ||||
| use futures::{Poll, Stream}; | ||||
| use futures::sync::mpsc; | ||||
|  | ||||
| pub type TokioBody = tokio_proto::streaming::Body<Chunk, ::Error>; | ||||
|  | ||||
| /// A `Stream` for `Chunk`s used in requests and responses. | ||||
| #[derive(Debug)] | ||||
| pub struct Body(TokioBody); | ||||
|  | ||||
| impl Body { | ||||
|     /// Return an empty body stream | ||||
|     pub fn empty() -> Body { | ||||
|         Body(TokioBody::empty()) | ||||
|     } | ||||
|  | ||||
|     /// Return a body stream with an associated sender half | ||||
|     pub fn pair() -> (mpsc::Sender<Result<Chunk, ::Error>>, Body) { | ||||
|         let (tx, rx) = TokioBody::pair(); | ||||
|         let rx = Body(rx); | ||||
|         (tx, rx) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Stream for Body { | ||||
|     type Item = Chunk; | ||||
|     type Error = ::Error; | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Option<Chunk>, ::Error> { | ||||
|         self.0.poll() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Body> for tokio_proto::streaming::Body<Chunk, ::Error> { | ||||
|     fn from(b: Body) -> tokio_proto::streaming::Body<Chunk, ::Error> { | ||||
|         b.0 | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<tokio_proto::streaming::Body<Chunk, ::Error>> for Body { | ||||
|     fn from(tokio_body: tokio_proto::streaming::Body<Chunk, ::Error>) -> Body { | ||||
|         Body(tokio_body) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<mpsc::Receiver<Result<Chunk, ::Error>>> for Body { | ||||
|     fn from(src: mpsc::Receiver<Result<Chunk, ::Error>>) -> Body { | ||||
|         Body(src.into()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Chunk> for Body { | ||||
|     fn from (chunk: Chunk) -> Body { | ||||
|         Body(TokioBody::from(chunk)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Vec<u8>> for Body { | ||||
|     fn from (vec: Vec<u8>) -> Body { | ||||
|         Body(TokioBody::from(Chunk::from(vec))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Arc<Vec<u8>>> for Body { | ||||
|     fn from (vec: Arc<Vec<u8>>) -> Body { | ||||
|         Body(TokioBody::from(Chunk::from(vec))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<&'static [u8]> for Body { | ||||
|     fn from (slice: &'static [u8]) -> Body { | ||||
|         Body(TokioBody::from(Chunk::from(slice))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<String> for Body { | ||||
|     fn from (s: String) -> Body { | ||||
|         Body(TokioBody::from(Chunk::from(s.into_bytes()))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<&'static str> for Body { | ||||
|     fn from (slice: &'static str) -> Body { | ||||
|         Body(TokioBody::from(Chunk::from(slice.as_bytes()))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn _assert_send() { | ||||
|     fn _assert<T: Send>() {} | ||||
|  | ||||
|     _assert::<Body>(); | ||||
|     _assert::<Chunk>(); | ||||
| } | ||||
| @@ -1,16 +1,16 @@ | ||||
| use std::cmp; | ||||
| use std::io::{self, Read}; | ||||
| use std::io::{self, Read, Write}; | ||||
| use std::ptr; | ||||
|  | ||||
|  | ||||
| const INIT_BUFFER_SIZE: usize = 4096; | ||||
| const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100; | ||||
| pub const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100; | ||||
|  | ||||
| #[derive(Debug, Default)] | ||||
| pub struct Buffer { | ||||
|     vec: Vec<u8>, | ||||
|     read_pos: usize, | ||||
|     write_pos: usize, | ||||
|     tail: usize, | ||||
|     head: usize, | ||||
| } | ||||
|  | ||||
| impl Buffer { | ||||
| @@ -24,7 +24,17 @@ impl Buffer { | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn len(&self) -> usize { | ||||
|         self.read_pos - self.write_pos | ||||
|         self.tail - self.head | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn available(&self) -> usize { | ||||
|         self.vec.len() - self.tail | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn is_max_size(&self) -> bool { | ||||
|         self.len() >= MAX_BUFFER_SIZE | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
| @@ -34,45 +44,88 @@ impl Buffer { | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn bytes(&self) -> &[u8] { | ||||
|         &self.vec[self.write_pos..self.read_pos] | ||||
|         &self.vec[self.head..self.tail] | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn consume(&mut self, pos: usize) { | ||||
|         debug_assert!(self.read_pos >= self.write_pos + pos); | ||||
|         self.write_pos += pos; | ||||
|         if self.write_pos == self.read_pos { | ||||
|             self.write_pos = 0; | ||||
|             self.read_pos = 0; | ||||
|         debug_assert!(self.tail >= self.head + pos); | ||||
|         self.head += pos; | ||||
|         if self.head == self.tail { | ||||
|             self.head = 0; | ||||
|             self.tail = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn consume_leading_lines(&mut self) { | ||||
|         while !self.is_empty() { | ||||
|             match self.vec[self.head] { | ||||
|                 b'\r' | b'\n' => { | ||||
|                     self.consume(1); | ||||
|                 }, | ||||
|                 _ => return | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn read_from<R: Read>(&mut self, r: &mut R) -> io::Result<usize> { | ||||
|         self.maybe_reserve(); | ||||
|         let n = try!(r.read(&mut self.vec[self.read_pos..])); | ||||
|         self.read_pos += n; | ||||
|         self.maybe_reserve(1); | ||||
|         let n = try!(r.read(&mut self.vec[self.tail..])); | ||||
|         self.tail += n; | ||||
|         self.maybe_reset(); | ||||
|         Ok(n) | ||||
|     } | ||||
|  | ||||
|     pub fn write_into<W: Write>(&mut self, w: &mut W) -> io::Result<usize> { | ||||
|         if self.is_empty() { | ||||
|             Ok(0) | ||||
|         } else { | ||||
|             let n = try!(w.write(&mut self.vec[self.head..self.tail])); | ||||
|             self.head += n; | ||||
|             self.maybe_reset(); | ||||
|             Ok(n) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn write(&mut self, data: &[u8]) -> usize { | ||||
|         trace!("Buffer::write len = {:?}", data.len()); | ||||
|         self.maybe_reserve(data.len()); | ||||
|         let len = cmp::min(self.available(), data.len()); | ||||
|         assert!(self.available() >= len); | ||||
|         unsafe { | ||||
|             // in rust 1.9, we could use slice::copy_from_slice | ||||
|             ptr::copy( | ||||
|                 data.as_ptr(), | ||||
|                 self.vec.as_mut_ptr().offset(self.tail as isize), | ||||
|                 len | ||||
|             ); | ||||
|         } | ||||
|         self.tail += len; | ||||
|         len | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn maybe_reserve(&mut self) { | ||||
|     fn maybe_reserve(&mut self, needed: usize) { | ||||
|         let cap = self.vec.len(); | ||||
|         if cap == 0 { | ||||
|             trace!("reserving initial {}", INIT_BUFFER_SIZE); | ||||
|             self.vec = vec![0; INIT_BUFFER_SIZE]; | ||||
|         } else if self.write_pos > 0  && self.read_pos == cap { | ||||
|             let count = self.read_pos - self.write_pos; | ||||
|             // first reserve | ||||
|             let init = cmp::max(INIT_BUFFER_SIZE, needed); | ||||
|             trace!("reserving initial {}", init); | ||||
|             self.vec = vec![0; init]; | ||||
|         } else if self.head > 0  && self.tail == cap && self.head >= needed { | ||||
|             // there is space to shift over | ||||
|             let count = self.tail - self.head; | ||||
|             trace!("moving buffer bytes over by {}", count); | ||||
|             unsafe { | ||||
|                 ptr::copy( | ||||
|                     self.vec.as_ptr().offset(self.write_pos as isize), | ||||
|                     self.vec.as_ptr().offset(self.head as isize), | ||||
|                     self.vec.as_mut_ptr(), | ||||
|                     count | ||||
|                 ); | ||||
|             } | ||||
|             self.read_pos -= count; | ||||
|             self.write_pos = 0; | ||||
|         } else if self.read_pos == cap && cap < MAX_BUFFER_SIZE { | ||||
|             self.tail -= count; | ||||
|             self.head = 0; | ||||
|         } else if self.tail == cap && cap < MAX_BUFFER_SIZE { | ||||
|             self.vec.reserve(cmp::min(cap * 4, MAX_BUFFER_SIZE) - cap); | ||||
|             let new = self.vec.capacity() - cap; | ||||
|             trace!("reserved {}", new); | ||||
| @@ -80,36 +133,11 @@ impl Buffer { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn wrap<'a, 'b: 'a, R: io::Read>(&'a mut self, reader: &'b mut R) -> BufReader<'a, R> { | ||||
|         BufReader { | ||||
|             buf: self, | ||||
|             reader: reader | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct BufReader<'a, R: io::Read + 'a> { | ||||
|     buf: &'a mut Buffer, | ||||
|     reader: &'a mut R | ||||
| } | ||||
|  | ||||
| impl<'a, R: io::Read + 'a> BufReader<'a, R> { | ||||
|     pub fn get_ref(&self) -> &R { | ||||
|         self.reader | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, R: io::Read> Read for BufReader<'a, R> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         trace!("BufReader.read self={}, buf={}", self.buf.len(), buf.len()); | ||||
|         let n = try!(self.buf.bytes().read(buf)); | ||||
|         self.buf.consume(n); | ||||
|         if n == 0 { | ||||
|             self.buf.reset(); | ||||
|             self.reader.read(&mut buf[n..]) | ||||
|         } else { | ||||
|             Ok(n) | ||||
|     #[inline] | ||||
|     fn maybe_reset(&mut self) { | ||||
|         if self.tail != 0 && self.tail == self.head { | ||||
|             self.tail = 0; | ||||
|             self.head = 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,96 +0,0 @@ | ||||
| use std::fmt; | ||||
| use std::sync::{Arc, mpsc}; | ||||
| use std::sync::atomic::{AtomicBool, Ordering}; | ||||
| use ::rotor; | ||||
|  | ||||
| pub use std::sync::mpsc::TryRecvError; | ||||
|  | ||||
| pub fn new<T>(notify: rotor::Notifier) -> (Sender<T>, Receiver<T>) { | ||||
|     let b = Arc::new(AtomicBool::new(false)); | ||||
|     let (tx, rx) = mpsc::channel(); | ||||
|     (Sender { | ||||
|         awake: b.clone(), | ||||
|         notify: notify, | ||||
|         tx: tx, | ||||
|     }, | ||||
|     Receiver { | ||||
|         awake: b, | ||||
|         rx: rx, | ||||
|     }) | ||||
| } | ||||
|  | ||||
| pub fn share<T, U>(other: &Sender<U>) -> (Sender<T>, Receiver<T>) { | ||||
|     let (tx, rx) = mpsc::channel(); | ||||
|     let notify = other.notify.clone(); | ||||
|     let b = other.awake.clone(); | ||||
|     (Sender { | ||||
|         awake: b.clone(), | ||||
|         notify: notify, | ||||
|         tx: tx, | ||||
|     }, | ||||
|     Receiver { | ||||
|         awake: b, | ||||
|         rx: rx, | ||||
|     }) | ||||
| } | ||||
|  | ||||
| pub struct Sender<T> { | ||||
|     awake: Arc<AtomicBool>, | ||||
|     notify: rotor::Notifier, | ||||
|     tx: mpsc::Sender<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Send> Sender<T> { | ||||
|     pub fn send(&self, val: T) -> Result<(), SendError<T>> { | ||||
|         try!(self.tx.send(val)); | ||||
|         if !self.awake.swap(true, Ordering::SeqCst) { | ||||
|             try!(self.notify.wakeup()); | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Clone for Sender<T> { | ||||
|     fn clone(&self) -> Sender<T> { | ||||
|         Sender { | ||||
|             awake: self.awake.clone(), | ||||
|             notify: self.notify.clone(), | ||||
|             tx: self.tx.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> fmt::Debug for Sender<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Sender") | ||||
|             .field("notify", &self.notify) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct SendError<T>(pub Option<T>); | ||||
|  | ||||
| impl<T> From<mpsc::SendError<T>> for SendError<T> { | ||||
|     fn from(e: mpsc::SendError<T>) -> SendError<T> { | ||||
|         SendError(Some(e.0)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> From<rotor::WakeupError> for SendError<T> { | ||||
|     fn from(_e: rotor::WakeupError) -> SendError<T> { | ||||
|         SendError(None) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Receiver<T> { | ||||
|     awake: Arc<AtomicBool>, | ||||
|     rx: mpsc::Receiver<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Send> Receiver<T> { | ||||
|     pub fn try_recv(&self) -> Result<T, mpsc::TryRecvError> { | ||||
|         self.awake.store(false, Ordering::Relaxed); | ||||
|         self.rx.try_recv() | ||||
|     } | ||||
| } | ||||
							
								
								
									
										77
									
								
								src/http/chunk.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/http/chunk.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| use std::borrow::Borrow; | ||||
| use std::fmt; | ||||
| use std::sync::Arc; | ||||
|  | ||||
| /// A piece of a message body. | ||||
| pub struct Chunk(Inner); | ||||
|  | ||||
| enum Inner { | ||||
|     Owned(Vec<u8>), | ||||
|     Referenced(Arc<Vec<u8>>), | ||||
|     Static(&'static [u8]), | ||||
| } | ||||
|  | ||||
| impl From<Vec<u8>> for Chunk { | ||||
|     #[inline] | ||||
|     fn from(v: Vec<u8>) -> Chunk { | ||||
|         Chunk(Inner::Owned(v)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Arc<Vec<u8>>> for Chunk { | ||||
|     #[inline] | ||||
|     fn from(v: Arc<Vec<u8>>) -> Chunk { | ||||
|         Chunk(Inner::Referenced(v)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<&'static [u8]> for Chunk { | ||||
|     #[inline] | ||||
|     fn from(slice: &'static [u8]) -> Chunk { | ||||
|         Chunk(Inner::Static(slice)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<String> for Chunk { | ||||
|     #[inline] | ||||
|     fn from(s: String) -> Chunk { | ||||
|         s.into_bytes().into() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<&'static str> for Chunk { | ||||
|     #[inline] | ||||
|     fn from(slice: &'static str) -> Chunk { | ||||
|         slice.as_bytes().into() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ::std::ops::Deref for Chunk { | ||||
|     type Target = [u8]; | ||||
|  | ||||
|     #[inline] | ||||
|     fn deref(&self) -> &Self::Target { | ||||
|         self.as_ref() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl AsRef<[u8]> for Chunk { | ||||
|     #[inline] | ||||
|     fn as_ref(&self) -> &[u8] { | ||||
|         match self.0 { | ||||
|             Inner::Owned(ref vec) => vec, | ||||
|             Inner::Referenced(ref vec) => { | ||||
|                 let v: &Vec<u8> = vec.borrow(); | ||||
|                 v.as_slice() | ||||
|             } | ||||
|             Inner::Static(slice) => slice, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Chunk { | ||||
|     #[inline] | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt::Debug::fmt(self.as_ref(), f) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1607
									
								
								src/http/conn.rs
									
									
									
									
									
								
							
							
						
						
									
										1607
									
								
								src/http/conn.rs
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -277,7 +277,7 @@ mod tests { | ||||
|     use std::io::Write; | ||||
|     use super::Decoder; | ||||
|     use super::ChunkedState; | ||||
|     use mock::Async; | ||||
|     use mock::AsyncIo; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_read_chunk_size() { | ||||
| @@ -422,7 +422,7 @@ mod tests { | ||||
|                   -> String { | ||||
|         let content_len = content.len(); | ||||
|         let mock_buf = io::Cursor::new(content.clone()); | ||||
|         let mut ins = Async::new(mock_buf, block_at); | ||||
|         let mut ins = AsyncIo::new(mock_buf, block_at); | ||||
|         let mut outs = vec![]; | ||||
|         loop { | ||||
|             let mut buf = vec![0; read_buffer_size]; | ||||
|   | ||||
| @@ -1,15 +1,12 @@ | ||||
| use std::borrow::Cow; | ||||
| use std::cmp; | ||||
| use std::io::{self, Write}; | ||||
|  | ||||
| use http::internal::{AtomicWrite, WriteBuf}; | ||||
| use http::io::AtomicWrite; | ||||
|  | ||||
| /// Encoders to handle different Transfer-Encodings. | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Encoder { | ||||
|     kind: Kind, | ||||
|     prefix: Prefix, | ||||
|     is_closed: bool, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, PartialEq, Clone)] | ||||
| @@ -26,27 +23,16 @@ impl Encoder { | ||||
|     pub fn chunked() -> Encoder { | ||||
|         Encoder { | ||||
|             kind: Kind::Chunked(Chunked::Init), | ||||
|             prefix: Prefix(None), | ||||
|             is_closed: false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn length(len: u64) -> Encoder { | ||||
|         Encoder { | ||||
|             kind: Kind::Length(len), | ||||
|             prefix: Prefix(None), | ||||
|             is_closed: false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn prefix(&mut self, prefix: WriteBuf<Vec<u8>>) { | ||||
|         self.prefix.0 = Some(prefix); | ||||
|     } | ||||
|  | ||||
|     pub fn is_eof(&self) -> bool { | ||||
|         if self.prefix.0.is_some() { | ||||
|             return false; | ||||
|         } | ||||
|         match self.kind { | ||||
|             Kind::Length(0) | | ||||
|             Kind::Chunked(Chunked::End) => true, | ||||
| @@ -54,71 +40,26 @@ impl Encoder { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// User has called `encoder.close()` in a `Handler`. | ||||
|     pub fn is_closed(&self) -> bool { | ||||
|         self.is_closed | ||||
|     } | ||||
|  | ||||
|     pub fn close(&mut self) { | ||||
|         self.is_closed = true; | ||||
|     } | ||||
|  | ||||
|     pub fn finish(self) -> Option<WriteBuf<Cow<'static, [u8]>>> { | ||||
|         let trailer = self.trailer(); | ||||
|         let buf = self.prefix.0; | ||||
|  | ||||
|         match (buf, trailer) { | ||||
|             (Some(mut buf), Some(trailer)) => { | ||||
|                 buf.bytes.extend_from_slice(trailer); | ||||
|                 Some(WriteBuf { | ||||
|                     bytes: Cow::Owned(buf.bytes), | ||||
|                     pos: buf.pos, | ||||
|                 }) | ||||
|             }, | ||||
|             (Some(buf), None) => Some(WriteBuf { | ||||
|                 bytes: Cow::Owned(buf.bytes), | ||||
|                 pos: buf.pos | ||||
|             }), | ||||
|             (None, Some(trailer)) => { | ||||
|                 Some(WriteBuf { | ||||
|                     bytes: Cow::Borrowed(trailer), | ||||
|                     pos: 0, | ||||
|                 }) | ||||
|             }, | ||||
|             (None, None) => None | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn trailer(&self) -> Option<&'static [u8]> { | ||||
|         match self.kind { | ||||
|             Kind::Chunked(Chunked::Init) => { | ||||
|                 Some(b"0\r\n\r\n") | ||||
|             } | ||||
|             _ => None | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn encode<W: AtomicWrite>(&mut self, w: &mut W, msg: &[u8]) -> io::Result<usize> { | ||||
|         match self.kind { | ||||
|             Kind::Chunked(ref mut chunked) => { | ||||
|                 chunked.encode(w, &mut self.prefix, msg) | ||||
|                 chunked.encode(w, msg) | ||||
|             }, | ||||
|             Kind::Length(ref mut remaining) => { | ||||
|                 let mut n = { | ||||
|                 let n = { | ||||
|                     let max = cmp::min(*remaining as usize, msg.len()); | ||||
|                     trace!("sized write, len = {}", max); | ||||
|                     let slice = &msg[..max]; | ||||
|  | ||||
|                     let prefix = self.prefix.0.as_ref().map(|buf| &buf.bytes[buf.pos..]).unwrap_or(b""); | ||||
|  | ||||
|                     try!(w.write_atomic(&[prefix, slice])) | ||||
|                     try!(w.write_atomic(&[slice])) | ||||
|                 }; | ||||
|  | ||||
|                 n = self.prefix.update(n); | ||||
|                 if n == 0 { | ||||
|                     return Err(io::Error::new(io::ErrorKind::WouldBlock, "would block")); | ||||
|                 } | ||||
|  | ||||
|                 *remaining -= n as u64; | ||||
|                 trace!("sized write complete, remaining = {}", remaining); | ||||
|                 Ok(n) | ||||
|             }, | ||||
|         } | ||||
| @@ -138,7 +79,7 @@ enum Chunked { | ||||
| } | ||||
|  | ||||
| impl Chunked { | ||||
|     fn encode<W: AtomicWrite>(&mut self, w: &mut W, prefix: &mut Prefix, msg: &[u8]) -> io::Result<usize> { | ||||
|     fn encode<W: AtomicWrite>(&mut self, w: &mut W, msg: &[u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             Chunked::Init => { | ||||
|                 let mut size = ChunkSize { | ||||
| @@ -158,28 +99,24 @@ impl Chunked { | ||||
|             let pieces = match *self { | ||||
|                 Chunked::Init => unreachable!("Chunked::Init should have become Chunked::Size"), | ||||
|                 Chunked::Size(ref size) => [ | ||||
|                     prefix.0.as_ref().map(|buf| &buf.bytes[buf.pos..]).unwrap_or(b""), | ||||
|                     &size.bytes[size.pos.into() .. size.len.into()], | ||||
|                     &b"\r\n"[..], | ||||
|                     msg, | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::SizeCr => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b"\r\n"[..], | ||||
|                     msg, | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::SizeLf => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b"\n"[..], | ||||
|                     msg, | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::Body(pos) => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &msg[pos..], | ||||
| @@ -189,14 +126,12 @@ impl Chunked { | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b"\r\n"[..], | ||||
|                 ], | ||||
|                 Chunked::BodyLf => [ | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b""[..], | ||||
|                     &b"\n"[..], | ||||
|                 ], | ||||
|                 Chunked::End => unreachable!("Chunked::End shouldn't write more") | ||||
| @@ -204,9 +139,6 @@ impl Chunked { | ||||
|             try!(w.write_atomic(&pieces)) | ||||
|         }; | ||||
|  | ||||
|         if n > 0 { | ||||
|             n = prefix.update(n); | ||||
|         } | ||||
|         while n > 0 { | ||||
|             match *self { | ||||
|                 Chunked::Init => unreachable!("Chunked::Init should have become Chunked::Size"), | ||||
| @@ -321,30 +253,10 @@ impl io::Write for ChunkSize { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| struct Prefix(Option<WriteBuf<Vec<u8>>>); | ||||
|  | ||||
| impl Prefix { | ||||
|     fn update(&mut self, n: usize) -> usize { | ||||
|         if let Some(mut buf) = self.0.take() { | ||||
|             if buf.bytes.len() - buf.pos > n { | ||||
|                 buf.pos += n; | ||||
|                 self.0 = Some(buf); | ||||
|                 0 | ||||
|             } else { | ||||
|                 let nbuf = buf.bytes.len() - buf.pos; | ||||
|                 n - nbuf | ||||
|             } | ||||
|         } else { | ||||
|             n | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::Encoder; | ||||
|     use mock::{Async, Buf}; | ||||
|     use mock::{AsyncIo, Buf}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_chunked_encode_sync() { | ||||
| @@ -359,7 +271,7 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn test_chunked_encode_async() { | ||||
|         let mut dst = Async::new(Buf::new(), 7); | ||||
|         let mut dst = AsyncIo::new(Buf::new(), 7); | ||||
|         let mut encoder = Encoder::chunked(); | ||||
|  | ||||
|         assert!(encoder.encode(&mut dst, b"foo bar").is_err()); | ||||
|   | ||||
| @@ -1,21 +1,3 @@ | ||||
| /* | ||||
| use std::fmt; | ||||
| use std::io::{self, Write}; | ||||
| use std::marker::PhantomData; | ||||
| use std::sync::mpsc; | ||||
|  | ||||
| use url::Url; | ||||
| use tick; | ||||
| use time::now_utc; | ||||
|  | ||||
| use header::{self, Headers}; | ||||
| use http::{self, conn}; | ||||
| use method::Method; | ||||
| use net::{Fresh, Streaming}; | ||||
| use status::StatusCode; | ||||
| use version::HttpVersion; | ||||
| */ | ||||
|  | ||||
| pub use self::decode::Decoder; | ||||
| pub use self::encode::Encoder; | ||||
|  | ||||
| @@ -23,7 +5,7 @@ pub use self::parse::parse; | ||||
|  | ||||
| mod decode; | ||||
| mod encode; | ||||
| mod parse; | ||||
| pub mod parse; | ||||
|  | ||||
| /* | ||||
| fn should_have_response_body(method: &Method, status: u16) -> bool { | ||||
|   | ||||
| @@ -4,7 +4,7 @@ use std::fmt::{self, Write}; | ||||
| use httparse; | ||||
|  | ||||
| use header::{self, Headers, ContentLength, TransferEncoding}; | ||||
| use http::{MessageHead, RawStatus, Http1Message, ParseResult, ServerMessage, ClientMessage, RequestLine}; | ||||
| use http::{MessageHead, RawStatus, Http1Transaction, ParseResult, ServerTransaction, ClientTransaction, RequestLine}; | ||||
| use http::h1::{Encoder, Decoder}; | ||||
| use method::Method; | ||||
| use status::StatusCode; | ||||
| @@ -13,17 +13,15 @@ use version::HttpVersion::{Http10, Http11}; | ||||
| const MAX_HEADERS: usize = 100; | ||||
| const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific | ||||
|  | ||||
| pub fn parse<T: Http1Message<Incoming=I>, I>(buf: &[u8]) -> ParseResult<I> { | ||||
| pub fn parse<T: Http1Transaction<Incoming=I>, I>(buf: &[u8]) -> ParseResult<I> { | ||||
|     if buf.len() == 0 { | ||||
|         return Ok(None); | ||||
|     } | ||||
|     trace!("parse({:?})", buf); | ||||
|     <T as Http1Message>::parse(buf) | ||||
|     <T as Http1Transaction>::parse(buf) | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| impl Http1Message for ServerMessage { | ||||
| impl Http1Transaction for ServerTransaction { | ||||
|     type Incoming = RequestLine; | ||||
|     type Outgoing = StatusCode; | ||||
|  | ||||
| @@ -60,7 +58,7 @@ impl Http1Message for ServerMessage { | ||||
|     } | ||||
|  | ||||
|  | ||||
|     fn encode(mut head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder { | ||||
|     fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder { | ||||
|         use ::header; | ||||
|         trace!("writing head: {:?}", head); | ||||
|  | ||||
| @@ -103,9 +101,14 @@ impl Http1Message for ServerMessage { | ||||
|         } | ||||
|         body | ||||
|     } | ||||
|  | ||||
|     fn should_set_length(_head: &MessageHead<Self::Outgoing>) -> bool { | ||||
|         //TODO: pass method, check if method == HEAD | ||||
|         true | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Http1Message for ClientMessage { | ||||
| impl Http1Transaction for ClientTransaction { | ||||
|     type Incoming = RawStatus; | ||||
|     type Outgoing = RequestLine; | ||||
|  | ||||
| @@ -162,7 +165,7 @@ impl Http1Message for ClientMessage { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn encode(mut head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder { | ||||
|     fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder { | ||||
|         trace!("writing head: {:?}", head); | ||||
|  | ||||
|  | ||||
| @@ -203,6 +206,14 @@ impl Http1Message for ClientMessage { | ||||
|  | ||||
|         body | ||||
|     } | ||||
|  | ||||
|  | ||||
|     fn should_set_length(head: &MessageHead<Self::Outgoing>) -> bool { | ||||
|         match &head.subject.0 { | ||||
|             &Method::Get | &Method::Head => false, | ||||
|             _ => true | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct FastWrite<'a>(&'a mut Vec<u8>); | ||||
| @@ -238,17 +249,17 @@ mod tests { | ||||
|     #[test] | ||||
|     fn test_parse_request() { | ||||
|         let raw = b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"; | ||||
|         parse::<http::ServerMessage, _>(raw).unwrap(); | ||||
|         parse::<http::ServerTransaction, _>(raw).unwrap(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_raw_status() { | ||||
|         let raw = b"HTTP/1.1 200 OK\r\n\r\n"; | ||||
|         let (res, _) = parse::<http::ClientMessage, _>(raw).unwrap().unwrap(); | ||||
|         let (res, _) = parse::<http::ClientTransaction, _>(raw).unwrap().unwrap(); | ||||
|         assert_eq!(res.subject.1, "OK"); | ||||
|  | ||||
|         let raw = b"HTTP/1.1 200 Howdy\r\n\r\n"; | ||||
|         let (res, _) = parse::<http::ClientMessage, _>(raw).unwrap().unwrap(); | ||||
|         let (res, _) = parse::<http::ClientTransaction, _>(raw).unwrap().unwrap(); | ||||
|         assert_eq!(res.subject.1, "Howdy"); | ||||
|     } | ||||
|  | ||||
| @@ -260,7 +271,7 @@ mod tests { | ||||
|     fn bench_parse_incoming(b: &mut Bencher) { | ||||
|         let raw = b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"; | ||||
|         b.iter(|| { | ||||
|             parse::<http::ServerMessage, _>(raw).unwrap() | ||||
|             parse::<http::ServerTransaction, _>(raw).unwrap() | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										207
									
								
								src/http/io.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								src/http/io.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,207 @@ | ||||
| use std::fmt; | ||||
| use std::io::{self, Read, Write}; | ||||
|  | ||||
| use futures::Async; | ||||
| use tokio::io::Io; | ||||
|  | ||||
| use http::{Http1Transaction, h1, MessageHead, ParseResult}; | ||||
| use http::buffer::Buffer; | ||||
|  | ||||
| pub struct Buffered<T> { | ||||
|     io: T, | ||||
|     read_buf: Buffer, | ||||
|     write_buf: Buffer, | ||||
| } | ||||
|  | ||||
| impl<T> fmt::Debug for Buffered<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Buffered") | ||||
|             .field("read_buf", &self.read_buf) | ||||
|             .field("write_buf", &self.write_buf) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Io> Buffered<T> { | ||||
|     pub fn new(io: T) -> Buffered<T> { | ||||
|         Buffered { | ||||
|             io: io, | ||||
|             read_buf: Buffer::new(), | ||||
|             write_buf: Buffer::new(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn read_buf(&self) -> &[u8] { | ||||
|         self.read_buf.bytes() | ||||
|     } | ||||
|  | ||||
|     pub fn consume_leading_lines(&mut self) { | ||||
|         self.read_buf.consume_leading_lines(); | ||||
|     } | ||||
|  | ||||
|     pub fn poll_read(&mut self) -> Async<()> { | ||||
|         self.io.poll_read() | ||||
|     } | ||||
|  | ||||
|     pub fn parse<S: Http1Transaction>(&mut self) -> ::Result<Option<MessageHead<S::Incoming>>> { | ||||
|         match self.read_buf.read_from(&mut self.io) { | ||||
|             Ok(0) => { | ||||
|                 trace!("parse eof"); | ||||
|                 return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "parse eof").into()); | ||||
|             } | ||||
|             Ok(_) => {}, | ||||
|             Err(e) => match e.kind() { | ||||
|                 io::ErrorKind::WouldBlock => {}, | ||||
|                 _ => return Err(e.into()) | ||||
|             } | ||||
|         } | ||||
|         match try!(parse::<S, _>(self.read_buf.bytes())) { | ||||
|             Some((head, len)) => { | ||||
|                 trace!("parsed {} bytes out of {}", len, self.read_buf.len()); | ||||
|                 self.read_buf.consume(len); | ||||
|                 Ok(Some(head)) | ||||
|             }, | ||||
|             None => { | ||||
|                 if self.read_buf.is_max_size() { | ||||
|                     debug!("MAX_BUFFER_SIZE reached, closing"); | ||||
|                     Err(::Error::TooLarge) | ||||
|                 } else { | ||||
|                     Ok(None) | ||||
|                 } | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn buffer<B: AsRef<[u8]>>(&mut self, buf: B) { | ||||
|         self.write_buf.write(buf.as_ref()); | ||||
|     } | ||||
|  | ||||
|     #[cfg(test)] | ||||
|     pub fn io_mut(&mut self) -> &mut T { | ||||
|         &mut self.io | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Read> Read for Buffered<T> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         trace!("Buffered.read self={}, buf={}", self.read_buf.len(), buf.len()); | ||||
|         let n = try!(self.read_buf.bytes().read(buf)); | ||||
|         self.read_buf.consume(n); | ||||
|         if n == 0 { | ||||
|             self.read_buf.reset(); | ||||
|             self.io.read(&mut buf[n..]) | ||||
|         } else { | ||||
|             Ok(n) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Write> Write for Buffered<T> { | ||||
|     fn write(&mut self, data: &[u8]) -> io::Result<usize> { | ||||
|         Ok(self.write_buf.write(data)) | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         self.write_buf.write_into(&mut self.io).and_then(|_n| { | ||||
|             if self.write_buf.is_empty() { | ||||
|                 Ok(()) | ||||
|             } else { | ||||
|                 Err(io::Error::new(io::ErrorKind::WouldBlock, "wouldblock")) | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| fn parse<T: Http1Transaction<Incoming=I>, I>(rdr: &[u8]) -> ParseResult<I> { | ||||
|     h1::parse::<T, I>(rdr) | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct Cursor<T: AsRef<[u8]>> { | ||||
|     bytes: T, | ||||
|     pos: usize, | ||||
| } | ||||
|  | ||||
| impl<T: AsRef<[u8]>> Cursor<T> { | ||||
|     pub fn new(bytes: T) -> Cursor<T> { | ||||
|         Cursor { | ||||
|             bytes: bytes, | ||||
|             pos: 0, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn is_written(&self) -> bool { | ||||
|         trace!("Cursor::is_written pos = {}, len = {}", self.pos, self.bytes.as_ref().len()); | ||||
|         self.pos >= self.bytes.as_ref().len() | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     pub fn write_to<W: Write>(&mut self, dst: &mut W) -> io::Result<usize> { | ||||
|         dst.write(&self.bytes.as_ref()[self.pos..]).map(|n| { | ||||
|             self.pos += n; | ||||
|             n | ||||
|         }) | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn buf(&self) -> &[u8] { | ||||
|         &self.bytes.as_ref()[self.pos..] | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub fn consume(&mut self, num: usize) { | ||||
|         trace!("Cursor::consume({})", num); | ||||
|         self.pos = ::std::cmp::min(self.bytes.as_ref().len(), self.pos + num); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: AsRef<[u8]>> fmt::Debug for Cursor<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         let bytes = self.buf(); | ||||
|         let reasonable_max = ::std::cmp::min(bytes.len(), 32); | ||||
|         write!(f, "Cursor({:?})", &bytes[..reasonable_max]) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait AtomicWrite { | ||||
|     fn write_atomic(&mut self, data: &[&[u8]]) -> io::Result<usize>; | ||||
| } | ||||
|  | ||||
| /* | ||||
| #[cfg(not(windows))] | ||||
| impl<T: Write + ::vecio::Writev> AtomicWrite for T { | ||||
|  | ||||
|     fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|         self.writev(bufs) | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| #[cfg(windows)] | ||||
| */ | ||||
| impl<T: Write> AtomicWrite for T { | ||||
|     fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|         if cfg!(not(windows)) { | ||||
|             warn!("write_atomic not using writev"); | ||||
|         } | ||||
|         let vec = bufs.concat(); | ||||
|         self.write(&vec) | ||||
|     } | ||||
| } | ||||
| //} | ||||
|  | ||||
|  | ||||
| #[test] | ||||
| fn test_iobuf_write_empty_slice() { | ||||
|     use mock::{AsyncIo, Buf as MockBuf}; | ||||
|  | ||||
|     let mut mock = AsyncIo::new(MockBuf::new(), 256); | ||||
|     mock.error(io::Error::new(io::ErrorKind::Other, "logic error")); | ||||
|  | ||||
|     let mut io_buf = Buffered::new(mock); | ||||
|  | ||||
|     // underlying io will return the logic error upon write, | ||||
|     // so we are testing that the io_buf does not trigger a write | ||||
|     // when there is nothing to flush | ||||
|     io_buf.flush().expect("should short-circuit flush"); | ||||
| } | ||||
							
								
								
									
										397
									
								
								src/http/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										397
									
								
								src/http/mod.rs
									
									
									
									
									
								
							| @@ -1,227 +1,44 @@ | ||||
| //! Pieces pertaining to the HTTP message protocol. | ||||
| use std::borrow::Cow; | ||||
| use std::fmt; | ||||
| use std::io::{self, Read, Write}; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use header::Connection; | ||||
| use header::ConnectionOption::{KeepAlive, Close}; | ||||
| use header::{Connection, ConnectionOption}; | ||||
| use header::Headers; | ||||
| use method::Method; | ||||
| use net::Transport; | ||||
| use status::StatusCode; | ||||
| use uri::RequestUri; | ||||
| use version::HttpVersion; | ||||
| use version::HttpVersion::{Http10, Http11}; | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||||
|  | ||||
| pub use self::conn::{Conn, MessageHandler, MessageHandlerFactory, Seed, Key, ReadyResult}; | ||||
| pub use self::conn::{Conn, KeepAlive, KA}; | ||||
| pub use self::body::{Body, TokioBody}; | ||||
| pub use self::chunk::Chunk; | ||||
|  | ||||
| mod body; | ||||
| //mod buf; | ||||
| mod buffer; | ||||
| pub mod channel; | ||||
| mod chunk; | ||||
| mod conn; | ||||
| mod io; | ||||
| mod h1; | ||||
| //mod h2; | ||||
|  | ||||
| /// Wraps a `Transport` to provide HTTP decoding when reading. | ||||
| #[derive(Debug)] | ||||
| pub struct Decoder<'a, T: Read + 'a>(DecoderImpl<'a, T>); | ||||
|  | ||||
| /// Wraps a `Transport` to provide HTTP encoding when writing. | ||||
| #[derive(Debug)] | ||||
| pub struct Encoder<'a, T: Transport + 'a>(EncoderImpl<'a, T>); | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum DecoderImpl<'a, T: Read + 'a> { | ||||
|     H1(&'a mut h1::Decoder, Trans<'a, T>), | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum Trans<'a, T: Read + 'a> { | ||||
|     Port(&'a mut T), | ||||
|     Buf(self::buffer::BufReader<'a, T>) | ||||
| } | ||||
|  | ||||
| impl<'a, T: Read + 'a> Trans<'a, T> { | ||||
|     fn get_ref(&self) -> &T { | ||||
|         match *self { | ||||
|             Trans::Port(ref t) => &*t, | ||||
|             Trans::Buf(ref buf) => buf.get_ref() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, T: Read + 'a> Read for Trans<'a, T> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             Trans::Port(ref mut t) => t.read(buf), | ||||
|             Trans::Buf(ref mut b) => b.read(buf) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum EncoderImpl<'a, T: Transport + 'a> { | ||||
|     H1(&'a mut h1::Encoder, &'a mut T), | ||||
| } | ||||
|  | ||||
| impl<'a, T: Read> Decoder<'a, T> { | ||||
|     fn h1(decoder: &'a mut h1::Decoder, transport: Trans<'a, T>) -> Decoder<'a, T> { | ||||
|         Decoder(DecoderImpl::H1(decoder, transport)) | ||||
|     } | ||||
|  | ||||
|     /// Read from the `Transport`. | ||||
|     #[inline] | ||||
|     pub fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match self.0 { | ||||
|             DecoderImpl::H1(ref mut decoder, ref mut transport) => { | ||||
|                 decoder.decode(transport, buf) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Try to read from the `Transport`. | ||||
|     /// | ||||
|     /// This method looks for the `WouldBlock` error. If the read did not block, | ||||
|     /// a return value would be `Ok(Some(x))`. If the read would block, | ||||
|     /// this method would return `Ok(None)`. | ||||
|     #[inline] | ||||
|     pub fn try_read(&mut self, buf: &mut [u8]) -> io::Result<Option<usize>> { | ||||
|         match self.read(buf) { | ||||
| /* | ||||
| macro_rules! nonblocking { | ||||
|     ($e:expr) => ({ | ||||
|         match $e { | ||||
|             Ok(n) => Ok(Some(n)), | ||||
|             Err(e) => match e.kind() { | ||||
|                 io::ErrorKind::WouldBlock => Ok(None), | ||||
|                 stdio::ErrorKind::WouldBlock => Ok(None), | ||||
|                 _ => Err(e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Get a reference to the transport. | ||||
|     pub fn get_ref(&self) -> &T { | ||||
|         match self.0 { | ||||
|             DecoderImpl::H1(_, ref transport) => transport.get_ref() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, T: Transport> Encoder<'a, T> { | ||||
|     fn h1(encoder: &'a mut h1::Encoder, transport: &'a mut T) -> Encoder<'a, T> { | ||||
|         Encoder(EncoderImpl::H1(encoder, transport)) | ||||
|     } | ||||
|  | ||||
|     /// Write to the `Transport`. | ||||
|     #[inline] | ||||
|     pub fn write(&mut self, data: &[u8]) -> io::Result<usize> { | ||||
|         if data.is_empty() { | ||||
|             return Ok(0); | ||||
|         } | ||||
|         match self.0 { | ||||
|             EncoderImpl::H1(ref mut encoder, ref mut transport) => { | ||||
|                 if encoder.is_closed() { | ||||
|                     Ok(0) | ||||
|                 } else { | ||||
|                     encoder.encode(*transport, data) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Try to write to the `Transport`. | ||||
|     /// | ||||
|     /// This method looks for the `WouldBlock` error. If the write did not block, | ||||
|     /// a return value would be `Ok(Some(x))`. If the write would block, | ||||
|     /// this method would return `Ok(None)`. | ||||
|     #[inline] | ||||
|     pub fn try_write(&mut self, data: &[u8]) -> io::Result<Option<usize>> { | ||||
|         match self.write(data) { | ||||
|             Ok(n) => Ok(Some(n)), | ||||
|             Err(e) => match e.kind() { | ||||
|                 io::ErrorKind::WouldBlock => Ok(None), | ||||
|                 _ => Err(e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Closes an encoder, signaling that no more writing will occur. | ||||
|     /// | ||||
|     /// This is needed for encodings that don't know the length of the content | ||||
|     /// beforehand. Most common instance would be usage of | ||||
|     /// `Transfer-Enciding: chunked`. You would call `close()` to signal | ||||
|     /// the `Encoder` should write the end chunk, or `0\r\n\r\n`. | ||||
|     pub fn close(&mut self) { | ||||
|         match self.0 { | ||||
|             EncoderImpl::H1(ref mut encoder, _) => encoder.close() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Get a reference to the transport. | ||||
|     pub fn get_ref(&self) -> &T { | ||||
|         match self.0 { | ||||
|             EncoderImpl::H1(_, ref transport) => &*transport | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, T: Read> Read for Decoder<'a, T> { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         self.read(buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, T: Transport> Write for Encoder<'a, T> { | ||||
|     #[inline] | ||||
|     fn write(&mut self, data: &[u8]) -> io::Result<usize> { | ||||
|         self.write(data) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         match self.0 { | ||||
|             EncoderImpl::H1(_, ref mut transport) => { | ||||
|                 transport.flush() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Because privacy rules. Reasons. | ||||
| /// https://github.com/rust-lang/rust/issues/30905 | ||||
| mod internal { | ||||
|     use std::io::{self, Write}; | ||||
|  | ||||
|     #[derive(Debug, Clone)] | ||||
|     pub struct WriteBuf<T: AsRef<[u8]>> { | ||||
|         pub bytes: T, | ||||
|         pub pos: usize, | ||||
|     } | ||||
|  | ||||
|     pub trait AtomicWrite { | ||||
|         fn write_atomic(&mut self, data: &[&[u8]]) -> io::Result<usize>; | ||||
|     } | ||||
|  | ||||
|     #[cfg(not(windows))] | ||||
|     impl<T: Write + ::vecio::Writev> AtomicWrite for T { | ||||
|  | ||||
|         fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|             self.writev(bufs) | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     #[cfg(windows)] | ||||
|     impl<T: Write> AtomicWrite for T { | ||||
|         fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|             let vec = bufs.concat(); | ||||
|             self.write(&vec) | ||||
|         } | ||||
|     } | ||||
|     }); | ||||
| } | ||||
| */ | ||||
|  | ||||
| /// An Incoming Message head. Includes request/status line, and headers. | ||||
| #[derive(Debug, Default)] | ||||
| #[derive(Debug, Default, PartialEq)] | ||||
| pub struct MessageHead<S> { | ||||
|     /// HTTP version of the message. | ||||
|     pub version: HttpVersion, | ||||
| @@ -234,7 +51,7 @@ pub struct MessageHead<S> { | ||||
| /// An incoming request message. | ||||
| pub type RequestHead = MessageHead<RequestLine>; | ||||
|  | ||||
| #[derive(Debug, Default)] | ||||
| #[derive(Debug, Default, PartialEq)] | ||||
| pub struct RequestLine(pub Method, pub RequestUri); | ||||
|  | ||||
| impl fmt::Display for RequestLine { | ||||
| @@ -274,18 +91,13 @@ impl Default for RawStatus { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| impl Serialize for RawStatus { | ||||
|     fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { | ||||
|         (self.0, &self.1).serialize(serializer) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| impl Deserialize for RawStatus { | ||||
|     fn deserialize<D>(deserializer: &mut D) -> Result<RawStatus, D::Error> where D: Deserializer { | ||||
|         let representation: (u16, String) = try!(Deserialize::deserialize(deserializer)); | ||||
|         Ok(RawStatus(representation.0, Cow::Owned(representation.1))) | ||||
| impl From<MessageHead<::StatusCode>> for MessageHead<RawStatus> { | ||||
|     fn from(head: MessageHead<::StatusCode>) -> MessageHead<RawStatus> { | ||||
|         MessageHead { | ||||
|             subject: head.subject.into(), | ||||
|             version: head.version, | ||||
|             headers: head.headers, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -294,174 +106,31 @@ impl Deserialize for RawStatus { | ||||
| pub fn should_keep_alive(version: HttpVersion, headers: &Headers) -> bool { | ||||
|     let ret = match (version, headers.get::<Connection>()) { | ||||
|         (Http10, None) => false, | ||||
|         (Http10, Some(conn)) if !conn.contains(&KeepAlive) => false, | ||||
|         (Http11, Some(conn)) if conn.contains(&Close)  => false, | ||||
|         (Http10, Some(conn)) if !conn.contains(&ConnectionOption::KeepAlive) => false, | ||||
|         (Http11, Some(conn)) if conn.contains(&ConnectionOption::Close)  => false, | ||||
|         _ => true | ||||
|     }; | ||||
|     trace!("should_keep_alive(version={:?}, header={:?}) = {:?}", version, headers.get::<Connection>(), ret); | ||||
|     ret | ||||
| } | ||||
|  | ||||
| pub type ParseResult<T> = ::Result<Option<(MessageHead<T>, usize)>>; | ||||
|  | ||||
| pub fn parse<T: Http1Message<Incoming=I>, I>(rdr: &[u8]) -> ParseResult<I> { | ||||
|     h1::parse::<T, I>(rdr) | ||||
| } | ||||
|  | ||||
| // These 2 enums are not actually dead_code. They are used in the server and | ||||
| // and client modules, respectively. However, their being used as associated | ||||
| // types doesn't mark them as used, so the dead_code linter complains. | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| #[derive(Debug)] | ||||
| pub enum ServerMessage {} | ||||
| pub enum ServerTransaction {} | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| #[derive(Debug)] | ||||
| pub enum ClientMessage {} | ||||
| pub enum ClientTransaction {} | ||||
|  | ||||
| pub trait Http1Message { | ||||
| pub trait Http1Transaction { | ||||
|     type Incoming; | ||||
|     type Outgoing: Default; | ||||
|     //type KeepAlive: KeepAlive; | ||||
|     fn parse(bytes: &[u8]) -> ParseResult<Self::Incoming>; | ||||
|     fn decoder(head: &MessageHead<Self::Incoming>) -> ::Result<h1::Decoder>; | ||||
|     fn encode(head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> h1::Encoder; | ||||
|     fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> h1::Encoder; | ||||
|     fn should_set_length(head: &MessageHead<Self::Outgoing>) -> bool; | ||||
| } | ||||
|  | ||||
| /// Used to signal desired events when working with asynchronous IO. | ||||
| #[must_use] | ||||
| #[derive(Clone)] | ||||
| pub struct Next { | ||||
|     interest: Next_, | ||||
|     timeout: Option<Duration>, | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Next { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         try!(write!(f, "Next::{:?}", &self.interest)); | ||||
|         match self.timeout { | ||||
|             Some(ref d) => write!(f, "({:?})", d), | ||||
|             None => Ok(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Internal enum for `Next` | ||||
| #[derive(Debug, Clone, Copy)] | ||||
| enum Next_ { | ||||
|     Read, | ||||
|     Write, | ||||
|     ReadWrite, | ||||
|     Wait, | ||||
|     End, | ||||
|     Remove, | ||||
| } | ||||
|  | ||||
| // An enum representing all the possible actions to taken when registering | ||||
| // with the event loop. | ||||
| #[derive(Debug, Clone, Copy)] | ||||
| enum Reg { | ||||
|     Read, | ||||
|     Write, | ||||
|     ReadWrite, | ||||
|     Wait, | ||||
|     Remove | ||||
| } | ||||
|  | ||||
| /// A notifier to wakeup a socket after having used `Next::wait()` | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Control { | ||||
|     tx: self::channel::Sender<Next>, | ||||
| } | ||||
|  | ||||
| impl Control { | ||||
|     /// Wakeup a waiting socket to listen for a certain event. | ||||
|     pub fn ready(&self, next: Next) -> Result<(), ControlError> { | ||||
|         //TODO: assert!( next.interest != Next_::Wait ) ? | ||||
|         self.tx.send(next).map_err(|_| ControlError(())) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// An error occured trying to tell a Control it is ready. | ||||
| #[derive(Debug)] | ||||
| pub struct ControlError(()); | ||||
|  | ||||
| impl ::std::error::Error for ControlError { | ||||
|     fn description(&self) -> &str { | ||||
|         "Cannot wakeup event loop: loop is closed" | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Display for ControlError { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_str(::std::error::Error::description(self)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Next { | ||||
|     fn new(interest: Next_) -> Next { | ||||
|         Next { | ||||
|             interest: interest, | ||||
|             timeout: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     fn reg(&self) -> Reg { | ||||
|         self.interest.register() | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     /// Signals the desire to read from the transport. | ||||
|     pub fn read() -> Next { | ||||
|         Next::new(Next_::Read) | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to write to the transport. | ||||
|     pub fn write() -> Next { | ||||
|         Next::new(Next_::Write) | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to read and write to the transport. | ||||
|     pub fn read_and_write() -> Next { | ||||
|         Next::new(Next_::ReadWrite) | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to end the current HTTP message. | ||||
|     pub fn end() -> Next { | ||||
|         Next::new(Next_::End) | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to abruptly remove the current transport from the | ||||
|     /// event loop. | ||||
|     pub fn remove() -> Next { | ||||
|         Next::new(Next_::Remove) | ||||
|     } | ||||
|  | ||||
|     /// Signals the desire to wait until some future time before acting again. | ||||
|     pub fn wait() -> Next { | ||||
|         Next::new(Next_::Wait) | ||||
|     } | ||||
|  | ||||
|     /// Signals a maximum duration to be waited for the desired event. | ||||
|     pub fn timeout(mut self, dur: Duration) -> Next { | ||||
|         self.timeout = Some(dur); | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Next_ { | ||||
|     fn register(&self) -> Reg { | ||||
|         match *self { | ||||
|             Next_::Read => Reg::Read, | ||||
|             Next_::Write => Reg::Write, | ||||
|             Next_::ReadWrite => Reg::ReadWrite, | ||||
|             Next_::Wait => Reg::Wait, | ||||
|             Next_::End => Reg::Remove, | ||||
|             Next_::Remove => Reg::Remove, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| type ParseResult<T> = ::Result<Option<(MessageHead<T>, usize)>>; | ||||
|  | ||||
| #[test] | ||||
| fn test_should_keep_alive() { | ||||
|   | ||||
							
								
								
									
										84
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										84
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -13,34 +13,22 @@ | ||||
| //! Hyper provides both a [Client](client/index.html) and a | ||||
| //! [Server](server/index.html), along with a | ||||
| //! [typed Headers system](header/index.html). | ||||
| //! | ||||
| //! If just getting started, consider looking over the **[Guide](../guide/)**. | ||||
|  | ||||
| extern crate cookie; | ||||
| extern crate futures; | ||||
| extern crate futures_cpupool; | ||||
| extern crate httparse; | ||||
| #[macro_use] extern crate language_tags; | ||||
| #[macro_use] extern crate log; | ||||
| #[macro_use] pub extern crate mime; | ||||
| extern crate relay; | ||||
| extern crate rustc_serialize as serialize; | ||||
| extern crate time; | ||||
| #[macro_use] extern crate url; | ||||
| #[cfg(feature = "openssl")] | ||||
| extern crate openssl; | ||||
| #[cfg(feature = "openssl-verify")] | ||||
| extern crate openssl_verify; | ||||
| #[cfg(feature = "security-framework")] | ||||
| extern crate security_framework; | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| extern crate serde; | ||||
| extern crate cookie; | ||||
| #[macro_use] extern crate tokio_core as tokio; | ||||
| extern crate tokio_proto; | ||||
| extern crate tokio_service; | ||||
| extern crate unicase; | ||||
| extern crate httparse; | ||||
| extern crate rotor; | ||||
| extern crate spmc; | ||||
| extern crate vecio; | ||||
|  | ||||
| #[macro_use] | ||||
| extern crate language_tags; | ||||
|  | ||||
| #[macro_use] | ||||
| extern crate mime as mime_crate; | ||||
|  | ||||
| #[macro_use] | ||||
| extern crate log; | ||||
| #[macro_use] extern crate url; | ||||
|  | ||||
| #[cfg(all(test, feature = "nightly"))] | ||||
| extern crate test; | ||||
| @@ -50,20 +38,22 @@ pub use url::Url; | ||||
| pub use client::Client; | ||||
| pub use error::{Result, Error}; | ||||
| pub use header::Headers; | ||||
| pub use http::{Next, Encoder, Decoder, Control, ControlError}; | ||||
| pub use http::{Body, Chunk}; | ||||
| pub use method::Method::{self, Get, Head, Post, Delete}; | ||||
| pub use net::{HttpStream, Transport}; | ||||
| pub use status::StatusCode::{self, Ok, BadRequest, NotFound}; | ||||
| pub use server::Server; | ||||
| pub use uri::RequestUri; | ||||
| pub use version::HttpVersion; | ||||
|  | ||||
| macro_rules! rotor_try { | ||||
|     ($e:expr) => ({ | ||||
|         match $e { | ||||
|             Ok(v) => v, | ||||
|             Err(e) => return ::rotor::Response::error(e.into()) | ||||
|         } | ||||
| macro_rules! unimplemented { | ||||
|     () => ({ | ||||
|         panic!("unimplemented") | ||||
|     }); | ||||
|     ($msg:expr) => ({ | ||||
|         unimplemented!("{}", $msg) | ||||
|     }); | ||||
|     ($fmt:expr, $($arg:tt)*) => ({ | ||||
|         panic!(concat!("unimplemented: ", $fmt), $($arg)*) | ||||
|     }); | ||||
| } | ||||
|  | ||||
| @@ -71,32 +61,10 @@ macro_rules! rotor_try { | ||||
| mod mock; | ||||
| pub mod client; | ||||
| pub mod error; | ||||
| pub mod method; | ||||
| mod method; | ||||
| pub mod header; | ||||
| mod http; | ||||
| pub mod net; | ||||
| pub mod server; | ||||
| pub mod status; | ||||
| pub mod uri; | ||||
| pub mod version; | ||||
|  | ||||
| /// Re-exporting the mime crate, for convenience. | ||||
| pub mod mime { | ||||
|     pub use mime_crate::*; | ||||
| } | ||||
|  | ||||
| /* | ||||
| #[allow(unconditional_recursion)] | ||||
| fn _assert_send<T: Send>() { | ||||
|     _assert_send::<Client>(); | ||||
|     _assert_send::<client::Request<net::Fresh>>(); | ||||
|     _assert_send::<client::Response>(); | ||||
|     _assert_send::<error::Error>(); | ||||
| } | ||||
|  | ||||
| #[allow(unconditional_recursion)] | ||||
| fn _assert_sync<T: Sync>() { | ||||
|     _assert_sync::<Client>(); | ||||
|     _assert_sync::<error::Error>(); | ||||
| } | ||||
| */ | ||||
| mod uri; | ||||
| mod version; | ||||
|   | ||||
| @@ -7,8 +7,6 @@ use error::Error; | ||||
| use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch, | ||||
|                    Extension}; | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||||
|  | ||||
| /// The Request Method (VERB) | ||||
| /// | ||||
| @@ -134,21 +132,6 @@ impl Default for Method { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| impl Serialize for Method { | ||||
|     fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { | ||||
|         format!("{}", self).serialize(serializer) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "serde-serialization")] | ||||
| impl Deserialize for Method { | ||||
|     fn deserialize<D>(deserializer: &mut D) -> Result<Method, D::Error> where D: Deserializer { | ||||
|         let string_representation: String = try!(Deserialize::deserialize(deserializer)); | ||||
|         Ok(FromStr::from_str(&string_representation[..]).unwrap()) | ||||
|     } | ||||
| }  | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::collections::HashMap; | ||||
|   | ||||
							
								
								
									
										65
									
								
								src/mock.rs
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								src/mock.rs
									
									
									
									
									
								
							| @@ -1,6 +1,9 @@ | ||||
| use std::cmp; | ||||
| use std::io::{self, Read, Write}; | ||||
|  | ||||
| use futures::Async; | ||||
| use tokio::io::Io; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct Buf { | ||||
|     vec: Vec<u8> | ||||
| @@ -8,8 +11,12 @@ pub struct Buf { | ||||
|  | ||||
| impl Buf { | ||||
|     pub fn new() -> Buf { | ||||
|         Buf::wrap(vec![]) | ||||
|     } | ||||
|  | ||||
|     pub fn wrap(vec: Vec<u8>) -> Buf { | ||||
|         Buf { | ||||
|             vec: vec![] | ||||
|             vec: vec, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -58,27 +65,41 @@ impl ::vecio::Writev for Buf { | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct Async<T> { | ||||
| pub struct AsyncIo<T> { | ||||
|     inner: T, | ||||
|     bytes_until_block: usize, | ||||
|     error: Option<io::Error>, | ||||
| } | ||||
|  | ||||
| impl<T> Async<T> { | ||||
|     pub fn new(inner: T, bytes: usize) -> Async<T> { | ||||
|         Async { | ||||
| impl<T> AsyncIo<T> { | ||||
|     pub fn new(inner: T, bytes: usize) -> AsyncIo<T> { | ||||
|         AsyncIo { | ||||
|             inner: inner, | ||||
|             bytes_until_block: bytes | ||||
|             bytes_until_block: bytes, | ||||
|             error: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn block_in(&mut self, bytes: usize) { | ||||
|         self.bytes_until_block = bytes; | ||||
|     } | ||||
|  | ||||
|     pub fn error(&mut self, err: io::Error) { | ||||
|         self.error = Some(err); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Read> Read for Async<T> { | ||||
| impl AsyncIo<Buf> { | ||||
|     pub fn new_buf<T: Into<Vec<u8>>>(buf: T, bytes: usize) -> AsyncIo<Buf> { | ||||
|         AsyncIo::new(Buf::wrap(buf.into()), bytes) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Read> Read for AsyncIo<T> { | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         if self.bytes_until_block == 0 { | ||||
|         if let Some(err) = self.error.take() { | ||||
|             Err(err) | ||||
|         } else if self.bytes_until_block == 0 { | ||||
|             Err(io::Error::new(io::ErrorKind::WouldBlock, "mock block")) | ||||
|         } else { | ||||
|             let n = cmp::min(self.bytes_until_block, buf.len()); | ||||
| @@ -89,9 +110,11 @@ impl<T: Read> Read for Async<T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Write> Write for Async<T> { | ||||
| impl<T: Write> Write for AsyncIo<T> { | ||||
|     fn write(&mut self, data: &[u8]) -> io::Result<usize> { | ||||
|         if self.bytes_until_block == 0 { | ||||
|         if let Some(err) = self.error.take() { | ||||
|             Err(err) | ||||
|         } else if self.bytes_until_block == 0 { | ||||
|             Err(io::Error::new(io::ErrorKind::WouldBlock, "mock block")) | ||||
|         } else { | ||||
|             let n = cmp::min(self.bytes_until_block, data.len()); | ||||
| @@ -106,7 +129,25 @@ impl<T: Write> Write for Async<T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Write> ::vecio::Writev for Async<T> { | ||||
| impl<T: Read + Write> Io for AsyncIo<T> { | ||||
|     fn poll_read(&mut self) -> Async<()> { | ||||
|         if self.bytes_until_block == 0 { | ||||
|             Async::NotReady | ||||
|         } else { | ||||
|             Async::Ready(()) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn poll_write(&mut self) -> Async<()> { | ||||
|         if self.bytes_until_block == 0 { | ||||
|             Async::NotReady | ||||
|         } else { | ||||
|             Async::Ready(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Write> ::vecio::Writev for AsyncIo<T> { | ||||
|     fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|         let cap = bufs.iter().map(|buf| buf.len()).fold(0, |total, next| total + next); | ||||
|         let mut vec = Vec::with_capacity(cap); | ||||
| @@ -118,7 +159,7 @@ impl<T: Write> ::vecio::Writev for Async<T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ::std::ops::Deref for Async<Buf> { | ||||
| impl ::std::ops::Deref for AsyncIo<Buf> { | ||||
|     type Target = [u8]; | ||||
|  | ||||
|     fn deref(&self) -> &[u8] { | ||||
|   | ||||
							
								
								
									
										605
									
								
								src/net.rs
									
									
									
									
									
								
							
							
						
						
									
										605
									
								
								src/net.rs
									
									
									
									
									
								
							| @@ -3,77 +3,12 @@ use std::io::{self, Read, Write}; | ||||
| use std::net::{SocketAddr}; | ||||
| use std::option; | ||||
|  | ||||
| use rotor::mio::tcp::{TcpStream, TcpListener}; | ||||
| use rotor::mio::{Selector, Token, Evented, EventSet, PollOpt, TryAccept}; | ||||
| use std::net::{TcpStream, TcpListener}; | ||||
|  | ||||
| #[cfg(feature = "openssl")] | ||||
| pub use self::openssl::{Openssl, OpensslStream}; | ||||
|  | ||||
| #[cfg(feature = "security-framework")] | ||||
| pub use self::security_framework::{SecureTransport, SecureTransportClient, SecureTransportServer}; | ||||
|  | ||||
| /// A trait representing a socket transport that can be used in a Client or Server. | ||||
| #[cfg(not(windows))] | ||||
| pub trait Transport: Read + Write + Evented + ::vecio::Writev { | ||||
|     /// Takes a socket error when event polling notices an `events.is_error()`. | ||||
|     fn take_socket_error(&mut self) -> io::Result<()>; | ||||
|  | ||||
|     /// Returns if the this transport is blocked on read or write. | ||||
|     /// | ||||
|     /// By default, the user will declare whether they wish to wait on read | ||||
|     /// or write events. However, some transports, such as those protected by | ||||
|     /// TLS, may be blocked on reading before it can write, or vice versa. | ||||
|     fn blocked(&self) -> Option<Blocked> { | ||||
|         None | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A trait representing a socket transport that can be used in a Client or Server. | ||||
| #[cfg(windows)] | ||||
| pub trait Transport: Read + Write + Evented { | ||||
|     /// Takes a socket error when event polling notices an `events.is_error()`. | ||||
|     fn take_socket_error(&mut self) -> io::Result<()>; | ||||
|  | ||||
|     /// Returns if the this transport is blocked on read or write. | ||||
|     /// | ||||
|     /// By default, the user will declare whether they wish to wait on read | ||||
|     /// or write events. However, some transports, such as those protected by | ||||
|     /// TLS, may be blocked on reading before it can write, or vice versa. | ||||
|     fn blocked(&self) -> Option<Blocked> { | ||||
|         None | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Declares when a transport is blocked from any further action, until the | ||||
| /// corresponding event has occured. | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||||
| pub enum Blocked { | ||||
|     /// Blocked on reading | ||||
|     Read, | ||||
|     /// blocked on writing | ||||
|     Write, | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Accepts sockets asynchronously. | ||||
| pub trait Accept: Evented { | ||||
|     /// The transport type that is accepted. | ||||
|     type Output: Transport; | ||||
|     /// Accept a socket from the listener, if it doesn not block. | ||||
|     fn accept(&self) -> io::Result<Option<Self::Output>>; | ||||
|     /// Return the local `SocketAddr` of this listener. | ||||
|     fn local_addr(&self) -> io::Result<SocketAddr>; | ||||
| } | ||||
|  | ||||
| /// An alias to `mio::tcp::TcpStream`. | ||||
| #[derive(Debug)] | ||||
| pub struct HttpStream(pub TcpStream); | ||||
|  | ||||
| impl Transport for HttpStream { | ||||
|     fn take_socket_error(&mut self) -> io::Result<()> { | ||||
|         self.0.take_socket_error() | ||||
|     } | ||||
| } | ||||
| //#[derive(Debug)] | ||||
| pub struct HttpStream(pub ::tokio::net::TcpStream); | ||||
|  | ||||
| impl Read for HttpStream { | ||||
|     #[inline] | ||||
| @@ -94,23 +29,7 @@ impl Write for HttpStream { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Evented for HttpStream { | ||||
|     #[inline] | ||||
|     fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|         self.0.register(selector, token, interest, opts) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|         self.0.reregister(selector, token, interest, opts) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn deregister(&self, selector: &mut Selector) -> io::Result<()> { | ||||
|         self.0.deregister(selector) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
| #[cfg(not(windows))] | ||||
| impl ::vecio::Writev for HttpStream { | ||||
|     #[inline] | ||||
| @@ -119,6 +38,7 @@ impl ::vecio::Writev for HttpStream { | ||||
|         self.0.writev(bufs) | ||||
|     } | ||||
| } | ||||
| */ | ||||
|  | ||||
| /// An alias to `mio::tcp::TcpListener`. | ||||
| #[derive(Debug)] | ||||
| @@ -137,59 +57,7 @@ impl HttpListener { | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| impl Accept for HttpListener { | ||||
|     type Output = HttpStream; | ||||
|  | ||||
|     #[inline] | ||||
|     fn accept(&self) -> io::Result<Option<HttpStream>> { | ||||
|         TryAccept::accept(&self.0).map(|ok| ok.map(HttpStream)) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn local_addr(&self) -> io::Result<SocketAddr> { | ||||
|         self.0.local_addr() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Evented for HttpListener { | ||||
|     #[inline] | ||||
|     fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|         self.0.register(selector, token, interest, opts) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|         self.0.reregister(selector, token, interest, opts) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn deregister(&self, selector: &mut Selector) -> io::Result<()> { | ||||
|         self.0.deregister(selector) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl IntoIterator for HttpListener { | ||||
|     type Item = Self; | ||||
|     type IntoIter = option::IntoIter<Self>; | ||||
|  | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         Some(self).into_iter() | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Deprecated | ||||
| /// | ||||
| /// Use `SslClient` and `SslServer` instead. | ||||
| pub trait Ssl { | ||||
|     /// The protected stream. | ||||
|     type Stream: Transport; | ||||
|     /// Wrap a client stream with SSL. | ||||
|     fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream>; | ||||
|     /// Wrap a server stream with SSL. | ||||
|     fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream>; | ||||
| } | ||||
|  | ||||
| /* | ||||
| /// An abstraction to allow any SSL implementation to be used with client-side `HttpsStream`s. | ||||
| pub trait SslClient { | ||||
|     /// The protected stream. | ||||
| @@ -206,21 +74,6 @@ pub trait SslServer { | ||||
|     fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream>; | ||||
| } | ||||
|  | ||||
| impl<S: Ssl> SslClient for S { | ||||
|     type Stream = <S as Ssl>::Stream; | ||||
|  | ||||
|     fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> { | ||||
|         Ssl::wrap_client(self, stream, host) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: Ssl> SslServer for S { | ||||
|     type Stream = <S as Ssl>::Stream; | ||||
|  | ||||
|     fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream> { | ||||
|         Ssl::wrap_server(self, stream) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A stream over the HTTP protocol, possibly protected by TLS. | ||||
| #[derive(Debug)] | ||||
| @@ -259,6 +112,7 @@ impl<S: Transport> Write for HttpsStream<S> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
| #[cfg(not(windows))] | ||||
| impl<S: Transport> ::vecio::Writev for HttpsStream<S> { | ||||
|     #[inline] | ||||
| @@ -269,8 +123,10 @@ impl<S: Transport> ::vecio::Writev for HttpsStream<S> { | ||||
|         } | ||||
|     } | ||||
| } | ||||
| */ | ||||
|  | ||||
|  | ||||
| /* | ||||
| #[cfg(unix)] | ||||
| impl ::std::os::unix::io::AsRawFd for HttpStream { | ||||
|     #[inline] | ||||
| @@ -289,50 +145,7 @@ impl<S: Transport + ::std::os::unix::io::AsRawFd> ::std::os::unix::io::AsRawFd f | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: Transport> Evented for HttpsStream<S> { | ||||
|     #[inline] | ||||
|     fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|         match *self { | ||||
|             HttpsStream::Http(ref s) => s.register(selector, token, interest, opts), | ||||
|             HttpsStream::Https(ref s) => s.register(selector, token, interest, opts), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|         match *self { | ||||
|             HttpsStream::Http(ref s) => s.reregister(selector, token, interest, opts), | ||||
|             HttpsStream::Https(ref s) => s.reregister(selector, token, interest, opts), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn deregister(&self, selector: &mut Selector) -> io::Result<()> { | ||||
|         match *self { | ||||
|             HttpsStream::Http(ref s) => s.deregister(selector), | ||||
|             HttpsStream::Https(ref s) => s.deregister(selector), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: Transport> Transport for HttpsStream<S> { | ||||
|     #[inline] | ||||
|     fn take_socket_error(&mut self) -> io::Result<()> { | ||||
|         match *self { | ||||
|             HttpsStream::Http(ref mut s) => s.take_socket_error(), | ||||
|             HttpsStream::Https(ref mut s) => s.take_socket_error(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn blocked(&self) -> Option<Blocked> { | ||||
|         match *self { | ||||
|             HttpsStream::Http(ref s) => s.blocked(), | ||||
|             HttpsStream::Https(ref s) => s.blocked(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| */ | ||||
|  | ||||
| /// An `HttpListener` over SSL. | ||||
| #[derive(Debug)] | ||||
| @@ -360,6 +173,7 @@ impl<S: SslServer> HttpsListener<S> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
| impl<S: SslServer> Accept for HttpsListener<S> { | ||||
|     type Output = S::Stream; | ||||
|  | ||||
| @@ -382,401 +196,6 @@ impl<S: SslServer> Accept for HttpsListener<S> { | ||||
|         self.listener.local_addr() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: SslServer> Evented for HttpsListener<S> { | ||||
|     #[inline] | ||||
|     fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|         self.listener.register(selector, token, interest, opts) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|         self.listener.reregister(selector, token, interest, opts) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn deregister(&self, selector: &mut Selector) -> io::Result<()> { | ||||
|         self.listener.deregister(selector) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: SslServer> IntoIterator for HttpsListener<S> { | ||||
|     type Item = Self; | ||||
|     type IntoIter = option::IntoIter<Self>; | ||||
|  | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         Some(self).into_iter() | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn _assert_transport() { | ||||
|     fn _assert<T: Transport>() {} | ||||
|     _assert::<HttpsStream<HttpStream>>(); | ||||
| } | ||||
|  | ||||
| /* | ||||
| #[cfg(all(not(feature = "openssl"), not(feature = "security-framework")))] | ||||
| #[doc(hidden)] | ||||
| pub type DefaultConnector = HttpConnector; | ||||
|  | ||||
| #[cfg(feature = "openssl")] | ||||
| #[doc(hidden)] | ||||
| pub type DefaultConnector = HttpsConnector<self::openssl::OpensslClient>; | ||||
|  | ||||
| #[cfg(all(feature = "security-framework", not(feature = "openssl")))] | ||||
| pub type DefaultConnector = HttpsConnector<self::security_framework::ClientWrapper>; | ||||
|  | ||||
| #[doc(hidden)] | ||||
| pub type DefaultTransport = <DefaultConnector as Connect>::Output; | ||||
| */ | ||||
|  | ||||
| #[cfg(feature = "openssl")] | ||||
| mod openssl { | ||||
|     use std::io::{self, Write}; | ||||
|     use std::path::Path; | ||||
|  | ||||
|     use rotor::mio::{Selector, Token, Evented, EventSet, PollOpt}; | ||||
|  | ||||
|     use openssl::ssl::{Ssl, SslContext, SslStream, SslMethod, SSL_VERIFY_PEER, SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION}; | ||||
|     use openssl::ssl::error::StreamError as SslIoError; | ||||
|     use openssl::ssl::error::SslError; | ||||
|     use openssl::ssl::error::Error as OpensslError; | ||||
|     use openssl::x509::X509FileType; | ||||
|  | ||||
|     use super::{HttpStream, Blocked}; | ||||
|  | ||||
|     /// An implementation of `Ssl` for OpenSSL. | ||||
|     /// | ||||
|     /// # Example | ||||
|     /// | ||||
|     /// ```no_run | ||||
|     /// use hyper::Server; | ||||
|     /// use hyper::net::Openssl; | ||||
|     /// | ||||
|     /// let ssl = Openssl::with_cert_and_key("/home/foo/cert", "/home/foo/key").unwrap(); | ||||
|     /// Server::https(&"0.0.0.0:443".parse().unwrap(), ssl).unwrap(); | ||||
|     /// ``` | ||||
|     /// | ||||
|     /// For complete control, create a `SslContext` with the options you desire | ||||
|     /// and then create `Openssl { context: ctx } | ||||
|     #[derive(Debug, Clone)] | ||||
|     pub struct Openssl { | ||||
|         /// The `SslContext` from openssl crate. | ||||
|         pub context: SslContext | ||||
|     } | ||||
|  | ||||
|     /// A client-specific implementation of OpenSSL. | ||||
|     #[derive(Debug, Clone)] | ||||
|     pub struct OpensslClient(SslContext); | ||||
|  | ||||
|     impl Default for OpensslClient { | ||||
|         fn default() -> OpensslClient { | ||||
|             let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap(); | ||||
|             ctx.set_default_verify_paths().unwrap(); | ||||
|             ctx.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION); | ||||
|             // cipher list taken from curl: | ||||
|             // https://github.com/curl/curl/blob/5bf5f6ebfcede78ef7c2b16daa41c4b7ba266087/lib/vtls/openssl.h#L120 | ||||
|             ctx.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH").unwrap(); | ||||
|             OpensslClient::new(ctx) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl OpensslClient { | ||||
|         /// Creates a new OpensslClient with a custom SslContext | ||||
|         pub fn new(ctx: SslContext) -> OpensslClient { | ||||
|             OpensslClient(ctx) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl super::SslClient for OpensslClient { | ||||
|         type Stream = OpensslStream<HttpStream>; | ||||
|  | ||||
|         #[cfg(not(windows))] | ||||
|         fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> { | ||||
|             let mut ssl = try!(Ssl::new(&self.0)); | ||||
|             try!(ssl.set_hostname(host)); | ||||
|             let host = host.to_owned(); | ||||
|             ssl.set_verify_callback(SSL_VERIFY_PEER, move |p, x| ::openssl_verify::verify_callback(&host, p, x)); | ||||
|             SslStream::connect(ssl, stream) | ||||
|                 .map(openssl_stream) | ||||
|                 .map_err(From::from) | ||||
|         } | ||||
|  | ||||
|  | ||||
|         #[cfg(windows)] | ||||
|         fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> { | ||||
|             let mut ssl = try!(Ssl::new(&self.0)); | ||||
|             try!(ssl.set_hostname(host)); | ||||
|             SslStream::connect(ssl, stream) | ||||
|                 .map(openssl_stream) | ||||
|                 .map_err(From::from) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl Default for Openssl { | ||||
|         fn default() -> Openssl { | ||||
|             Openssl { | ||||
|                 context: SslContext::new(SslMethod::Sslv23).unwrap_or_else(|e| { | ||||
|                     // if we cannot create a SslContext, that's because of a | ||||
|                     // serious problem. just crash. | ||||
|                     panic!("{}", e) | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl Openssl { | ||||
|         /// Ease creating an `Openssl` with a certificate and key. | ||||
|         pub fn with_cert_and_key<C, K>(cert: C, key: K) -> Result<Openssl, SslError> | ||||
|         where C: AsRef<Path>, K: AsRef<Path> { | ||||
|             let mut ctx = try!(SslContext::new(SslMethod::Sslv23)); | ||||
|             try!(ctx.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH")); | ||||
|             try!(ctx.set_certificate_file(cert.as_ref(), X509FileType::PEM)); | ||||
|             try!(ctx.set_private_key_file(key.as_ref(), X509FileType::PEM)); | ||||
|             Ok(Openssl { context: ctx }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl super::Ssl for Openssl { | ||||
|         type Stream = OpensslStream<HttpStream>; | ||||
|  | ||||
|         fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> { | ||||
|             let ssl = try!(Ssl::new(&self.context)); | ||||
|             try!(ssl.set_hostname(host)); | ||||
|             SslStream::connect(ssl, stream) | ||||
|                 .map(openssl_stream) | ||||
|                 .map_err(From::from) | ||||
|         } | ||||
|  | ||||
|         fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream> { | ||||
|             match SslStream::accept(&self.context, stream) { | ||||
|                 Ok(ssl_stream) => Ok(openssl_stream(ssl_stream)), | ||||
|                 Err(SslIoError(e)) => { | ||||
|                     Err(io::Error::new(io::ErrorKind::ConnectionAborted, e).into()) | ||||
|                 }, | ||||
|                 Err(e) => Err(e.into()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// A transport protected by OpenSSL. | ||||
|     #[derive(Debug)] | ||||
|     pub struct OpensslStream<T> { | ||||
|         stream: SslStream<T>, | ||||
|         blocked: Option<Blocked>, | ||||
|     } | ||||
|  | ||||
|     fn openssl_stream<T>(inner: SslStream<T>) -> OpensslStream<T> { | ||||
|         OpensslStream { | ||||
|             stream: inner, | ||||
|             blocked: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T: super::Transport> io::Read for OpensslStream<T> { | ||||
|         fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|             self.blocked = None; | ||||
|             self.stream.ssl_read(buf).or_else(|e| match e { | ||||
|                 OpensslError::ZeroReturn => Ok(0), | ||||
|                 OpensslError::WantWrite(e) => { | ||||
|                     self.blocked = Some(Blocked::Write); | ||||
|                     Err(e) | ||||
|                 }, | ||||
|                 OpensslError::WantRead(e) | OpensslError::Stream(e) => Err(e), | ||||
|                 e => Err(io::Error::new(io::ErrorKind::Other, e)) | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T: super::Transport> io::Write for OpensslStream<T> { | ||||
|         fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | ||||
|             self.blocked = None; | ||||
|             self.stream.ssl_write(buf).or_else(|e| match e { | ||||
|                 OpensslError::ZeroReturn => Ok(0), | ||||
|                 OpensslError::WantRead(e) => { | ||||
|                     self.blocked = Some(Blocked::Read); | ||||
|                     Err(e) | ||||
|                 }, | ||||
|                 OpensslError::WantWrite(e) | OpensslError::Stream(e) => Err(e), | ||||
|                 e => Err(io::Error::new(io::ErrorKind::Other, e)) | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         fn flush(&mut self) -> io::Result<()> { | ||||
|             self.stream.flush() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     impl<T: super::Transport> Evented for OpensslStream<T> { | ||||
|         #[inline] | ||||
|         fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|             self.stream.get_ref().register(selector, token, interest, opts) | ||||
|         } | ||||
|  | ||||
|         #[inline] | ||||
|         fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|             self.stream.get_ref().reregister(selector, token, interest, opts) | ||||
|         } | ||||
|  | ||||
|         #[inline] | ||||
|         fn deregister(&self, selector: &mut Selector) -> io::Result<()> { | ||||
|             self.stream.get_ref().deregister(selector) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T: super::Transport> ::vecio::Writev for OpensslStream<T> { | ||||
|         fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|             let vec = bufs.concat(); | ||||
|             self.write(&vec) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T: super::Transport> super::Transport for OpensslStream<T> { | ||||
|         fn take_socket_error(&mut self) -> io::Result<()> { | ||||
|             self.stream.get_mut().take_socket_error() | ||||
|         } | ||||
|  | ||||
|         fn blocked(&self) -> Option<super::Blocked> { | ||||
|             self.blocked | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "security-framework")] | ||||
| mod security_framework { | ||||
|     use std::io::{self, Read, Write}; | ||||
|  | ||||
|     use error::Error; | ||||
|     use net::{SslClient, SslServer, HttpStream, Transport, Blocked}; | ||||
|  | ||||
|     use security_framework::secure_transport::SslStream; | ||||
|     pub use security_framework::secure_transport::{ClientBuilder as SecureTransportClient, ServerBuilder as SecureTransportServer}; | ||||
|     use rotor::mio::{Selector, Token, Evented, EventSet, PollOpt}; | ||||
|  | ||||
|     impl SslClient for SecureTransportClient { | ||||
|         type Stream = SecureTransport<HttpStream>; | ||||
|  | ||||
|         fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> { | ||||
|             match self.handshake(host, journal(stream)) { | ||||
|                 Ok(s) => Ok(SecureTransport(s)), | ||||
|                 Err(e) => Err(Error::Ssl(e.into())), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl SslServer for SecureTransportServer { | ||||
|         type Stream = SecureTransport<HttpStream>; | ||||
|  | ||||
|         fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream> { | ||||
|             match self.handshake(journal(stream)) { | ||||
|                 Ok(s) => Ok(SecureTransport(s)), | ||||
|                 Err(e) => Err(Error::Ssl(e.into())), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// A transport protected by Security Framework. | ||||
|     #[derive(Debug)] | ||||
|     pub struct SecureTransport<T>(SslStream<Journal<T>>); | ||||
|  | ||||
|     impl<T: Transport> io::Read for SecureTransport<T> { | ||||
|         #[inline] | ||||
|         fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|             self.0.read(buf) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T: Transport> io::Write for SecureTransport<T> { | ||||
|         #[inline] | ||||
|         fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | ||||
|             self.0.write(buf) | ||||
|         } | ||||
|  | ||||
|         #[inline] | ||||
|         fn flush(&mut self) -> io::Result<()> { | ||||
|             self.0.flush() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     impl<T: Transport> Evented for SecureTransport<T> { | ||||
|         #[inline] | ||||
|         fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|             self.0.get_ref().inner.register(selector, token, interest, opts) | ||||
|         } | ||||
|  | ||||
|         #[inline] | ||||
|         fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> { | ||||
|             self.0.get_ref().inner.reregister(selector, token, interest, opts) | ||||
|         } | ||||
|  | ||||
|         #[inline] | ||||
|         fn deregister(&self, selector: &mut Selector) -> io::Result<()> { | ||||
|             self.0.get_ref().inner.deregister(selector) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T: Transport> ::vecio::Writev for SecureTransport<T> { | ||||
|         fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> { | ||||
|             let vec = bufs.concat(); | ||||
|             self.write(&vec) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T: Transport> Transport for SecureTransport<T> { | ||||
|         fn take_socket_error(&mut self) -> io::Result<()> { | ||||
|             self.0.get_mut().inner.take_socket_error() | ||||
|         } | ||||
|  | ||||
|         fn blocked(&self) -> Option<super::Blocked> { | ||||
|             self.0.get_ref().blocked | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // Records if this object was blocked on reading or writing. | ||||
|     #[derive(Debug)] | ||||
|     struct Journal<T> { | ||||
|         inner: T, | ||||
|         blocked: Option<Blocked>, | ||||
|     } | ||||
|  | ||||
|     fn journal<T: Read + Write>(inner: T) -> Journal<T> { | ||||
|         Journal { | ||||
|             inner: inner, | ||||
|             blocked: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T: Read> Read for Journal<T> { | ||||
|         fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|             self.blocked = None; | ||||
|             self.inner.read(buf).map_err(|e| match e.kind() { | ||||
|                 io::ErrorKind::WouldBlock => { | ||||
|                     self.blocked = Some(Blocked::Read); | ||||
|                     e | ||||
|                 }, | ||||
|                 _ => e | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T: Write> Write for Journal<T> { | ||||
|         fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | ||||
|             self.blocked = None; | ||||
|             self.inner.write(buf).map_err(|e| match e.kind() { | ||||
|                 io::ErrorKind::WouldBlock => { | ||||
|                     self.blocked = Some(Blocked::Write); | ||||
|                     e | ||||
|                 }, | ||||
|                 _ => e | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         fn flush(&mut self) -> io::Result<()> { | ||||
|             self.inner.flush() | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| */ | ||||
|   | ||||
| @@ -1,57 +0,0 @@ | ||||
| use std::marker::PhantomData; | ||||
|  | ||||
|  | ||||
| use http::{self, Next}; | ||||
| use net::Transport; | ||||
|  | ||||
| use super::{Handler, request, response}; | ||||
|  | ||||
| /// A `MessageHandler` for a Server. | ||||
| /// | ||||
| /// This should be really thin glue between `http::MessageHandler` and | ||||
| /// `server::Handler`, but largely just providing the proper types one | ||||
| /// would expect in a Server Handler. | ||||
| pub struct Message<H: Handler<T>, T: Transport> { | ||||
|     handler: H, | ||||
|     _marker: PhantomData<T> | ||||
| } | ||||
|  | ||||
| impl<H: Handler<T>, T: Transport> Message<H, T> { | ||||
|     pub fn new(handler: H) -> Message<H, T> { | ||||
|         Message { | ||||
|             handler: handler, | ||||
|             _marker: PhantomData, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<H: Handler<T>, T: Transport> http::MessageHandler<T> for Message<H, T> { | ||||
|     type Message = http::ServerMessage; | ||||
|  | ||||
|     fn on_incoming(&mut self, head: http::RequestHead, transport: &T) -> Next { | ||||
|         trace!("on_incoming {:?}", head); | ||||
|         let req = request::new(head, transport); | ||||
|         self.handler.on_request(req) | ||||
|     } | ||||
|  | ||||
|     fn on_decode(&mut self, transport: &mut http::Decoder<T>) -> Next { | ||||
|         self.handler.on_request_readable(transport) | ||||
|     } | ||||
|  | ||||
|     fn on_outgoing(&mut self, head: &mut http::MessageHead<::status::StatusCode>) -> Next { | ||||
|         let mut res = response::new(head); | ||||
|         self.handler.on_response(&mut res) | ||||
|     } | ||||
|  | ||||
|     fn on_encode(&mut self, transport: &mut http::Encoder<T>) -> Next { | ||||
|         self.handler.on_response_writable(transport) | ||||
|     } | ||||
|  | ||||
|     fn on_error(&mut self, error: ::Error) -> Next { | ||||
|         self.handler.on_error(error) | ||||
|     } | ||||
|  | ||||
|     fn on_remove(self, transport: T) { | ||||
|         self.handler.on_remove(transport); | ||||
|     } | ||||
| } | ||||
| @@ -1,65 +1,55 @@ | ||||
| //! HTTP Server | ||||
| //! | ||||
| //! A `Server` is created to listen on a port, parse HTTP requests, and hand | ||||
| //! them off to a `Handler`. | ||||
| //! them off to a `Service`. | ||||
| use std::fmt; | ||||
| use std::net::SocketAddr; | ||||
| use std::sync::Arc; | ||||
| use std::sync::atomic::{AtomicBool, Ordering}; | ||||
| use std::time::Duration; | ||||
| use std::io; | ||||
| use std::net::{SocketAddr, TcpListener as StdTcpListener}; | ||||
|  | ||||
| use rotor::mio::{EventSet, PollOpt}; | ||||
| use rotor::{self, Scope}; | ||||
| use futures::{Future, Map}; | ||||
| use futures::stream::{Stream}; | ||||
| use futures::sync::oneshot; | ||||
|  | ||||
| use tokio::io::Io; | ||||
| use tokio::net::TcpListener; | ||||
| use tokio::reactor::{Core, Handle}; | ||||
| use tokio_proto::BindServer; | ||||
| use tokio_proto::streaming::Message; | ||||
| use tokio_proto::streaming::pipeline::ServerProto; | ||||
| pub use tokio_service::{NewService, Service}; | ||||
|  | ||||
| pub use self::accept::Accept; | ||||
| pub use self::request::Request; | ||||
| pub use self::response::Response; | ||||
|  | ||||
| use http::{self, Next, ReadyResult}; | ||||
|  | ||||
| pub use net::{Accept, HttpListener, HttpsListener}; | ||||
| use net::{SslServer, Transport}; | ||||
|  | ||||
| use http; | ||||
|  | ||||
| mod request; | ||||
| mod response; | ||||
| mod message; | ||||
|  | ||||
| /// A configured `Server` ready to run. | ||||
| pub struct ServerLoop<A, H> where A: Accept, H: HandlerFactory<A::Output> { | ||||
|     inner: Option<(rotor::Loop<ServerFsm<A, H>>, Context<H>)>, | ||||
| } | ||||
|  | ||||
| impl<A: Accept, H: HandlerFactory<A::Output>> fmt::Debug for ServerLoop<A, H> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.pad("ServerLoop") | ||||
|     } | ||||
| } | ||||
| type HttpIncoming = ::tokio::net::Incoming; | ||||
|  | ||||
| /// A Server that can accept incoming network requests. | ||||
| #[derive(Debug)] | ||||
| pub struct Server<A> { | ||||
|     lead_listener: A, | ||||
|     other_listeners: Vec<A>, | ||||
|     accepter: A, | ||||
|     addr: SocketAddr, | ||||
|     keep_alive: bool, | ||||
|     idle_timeout: Option<Duration>, | ||||
|     max_sockets: usize, | ||||
|     //idle_timeout: Option<Duration>, | ||||
|     //max_sockets: usize, | ||||
| } | ||||
|  | ||||
| impl<A: Accept> Server<A> { | ||||
|     /// Creates a new Server from one or more Listeners. | ||||
|     /// Creates a new Server from a Stream of Ios. | ||||
|     /// | ||||
|     /// Panics if listeners is an empty iterator. | ||||
|     pub fn new<I: IntoIterator<Item = A>>(listeners: I) -> Server<A> { | ||||
|         let mut listeners = listeners.into_iter(); | ||||
|         let lead_listener = listeners.next().expect("Server::new requires at least 1 listener"); | ||||
|         let other_listeners = listeners.collect::<Vec<_>>(); | ||||
|  | ||||
|     /// The addr is the socket address the accepter is listening on. | ||||
|     pub fn new(accepter: A, addr: SocketAddr) -> Server<A> { | ||||
|         Server { | ||||
|             lead_listener: lead_listener, | ||||
|             other_listeners: other_listeners, | ||||
|             accepter: accepter, | ||||
|             addr: addr, | ||||
|             keep_alive: true, | ||||
|             idle_timeout: Some(Duration::from_secs(10)), | ||||
|             max_sockets: 4096, | ||||
|             //idle_timeout: Some(Duration::from_secs(75)), | ||||
|             //max_sockets: 4096, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -71,14 +61,17 @@ impl<A: Accept> Server<A> { | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|     /// Sets how long an idle connection will be kept before closing. | ||||
|     /// | ||||
|     /// Default is 10 seconds. | ||||
|     /// Default is 75 seconds. | ||||
|     pub fn idle_timeout(mut self, val: Option<Duration>) -> Server<A> { | ||||
|         self.idle_timeout = val; | ||||
|         self | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     /* | ||||
|     /// Sets the maximum open sockets for this Server. | ||||
|     /// | ||||
|     /// Default is 4096, but most servers can handle much more than this. | ||||
| @@ -86,20 +79,21 @@ impl<A: Accept> Server<A> { | ||||
|         self.max_sockets = val; | ||||
|         self | ||||
|     } | ||||
|     */ | ||||
| } | ||||
|  | ||||
| impl Server<HttpListener> { //<H: HandlerFactory<<HttpListener as Accept>::Output>> Server<HttpListener, H> { | ||||
| impl Server<HttpIncoming> { | ||||
|     /// Creates a new HTTP server config listening on the provided address. | ||||
|     pub fn http(addr: &SocketAddr) -> ::Result<Server<HttpListener>> { | ||||
|         use ::rotor::mio::tcp::TcpListener; | ||||
|         TcpListener::bind(addr) | ||||
|             .map(HttpListener) | ||||
|             .map(Server::new) | ||||
|             .map_err(From::from) | ||||
|     pub fn http(addr: &SocketAddr, handle: &Handle) -> ::Result<Server<HttpIncoming>> { | ||||
|         let listener = try!(StdTcpListener::bind(addr)); | ||||
|         let addr = try!(listener.local_addr()); | ||||
|         let listener = try!(TcpListener::from_listener(listener, &addr, handle)); | ||||
|         Ok(Server::new(listener.incoming(), addr)) | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
| impl<S: SslServer> Server<HttpsListener<S>> { | ||||
|     /// Creates a new server config that will handle `HttpStream`s over SSL. | ||||
|     /// | ||||
| @@ -110,304 +104,227 @@ impl<S: SslServer> Server<HttpsListener<S>> { | ||||
|             .map_err(From::from) | ||||
|     } | ||||
| } | ||||
| */ | ||||
|  | ||||
|  | ||||
| impl<A: Accept> Server<A> { | ||||
|     /// Binds to a socket and starts handling connections. | ||||
|     pub fn handle<H>(self, factory: H) -> ::Result<(Listening, ServerLoop<A, H>)> | ||||
|     where H: HandlerFactory<A::Output> { | ||||
|         let shutdown = Arc::new(AtomicBool::new(false)); | ||||
|          | ||||
|         let mut config = rotor::Config::new(); | ||||
|         config.slab_capacity(self.max_sockets); | ||||
|         config.mio().notify_capacity(self.max_sockets); | ||||
|         let keep_alive = self.keep_alive; | ||||
|         let idle_timeout = self.idle_timeout; | ||||
|         let mut loop_ = rotor::Loop::new(&config).unwrap(); | ||||
|  | ||||
|         let mut addrs = Vec::with_capacity(1 + self.other_listeners.len()); | ||||
|  | ||||
|         // Add the lead listener. This one handles shutdown messages. | ||||
|         let mut notifier = None; | ||||
|         { | ||||
|             let notifier = &mut notifier; | ||||
|             let listener = self.lead_listener; | ||||
|             addrs.push(try!(listener.local_addr())); | ||||
|             let shutdown_rx = shutdown.clone(); | ||||
|             loop_.add_machine_with(move |scope| { | ||||
|                 *notifier = Some(scope.notifier()); | ||||
|                 rotor_try!(scope.register(&listener, EventSet::readable(), PollOpt::level())); | ||||
|                 rotor::Response::ok(ServerFsm::Listener(listener, shutdown_rx)) | ||||
|             }).unwrap(); | ||||
|         } | ||||
|         let notifier = notifier.expect("loop.add_machine failed"); | ||||
|  | ||||
|         // Add the other listeners. | ||||
|         for listener in self.other_listeners { | ||||
|             addrs.push(try!(listener.local_addr())); | ||||
|             let shutdown_rx = shutdown.clone(); | ||||
|             loop_.add_machine_with(move |scope| { | ||||
|                 rotor_try!(scope.register(&listener, EventSet::readable(), PollOpt::level())); | ||||
|                 rotor::Response::ok(ServerFsm::Listener(listener, shutdown_rx)) | ||||
|             }).unwrap(); | ||||
|         } | ||||
|  | ||||
|         let listening = Listening { | ||||
|             addrs: addrs, | ||||
|             shutdown: (shutdown, notifier), | ||||
|     pub fn handle<H>(self, factory: H, handle: &Handle) -> ::Result<SocketAddr> | ||||
|     where H: NewService<Request=Request, Response=Response, Error=::Error> + 'static { | ||||
|         let binder = HttpServer { | ||||
|             keep_alive: self.keep_alive, | ||||
|         }; | ||||
|         let server = ServerLoop { | ||||
|             inner: Some((loop_, Context { | ||||
|                 factory: factory, | ||||
|                 idle_timeout: idle_timeout, | ||||
|                 keep_alive: keep_alive, | ||||
|             })) | ||||
|         }; | ||||
|         Ok((listening, server)) | ||||
|         let inner_handle = handle.clone(); | ||||
|         handle.spawn(self.accepter.accept().for_each(move |(socket, remote_addr)| { | ||||
|             let service = HttpService { | ||||
|                 inner: try!(factory.new_service()), | ||||
|                 remote_addr: remote_addr, | ||||
|             }; | ||||
|             binder.bind_server(&inner_handle, socket, service); | ||||
|             Ok(()) | ||||
|         }).map_err(|e| { | ||||
|             error!("listener io error: {:?}", e); | ||||
|             () | ||||
|         })); | ||||
|  | ||||
|         Ok(self.addr) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Server<()> { | ||||
|     /// Create a server that owns its event loop. | ||||
|     /// | ||||
|     /// The returned `ServerLoop` can be used to run the loop forever in the | ||||
|     /// thread. The returned `Listening` can be sent to another thread, and | ||||
|     /// used to shutdown the `ServerLoop`. | ||||
|     pub fn standalone<F>(closure: F) -> ::Result<(Listening, ServerLoop)> | ||||
|     where F: FnOnce(&Handle) -> ::Result<SocketAddr> { | ||||
|         let core = try!(Core::new()); | ||||
|         let handle = core.handle(); | ||||
|         let addr = try!(closure(&handle)); | ||||
|         let (shutdown_tx, shutdown_rx) = oneshot::channel(); | ||||
|         Ok(( | ||||
|             Listening { | ||||
|                 addr: addr, | ||||
|                 shutdown: shutdown_tx, | ||||
|             }, | ||||
|              ServerLoop { | ||||
|                 inner: Some((core, shutdown_rx)), | ||||
|             } | ||||
|         )) | ||||
|  | ||||
| impl<A: Accept, H: HandlerFactory<A::Output>> ServerLoop<A, H> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A configured `Server` ready to run. | ||||
| pub struct ServerLoop { | ||||
|     inner: Option<(Core, oneshot::Receiver<()>)>, | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for ServerLoop { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.pad("ServerLoop") | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ServerLoop { | ||||
|     /// Runs the server forever in this loop. | ||||
|     /// | ||||
|     /// This will block the current thread. | ||||
|     pub fn run(self) { | ||||
|         // drop will take care of it. | ||||
|         trace!("ServerLoop::run()"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<A: Accept, H: HandlerFactory<A::Output>> Drop for ServerLoop<A, H> { | ||||
| impl Drop for ServerLoop { | ||||
|     fn drop(&mut self) { | ||||
|         self.inner.take().map(|(loop_, ctx)| { | ||||
|             let _ = loop_.run(ctx); | ||||
|         self.inner.take().map(|(mut loop_, shutdown)| { | ||||
|             debug!("ServerLoop::drop running"); | ||||
|             let _ = loop_.run(shutdown.or_else(|_dropped| ::futures::future::empty::<(), oneshot::Canceled>())); | ||||
|             debug!("Server closed"); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct Context<F> { | ||||
|     factory: F, | ||||
|     idle_timeout: Option<Duration>, | ||||
|     keep_alive: bool, | ||||
| } | ||||
|  | ||||
| impl<F: HandlerFactory<T>, T: Transport> http::MessageHandlerFactory<(), T> for Context<F> { | ||||
|     type Output = message::Message<F::Output, T>; | ||||
|  | ||||
|     fn create(&mut self, seed: http::Seed<()>) -> Option<Self::Output> { | ||||
|         Some(message::Message::new(self.factory.create(seed.control()))) | ||||
|     } | ||||
|  | ||||
|     fn keep_alive_interest(&self) -> Next { | ||||
|         if let Some(dur) = self.idle_timeout { | ||||
|             Next::read().timeout(dur) | ||||
|         } else { | ||||
|             Next::read() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| enum ServerFsm<A, H> | ||||
| where A: Accept, | ||||
|       A::Output: Transport, | ||||
|       H: HandlerFactory<A::Output> { | ||||
|     Listener(A, Arc<AtomicBool>), | ||||
|     Conn(http::Conn<(), A::Output, message::Message<H::Output, A::Output>>) | ||||
| } | ||||
|  | ||||
| impl<A, H> rotor::Machine for ServerFsm<A, H> | ||||
| where A: Accept, | ||||
|       A::Output: Transport, | ||||
|       H: HandlerFactory<A::Output> { | ||||
|     type Context = Context<H>; | ||||
|     type Seed = A::Output; | ||||
|  | ||||
|     fn create(seed: Self::Seed, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, rotor::Void> { | ||||
|         rotor_try!(scope.register(&seed, EventSet::readable(), PollOpt::level())); | ||||
|         rotor::Response::ok( | ||||
|             ServerFsm::Conn( | ||||
|                 http::Conn::new((), seed, Next::read(), scope.notifier(), scope.now()) | ||||
|                     .keep_alive(scope.keep_alive) | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     fn ready(self, events: EventSet, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ServerFsm::Listener(listener, rx) => { | ||||
|                 match listener.accept() { | ||||
|                     Ok(Some(conn)) => { | ||||
|                         rotor::Response::spawn(ServerFsm::Listener(listener, rx), conn) | ||||
|                     }, | ||||
|                     Ok(None) => rotor::Response::ok(ServerFsm::Listener(listener, rx)), | ||||
|                     Err(e) => { | ||||
|                         error!("listener accept error {}", e); | ||||
|                         // usually fine, just keep listening | ||||
|                         rotor::Response::ok(ServerFsm::Listener(listener, rx)) | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             ServerFsm::Conn(conn) => { | ||||
|                 let mut conn = Some(conn); | ||||
|                 loop { | ||||
|                     match conn.take().unwrap().ready(events, scope) { | ||||
|                         ReadyResult::Continue(c) => conn = Some(c), | ||||
|                         ReadyResult::Done(res) => { | ||||
|                             return match res { | ||||
|                                 Some((conn, None)) => rotor::Response::ok(ServerFsm::Conn(conn)), | ||||
|                                 Some((conn, Some(dur))) => { | ||||
|                                     rotor::Response::ok(ServerFsm::Conn(conn)) | ||||
|                                         .deadline(scope.now() + dur) | ||||
|                                 } | ||||
|                                 None => rotor::Response::done() | ||||
|                             }; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn spawned(self, _scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ServerFsm::Listener(listener, rx) => { | ||||
|                 match listener.accept() { | ||||
|                     Ok(Some(conn)) => { | ||||
|                         rotor::Response::spawn(ServerFsm::Listener(listener, rx), conn) | ||||
|                     }, | ||||
|                     Ok(None) => rotor::Response::ok(ServerFsm::Listener(listener, rx)), | ||||
|                     Err(e) => { | ||||
|                         error!("listener accept error {}", e); | ||||
|                         // usually fine, just keep listening | ||||
|                         rotor::Response::ok(ServerFsm::Listener(listener, rx)) | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             sock => rotor::Response::ok(sock) | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     fn timeout(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ServerFsm::Listener(..) => unreachable!("Listener cannot timeout"), | ||||
|             ServerFsm::Conn(conn) => { | ||||
|                 match conn.timeout(scope) { | ||||
|                     Some((conn, None)) => rotor::Response::ok(ServerFsm::Conn(conn)), | ||||
|                     Some((conn, Some(dur))) => { | ||||
|                         rotor::Response::ok(ServerFsm::Conn(conn)) | ||||
|                             .deadline(scope.now() + dur) | ||||
|                     } | ||||
|                     None => rotor::Response::done() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn wakeup(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> { | ||||
|         match self { | ||||
|             ServerFsm::Listener(lst, shutdown) => { | ||||
|                 if shutdown.load(Ordering::Acquire) { | ||||
|                     let _ = scope.deregister(&lst); | ||||
|                     scope.shutdown_loop(); | ||||
|                     rotor::Response::done() | ||||
|                 } else { | ||||
|                     rotor::Response::ok(ServerFsm::Listener(lst, shutdown)) | ||||
|                 } | ||||
|             }, | ||||
|             ServerFsm::Conn(conn) => match conn.wakeup(scope) { | ||||
|                 Some((conn, None)) => rotor::Response::ok(ServerFsm::Conn(conn)), | ||||
|                 Some((conn, Some(dur))) => { | ||||
|                     rotor::Response::ok(ServerFsm::Conn(conn)) | ||||
|                         .deadline(scope.now() + dur) | ||||
|                 } | ||||
|                 None => rotor::Response::done() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A handle of the running server. | ||||
| pub struct Listening { | ||||
|     addrs: Vec<SocketAddr>, | ||||
|     shutdown: (Arc<AtomicBool>, rotor::Notifier), | ||||
|     addr: SocketAddr, | ||||
|     shutdown: ::futures::sync::oneshot::Sender<()>, | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Listening { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Listening") | ||||
|             .field("addrs", &self.addrs) | ||||
|             .field("closed", &self.shutdown.0.load(Ordering::Relaxed)) | ||||
|             .field("addr", &self.addr) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Display for Listening { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         for (i, addr) in self.addrs().iter().enumerate() { | ||||
|             if i > 1 { | ||||
|                 try!(f.write_str(", ")); | ||||
|             } | ||||
|             try!(fmt::Display::fmt(addr, f)); | ||||
|         } | ||||
|         Ok(()) | ||||
|         fmt::Display::fmt(&self.addr, f) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Listening { | ||||
|     /// The addresses this server is listening on. | ||||
|     pub fn addrs(&self) -> &[SocketAddr] { | ||||
|         &self.addrs | ||||
|     pub fn addr(&self) -> &SocketAddr { | ||||
|         &self.addr | ||||
|     } | ||||
|  | ||||
|     /// Stop the server from listening to its socket address. | ||||
|     pub fn close(self) { | ||||
|         debug!("closing server {}", self); | ||||
|         self.shutdown.0.store(true, Ordering::Release); | ||||
|         self.shutdown.1.wakeup().unwrap(); | ||||
|         self.shutdown.complete(()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A trait to react to server events that happen for each message. | ||||
| /// | ||||
| /// Each event handler returns its desired `Next` action. | ||||
| pub trait Handler<T: Transport> { | ||||
|     /// This event occurs first, triggering when a `Request` has been parsed. | ||||
|     fn on_request(&mut self, request: Request<T>) -> Next; | ||||
|     /// This event occurs each time the `Request` is ready to be read from. | ||||
|     fn on_request_readable(&mut self, request: &mut http::Decoder<T>) -> Next; | ||||
|     /// This event occurs after the first time this handled signals `Next::write()`. | ||||
|     fn on_response(&mut self, response: &mut Response) -> Next; | ||||
|     /// This event occurs each time the `Response` is ready to be written to. | ||||
|     fn on_response_writable(&mut self, response: &mut http::Encoder<T>) -> Next; | ||||
| struct HttpServer { | ||||
|     keep_alive: bool, | ||||
| } | ||||
|  | ||||
|     /// This event occurs whenever an `Error` occurs outside of the other events. | ||||
| impl<T: Io + 'static> ServerProto<T> for HttpServer { | ||||
|     type Request = http::RequestHead; | ||||
|     type RequestBody = http::Chunk; | ||||
|     type Response = ResponseHead; | ||||
|     type ResponseBody = http::Chunk; | ||||
|     type Error = ::Error; | ||||
|     type Transport = http::Conn<T, http::ServerTransaction>; | ||||
|     type BindTransport = io::Result<http::Conn<T, http::ServerTransaction>>; | ||||
|  | ||||
|     fn bind_transport(&self, io: T) -> Self::BindTransport { | ||||
|         let ka = if self.keep_alive { | ||||
|             http::KA::Busy | ||||
|         } else { | ||||
|             http::KA::Disabled | ||||
|         }; | ||||
|         Ok(http::Conn::new(io, ka)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct HttpService<T> { | ||||
|     inner: T, | ||||
|     remote_addr: SocketAddr, | ||||
| } | ||||
|  | ||||
| fn map_response_to_message(res: Response) -> Message<ResponseHead, http::TokioBody> { | ||||
|     let (head, body) = response::split(res); | ||||
|     if let Some(body) = body { | ||||
|         Message::WithBody(head, body.into()) | ||||
|     } else { | ||||
|         Message::WithoutBody(head) | ||||
|     } | ||||
| } | ||||
|  | ||||
| type ResponseHead = http::MessageHead<::StatusCode>; | ||||
|  | ||||
| impl<T> Service for HttpService<T> | ||||
|     where T: Service<Request=Request, Response=Response, Error=::Error>, | ||||
| { | ||||
|     type Request = Message<http::RequestHead, http::TokioBody>; | ||||
|     type Response = Message<ResponseHead, http::TokioBody>; | ||||
|     type Error = ::Error; | ||||
|     type Future = Map<T::Future, fn(Response) -> Message<ResponseHead, http::TokioBody>>; | ||||
|  | ||||
|     fn call(&self, message: Self::Request) -> Self::Future { | ||||
|         let (head, body) = match message { | ||||
|             Message::WithoutBody(head) => (head, http::Body::empty()), | ||||
|             Message::WithBody(head, body) => (head, body.into()), | ||||
|         }; | ||||
|         let req = request::new(self.remote_addr, head, body); | ||||
|         self.inner.call(req).map(map_response_to_message) | ||||
|     } | ||||
| } | ||||
|  | ||||
| //private so the `Acceptor` type can stay internal | ||||
| mod accept { | ||||
|     use std::io; | ||||
|     use std::net::SocketAddr; | ||||
|     use futures::{Stream, Poll}; | ||||
|     use tokio::io::Io; | ||||
|  | ||||
|     /// An Acceptor is an incoming Stream of Io. | ||||
|     /// | ||||
|     /// This could IO errors while waiting for events, or a timeout, etc. | ||||
|     fn on_error(&mut self, err: ::Error) -> Next where Self: Sized { | ||||
|         debug!("default Handler.on_error({:?})", err); | ||||
|         http::Next::remove() | ||||
|     /// This trait is not implemented directly, and only exists to make the | ||||
|     /// intent clearer. A `Stream<Item=(Io, SocketAddr), Error=io::Error>` | ||||
|     /// should be implemented instead. | ||||
|     pub trait Accept: Stream<Error=io::Error> { | ||||
|         #[doc(hidden)] | ||||
|         type Output: Io + 'static; | ||||
|         #[doc(hidden)] | ||||
|         type Stream: Stream<Item=(Self::Output, SocketAddr), Error=io::Error> + 'static; | ||||
|  | ||||
|         #[doc(hidden)] | ||||
|         fn accept(self) -> Accepter<Self::Stream, Self::Output> | ||||
|             where Self: Sized; | ||||
|     } | ||||
|  | ||||
|     /// This event occurs when this Handler has requested to remove the Transport. | ||||
|     fn on_remove(self, _transport: T) where Self: Sized { | ||||
|         debug!("default Handler.on_remove"); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Used to create a `Handler` when a new message is received by the server. | ||||
| pub trait HandlerFactory<T: Transport> { | ||||
|     /// The `Handler` to use for the incoming message. | ||||
|     type Output: Handler<T>; | ||||
|     /// Creates the associated `Handler`. | ||||
|     fn create(&mut self, ctrl: http::Control) -> Self::Output; | ||||
| } | ||||
|  | ||||
| impl<F, H, T> HandlerFactory<T> for F | ||||
| where F: FnMut(http::Control) -> H, H: Handler<T>, T: Transport { | ||||
|     type Output = H; | ||||
|     fn create(&mut self, ctrl: http::Control) -> H { | ||||
|         self(ctrl) | ||||
|     #[allow(missing_debug_implementations)] | ||||
|     pub struct Accepter<T: Stream<Item=(I, SocketAddr), Error=io::Error> + 'static, I: Io + 'static>(T, ::std::marker::PhantomData<I>); | ||||
|  | ||||
|     impl<T, I> Stream for Accepter<T, I> | ||||
|     where T: Stream<Item=(I, SocketAddr), Error=io::Error>, | ||||
|           I: Io + 'static, | ||||
|     { | ||||
|         type Item = T::Item; | ||||
|         type Error = io::Error; | ||||
|  | ||||
|         #[inline] | ||||
|         fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { | ||||
|             self.0.poll() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<T, I> Accept for T | ||||
|     where T: Stream<Item=(I, SocketAddr), Error=io::Error> + 'static, | ||||
|           I: Io + 'static, | ||||
|     { | ||||
|         type Output = I; | ||||
|         type Stream = T; | ||||
|  | ||||
|         fn accept(self) -> Accepter<Self, I> { | ||||
|             Accepter(self, ::std::marker::PhantomData) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,48 +4,25 @@ | ||||
| //! target URI, headers, and message body. | ||||
|  | ||||
| use std::fmt; | ||||
| use std::net::SocketAddr; | ||||
|  | ||||
| use version::HttpVersion; | ||||
| use method::Method; | ||||
| use header::Headers; | ||||
| use http::{RequestHead, MessageHead, RequestLine}; | ||||
| use http::{RequestHead, MessageHead, RequestLine, Body}; | ||||
| use uri::RequestUri; | ||||
|  | ||||
| pub fn new<'a, T>(incoming: RequestHead, transport: &'a T) -> Request<'a, T> { | ||||
|     let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming; | ||||
|     debug!("Request Line: {:?} {:?} {:?}", method, uri, version); | ||||
|     debug!("{:#?}", headers); | ||||
|  | ||||
|     Request { | ||||
|         method: method, | ||||
|         uri: uri, | ||||
|         headers: headers, | ||||
|         version: version, | ||||
|         transport: transport, | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`. | ||||
| pub struct Request<'a, T: 'a> { | ||||
| pub struct Request { | ||||
|     method: Method, | ||||
|     uri: RequestUri, | ||||
|     version: HttpVersion, | ||||
|     headers: Headers, | ||||
|     transport: &'a T, | ||||
|     remote_addr: SocketAddr, | ||||
|     body: Body, | ||||
| } | ||||
|  | ||||
| impl<'a, T> fmt::Debug for Request<'a, T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Request") | ||||
|             .field("method", &self.method) | ||||
|             .field("uri", &self.uri) | ||||
|             .field("version", &self.version) | ||||
|             .field("headers", &self.headers) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, T> Request<'a, T> { | ||||
| impl Request { | ||||
|     /// The `Method`, such as `Get`, `Post`, etc. | ||||
|     #[inline] | ||||
|     pub fn method(&self) -> &Method { &self.method } | ||||
| @@ -54,10 +31,6 @@ impl<'a, T> Request<'a, T> { | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &Headers { &self.headers } | ||||
|  | ||||
|     /// The underlying `Transport` of this request. | ||||
|     #[inline] | ||||
|     pub fn transport(&self) -> &'a T { self.transport } | ||||
|  | ||||
|     /// The target request-uri for this request. | ||||
|     #[inline] | ||||
|     pub fn uri(&self) -> &RequestUri { &self.uri } | ||||
| @@ -66,6 +39,10 @@ impl<'a, T> Request<'a, T> { | ||||
|     #[inline] | ||||
|     pub fn version(&self) -> &HttpVersion { &self.version } | ||||
|  | ||||
|     /// The remote socket address of this request | ||||
|     #[inline] | ||||
|     pub fn remote_addr(&self) -> &SocketAddr { &self.remote_addr } | ||||
|  | ||||
|     /// The target path of this Request. | ||||
|     #[inline] | ||||
|     pub fn path(&self) -> Option<&str> { | ||||
| @@ -86,12 +63,44 @@ impl<'a, T> Request<'a, T> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Take the `Body` of this `Request`. | ||||
|     #[inline] | ||||
|     pub fn body(self) -> Body { | ||||
|         self.body | ||||
|     } | ||||
|  | ||||
|     /// Deconstruct this Request into its pieces. | ||||
|     /// | ||||
|     /// Modifying these pieces will have no effect on how hyper behaves. | ||||
|     #[inline] | ||||
|     pub fn deconstruct(self) -> (Method, RequestUri, HttpVersion, Headers) { | ||||
|         (self.method, self.uri, self.version, self.headers) | ||||
|     pub fn deconstruct(self) -> (Method, RequestUri, HttpVersion, Headers, Body) { | ||||
|         (self.method, self.uri, self.version, self.headers, self.body) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Request { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Request") | ||||
|             .field("method", &self.method) | ||||
|             .field("uri", &self.uri) | ||||
|             .field("version", &self.version) | ||||
|             .field("remote_addr", &self.remote_addr) | ||||
|             .field("headers", &self.headers) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn new(addr: SocketAddr, incoming: RequestHead, body: Body) -> Request { | ||||
|     let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming; | ||||
|     debug!("Request Line: {:?} {:?} {:?}", method, uri, version); | ||||
|     debug!("{:#?}", headers); | ||||
|  | ||||
|     Request { | ||||
|         method: method, | ||||
|         uri: uri, | ||||
|         headers: headers, | ||||
|         version: version, | ||||
|         remote_addr: addr, | ||||
|         body: body, | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,22 +1,26 @@ | ||||
| //! Server Responses | ||||
| //! | ||||
| //! These are responses sent by a `hyper::Server` to clients, after | ||||
| //! receiving a request. | ||||
| use std::fmt; | ||||
|  | ||||
| use header; | ||||
| use http; | ||||
| use http::{self, Body}; | ||||
| use status::StatusCode; | ||||
| use version; | ||||
|  | ||||
|  | ||||
| /// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`. | ||||
| /// The Response sent to a client after receiving a Request in a Service. | ||||
| /// | ||||
| /// The default `StatusCode` for a `Response` is `200 OK`. | ||||
| #[derive(Debug)] | ||||
| pub struct Response<'a> { | ||||
|     head: &'a mut http::MessageHead<StatusCode>, | ||||
| #[derive(Default)] | ||||
| pub struct Response { | ||||
|     head: http::MessageHead<StatusCode>, | ||||
|     body: Option<Body>, | ||||
| } | ||||
|  | ||||
| impl<'a> Response<'a> { | ||||
| impl Response { | ||||
|     /// Create a new Response. | ||||
|     #[inline] | ||||
|     pub fn new() -> Response { | ||||
|         Response::default() | ||||
|     } | ||||
|  | ||||
|     /// The headers of this response. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &header::Headers { &self.head.headers } | ||||
| @@ -35,16 +39,65 @@ impl<'a> Response<'a> { | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut header::Headers { &mut self.head.headers } | ||||
|  | ||||
|     /// Set the status of this response. | ||||
|     /// Set the `StatusCode` for this response. | ||||
|     #[inline] | ||||
|     pub fn set_status(&mut self, status: StatusCode) { | ||||
|         self.head.subject = status; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Creates a new Response that can be used to write to a network stream. | ||||
| pub fn new(head: &mut http::MessageHead<StatusCode>) -> Response { | ||||
|     Response { | ||||
|         head: head | ||||
|     /// Set the body. | ||||
|     #[inline] | ||||
|     pub fn set_body<T: Into<Body>>(&mut self, body: T) { | ||||
|         self.body = Some(body.into()); | ||||
|     } | ||||
|  | ||||
|     /// Set the status and move the Response. | ||||
|     /// | ||||
|     /// Useful for the "builder-style" pattern. | ||||
|     #[inline] | ||||
|     pub fn with_status(mut self, status: StatusCode) -> Self { | ||||
|         self.set_status(status); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Set a header and move the Response. | ||||
|     /// | ||||
|     /// Useful for the "builder-style" pattern. | ||||
|     #[inline] | ||||
|     pub fn with_header<H: header::Header>(mut self, header: H) -> Self { | ||||
|         self.head.headers.set(header); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Set the headers and move the Response. | ||||
|     /// | ||||
|     /// Useful for the "builder-style" pattern. | ||||
|     #[inline] | ||||
|     pub fn with_headers(mut self, headers: header::Headers) -> Self { | ||||
|         self.head.headers = headers; | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Set the body and move the Response. | ||||
|     /// | ||||
|     /// Useful for the "builder-style" pattern. | ||||
|     #[inline] | ||||
|     pub fn with_body<T: Into<Body>>(mut self, body: T) -> Self { | ||||
|         self.set_body(body); | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Response { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Response") | ||||
|             .field("status", &self.head.subject) | ||||
|             .field("version", &self.head.version) | ||||
|             .field("headers", &self.head.headers) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn split(res: Response) -> (http::MessageHead<StatusCode>, Option<Body>) { | ||||
|     (res.head, res.body) | ||||
| } | ||||
|   | ||||
| @@ -66,7 +66,7 @@ impl FromStr for RequestUri { | ||||
|  | ||||
|     fn from_str(s: &str) -> Result<RequestUri, Error> { | ||||
|         let bytes = s.as_bytes(); | ||||
|         if bytes == [] { | ||||
|         if bytes.len() == 0 { | ||||
|             Err(Error::Uri(UrlError::RelativeUrlWithoutBase)) | ||||
|         } else if bytes == b"*" { | ||||
|             Ok(RequestUri::Star) | ||||
|   | ||||
| @@ -19,6 +19,8 @@ pub enum HttpVersion { | ||||
|     H2, | ||||
|     /// `HTTP/2.0` over cleartext | ||||
|     H2c, | ||||
|     #[doc(hidden)] | ||||
|     __DontMatchMe, | ||||
| } | ||||
|  | ||||
| impl fmt::Display for HttpVersion { | ||||
| @@ -29,6 +31,7 @@ impl fmt::Display for HttpVersion { | ||||
|             Http11 => "HTTP/1.1", | ||||
|             H2 => "h2", | ||||
|             H2c => "h2c", | ||||
|             HttpVersion::__DontMatchMe => unreachable!(), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user