feat(service): introduce hyper-specific Service
				
					
				
			This introduces the `hyper::service` module, which replaces `tokio-service`. Since the trait is specific to hyper, its associated types have been adjusted. It didn't make sense to need to define `Service<Request=http::Request>`, since we already know the context is HTTP. Instead, the request and response bodies are associated types now, and slightly stricter bounds have been placed on `Error`. The helpers `service_fn` and `service_fn_ok` should be sufficient for now to ease creating `Service`s. The `NewService` trait now allows service creation to also be asynchronous. These traits are similar to `tower` in nature, and possibly will be replaced completely by it in the future. For now, hyper defining its own allows the traits to have better context, and prevents breaking changes in `tower` from affecting hyper. Closes #1461 BREAKING CHANGE: The `Service` trait has changed: it has some changed associated types, and `call` is now bound to `&mut self`. The `NewService` trait has changed: it has some changed associated types, and `new_service` now returns a `Future`. `Client` no longer implements `Service` for now. `hyper::server::conn::Serve` now returns `Connecting` instead of `Connection`s, since `new_service` can now return a `Future`. The `Connecting` is a future wrapping the new service future, returning a `Connection` afterwards. In many cases, `Future::flatten` can be used.
This commit is contained in:
		
							
								
								
									
										35
									
								
								src/service/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/service/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| //! Services and NewServices | ||||
| //! | ||||
| //! - 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 | ||||
| //!   `Service`. | ||||
| //! | ||||
| //! These types are conceptually similar to those in | ||||
| //! [tower](https://crates.io/crates/tower), while being specific to hyper. | ||||
| //! | ||||
| //! # Service | ||||
| //! | ||||
| //! In hyper, especially in the server setting, a `Service` is usually bound | ||||
| //! to a single connection. It defines how to respond to **all** requests that | ||||
| //! connection will receive. | ||||
| //! | ||||
| //! While it's possible to implement `Service` for a type manually, the helpers | ||||
| //! [`service_fn`](service_fn) and [`service_fn_ok`](service_fn_ok) should be | ||||
| //! sufficient for most cases. | ||||
| //! | ||||
| //! # NewService | ||||
| //! | ||||
| //! 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. | ||||
| //! | ||||
| //! 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` | ||||
| //! is called. | ||||
| mod new_service; | ||||
| mod service; | ||||
|  | ||||
| pub use self::new_service::{NewService}; | ||||
| pub use self::service::{service_fn, service_fn_ok, Service}; | ||||
							
								
								
									
										55
									
								
								src/service/new_service.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/service/new_service.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| use std::error::Error as StdError; | ||||
