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:
		
							
								
								
									
										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 | ||||
| //!   of a request to a response. It's similar to | ||||
| //!   `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`. | ||||
| //! | ||||
| //! 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 | ||||
| //! sufficient for most cases. | ||||
| //! | ||||
| //! # NewService | ||||
| //! # MakeService | ||||
| //! | ||||
| //! 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 | ||||
| //! `NewService` does. | ||||
| //! `MakeService` does. | ||||
| //! | ||||
| //! 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. | ||||
|  | ||||
| mod make_service; | ||||
| mod new_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}; | ||||
|   | ||||
| @@ -3,7 +3,7 @@ use std::error::Error as StdError; | ||||
| use futures::{Future, IntoFuture}; | ||||
|  | ||||
| use body::Payload; | ||||
| use super::Service; | ||||
| use super::{MakeService, Service}; | ||||
|  | ||||
| /// An asynchronous constructor of `Service`s. | ||||
| pub trait NewService { | ||||
| @@ -47,9 +47,24 @@ where | ||||
|     type Future = R::Future; | ||||
|     type InitError = R::Error; | ||||
|  | ||||
|  | ||||
|     fn new_service(&self) -> Self::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