feat(server): change NewService to MakeService with connection context
				
					
				
			This adjusts the way `Service`s are created for a `hyper::Server`. The `MakeService` trait allows receiving an argument when creating a `Service`. The implementation for `hyper::Server` expects to pass a reference to the accepted transport (so, `&Incoming::Item`). The user can inspect the transport before making a `Service`. In practice, this allows for things like getting the remote socket address, or the TLS certification, or similar. To prevent a breaking change, there is a blanket implementation of `MakeService` for any `NewService`. Besides implementing `MakeService` directly, there is also added `hyper::service::make_service_fn`. Closes #1650
This commit is contained in:
		| @@ -2,29 +2,23 @@ | |||||||
| extern crate hyper; | extern crate hyper; | ||||||
| extern crate pretty_env_logger; | extern crate pretty_env_logger; | ||||||
|  |  | ||||||
| use hyper::{Body, Response, Server}; | use hyper::{Body, Request, Response, Server}; | ||||||
| use hyper::service::service_fn_ok; | use hyper::service::service_fn_ok; | ||||||
| use hyper::rt::{self, Future}; | use hyper::rt::{self, Future}; | ||||||
|  |  | ||||||
| static PHRASE: &'static [u8] = b"Hello World!"; |  | ||||||
|  |  | ||||||
| fn main() { | fn main() { | ||||||
|     pretty_env_logger::init(); |     pretty_env_logger::init(); | ||||||
|     let addr = ([127, 0, 0, 1], 3000).into(); |     let addr = ([127, 0, 0, 1], 3000).into(); | ||||||
|  |  | ||||||
|     // new_service is run for each connection, creating a 'service' |     let server = Server::bind(&addr) | ||||||
|     // to handle requests for that specific connection. |         .serve(|| { | ||||||
|     let new_service = || { |  | ||||||
|             // This is the `Service` that will handle the connection. |             // This is the `Service` that will handle the connection. | ||||||
|             // `service_fn_ok` is a helper to convert a function that |             // `service_fn_ok` is a helper to convert a function that | ||||||
|             // returns a Response into a `Service`. |             // returns a Response into a `Service`. | ||||||
|         service_fn_ok(|_| { |             service_fn_ok(move |_: Request<Body>| { | ||||||
|             Response::new(Body::from(PHRASE)) |                 Response::new(Body::from("Hello World!")) | ||||||
|  |             }) | ||||||
|         }) |         }) | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let server = Server::bind(&addr) |  | ||||||
|         .serve(new_service) |  | ||||||
|         .map_err(|e| eprintln!("server error: {}", e)); |         .map_err(|e| eprintln!("server error: {}", e)); | ||||||
|  |  | ||||||
|     println!("Listening on http://{}", addr); |     println!("Listening on http://{}", addr); | ||||||
|   | |||||||
| @@ -25,15 +25,16 @@ use common::exec::{Exec, H2Exec, NewSvcExec}; | |||||||
| use common::io::Rewind; | use common::io::Rewind; | ||||||
| use error::{Kind, Parse}; | use error::{Kind, Parse}; | ||||||
| use proto; | use proto; | ||||||
| use service::{NewService, Service}; | use service::Service; | ||||||
| use upgrade::Upgraded; | use upgrade::Upgraded; | ||||||
|  |  | ||||||
|  | pub(super) use self::make_service::MakeServiceRef; | ||||||
| pub(super) use self::spawn_all::NoopWatcher; | pub(super) use self::spawn_all::NoopWatcher; | ||||||
| use self::spawn_all::NewSvcTask; | use self::spawn_all::NewSvcTask; | ||||||
| pub(super) use self::spawn_all::Watcher; | pub(super) use self::spawn_all::Watcher; | ||||||
| pub(super) use self::upgrades::UpgradeableConnection; | pub(super) use self::upgrades::UpgradeableConnection; | ||||||
|  |  | ||||||
| #[cfg(feature = "runtime")] pub use super::tcp::AddrIncoming; | #[cfg(feature = "runtime")] pub use super::tcp::{AddrIncoming, AddrStream}; | ||||||
|  |  | ||||||
| /// A lower-level configuration of the HTTP protocol. | /// A lower-level configuration of the HTTP protocol. | ||||||
| /// | /// | ||||||
| @@ -69,13 +70,13 @@ enum ConnectionMode { | |||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Serve<I, S, E = Exec> { | pub struct Serve<I, S, E = Exec> { | ||||||
|     incoming: I, |     incoming: I, | ||||||
|     new_service: S, |     make_service: S, | ||||||
|     protocol: Http<E>, |     protocol: Http<E>, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// A future building a new `Service` to a `Connection`. | /// A future building a new `Service` to a `Connection`. | ||||||
| /// | /// | ||||||
| /// Wraps the future returned from `NewService` into one that returns | /// Wraps the future returned from `MakeService` into one that returns | ||||||
| /// a `Connection`. | /// a `Connection`. | ||||||
| #[must_use = "futures do nothing unless polled"] | #[must_use = "futures do nothing unless polled"] | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| @@ -349,12 +350,16 @@ impl<E> Http<E> { | |||||||
|     /// |     /// | ||||||
|     /// This method will bind the `addr` provided with a new TCP listener ready |     /// This method will bind the `addr` provided with a new TCP listener ready | ||||||
|     /// to accept connections. Each connection will be processed with the |     /// to accept connections. Each connection will be processed with the | ||||||
|     /// `new_service` object provided, creating a new service per |     /// `make_service` object provided, creating a new service per | ||||||
|     /// connection. |     /// connection. | ||||||
|     #[cfg(feature = "runtime")] |     #[cfg(feature = "runtime")] | ||||||
|     pub fn serve_addr<S, Bd>(&self, addr: &SocketAddr, new_service: S) -> ::Result<Serve<AddrIncoming, S, E>> |     pub fn serve_addr<S, Bd>(&self, addr: &SocketAddr, make_service: S) -> ::Result<Serve<AddrIncoming, S, E>> | ||||||
|     where |     where | ||||||
|         S: NewService<ReqBody=Body, ResBody=Bd>, |         S: MakeServiceRef< | ||||||
|  |             AddrStream, | ||||||
|  |             ReqBody=Body, | ||||||
|  |             ResBody=Bd, | ||||||
|  |         >, | ||||||
|         S::Error: Into<Box<::std::error::Error + Send + Sync>>, |         S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|         Bd: Payload, |         Bd: Payload, | ||||||
|         E: H2Exec<<S::Service as Service>::Future, Bd>, |         E: H2Exec<<S::Service as Service>::Future, Bd>, | ||||||
| @@ -363,19 +368,23 @@ impl<E> Http<E> { | |||||||
|         if self.keep_alive { |         if self.keep_alive { | ||||||
|             incoming.set_keepalive(Some(Duration::from_secs(90))); |             incoming.set_keepalive(Some(Duration::from_secs(90))); | ||||||
|         } |         } | ||||||
|         Ok(self.serve_incoming(incoming, new_service)) |         Ok(self.serve_incoming(incoming, make_service)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Bind the provided `addr` with the `Handle` and return a [`Serve`](Serve) |     /// Bind the provided `addr` with the `Handle` and return a [`Serve`](Serve) | ||||||
|     /// |     /// | ||||||
|     /// This method will bind the `addr` provided with a new TCP listener ready |     /// This method will bind the `addr` provided with a new TCP listener ready | ||||||
|     /// to accept connections. Each connection will be processed with the |     /// to accept connections. Each connection will be processed with the | ||||||
|     /// `new_service` object provided, creating a new service per |     /// `make_service` object provided, creating a new service per | ||||||
|     /// connection. |     /// connection. | ||||||
|     #[cfg(feature = "runtime")] |     #[cfg(feature = "runtime")] | ||||||
|     pub fn serve_addr_handle<S, Bd>(&self, addr: &SocketAddr, handle: &Handle, new_service: S) -> ::Result<Serve<AddrIncoming, S, E>> |     pub fn serve_addr_handle<S, Bd>(&self, addr: &SocketAddr, handle: &Handle, make_service: S) -> ::Result<Serve<AddrIncoming, S, E>> | ||||||
|     where |     where | ||||||
|         S: NewService<ReqBody=Body, ResBody=Bd>, |         S: MakeServiceRef< | ||||||
|  |             AddrStream, | ||||||
|  |             ReqBody=Body, | ||||||
|  |             ResBody=Bd, | ||||||
|  |         >, | ||||||
|         S::Error: Into<Box<::std::error::Error + Send + Sync>>, |         S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|         Bd: Payload, |         Bd: Payload, | ||||||
|         E: H2Exec<<S::Service as Service>::Future, Bd>, |         E: H2Exec<<S::Service as Service>::Future, Bd>, | ||||||
| @@ -384,23 +393,27 @@ impl<E> Http<E> { | |||||||
|         if self.keep_alive { |         if self.keep_alive { | ||||||
|             incoming.set_keepalive(Some(Duration::from_secs(90))); |             incoming.set_keepalive(Some(Duration::from_secs(90))); | ||||||
|         } |         } | ||||||
|         Ok(self.serve_incoming(incoming, new_service)) |         Ok(self.serve_incoming(incoming, make_service)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Bind the provided stream of incoming IO objects with a `NewService`. |     /// Bind the provided stream of incoming IO objects with a `MakeService`. | ||||||
|     pub fn serve_incoming<I, S, Bd>(&self, incoming: I, new_service: S) -> Serve<I, S, E> |     pub fn serve_incoming<I, S, Bd>(&self, incoming: I, make_service: S) -> Serve<I, S, E> | ||||||
|     where |     where | ||||||
|         I: Stream, |         I: Stream, | ||||||
|         I::Error: Into<Box<::std::error::Error + Send + Sync>>, |         I::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|         I::Item: AsyncRead + AsyncWrite, |         I::Item: AsyncRead + AsyncWrite, | ||||||
|         S: NewService<ReqBody=Body, ResBody=Bd>, |         S: MakeServiceRef< | ||||||
|  |             I::Item, | ||||||
|  |             ReqBody=Body, | ||||||
|  |             ResBody=Bd, | ||||||
|  |         >, | ||||||
|         S::Error: Into<Box<::std::error::Error + Send + Sync>>, |         S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|         Bd: Payload, |         Bd: Payload, | ||||||
|         E: H2Exec<<S::Service as Service>::Future, Bd>, |         E: H2Exec<<S::Service as Service>::Future, Bd>, | ||||||
|     { |     { | ||||||
|         Serve { |         Serve { | ||||||
|             incoming: incoming, |             incoming, | ||||||
|             new_service: new_service, |             make_service, | ||||||
|             protocol: self.clone(), |             protocol: self.clone(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -604,8 +617,9 @@ where | |||||||
|     I: Stream, |     I: Stream, | ||||||
|     I::Item: AsyncRead + AsyncWrite, |     I::Item: AsyncRead + AsyncWrite, | ||||||
|     I::Error: Into<Box<::std::error::Error + Send + Sync>>, |     I::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|     S: NewService<ReqBody=Body, ResBody=B>, |     S: MakeServiceRef<I::Item, ReqBody=Body, ResBody=B>, | ||||||
|     S::Error: Into<Box<::std::error::Error + Send + Sync>>, |     //S::Error2: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|  |     //SME: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|     B: Payload, |     B: Payload, | ||||||
|     E: H2Exec<<S::Service as Service>::Future, B>, |     E: H2Exec<<S::Service as Service>::Future, B>, | ||||||
| { | { | ||||||
| @@ -614,7 +628,7 @@ where | |||||||
|  |  | ||||||
|     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { |     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { | ||||||
|         if let Some(io) = try_ready!(self.incoming.poll().map_err(::Error::new_accept)) { |         if let Some(io) = try_ready!(self.incoming.poll().map_err(::Error::new_accept)) { | ||||||
|             let new_fut = self.new_service.new_service(); |             let new_fut = self.make_service.make_service_ref(&io); | ||||||
|             Ok(Async::Ready(Some(Connecting { |             Ok(Async::Ready(Some(Connecting { | ||||||
|                 future: new_fut, |                 future: new_fut, | ||||||
|                 io: Some(io), |                 io: Some(io), | ||||||
| @@ -666,8 +680,11 @@ where | |||||||
|     I: Stream, |     I: Stream, | ||||||
|     I::Error: Into<Box<::std::error::Error + Send + Sync>>, |     I::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|     I::Item: AsyncRead + AsyncWrite + Send + 'static, |     I::Item: AsyncRead + AsyncWrite + Send + 'static, | ||||||
|     S: NewService<ReqBody=Body, ResBody=B>, |     S: MakeServiceRef< | ||||||
|     S::Error: Into<Box<::std::error::Error + Send + Sync>>, |         I::Item, | ||||||
|  |         ReqBody=Body, | ||||||
|  |         ResBody=B, | ||||||
|  |     >, | ||||||
|     B: Payload, |     B: Payload, | ||||||
|     E: H2Exec<<S::Service as Service>::Future, B>, |     E: H2Exec<<S::Service as Service>::Future, B>, | ||||||
| { | { | ||||||
| @@ -873,3 +890,37 @@ mod upgrades { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub(crate) mod make_service { | ||||||
|  |     use std::error::Error as StdError; | ||||||
|  |  | ||||||
|  |     pub trait MakeServiceRef<Ctx> { | ||||||
|  |         type Error: Into<Box<StdError + Send + Sync>>; | ||||||
|  |         type ReqBody: ::body::Payload; | ||||||
|  |         type ResBody: ::body::Payload; | ||||||
|  |         type Service: ::service::Service<ReqBody=Self::ReqBody, ResBody=Self::ResBody, Error=Self::Error>; | ||||||
|  |         type Future: ::futures::Future<Item=Self::Service>; | ||||||
|  |  | ||||||
|  |         fn make_service_ref(&mut self, ctx: &Ctx) -> Self::Future; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     impl<T, Ctx, E, ME, S, F, IB, OB> MakeServiceRef<Ctx> for T | ||||||
|  |     where | ||||||
|  |         T: for<'a> ::service::MakeService<&'a Ctx, Error=E, MakeError=ME, Service=S, Future=F, ReqBody=IB, ResBody=OB>, | ||||||
|  |         E: Into<Box<StdError + Send + Sync>>, | ||||||
|  |         ME: Into<Box<StdError + Send + Sync>>, | ||||||
|  |         S: ::service::Service<ReqBody=IB, ResBody=OB, Error=E>, | ||||||
|  |         F: ::futures::Future<Item=S, Error=ME>, | ||||||
|  |         IB: ::body::Payload, | ||||||
|  |         OB: ::body::Payload, | ||||||
|  |     { | ||||||
|  |         type Error = E; | ||||||
|  |         type Service = S; | ||||||
|  |         type ReqBody = IB; | ||||||
|  |         type ResBody = OB; | ||||||
|  |         type Future = F; | ||||||
|  |  | ||||||
|  |         fn make_service_ref(&mut self, ctx: &Ctx) -> Self::Future { | ||||||
|  |             self.make_service(ctx) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
| //! # Server | //! # Server | ||||||
| //! | //! | ||||||
| //! The [`Server`](Server) is main way to start listening for HTTP requests. | //! The [`Server`](Server) is main way to start listening for HTTP requests. | ||||||
| //! It wraps a listener with a [`NewService`](::service), and then should | //! It wraps a listener with a [`MakeService`](::service), and then should | ||||||
| //! be executed to start serving requests. | //! be executed to start serving requests. | ||||||
| //! | //! | ||||||
| //! [`Server`](Server) accepts connections in both HTTP1 and HTTP2 by default. | //! [`Server`](Server) accepts connections in both HTTP1 and HTTP2 by default. | ||||||
| @@ -30,8 +30,8 @@ | |||||||
| //!     // Construct our SocketAddr to listen on... | //!     // Construct our SocketAddr to listen on... | ||||||
| //!     let addr = ([127, 0, 0, 1], 3000).into(); | //!     let addr = ([127, 0, 0, 1], 3000).into(); | ||||||
| //! | //! | ||||||
| //!     // And a NewService to handle each connection... | //!     // And a MakeService to handle each connection... | ||||||
| //!     let new_service = || { | //!     let make_service = || { | ||||||
| //!         service_fn_ok(|_req| { | //!         service_fn_ok(|_req| { | ||||||
| //!             Response::new(Body::from("Hello World")) | //!             Response::new(Body::from("Hello World")) | ||||||
| //!         }) | //!         }) | ||||||
| @@ -39,7 +39,7 @@ | |||||||
| //! | //! | ||||||
| //!     // Then bind and serve... | //!     // Then bind and serve... | ||||||
| //!     let server = Server::bind(&addr) | //!     let server = Server::bind(&addr) | ||||||
| //!         .serve(new_service); | //!         .serve(make_service); | ||||||
| //! | //! | ||||||
| //!     // Finally, spawn `server` onto an Executor... | //!     // Finally, spawn `server` onto an Executor... | ||||||
| //!     hyper::rt::run(server.map_err(|e| { | //!     hyper::rt::run(server.map_err(|e| { | ||||||
| @@ -65,10 +65,10 @@ use tokio_io::{AsyncRead, AsyncWrite}; | |||||||
|  |  | ||||||
| use body::{Body, Payload}; | use body::{Body, Payload}; | ||||||
| use common::exec::{Exec, H2Exec, NewSvcExec}; | use common::exec::{Exec, H2Exec, NewSvcExec}; | ||||||
| use service::{NewService, Service}; | use service::Service; | ||||||
| // Renamed `Http` as `Http_` for now so that people upgrading don't see an | // Renamed `Http` as `Http_` for now so that people upgrading don't see an | ||||||
| // error that `hyper::server::Http` is private... | // error that `hyper::server::Http` is private... | ||||||
| use self::conn::{Http as Http_, NoopWatcher, SpawnAll}; | use self::conn::{Http as Http_, MakeServiceRef, NoopWatcher, SpawnAll}; | ||||||
| use self::shutdown::{Graceful, GracefulWatcher}; | use self::shutdown::{Graceful, GracefulWatcher}; | ||||||
| #[cfg(feature = "runtime")] use self::tcp::AddrIncoming; | #[cfg(feature = "runtime")] use self::tcp::AddrIncoming; | ||||||
|  |  | ||||||
| @@ -144,7 +144,7 @@ where | |||||||
|     I: Stream, |     I: Stream, | ||||||
|     I::Error: Into<Box<::std::error::Error + Send + Sync>>, |     I::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|     I::Item: AsyncRead + AsyncWrite + Send + 'static, |     I::Item: AsyncRead + AsyncWrite + Send + 'static, | ||||||
|     S: NewService<ReqBody=Body, ResBody=B>, |     S: MakeServiceRef<I::Item, ReqBody=Body, ResBody=B>, | ||||||
|     S::Error: Into<Box<::std::error::Error + Send + Sync>>, |     S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|     S::Service: 'static, |     S::Service: 'static, | ||||||
|     B: Payload, |     B: Payload, | ||||||
| @@ -203,7 +203,7 @@ where | |||||||
|     I: Stream, |     I: Stream, | ||||||
|     I::Error: Into<Box<::std::error::Error + Send + Sync>>, |     I::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|     I::Item: AsyncRead + AsyncWrite + Send + 'static, |     I::Item: AsyncRead + AsyncWrite + Send + 'static, | ||||||
|     S: NewService<ReqBody=Body, ResBody=B>, |     S: MakeServiceRef<I::Item, ReqBody=Body, ResBody=B>, | ||||||
|     S::Error: Into<Box<::std::error::Error + Send + Sync>>, |     S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|     S::Service: 'static, |     S::Service: 'static, | ||||||
|     B: Payload, |     B: Payload, | ||||||
| @@ -332,7 +332,7 @@ impl<I, E> Builder<I, E> { | |||||||
|         I: Stream, |         I: Stream, | ||||||
|         I::Error: Into<Box<::std::error::Error + Send + Sync>>, |         I::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|         I::Item: AsyncRead + AsyncWrite + Send + 'static, |         I::Item: AsyncRead + AsyncWrite + Send + 'static, | ||||||
|         S: NewService<ReqBody=Body, ResBody=B>, |         S: MakeServiceRef<I::Item, ReqBody=Body, ResBody=B>, | ||||||
|         S::Error: Into<Box<::std::error::Error + Send + Sync>>, |         S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|         S::Service: 'static, |         S::Service: 'static, | ||||||
|         B: Payload, |         B: Payload, | ||||||
|   | |||||||
| @@ -4,8 +4,8 @@ use tokio_io::{AsyncRead, AsyncWrite}; | |||||||
| use body::{Body, Payload}; | use body::{Body, Payload}; | ||||||
| use common::drain::{self, Draining, Signal, Watch, Watching}; | use common::drain::{self, Draining, Signal, Watch, Watching}; | ||||||
| use common::exec::{H2Exec, NewSvcExec}; | use common::exec::{H2Exec, NewSvcExec}; | ||||||
| use service::{Service, NewService}; | use service::Service; | ||||||
| use super::conn::{SpawnAll, UpgradeableConnection, Watcher}; | use super::conn::{MakeServiceRef, SpawnAll, UpgradeableConnection, Watcher}; | ||||||
|  |  | ||||||
| #[allow(missing_debug_implementations)] | #[allow(missing_debug_implementations)] | ||||||
| pub struct Graceful<I, S, F, E> { | pub struct Graceful<I, S, F, E> { | ||||||
| @@ -40,7 +40,7 @@ where | |||||||
|     I: Stream, |     I: Stream, | ||||||
|     I::Error: Into<Box<::std::error::Error + Send + Sync>>, |     I::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|     I::Item: AsyncRead + AsyncWrite + Send + 'static, |     I::Item: AsyncRead + AsyncWrite + Send + 'static, | ||||||
|     S: NewService<ReqBody=Body, ResBody=B>, |     S: MakeServiceRef<I::Item, ReqBody=Body, ResBody=B>, | ||||||
|     S::Service: 'static, |     S::Service: 'static, | ||||||
|     S::Error: Into<Box<::std::error::Error + Send + Sync>>, |     S::Error: Into<Box<::std::error::Error + Send + Sync>>, | ||||||
|     B: Payload, |     B: Payload, | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ use tokio_reactor::Handle; | |||||||
| use tokio_tcp::TcpListener; | use tokio_tcp::TcpListener; | ||||||
| use tokio_timer::Delay; | use tokio_timer::Delay; | ||||||
|  |  | ||||||
| use self::addr_stream::AddrStream; | pub use self::addr_stream::AddrStream; | ||||||
|  |  | ||||||
| /// A stream of connections from binding to an address. | /// A stream of connections from binding to an address. | ||||||
| #[must_use = "streams do nothing unless polled"] | #[must_use = "streams do nothing unless polled"] | ||||||
| @@ -194,6 +194,7 @@ mod addr_stream { | |||||||
|     use tokio_io::{AsyncRead, AsyncWrite}; |     use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// A transport returned yieled by `AddrIncoming`. | ||||||
|     #[derive(Debug)] |     #[derive(Debug)] | ||||||
|     pub struct AddrStream { |     pub struct AddrStream { | ||||||
|         inner: TcpStream, |         inner: TcpStream, | ||||||
|   | |||||||
							
								
								
									
										96
									
								
								src/service/make_service.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/service/make_service.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | |||||||
|  | use std::error::Error as StdError; | ||||||
|  | use std::fmt; | ||||||
|  |  | ||||||
|  | use futures::{Future, IntoFuture}; | ||||||
|  |  | ||||||
|  | use body::Payload; | ||||||
|  | use super::Service; | ||||||
|  |  | ||||||
|  | /// An asynchronous constructor of `Service`s. | ||||||
|  | pub trait MakeService<Ctx> { | ||||||
|  |     /// The `Payload` body of the `http::Request`. | ||||||
|  |     type ReqBody: Payload; | ||||||
|  |  | ||||||
|  |     /// The `Payload` body of the `http::Response`. | ||||||
|  |     type ResBody: Payload; | ||||||
|  |  | ||||||
|  |     /// The error type that can be returned by `Service`s. | ||||||
|  |     type Error: Into<Box<StdError + Send + Sync>>; | ||||||
|  |  | ||||||
|  |     /// The resolved `Service` from `new_service()`. | ||||||
|  |     type Service: Service< | ||||||
|  |         ReqBody=Self::ReqBody, | ||||||
|  |         ResBody=Self::ResBody, | ||||||
|  |         Error=Self::Error, | ||||||
|  |     >; | ||||||
|  |  | ||||||
|  |     /// The future returned from `new_service` of a `Service`. | ||||||
|  |     type Future: Future<Item=Self::Service, Error=Self::MakeError>; | ||||||
|  |  | ||||||
|  |     /// The error type that can be returned when creating a new `Service`. | ||||||
|  |     type MakeError: Into<Box<StdError + Send + Sync>>; | ||||||
|  |  | ||||||
|  |     /// Create a new `Service`. | ||||||
|  |     fn make_service(&mut self, ctx: Ctx) -> Self::Future; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// Create a `MakeService` from a function. | ||||||
|  | /// | ||||||
|  | /// # Example | ||||||
|  | /// | ||||||
|  | /// ```rust | ||||||
|  | /// use std::net::TcpStream; | ||||||
|  | /// use hyper::{Body, Request, Response}; | ||||||
|  | /// use hyper::service::{make_service_fn, service_fn_ok}; | ||||||
|  | /// | ||||||
|  | /// let make_svc = make_service_fn(|socket: &TcpStream| { | ||||||
|  | ///     let remote_addr = socket.peer_addr().unwrap(); | ||||||
|  | ///     service_fn_ok(move |_: Request<Body>| { | ||||||
|  | ///         Response::new(Body::from(format!("Hello, {}", remote_addr))) | ||||||
|  | ///     }) | ||||||
|  | /// }); | ||||||
|  | /// ``` | ||||||
|  | pub fn make_service_fn<F, Ctx, Ret>(f: F) -> MakeServiceFn<F> | ||||||
|  | where | ||||||
|  |     F: Fn(&Ctx) -> Ret, | ||||||
|  |     Ret: IntoFuture, | ||||||
|  | { | ||||||
|  |     MakeServiceFn { | ||||||
|  |         f, | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Not exported from crate as this will likely be replaced with `impl Service`. | ||||||
|  | pub struct MakeServiceFn<F> { | ||||||
|  |     f: F, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<'c, F, Ctx, Ret, ReqBody, ResBody> MakeService<&'c Ctx> for MakeServiceFn<F> | ||||||
|  | where | ||||||
|  |     F: Fn(&Ctx) -> Ret, | ||||||
|  |     Ret: IntoFuture, | ||||||
|  |     Ret::Item: Service<ReqBody=ReqBody, ResBody=ResBody>, | ||||||
|  |     Ret::Error: Into<Box<StdError + Send + Sync>>, | ||||||
|  |     ReqBody: Payload, | ||||||
|  |     ResBody: Payload, | ||||||
|  | { | ||||||
|  |     type ReqBody = ReqBody; | ||||||
|  |     type ResBody = ResBody; | ||||||
|  |     type Error = <Ret::Item as Service>::Error; | ||||||
|  |     type Service = Ret::Item; | ||||||
|  |     type Future = Ret::Future; | ||||||
|  |     type MakeError = Ret::Error; | ||||||
|  |  | ||||||
|  |     fn make_service(&mut self, ctx: &'c Ctx) -> Self::Future { | ||||||
|  |         (self.f)(ctx).into_future() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<F> fmt::Debug for MakeServiceFn<F> { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         f.debug_struct("MakeServiceFn") | ||||||
|  |             .finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| @@ -1,9 +1,9 @@ | |||||||
| //! Services and NewServices | //! Services and MakeServices | ||||||
| //! | //! | ||||||
| //! - A [`Service`](Service) is a trait representing an asynchronous function | //! - A [`Service`](Service) is a trait representing an asynchronous function | ||||||
| //!   of a request to a response. It's similar to | //!   of a request to a response. It's similar to | ||||||
| //!   `async fn(Request) -> Result<Response, Error>`. | //!   `async fn(Request) -> Result<Response, Error>`. | ||||||
| //! - A [`NewService`](NewService) is a trait creating specific instances of a | //! - A [`MakeService`](MakeService) is a trait creating specific instances of a | ||||||
| //!   `Service`. | //!   `Service`. | ||||||
| //! | //! | ||||||
| //! These types are conceptually similar to those in | //! These types are conceptually similar to those in | ||||||
| @@ -19,17 +19,21 @@ | |||||||
| //! [`service_fn`](service_fn) and [`service_fn_ok`](service_fn_ok) should be | //! [`service_fn`](service_fn) and [`service_fn_ok`](service_fn_ok) should be | ||||||
| //! sufficient for most cases. | //! sufficient for most cases. | ||||||
| //! | //! | ||||||
| //! # NewService | //! # MakeService | ||||||
| //! | //! | ||||||
| //! Since a `Service` is bound to a single connection, a [`Server`](::Server) | //! Since a `Service` is bound to a single connection, a [`Server`](::Server) | ||||||
| //! needs a way to make them as it accepts connections. This is what a | //! needs a way to make them as it accepts connections. This is what a | ||||||
| //! `NewService` does. | //! `MakeService` does. | ||||||
| //! | //! | ||||||
| //! Resources that need to be shared by all `Service`s can be put into a | //! Resources that need to be shared by all `Service`s can be put into a | ||||||
| //! `NewService`, and then passed to individual `Service`s when `new_service` | //! `MakeService`, and then passed to individual `Service`s when `make_service` | ||||||
| //! is called. | //! is called. | ||||||
|  |  | ||||||
|  | mod make_service; | ||||||
| mod new_service; | mod new_service; | ||||||
| mod service; | mod service; | ||||||
|  |  | ||||||
| pub use self::new_service::{NewService}; | pub use self::make_service::{make_service_fn, MakeService}; | ||||||
|  | #[doc(hidden)] | ||||||
|  | pub use self::new_service::NewService; | ||||||
| pub use self::service::{service_fn, service_fn_ok, Service}; | pub use self::service::{service_fn, service_fn_ok, Service}; | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ use std::error::Error as StdError; | |||||||
| use futures::{Future, IntoFuture}; | use futures::{Future, IntoFuture}; | ||||||
|  |  | ||||||
| use body::Payload; | use body::Payload; | ||||||
| use super::Service; | use super::{MakeService, Service}; | ||||||
|  |  | ||||||
| /// An asynchronous constructor of `Service`s. | /// An asynchronous constructor of `Service`s. | ||||||
| pub trait NewService { | pub trait NewService { | ||||||
| @@ -47,9 +47,24 @@ where | |||||||
|     type Future = R::Future; |     type Future = R::Future; | ||||||
|     type InitError = R::Error; |     type InitError = R::Error; | ||||||
|  |  | ||||||
|  |  | ||||||
|     fn new_service(&self) -> Self::Future { |     fn new_service(&self) -> Self::Future { | ||||||
|         (*self)().into_future() |         (*self)().into_future() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl<N, Ctx> MakeService<Ctx> for N | ||||||
|  | where | ||||||
|  |     N: NewService, | ||||||
|  | { | ||||||
|  |     type ReqBody = N::ReqBody; | ||||||
|  |     type ResBody = N::ResBody; | ||||||
|  |     type Error = N::Error; | ||||||
|  |     type Service = N::Service; | ||||||
|  |     type Future = N::Future; | ||||||
|  |     type MakeError = N::InitError; | ||||||
|  |  | ||||||
|  |     fn make_service(&mut self, _: Ctx) -> Self::Future { | ||||||
|  |         self.new_service() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user