|  | ||||
| use futures::{Future, IntoFuture}; | ||||
|  | ||||
| use body::Payload; | ||||
| use super::Service; | ||||
|  | ||||
| /// An asynchronous constructor of `Service`s. | ||||
| pub trait NewService { | ||||
|     /// 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::InitError>; | ||||
|  | ||||
|     /// The error type that can be returned when creating a new `Service. | ||||
|     type InitError: Into<Box<StdError + Send + Sync>>; | ||||
|  | ||||
|     /// Create a new `Service`. | ||||
|     fn new_service(&self) -> Self::Future; | ||||
| } | ||||
|  | ||||
| impl<F, R, S> NewService for F | ||||
| where | ||||
|     F: Fn() -> R, | ||||
|     R: IntoFuture<Item=S>, | ||||
|     R::Error: Into<Box<StdError + Send + Sync>>, | ||||
|     S: Service, | ||||
| { | ||||
|     type ReqBody = S::ReqBody; | ||||
|     type ResBody = S::ResBody; | ||||
|     type Error = S::Error; | ||||
|     type Service = S; | ||||
|     type Future = R::Future; | ||||
|     type InitError = R::Error; | ||||
|  | ||||
|  | ||||
|     fn new_service(&self) -> Self::Future { | ||||
|         (*self)().into_future() | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										165
									
								
								src/service/service.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								src/service/service.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | ||||
| use std::error::Error as StdError; | ||||
| use std::fmt; | ||||
| use std::marker::PhantomData; | ||||
|  | ||||
| use futures::{future, Future, IntoFuture}; | ||||
|  | ||||
| use body::Payload; | ||||
| use common::Never; | ||||
| use ::{Request, Response}; | ||||
|  | ||||
| /// An asynchronous function from `Request` to `Response`. | ||||
| pub trait Service { | ||||
|     /// 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 occur within this `Service. | ||||
|     /// | ||||
|     /// Note: Returning an `Error` to a hyper server will cause the connection | ||||
|     /// to be abruptly aborted. In most cases, it is better to return a `Response` | ||||
|     /// with a 4xx or 5xx status code. | ||||
|     type Error: Into<Box<StdError + Send + Sync>>; | ||||
|  | ||||
|     /// The `Future` returned by this `Service`. | ||||
|     type Future: Future<Item=Response<Self::ResBody>, Error=Self::Error>; | ||||
|  | ||||
|     /// Calls this `Service` with a request, returning a `Future` of the response. | ||||
|     fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future; | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Create a `Service` from a function. | ||||
| /// | ||||
| /// # Example | ||||
| /// | ||||
| /// ```rust | ||||
| /// use hyper::{Body, Request, Response, Version}; | ||||
| /// use hyper::service::service_fn; | ||||
| /// | ||||
| /// let service = service_fn(|req: Request<Body>| { | ||||
| ///     if req.version() == Version::HTTP_11 { | ||||
| ///         Ok(Response::new(Body::from("Hello World"))) | ||||
| ///     } else { | ||||
| ///         // Note: it's usually better to return a Response | ||||
| ///         // with an appropriate StatusCode instead of an Err. | ||||
| ///         Err("not HTTP/1.1, abort connection") | ||||
| ///     } | ||||
| /// }); | ||||
| /// ``` | ||||
| pub fn service_fn<F, R, S>(f: F) -> ServiceFn<F, R> | ||||
| where | ||||
|     F: FnMut(Request<R>) -> S, | ||||
|     S: IntoFuture, | ||||
| { | ||||
|     ServiceFn { | ||||
|         f, | ||||
|         _req: PhantomData, | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Create a `Service` from a function that never errors. | ||||
| /// | ||||
| /// # Example | ||||
| /// | ||||
| /// ```rust | ||||
| /// use hyper::{Body, Request, Response}; | ||||
| /// use hyper::service::service_fn_ok; | ||||
| /// | ||||
| /// let service = service_fn_ok(|req: Request<Body>| { | ||||
| ///     println!("request: {} {}", req.method(), req.uri()); | ||||
| ///     Response::new(Body::from("Hello World")) | ||||
| /// }); | ||||
| /// ``` | ||||
| pub fn service_fn_ok<F, R, S>(f: F) -> ServiceFnOk<F, R> | ||||
| where | ||||
|     F: FnMut(Request<R>) -> Response<S>, | ||||
|     S: Payload, | ||||
| { | ||||
|     ServiceFnOk { | ||||
|         f, | ||||
|         _req: PhantomData, | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Not exported from crate as this will likely be replaced with `impl Service`. | ||||
| pub struct ServiceFn<F, R> { | ||||
|     f: F, | ||||
|     _req: PhantomData<fn(R)>, | ||||
| } | ||||
|  | ||||
| impl<F, ReqBody, Ret, ResBody> Service for ServiceFn<F, ReqBody> | ||||
| where | ||||
|     F: FnMut(Request<ReqBody>) -> Ret, | ||||
|     ReqBody: Payload, | ||||
|     Ret: IntoFuture<Item=Response<ResBody>>, | ||||
|     Ret::Error: Into<Box<StdError + Send + Sync>>, | ||||
|     ResBody: Payload, | ||||
| { | ||||
|     type ReqBody = ReqBody; | ||||
|     type ResBody = ResBody; | ||||
|     type Error = Ret::Error; | ||||
|     type Future = Ret::Future; | ||||
|  | ||||
|     fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { | ||||
|         (self.f)(req).into_future() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<F, R> IntoFuture for ServiceFn<F, R> { | ||||
|     type Future = future::FutureResult<Self::Item, Self::Error>; | ||||
|     type Item = Self; | ||||
|     type Error = Never; | ||||
|  | ||||
|     fn into_future(self) -> Self::Future { | ||||
|         future::ok(self) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<F, R> fmt::Debug for ServiceFn<F, R> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("impl Service") | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Not exported from crate as this will likely be replaced with `impl Service`. | ||||
| pub struct ServiceFnOk<F, R> { | ||||
|     f: F, | ||||
|     _req: PhantomData<fn(R)>, | ||||
| } | ||||
|  | ||||
| impl<F, ReqBody, ResBody> Service for ServiceFnOk<F, ReqBody> | ||||
| where | ||||
|     F: FnMut(Request<ReqBody>) -> Response<ResBody>, | ||||
|     ReqBody: Payload, | ||||
|     ResBody: Payload, | ||||
| { | ||||
|     type ReqBody = ReqBody; | ||||
|     type ResBody = ResBody; | ||||
|     type Error = Never; | ||||
|     type Future = future::FutureResult<Response<ResBody>, Never>; | ||||
|  | ||||
|     fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { | ||||
|         future::ok((self.f)(req)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<F, R> IntoFuture for ServiceFnOk<F, R> { | ||||
|     type Future = future::FutureResult<Self::Item, Self::Error>; | ||||
|     type Item = Self; | ||||
|     type Error = Never; | ||||
|  | ||||
|     fn into_future(self) -> Self::Future { | ||||
|         future::ok(self) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<F, R> fmt::Debug for ServiceFnOk<F, R> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("impl Service") | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user