feat(server): add server::Serve that can use a shared Handle
				
					
				
			- Adds `Http::serve_addr_handle` which will bind to an address with a provided `Handle`, and return a `Serve`. - Adds `server::Serve` which is a `Stream` of incoming `Connection`s being bound by a `NewService`. - Renames `Http::no_proto` to `Http::serve_connection`.
This commit is contained in:
		| @@ -4,6 +4,7 @@ extern crate futures; | |||||||
| extern crate tokio_core; | extern crate tokio_core; | ||||||
| extern crate pretty_env_logger; | extern crate pretty_env_logger; | ||||||
|  |  | ||||||
|  | use futures::{Future, Stream}; | ||||||
| use futures::future::FutureResult; | use futures::future::FutureResult; | ||||||
|  |  | ||||||
| use hyper::{Get, StatusCode}; | use hyper::{Get, StatusCode}; | ||||||
| @@ -14,10 +15,9 @@ use hyper::server::{Http, Service, Request, Response}; | |||||||
| static INDEX1: &'static [u8] = b"The 1st service!"; | static INDEX1: &'static [u8] = b"The 1st service!"; | ||||||
| static INDEX2: &'static [u8] = b"The 2nd service!"; | static INDEX2: &'static [u8] = b"The 2nd service!"; | ||||||
|  |  | ||||||
| struct Service1; | struct Srv(&'static [u8]); | ||||||
| struct Service2; |  | ||||||
|  |  | ||||||
| impl Service for Service1 { | impl Service for Srv { | ||||||
|     type Request = Request; |     type Request = Request; | ||||||
|     type Response = Response; |     type Response = Response; | ||||||
|     type Error = hyper::Error; |     type Error = hyper::Error; | ||||||
| @@ -27,30 +27,8 @@ impl Service for Service1 { | |||||||
|         futures::future::ok(match (req.method(), req.path()) { |         futures::future::ok(match (req.method(), req.path()) { | ||||||
|             (&Get, "/") => { |             (&Get, "/") => { | ||||||
|                 Response::new() |                 Response::new() | ||||||
|                     .with_header(ContentLength(INDEX1.len() as u64)) |                     .with_header(ContentLength(self.0.len() as u64)) | ||||||
|                     .with_body(INDEX1) |                     .with_body(self.0) | ||||||
|             }, |  | ||||||
|             _ => { |  | ||||||
|                 Response::new() |  | ||||||
|                     .with_status(StatusCode::NotFound) |  | ||||||
|             } |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Service for Service2 { |  | ||||||
|     type Request = Request; |  | ||||||
|     type Response = Response; |  | ||||||
|     type Error = hyper::Error; |  | ||||||
|     type Future = FutureResult<Response, hyper::Error>; |  | ||||||
|  |  | ||||||
|     fn call(&self, req: Request) -> Self::Future { |  | ||||||
|         futures::future::ok(match (req.method(), req.path()) { |  | ||||||
|             (&Get, "/") => { |  | ||||||
|                 Response::new() |  | ||||||
|                     .with_header(ContentLength(INDEX2.len() as u64)) |  | ||||||
|                     .with_body(INDEX2) |  | ||||||
|             }, |             }, | ||||||
|             _ => { |             _ => { | ||||||
|                 Response::new() |                 Response::new() | ||||||
| @@ -70,13 +48,23 @@ fn main() { | |||||||
|     let mut core = Core::new().unwrap(); |     let mut core = Core::new().unwrap(); | ||||||
|     let handle = core.handle(); |     let handle = core.handle(); | ||||||
|  |  | ||||||
|     let srv1 = Http::new().bind_handle(&addr1,|| Ok(Service1), &handle).unwrap(); |     let srv1 = Http::new().serve_addr_handle(&addr1, &handle, || Ok(Srv(INDEX1))).unwrap(); | ||||||
|     let srv2 = Http::new().bind_handle(&addr2,|| Ok(Service2), &handle).unwrap(); |     let srv2 = Http::new().serve_addr_handle(&addr2, &handle, || Ok(Srv(INDEX2))).unwrap(); | ||||||
|  |  | ||||||
|     println!("Listening on http://{}", srv1.local_addr().unwrap()); |     println!("Listening on http://{}", srv1.incoming_ref().local_addr()); | ||||||
|     println!("Listening on http://{}", srv2.local_addr().unwrap()); |     println!("Listening on http://{}", srv2.incoming_ref().local_addr()); | ||||||
|  |  | ||||||
|  |     let handle1 = handle.clone(); | ||||||
|  |     handle.spawn(srv1.for_each(move |conn| { | ||||||
|  |         handle1.spawn(conn.map(|_| ()).map_err(|err| println!("srv1 error: {:?}", err))); | ||||||
|  |         Ok(()) | ||||||
|  |     }).map_err(|_| ())); | ||||||
|  |  | ||||||
|  |     let handle2 = handle.clone(); | ||||||
|  |     handle.spawn(srv2.for_each(move |conn| { | ||||||
|  |         handle2.spawn(conn.map(|_| ()).map_err(|err| println!("srv2 error: {:?}", err))); | ||||||
|  |         Ok(()) | ||||||
|  |     }).map_err(|_| ())); | ||||||
|  |  | ||||||
|     handle.spawn(srv1.shutdown_signal(futures::future::empty::<(), ()>())); |  | ||||||
|     handle.spawn(srv2.shutdown_signal(futures::future::empty::<(), ()>())); |  | ||||||
|     core.run(futures::future::empty::<(), ()>()).unwrap(); |     core.run(futures::future::empty::<(), ()>()).unwrap(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ use std::rc::{Rc, Weak}; | |||||||
| use std::time::Duration; | use std::time::Duration; | ||||||
|  |  | ||||||
| use futures::task::{self, Task}; | use futures::task::{self, Task}; | ||||||
| use futures::future::{self, Select, Map}; | use futures::future::{self, Map}; | ||||||
| use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; | use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; | ||||||
|  |  | ||||||
| #[cfg(feature = "compat")] | #[cfg(feature = "compat")] | ||||||
| @@ -25,7 +25,7 @@ use http; | |||||||
|  |  | ||||||
| use tokio_io::{AsyncRead, AsyncWrite}; | use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
| use tokio::reactor::{Core, Handle, Timeout}; | use tokio::reactor::{Core, Handle, Timeout}; | ||||||
| use tokio::net::TcpListener; | use tokio::net::{TcpListener, TcpStream}; | ||||||
| use tokio_proto::BindServer; | use tokio_proto::BindServer; | ||||||
| use tokio_proto::streaming::Message; | use tokio_proto::streaming::Message; | ||||||
| use tokio_proto::streaming::pipeline::{Transport, Frame, ServerProto}; | use tokio_proto::streaming::pipeline::{Transport, Frame, ServerProto}; | ||||||
| @@ -36,30 +36,11 @@ use proto::response; | |||||||
| use proto::request; | use proto::request; | ||||||
| #[cfg(feature = "compat")] | #[cfg(feature = "compat")] | ||||||
| use proto::Body; | use proto::Body; | ||||||
|  | use self::hyper_service::HyperService; | ||||||
|  |  | ||||||
| pub use proto::response::Response; | pub use proto::response::Response; | ||||||
| pub use proto::request::Request; | pub use proto::request::Request; | ||||||
|  |  | ||||||
| // The `Server` can be created use its own `Core`, or an shared `Handle`. |  | ||||||
| enum Reactor { |  | ||||||
|     // Own its `Core` |  | ||||||
|     Core(Core), |  | ||||||
|     // Share `Handle` with others |  | ||||||
|     Handle(Handle), |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Reactor { |  | ||||||
|     /// Returns a handle to the underlying event loop that this server will be |  | ||||||
|     /// running on. |  | ||||||
|     #[inline] |  | ||||||
|     pub fn handle(&self) -> Handle { |  | ||||||
|         match *self { |  | ||||||
|             Reactor::Core(ref core) => core.handle(), |  | ||||||
|             Reactor::Handle(ref handle) => handle.clone(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// An instance of the HTTP protocol, and implementation of tokio-proto's | /// An instance of the HTTP protocol, and implementation of tokio-proto's | ||||||
| /// `ServerProto` trait. | /// `ServerProto` trait. | ||||||
| /// | /// | ||||||
| @@ -82,23 +63,62 @@ where B: Stream<Error=::Error>, | |||||||
| { | { | ||||||
|     protocol: Http<B::Item>, |     protocol: Http<B::Item>, | ||||||
|     new_service: S, |     new_service: S, | ||||||
|     reactor: Reactor, |     reactor: Core, | ||||||
|     listener: TcpListener, |     listener: TcpListener, | ||||||
|     shutdown_timeout: Duration, |     shutdown_timeout: Duration, | ||||||
|     no_proto: bool, |     no_proto: bool, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// The Future of an Server. | /// A stream mapping incoming IOs to new services. | ||||||
| pub struct ServerFuture<F, S, B> | /// | ||||||
| where B: Stream<Error=::Error>, | /// Yields `Connection`s that are futures that should be put on a reactor. | ||||||
|       B::Item: AsRef<[u8]>, | #[must_use = "streams do nothing unless polled"] | ||||||
| { | #[derive(Debug)] | ||||||
|     server: Server<S, B>, | pub struct Serve<I, S> { | ||||||
|     info: Rc<RefCell<Info>>, |     incoming: I, | ||||||
|     shutdown_signal: F, |     new_service: S, | ||||||
|     shutdown: Option<Select<WaitUntilZero, Timeout>>, |     protocol: Http, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | #[must_use = "futures do nothing unless polled"] | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct SpawnAll<I, S, E> { | ||||||
|  |     executor: E, | ||||||
|  |     serve: Serve<I, S>, | ||||||
|  | } | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | /// A stream of connections from binding to an address. | ||||||
|  | #[must_use = "streams do nothing unless polled"] | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct AddrStream { | ||||||
|  |     addr: SocketAddr, | ||||||
|  |     listener: TcpListener, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// A future binding a connection with a Service. | ||||||
|  | /// | ||||||
|  | /// Polling this future will drive HTTP forward. | ||||||
|  | #[must_use = "futures do nothing unless polled"] | ||||||
|  | pub struct Connection<I, S> | ||||||
|  | where | ||||||
|  |     S: HyperService, | ||||||
|  |     S::ResponseBody: Stream<Error=::Error>, | ||||||
|  |     <S::ResponseBody as Stream>::Item: AsRef<[u8]>, | ||||||
|  | { | ||||||
|  |     conn: proto::dispatch::Dispatcher< | ||||||
|  |         proto::dispatch::Server<S>, | ||||||
|  |         S::ResponseBody, | ||||||
|  |         I, | ||||||
|  |         <S::ResponseBody as Stream>::Item, | ||||||
|  |         proto::ServerTransaction, | ||||||
|  |         proto::KA, | ||||||
|  |     >, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ===== impl Http ===== | ||||||
|  |  | ||||||
| impl<B: AsRef<[u8]> + 'static> Http<B> { | impl<B: AsRef<[u8]> + 'static> Http<B> { | ||||||
|     /// Creates a new instance of the HTTP protocol, ready to spawn a server or |     /// Creates a new instance of the HTTP protocol, ready to spawn a server or | ||||||
|     /// start accepting connections. |     /// start accepting connections. | ||||||
| @@ -148,30 +168,7 @@ impl<B: AsRef<[u8]> + 'static> Http<B> { | |||||||
|  |  | ||||||
|         Ok(Server { |         Ok(Server { | ||||||
|             new_service: new_service, |             new_service: new_service, | ||||||
|             reactor: Reactor::Core(core), |             reactor: core, | ||||||
|             listener: listener, |  | ||||||
|             protocol: self.clone(), |  | ||||||
|             shutdown_timeout: Duration::new(1, 0), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// This method allows the ability to share a `Core` with multiple servers. |  | ||||||
|     /// |  | ||||||
|     /// Bind the provided `addr` and return a server with a shared `Core`. |  | ||||||
|     /// |  | ||||||
|     /// This is method will bind the `addr` provided with a new TCP listener ready |  | ||||||
|     /// to accept connections. Each connection will be processed with the |  | ||||||
|     /// `new_service` object provided as well, creating a new service per |  | ||||||
|     /// connection. |  | ||||||
|     pub fn bind_handle<S, Bd>(&self, addr: &SocketAddr, new_service: S, handle: &Handle) -> ::Result<Server<S, Bd>> |  | ||||||
|         where S: NewService<Request = Request, Response = Response<Bd>, Error = ::Error> + 'static, |  | ||||||
|               Bd: Stream<Item=B, Error=::Error>, |  | ||||||
|     { |  | ||||||
|         let listener = TcpListener::bind(addr, &handle)?; |  | ||||||
|  |  | ||||||
|         Ok(Server { |  | ||||||
|             new_service: new_service, |  | ||||||
|             reactor: Reactor::Handle(handle.clone()), |  | ||||||
|             listener: listener, |             listener: listener, | ||||||
|             protocol: self.clone(), |             protocol: self.clone(), | ||||||
|             shutdown_timeout: Duration::new(1, 0), |             shutdown_timeout: Duration::new(1, 0), | ||||||
| @@ -179,6 +176,7 @@ impl<B: AsRef<[u8]> + 'static> Http<B> { | |||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /// Bind a `NewService` using types from the `http` crate. |     /// Bind a `NewService` using types from the `http` crate. | ||||||
|     /// |     /// | ||||||
|     /// See `Http::bind`. |     /// See `Http::bind`. | ||||||
| @@ -220,29 +218,6 @@ impl<B: AsRef<[u8]> + 'static> Http<B> { | |||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Bind a connection together with a Service. |  | ||||||
|     /// |  | ||||||
|     /// This returns a Future that must be polled in order for HTTP to be |  | ||||||
|     /// driven on the connection. |  | ||||||
|     /// |  | ||||||
|     /// This additionally skips the tokio-proto infrastructure internally. |  | ||||||
|     pub fn no_proto<S, I, Bd>(&self, io: I, service: S) -> Connection<I, S> |  | ||||||
|         where S: Service<Request = Request, Response = Response<Bd>, Error = ::Error> + 'static, |  | ||||||
|               Bd: Stream<Item=B, Error=::Error> + 'static, |  | ||||||
|               I: AsyncRead + AsyncWrite + 'static, |  | ||||||
|  |  | ||||||
|     { |  | ||||||
|         let ka = if self.keep_alive { |  | ||||||
|             proto::KA::Busy |  | ||||||
|         } else { |  | ||||||
|             proto::KA::Disabled |  | ||||||
|         }; |  | ||||||
|         let mut conn = proto::Conn::new(io, ka); |  | ||||||
|         conn.set_flush_pipeline(self.pipeline); |  | ||||||
|         Connection { |  | ||||||
|             conn: proto::dispatch::Dispatcher::new(proto::dispatch::Server::new(service), conn), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Bind a `Service` using types from the `http` crate. |     /// Bind a `Service` using types from the `http` crate. | ||||||
|     /// |     /// | ||||||
| @@ -262,124 +237,71 @@ impl<B: AsRef<[u8]> + 'static> Http<B> { | |||||||
|             remote_addr: remote_addr, |             remote_addr: remote_addr, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } |  | ||||||
|  |  | ||||||
| use self::hyper_service::HyperService; |     /// This method allows the ability to share a `Core` with multiple servers. | ||||||
| mod hyper_service { |  | ||||||
|     use super::{Request, Response, Service, Stream}; |  | ||||||
|     /// A "trait alias" for any type that implements `Service` with hyper's |  | ||||||
|     /// Request, Response, and Error types, and a streaming body. |  | ||||||
|     /// |     /// | ||||||
|     /// There is an auto implementation inside hyper, so no one can actually |     /// Bind the provided `addr` and return a server with a shared `Core`. | ||||||
|     /// implement this trait. It simply exists to reduce the amount of generics |     /// | ||||||
|     /// needed. |     /// This is method will bind the `addr` provided with a new TCP listener ready | ||||||
|     pub trait HyperService: Service + Sealed { |     /// to accept connections. Each connection will be processed with the | ||||||
|         #[doc(hidden)] |     /// `new_service` object provided as well, creating a new service per | ||||||
|         type ResponseBody; |     /// connection. | ||||||
|         #[doc(hidden)] |     pub fn serve_addr_handle<S, Bd>(&self, addr: &SocketAddr, handle: &Handle, new_service: S) -> ::Result<Serve<AddrStream, S>> | ||||||
|         type Sealed: Sealed2; |         where S: NewService<Request = Request, Response = Response<Bd>, Error = ::Error>, | ||||||
|     } |               Bd: Stream<Item=B, Error=::Error>, | ||||||
|  |  | ||||||
|     pub trait Sealed {} |  | ||||||
|     pub trait Sealed2 {} |  | ||||||
|  |  | ||||||
|     #[allow(missing_debug_implementations)] |  | ||||||
|     pub struct Opaque { |  | ||||||
|         _inner: (), |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl Sealed2 for Opaque {} |  | ||||||
|  |  | ||||||
|     impl<S, B> Sealed for S |  | ||||||
|     where |  | ||||||
|         S: Service< |  | ||||||
|             Request=Request, |  | ||||||
|             Response=Response<B>, |  | ||||||
|             Error=::Error, |  | ||||||
|         >, |  | ||||||
|         B: Stream<Error=::Error>, |  | ||||||
|         B::Item: AsRef<[u8]>, |  | ||||||
|     {} |  | ||||||
|  |  | ||||||
|     impl<S, B> HyperService for S |  | ||||||
|     where |  | ||||||
|         S: Service< |  | ||||||
|             Request=Request, |  | ||||||
|             Response=Response<B>, |  | ||||||
|             Error=::Error, |  | ||||||
|         >, |  | ||||||
|         S: Sealed, |  | ||||||
|         B: Stream<Error=::Error>, |  | ||||||
|         B::Item: AsRef<[u8]>, |  | ||||||
|     { |     { | ||||||
|         type ResponseBody = B; |         let listener = TcpListener::bind(addr, &handle)?; | ||||||
|         type Sealed = Opaque; |         let incoming = AddrStream { | ||||||
|  |             addr: listener.local_addr()?, | ||||||
|  |             listener: listener, | ||||||
|  |         }; | ||||||
|  |         Ok(self.serve(incoming, new_service)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } |     //TODO: make public | ||||||
| /// A future binding a connection with a Service. |     fn serve<I, S, Bd>(&self, incoming: I, new_service: S) -> Serve<I, S> | ||||||
| /// |         where I: Stream<Error=::std::io::Error>, | ||||||
| /// Polling this future will drive HTTP forward. |               I::Item: AsyncRead + AsyncWrite, | ||||||
| #[must_use = "futures do nothing unless polled"] |               S: NewService<Request = Request, Response = Response<Bd>, Error = ::Error>, | ||||||
| pub struct Connection<I, S> |               Bd: Stream<Item=B, Error=::Error>, | ||||||
| where |     { | ||||||
|     S: HyperService, |         Serve { | ||||||
|     S::ResponseBody: Stream<Error=::Error>, |             incoming: incoming, | ||||||
|     <S::ResponseBody as Stream>::Item: AsRef<[u8]>, |             new_service: new_service, | ||||||
| { |             protocol: Http { | ||||||
|     conn: proto::dispatch::Dispatcher<proto::dispatch::Server<S>, S::ResponseBody, I, <S::ResponseBody as Stream>::Item, proto::ServerTransaction, proto::KA>, |                 keep_alive: self.keep_alive, | ||||||
| } |                 pipeline: self.pipeline, | ||||||
|  |                 _marker: PhantomData, | ||||||
| impl<I, B, S> Future for Connection<I, S> |             }, | ||||||
| where S: Service<Request = Request, Response = Response<B>, Error = ::Error> + 'static, |         } | ||||||
|       I: AsyncRead + AsyncWrite + 'static, |  | ||||||
|       B: Stream<Error=::Error> + 'static, |  | ||||||
|       B::Item: AsRef<[u8]>, |  | ||||||
| { |  | ||||||
|     type Item = self::unnameable::Opaque; |  | ||||||
|     type Error = ::Error; |  | ||||||
|  |  | ||||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { |  | ||||||
|         try_ready!(self.conn.poll()); |  | ||||||
|         Ok(self::unnameable::opaque().into()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<I, S> fmt::Debug for Connection<I, S> |  | ||||||
| where |  | ||||||
|     S: HyperService, |  | ||||||
|     S::ResponseBody: Stream<Error=::Error>, |  | ||||||
|     <S::ResponseBody as Stream>::Item: AsRef<[u8]>, |  | ||||||
| { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |  | ||||||
|         f.debug_struct("Connection") |  | ||||||
|             .finish() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| mod unnameable { |  | ||||||
|     // This type is specifically not exported outside the crate, |  | ||||||
|     // so no one can actually name the type. With no methods, we make no |  | ||||||
|     // promises about this type. |  | ||||||
|     // |  | ||||||
|     // All of that to say we can eventually replace the type returned |  | ||||||
|     // to something else, and it would not be a breaking change. |  | ||||||
|     // |  | ||||||
|     // We may want to eventually yield the `T: AsyncRead + AsyncWrite`, which |  | ||||||
|     // doesn't have a `Debug` bound. So, this type can't implement `Debug` |  | ||||||
|     // either, so the type change doesn't break people. |  | ||||||
|     #[allow(missing_debug_implementations)] |  | ||||||
|     pub struct Opaque { |  | ||||||
|         _inner: (), |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn opaque() -> Opaque { |     /// Bind a connection together with a Service. | ||||||
|         Opaque { |     /// | ||||||
|             _inner: (), |     /// This returns a Future that must be polled in order for HTTP to be | ||||||
|  |     /// driven on the connection. | ||||||
|  |     pub fn serve_connection<S, I, Bd>(&self, io: I, service: S) -> Connection<I, S> | ||||||
|  |         where S: Service<Request = Request, Response = Response<Bd>, Error = ::Error>, | ||||||
|  |               Bd: Stream<Error=::Error>, | ||||||
|  |               Bd::Item: AsRef<[u8]>, | ||||||
|  |               I: AsyncRead + AsyncWrite, | ||||||
|  |  | ||||||
|  |     { | ||||||
|  |         let ka = if self.keep_alive { | ||||||
|  |             proto::KA::Busy | ||||||
|  |         } else { | ||||||
|  |             proto::KA::Disabled | ||||||
|  |         }; | ||||||
|  |         let mut conn = proto::Conn::new(io, ka); | ||||||
|  |         conn.set_flush_pipeline(self.pipeline); | ||||||
|  |         Connection { | ||||||
|  |             conn: proto::dispatch::Dispatcher::new(proto::dispatch::Server::new(service), conn), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| impl<B> Clone for Http<B> { | impl<B> Clone for Http<B> { | ||||||
|     fn clone(&self) -> Http<B> { |     fn clone(&self) -> Http<B> { | ||||||
|         Http { |         Http { | ||||||
| @@ -584,6 +506,8 @@ impl<T, B> Service for HttpService<T> | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ===== impl Server ===== | ||||||
|  |  | ||||||
| impl<S, B> Server<S, B> | impl<S, B> Server<S, B> | ||||||
|     where S: NewService<Request = Request, Response = Response<B>, Error = ::Error> + 'static, |     where S: NewService<Request = Request, Response = Response<B>, Error = ::Error> + 'static, | ||||||
|           B: Stream<Error=::Error> + 'static, |           B: Stream<Error=::Error> + 'static, | ||||||
| @@ -619,21 +543,6 @@ impl<S, B> Server<S, B> | |||||||
|         self |         self | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Configure the `shutdown_signal`. |  | ||||||
|     pub fn shutdown_signal<F>(self, signal: F) -> ServerFuture<F, S, B> |  | ||||||
|         where F: Future<Item = (), Error = ()> |  | ||||||
|     { |  | ||||||
|         ServerFuture { |  | ||||||
|             server: self, |  | ||||||
|             info: Rc::new(RefCell::new(Info { |  | ||||||
|                 active: 0, |  | ||||||
|                 blocker: None, |  | ||||||
|             })), |  | ||||||
|             shutdown_signal: signal, |  | ||||||
|             shutdown: None, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Execute this server infinitely. |     /// Execute this server infinitely. | ||||||
|     /// |     /// | ||||||
|     /// This method does not currently return, but it will return an error if |     /// This method does not currently return, but it will return an error if | ||||||
| @@ -658,14 +567,9 @@ impl<S, B> Server<S, B> | |||||||
|     pub fn run_until<F>(self, shutdown_signal: F) -> ::Result<()> |     pub fn run_until<F>(self, shutdown_signal: F) -> ::Result<()> | ||||||
|         where F: Future<Item = (), Error = ()>, |         where F: Future<Item = (), Error = ()>, | ||||||
|     { |     { | ||||||
|         let Server { protocol, new_service, reactor, listener, shutdown_timeout, no_proto } = self; |         let Server { protocol, new_service, mut reactor, listener, shutdown_timeout, no_proto } = self; | ||||||
|  |  | ||||||
|         let mut core = match reactor { |         let handle = reactor.handle(); | ||||||
|             Reactor::Core(core) => core, |  | ||||||
|             _ => panic!("Server does not own its core, use `Handle::spawn()` to run the service!"), |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         let handle = core.handle(); |  | ||||||
|  |  | ||||||
|         // Mini future to track the number of active services |         // Mini future to track the number of active services | ||||||
|         let info = Rc::new(RefCell::new(Info { |         let info = Rc::new(RefCell::new(Info { | ||||||
| @@ -681,7 +585,7 @@ impl<S, B> Server<S, B> | |||||||
|             }; |             }; | ||||||
|             info.borrow_mut().active += 1; |             info.borrow_mut().active += 1; | ||||||
|             if no_proto { |             if no_proto { | ||||||
|                 let fut = protocol.no_proto(socket, s) |                 let fut = protocol.serve_connection(socket, s) | ||||||
|                     .map(|_| ()) |                     .map(|_| ()) | ||||||
|                     .map_err(|err| error!("no_proto error: {}", err)); |                     .map_err(|err| error!("no_proto error: {}", err)); | ||||||
|                 handle.spawn(fut); |                 handle.spawn(fut); | ||||||
| @@ -702,7 +606,7 @@ impl<S, B> Server<S, B> | |||||||
|         // |         // | ||||||
|         // When we get a shutdown signal (`Ok`) then we drop the TCP listener to |         // When we get a shutdown signal (`Ok`) then we drop the TCP listener to | ||||||
|         // stop accepting incoming connections. |         // stop accepting incoming connections. | ||||||
|         match core.run(shutdown_signal.select(srv)) { |         match reactor.run(shutdown_signal.select(srv)) { | ||||||
|             Ok(((), _incoming)) => {} |             Ok(((), _incoming)) => {} | ||||||
|             Err((e, _other)) => return Err(e.into()), |             Err((e, _other)) => return Err(e.into()), | ||||||
|         } |         } | ||||||
| @@ -716,97 +620,13 @@ impl<S, B> Server<S, B> | |||||||
|         // here have been destroyed. |         // here have been destroyed. | ||||||
|         let timeout = try!(Timeout::new(shutdown_timeout, &handle)); |         let timeout = try!(Timeout::new(shutdown_timeout, &handle)); | ||||||
|         let wait = WaitUntilZero { info: info.clone() }; |         let wait = WaitUntilZero { info: info.clone() }; | ||||||
|         match core.run(wait.select(timeout)) { |         match reactor.run(wait.select(timeout)) { | ||||||
|             Ok(_) => Ok(()), |             Ok(_) => Ok(()), | ||||||
|             Err((e, _)) => Err(e.into()) |             Err((e, _)) => Err(e.into()) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<S, B> Future for Server<S, B> |  | ||||||
|     where S: NewService<Request = Request, Response = Response<B>, Error = ::Error> + 'static, |  | ||||||
|           B: Stream<Error=::Error> + 'static, |  | ||||||
|           B::Item: AsRef<[u8]>, |  | ||||||
| { |  | ||||||
|     type Item = (); |  | ||||||
|     type Error = (); |  | ||||||
|  |  | ||||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { |  | ||||||
|         if let Reactor::Core(_) = self.reactor { |  | ||||||
|             panic!("Server owns its core, use `Server::run()` to run the service!") |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         loop { |  | ||||||
|             match self.listener.accept() { |  | ||||||
|                 Ok((socket, addr)) => { |  | ||||||
|                     // TODO: use the NotifyService |  | ||||||
|                     match self.new_service.new_service() { |  | ||||||
|                         Ok(srv) => self.protocol.bind_connection(&self.handle(), |  | ||||||
|                                                                  socket, |  | ||||||
|                                                                  addr, |  | ||||||
|                                                                  srv), |  | ||||||
|                         Err(e) => debug!("internal error: {:?}", e), |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(Async::NotReady), |  | ||||||
|                 Err(e) => debug!("internal error: {:?}", e), |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl<F, S, B> Future for ServerFuture<F, S, B> |  | ||||||
|     where F: Future<Item = (), Error = ()>, |  | ||||||
|           S: NewService<Request = Request, Response = Response<B>, Error = ::Error> + 'static, |  | ||||||
|           B: Stream<Error=::Error> + 'static, |  | ||||||
|           B::Item: AsRef<[u8]>, |  | ||||||
| { |  | ||||||
|     type Item = (); |  | ||||||
|     type Error = (); |  | ||||||
|  |  | ||||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { |  | ||||||
|         loop { |  | ||||||
|             if let Some(ref mut shutdown) = self.shutdown { |  | ||||||
|                 match shutdown.poll() { |  | ||||||
|                     Ok(Async::Ready(_)) => return Ok(Async::Ready(())), |  | ||||||
|                     Ok(Async::NotReady) => return Ok(Async::NotReady), |  | ||||||
|                     Err((e, _)) => debug!("internal error: {:?}", e), |  | ||||||
|                 } |  | ||||||
|             } else if let Ok(Async::Ready(())) = self.shutdown_signal.poll() { |  | ||||||
|                  match Timeout::new(self.server.shutdown_timeout, &self.server.handle()) { |  | ||||||
|                     Ok(timeout) => { |  | ||||||
|                         let wait = WaitUntilZero { info: self.info.clone() }; |  | ||||||
|                         self.shutdown = Some(wait.select(timeout)) |  | ||||||
|                     }, |  | ||||||
|                     Err(e) => debug!("internal error: {:?}", e), |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 match self.server.listener.accept() { |  | ||||||
|                     Ok((socket, addr)) => { |  | ||||||
|                         match self.server.new_service.new_service() { |  | ||||||
|                             Ok(inner_srv) => { |  | ||||||
|                                 let srv = NotifyService { |  | ||||||
|                                     inner: inner_srv, |  | ||||||
|                                     info: Rc::downgrade(&self.info), |  | ||||||
|                                 }; |  | ||||||
|                                 self.info.borrow_mut().active += 1; |  | ||||||
|                                 self.server.protocol.bind_connection(&self.server.handle(), |  | ||||||
|                                                                      socket, |  | ||||||
|                                                                      addr, |  | ||||||
|                                                                      srv) |  | ||||||
|                             }, |  | ||||||
|                             Err(e) => debug!("internal error: {:?}", e), |  | ||||||
|                         } |  | ||||||
|                     }, |  | ||||||
|                     Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(Async::NotReady), |  | ||||||
|                     Err(e) => debug!("internal error: {:?}", e), |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| impl<S: fmt::Debug, B: Stream<Error=::Error>> fmt::Debug for Server<S, B> | impl<S: fmt::Debug, B: Stream<Error=::Error>> fmt::Debug for Server<S, B> | ||||||
| where B::Item: AsRef<[u8]> | where B::Item: AsRef<[u8]> | ||||||
| { | { | ||||||
| @@ -820,17 +640,164 @@ where B::Item: AsRef<[u8]> | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl <F, S: fmt::Debug, B: Stream<Error=::Error>> fmt::Debug for ServerFuture<F, S, B> | // ===== impl Serve ===== | ||||||
| where B::Item: AsRef<[u8]>, |  | ||||||
| F: Future<Item = (), Error = ()> | impl<I, S> Serve<I, S> { | ||||||
|  |     /* | ||||||
|  |     /// Spawn all incoming connections onto the provide executor. | ||||||
|  |     pub fn spawn_all<E>(self, executor: E) -> SpawnAll<I, S, E> { | ||||||
|  |         SpawnAll { | ||||||
|  |             executor: executor, | ||||||
|  |             serve: self, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     */ | ||||||
|  |  | ||||||
|  |     /// Get a reference to the incoming stream. | ||||||
|  |     #[inline] | ||||||
|  |     pub fn incoming_ref(&self) -> &I { | ||||||
|  |         &self.incoming | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<I, S, B> Stream for Serve<I, S> | ||||||
|  | where | ||||||
|  |     I: Stream<Error=io::Error>, | ||||||
|  |     I::Item: AsyncRead + AsyncWrite, | ||||||
|  |     S: NewService<Request=Request, Response=Response<B>, Error=::Error>, | ||||||
|  |     B: Stream<Error=::Error>, | ||||||
|  |     B::Item: AsRef<[u8]>, | ||||||
|  | { | ||||||
|  |     type Item = Connection<I::Item, S::Instance>; | ||||||
|  |     type Error = ::Error; | ||||||
|  |  | ||||||
|  |     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { | ||||||
|  |         if let Some(io) = try_ready!(self.incoming.poll()) { | ||||||
|  |             let service = self.new_service.new_service()?; | ||||||
|  |             Ok(Async::Ready(Some(self.protocol.serve_connection(io, service)))) | ||||||
|  |         } else { | ||||||
|  |             Ok(Async::Ready(None)) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ===== impl SpawnAll ===== | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | impl<I, S, E> Future for SpawnAll<I, S, E> | ||||||
|  | where | ||||||
|  |     I: Stream<Error=io::Error>, | ||||||
|  |     I::Item: AsyncRead + AsyncWrite, | ||||||
|  |     S: NewService<Request=Request, Response=Response<B>, Error=::Error>, | ||||||
|  |     B: Stream<Error=::Error>, | ||||||
|  |     B::Item: AsRef<[u8]>, | ||||||
|  |     //E: Executor<Connection<I::Item, S::Instance>>, | ||||||
|  | { | ||||||
|  |     type Item = (); | ||||||
|  |     type Error = ::Error; | ||||||
|  |  | ||||||
|  |     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||||
|  |         loop { | ||||||
|  |             if let Some(conn) = try_ready!(self.serve.poll()) { | ||||||
|  |                 let fut = conn | ||||||
|  |                     .map(|_| ()) | ||||||
|  |                     .map_err(|err| debug!("conn error: {}", err)); | ||||||
|  |                 match self.executor.execute(fut) { | ||||||
|  |                     Ok(()) => (), | ||||||
|  |                     Err(err) => match err.kind() { | ||||||
|  |                         ExecuteErrorKind::NoCapacity => { | ||||||
|  |                             debug!("SpawnAll::poll; executor no capacity"); | ||||||
|  |                             // continue loop | ||||||
|  |                         }, | ||||||
|  |                         ExecuteErrorKind::Shutdown | _ => { | ||||||
|  |                             debug!("SpawnAll::poll; executor shutdown"); | ||||||
|  |                             return Ok(Async::Ready(())) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 return Ok(Async::Ready(())) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | // ===== impl Connection ===== | ||||||
|  |  | ||||||
|  | impl<I, B, S> Future for Connection<I, S> | ||||||
|  | where S: Service<Request = Request, Response = Response<B>, Error = ::Error> + 'static, | ||||||
|  |       I: AsyncRead + AsyncWrite + 'static, | ||||||
|  |       B: Stream<Error=::Error> + 'static, | ||||||
|  |       B::Item: AsRef<[u8]>, | ||||||
|  | { | ||||||
|  |     type Item = self::unnameable::Opaque; | ||||||
|  |     type Error = ::Error; | ||||||
|  |  | ||||||
|  |     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||||
|  |         try_ready!(self.conn.poll()); | ||||||
|  |         Ok(self::unnameable::opaque().into()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<I, S> fmt::Debug for Connection<I, S> | ||||||
|  | where | ||||||
|  |     S: HyperService, | ||||||
|  |     S::ResponseBody: Stream<Error=::Error>, | ||||||
|  |     <S::ResponseBody as Stream>::Item: AsRef<[u8]>, | ||||||
| { | { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_struct("ServerFuture") |         f.debug_struct("Connection") | ||||||
|          .field("server", &self.server) |             .finish() | ||||||
|          .field("info", &"...") |     } | ||||||
|          .field("shutdown_signal", &"...") | } | ||||||
|          .field("shutdown", &"...") |  | ||||||
|          .finish() | mod unnameable { | ||||||
|  |     // This type is specifically not exported outside the crate, | ||||||
|  |     // so no one can actually name the type. With no methods, we make no | ||||||
|  |     // promises about this type. | ||||||
|  |     // | ||||||
|  |     // All of that to say we can eventually replace the type returned | ||||||
|  |     // to something else, and it would not be a breaking change. | ||||||
|  |     // | ||||||
|  |     // We may want to eventually yield the `T: AsyncRead + AsyncWrite`, which | ||||||
|  |     // doesn't have a `Debug` bound. So, this type can't implement `Debug` | ||||||
|  |     // either, so the type change doesn't break people. | ||||||
|  |     #[allow(missing_debug_implementations)] | ||||||
|  |     pub struct Opaque { | ||||||
|  |         _inner: (), | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn opaque() -> Opaque { | ||||||
|  |         Opaque { | ||||||
|  |             _inner: (), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ===== impl AddrStream ===== | ||||||
|  |  | ||||||
|  | impl AddrStream { | ||||||
|  |     /// Get the local address bound to this listener. | ||||||
|  |     pub fn local_addr(&self) -> SocketAddr { | ||||||
|  |         self.addr | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Stream for AddrStream { | ||||||
|  |     type Item = TcpStream; | ||||||
|  |     type Error = ::std::io::Error; | ||||||
|  |  | ||||||
|  |     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { | ||||||
|  |         loop { | ||||||
|  |             match self.listener.accept() { | ||||||
|  |                 Ok((socket, _addr)) => { | ||||||
|  |                     return Ok(Async::Ready(Some(socket))); | ||||||
|  |                 }, | ||||||
|  |                 Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(Async::NotReady), | ||||||
|  |                 Err(e) => debug!("internal error: {:?}", e), | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -889,3 +856,55 @@ impl Future for WaitUntilZero { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | mod hyper_service { | ||||||
|  |     use super::{Request, Response, Service, Stream}; | ||||||
|  |     /// A "trait alias" for any type that implements `Service` with hyper's | ||||||
|  |     /// Request, Response, and Error types, and a streaming body. | ||||||
|  |     /// | ||||||
|  |     /// There is an auto implementation inside hyper, so no one can actually | ||||||
|  |     /// implement this trait. It simply exists to reduce the amount of generics | ||||||
|  |     /// needed. | ||||||
|  |     pub trait HyperService: Service + Sealed { | ||||||
|  |         #[doc(hidden)] | ||||||
|  |         type ResponseBody; | ||||||
|  |         #[doc(hidden)] | ||||||
|  |         type Sealed: Sealed2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub trait Sealed {} | ||||||
|  |     pub trait Sealed2 {} | ||||||
|  |  | ||||||
|  |     #[allow(missing_debug_implementations)] | ||||||
|  |     pub struct Opaque { | ||||||
|  |         _inner: (), | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     impl Sealed2 for Opaque {} | ||||||
|  |  | ||||||
|  |     impl<S, B> Sealed for S | ||||||
|  |     where | ||||||
|  |         S: Service< | ||||||
|  |             Request=Request, | ||||||
|  |             Response=Response<B>, | ||||||
|  |             Error=::Error, | ||||||
|  |         >, | ||||||
|  |         B: Stream<Error=::Error>, | ||||||
|  |         B::Item: AsRef<[u8]>, | ||||||
|  |     {} | ||||||
|  |  | ||||||
|  |     impl<S, B> HyperService for S | ||||||
|  |     where | ||||||
|  |         S: Service< | ||||||
|  |             Request=Request, | ||||||
|  |             Response=Response<B>, | ||||||
|  |             Error=::Error, | ||||||
|  |         >, | ||||||
|  |         S: Sealed, | ||||||
|  |         B: Stream<Error=::Error>, | ||||||
|  |         B::Item: AsRef<[u8]>, | ||||||
|  |     { | ||||||
|  |         type ResponseBody = B; | ||||||
|  |         type Sealed = Opaque; | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -524,7 +524,7 @@ fn no_proto_empty_parse_eof_does_not_return_error() { | |||||||
|         .map_err(|_| unreachable!()) |         .map_err(|_| unreachable!()) | ||||||
|         .and_then(|(item, _incoming)| { |         .and_then(|(item, _incoming)| { | ||||||
|             let (socket, _) = item.unwrap(); |             let (socket, _) = item.unwrap(); | ||||||
|             Http::new().no_proto(socket, HelloWorld) |             Http::<hyper::Chunk>::new().serve_connection(socket, HelloWorld) | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     core.run(fut).unwrap(); |     core.run(fut).unwrap(); | ||||||
| @@ -546,7 +546,7 @@ fn no_proto_nonempty_parse_eof_returns_error() { | |||||||
|         .map_err(|_| unreachable!()) |         .map_err(|_| unreachable!()) | ||||||
|         .and_then(|(item, _incoming)| { |         .and_then(|(item, _incoming)| { | ||||||
|             let (socket, _) = item.unwrap(); |             let (socket, _) = item.unwrap(); | ||||||
|             Http::new().no_proto(socket, HelloWorld) |             Http::<hyper::Chunk>::new().serve_connection(socket, HelloWorld) | ||||||
|                 .map(|_| ()) |                 .map(|_| ()) | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user