feat(service): rename Service to HttpService, re-export tower::Service`
The only important trait for a user is the `tower::Service` trait, which is now available also at `hyper::service::Service`. The other "trait aliases" are no longer publicly exported, as people thought they had to implement them. Also removes dependency on `tower-make`, which is trivial but otherwise shouldn't affect anyone. Closes #1959
This commit is contained in:
58
src/service/http.rs
Normal file
58
src/service/http.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use std::error::Error as StdError;
|
||||
|
||||
use crate::body::Payload;
|
||||
use crate::common::{Future, Poll, task};
|
||||
use crate::{Request, Response};
|
||||
|
||||
/// An asynchronous function from `Request` to `Response`.
|
||||
pub trait HttpService<ReqBody>: sealed::Sealed<ReqBody> {
|
||||
/// 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<dyn StdError + Send + Sync>>;
|
||||
|
||||
/// The `Future` returned by this `Service`.
|
||||
type Future: Future<Output=Result<Response<Self::ResBody>, Self::Error>>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future;
|
||||
}
|
||||
|
||||
impl<T, B1, B2> HttpService<B1> for T
|
||||
where
|
||||
T: tower_service::Service<Request<B1>, Response = Response<B2>>,
|
||||
B2: Payload,
|
||||
T::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
{
|
||||
type ResBody = B2;
|
||||
|
||||
type Error = T::Error;
|
||||
type Future = T::Future;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
tower_service::Service::poll_ready(self, cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<B1>) -> Self::Future {
|
||||
tower_service::Service::call(self, req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B1, B2> sealed::Sealed<B1> for T
|
||||
where
|
||||
T: tower_service::Service<Request<B1>, Response = Response<B2>>,
|
||||
B2: Payload,
|
||||
{}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed<T> {}
|
||||
}
|
||||
|
||||
@@ -1,81 +1,50 @@
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use crate::body::Payload;
|
||||
use crate::common::{Future, Poll, task};
|
||||
use super::Service;
|
||||
use super::{HttpService, Service};
|
||||
|
||||
/// An asynchronous constructor of `Service`s.
|
||||
pub trait MakeService<Target, ReqBody>: sealed::Sealed<Target, ReqBody> {
|
||||
/// The `Payload` body of the `http::Response`.
|
||||
type ResBody: Payload;
|
||||
// The same "trait alias" as tower::MakeConnection, but inlined to reduce
|
||||
// dependencies.
|
||||
pub trait MakeConnection<Target>: self::sealed::Sealed<(Target,)> {
|
||||
type Connection: AsyncRead + AsyncWrite;
|
||||
type Error;
|
||||
type Future: Future<Output = Result<Self::Connection, Self::Error>>;
|
||||
|
||||
/// The error type that can be returned by `Service`s.
|
||||
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
|
||||
|
||||
/// The resolved `Service` from `make_service()`.
|
||||
type Service: Service<
|
||||
ReqBody,
|
||||
ResBody=Self::ResBody,
|
||||
Error=Self::Error,
|
||||
>;
|
||||
|
||||
/// The future returned from `new_service` of a `Service`.
|
||||
type Future: Future<Output=Result<Self::Service, Self::MakeError>>;
|
||||
|
||||
/// The error type that can be returned when creating a new `Service`.
|
||||
type MakeError: Into<Box<dyn StdError + Send + Sync>>;
|
||||
|
||||
/// Returns `Ready` when the constructor is ready to create a new `Service`.
|
||||
///
|
||||
/// The implementation of this method is allowed to return a `Ready` even if
|
||||
/// the factory is not ready to create a new service. In this case, the future
|
||||
/// returned from `make_service` will resolve to an error.
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::MakeError>>;
|
||||
|
||||
/// Create a new `Service`.
|
||||
fn make_service(&mut self, target: Target) -> Self::Future;
|
||||
fn make_connection(&mut self, target: Target) -> Self::Future;
|
||||
}
|
||||
|
||||
impl<T, Target, S, B1, B2, E, F> MakeService<Target, B1> for T
|
||||
impl<S, Target> self::sealed::Sealed<(Target,)> for S where S: Service<Target> {}
|
||||
|
||||
impl<S, Target> MakeConnection<Target> for S
|
||||
where
|
||||
T: for<'a> tower_service::Service<&'a Target, Response = S, Error = E, Future = F>,
|
||||
S: tower_service::Service<crate::Request<B1>, Response = crate::Response<B2>>,
|
||||
E: Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||
S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||
B1: Payload,
|
||||
B2: Payload,
|
||||
F: Future<Output = Result<S, E>>,
|
||||
S: Service<Target>,
|
||||
S::Response: AsyncRead + AsyncWrite,
|
||||
{
|
||||
type ResBody = B2;
|
||||
type Connection = S::Response;
|
||||
type Error = S::Error;
|
||||
type Service = S;
|
||||
type Future = F;
|
||||
type MakeError = E;
|
||||
type Future = S::Future;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::MakeError>> {
|
||||
tower_service::Service::poll_ready(self, cx)
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Service::poll_ready(self, cx)
|
||||
}
|
||||
|
||||
fn make_service(&mut self, req: Target) -> Self::Future {
|
||||
tower_service::Service::call(self, &req)
|
||||
fn make_connection(&mut self, target: Target) -> Self::Future {
|
||||
Service::call(self, target)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Target, S, B1, B2> sealed::Sealed<Target, B1> for T
|
||||
where
|
||||
T: for<'a> tower_service::Service<&'a Target, Response = S>,
|
||||
S: tower_service::Service<crate::Request<B1>, Response = crate::Response<B2>>
|
||||
{
|
||||
}
|
||||
|
||||
// Just a sort-of "trait alias" of `MakeService`, not to be implemented
|
||||
// by anyone, only used as bounds.
|
||||
#[doc(hidden)]
|
||||
pub trait MakeServiceRef<Target, ReqBody>: self::sealed::Sealed<Target, ReqBody> {
|
||||
pub trait MakeServiceRef<Target, ReqBody>: self::sealed::Sealed<(Target, ReqBody)> {
|
||||
type ResBody: Payload;
|
||||
type Error: Into<Box<dyn StdError + Send + Sync>>;
|
||||
type Service: Service<
|
||||
type Service: HttpService<
|
||||
ReqBody,
|
||||
ResBody=Self::ResBody,
|
||||
Error=Self::Error,
|
||||
@@ -101,10 +70,10 @@ pub trait MakeServiceRef<Target, ReqBody>: self::sealed::Sealed<Target, ReqBody>
|
||||
|
||||
impl<T, Target, E, ME, S, F, IB, OB> MakeServiceRef<Target, IB> for T
|
||||
where
|
||||
T: for<'a> tower_service::Service<&'a Target, Error=ME, Response=S, Future=F>,
|
||||
T: for<'a> Service<&'a Target, Error=ME, Response=S, Future=F>,
|
||||
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||
ME: Into<Box<dyn StdError + Send + Sync>>,
|
||||
S: tower_service::Service<crate::Request<IB>, Response=crate::Response<OB>, Error=E>,
|
||||
S: HttpService<IB, ResBody=OB, Error=E>,
|
||||
F: Future<Output=Result<S, ME>>,
|
||||
IB: Payload,
|
||||
OB: Payload,
|
||||
@@ -126,17 +95,24 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Target, S, B1, B2> self::sealed::Sealed<(Target, B1)> for T
|
||||
where
|
||||
T: for<'a> Service<&'a Target, Response = S>,
|
||||
S: HttpService<B1, ResBody = B2>,
|
||||
B1: Payload,
|
||||
B2: Payload,
|
||||
{
|
||||
}
|
||||
|
||||
/// Create a `MakeService` from a function.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// ```
|
||||
/// # #[cfg(feature = "runtime")]
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
/// use std::net::TcpStream;
|
||||
/// use hyper::{Body, Error, Request, Response, Server};
|
||||
/// use hyper::rt::{self, Future};
|
||||
/// # async fn run() {
|
||||
/// use std::convert::Infallible;
|
||||
/// use hyper::{Body, Request, Response, Server};
|
||||
/// use hyper::server::conn::AddrStream;
|
||||
/// use hyper::service::{make_service_fn, service_fn};
|
||||
///
|
||||
@@ -145,8 +121,8 @@ where
|
||||
/// let make_svc = make_service_fn(|socket: &AddrStream| {
|
||||
/// let remote_addr = socket.remote_addr();
|
||||
/// async move {
|
||||
/// Ok::<_, Error>(service_fn(move |_: Request<Body>| async move {
|
||||
/// Ok::<_, Error>(
|
||||
/// Ok::<_, Infallible>(service_fn(move |_: Request<Body>| async move {
|
||||
/// Ok::<_, Infallible>(
|
||||
/// Response::new(Body::from(format!("Hello, {}!", remote_addr)))
|
||||
/// )
|
||||
/// }))
|
||||
@@ -162,7 +138,7 @@ where
|
||||
/// eprintln!("server error: {}", e);
|
||||
/// }
|
||||
/// # }
|
||||
/// # #[cfg(not(feature = "runtime"))] fn main() {}
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
pub fn make_service_fn<F, Target, Ret>(f: F) -> MakeServiceFn<F>
|
||||
where
|
||||
@@ -179,7 +155,7 @@ pub struct MakeServiceFn<F> {
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<'t, F, Ret, Target, Svc, MkErr> tower_service::Service<&'t Target> for MakeServiceFn<F>
|
||||
impl<'t, F, Ret, Target, Svc, MkErr> Service<&'t Target> for MakeServiceFn<F>
|
||||
where
|
||||
F: FnMut(&Target) -> Ret,
|
||||
Ret: Future<Output=Result<Svc, MkErr>>,
|
||||
@@ -206,7 +182,7 @@ impl<F> fmt::Debug for MakeServiceFn<F> {
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed<T, B> {}
|
||||
pub trait Sealed<X> {}
|
||||
|
||||
pub trait CantImpl {}
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
//! Services and MakeServices
|
||||
//! Asynchronous Services
|
||||
//!
|
||||
//! - A [`Service`](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 [`MakeService`](service::MakeService) is a trait creating specific
|
||||
//! instances of a `Service`.
|
||||
//! A [`Service`](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>`.
|
||||
//!
|
||||
//! These types are conceptually similar to those in
|
||||
//! [tower](https://crates.io/crates/tower), while being specific to hyper.
|
||||
//! The argument and return value isn't strictly required to be for HTTP.
|
||||
//! Therefore, hyper uses several "trait aliases" to reduce clutter around
|
||||
//! bounds. These are:
|
||||
//!
|
||||
//! # Service
|
||||
//! - `HttpService`: This is blanketly implemented for all types that
|
||||
//! implement `Service<http::Request<B1>, Response = http::Response<B2>>`.
|
||||
//! - `MakeService`: When a `Service` returns a new `Service` as its "response",
|
||||
//! we consider it a `MakeService`. Again, blanketly implemented in those cases.
|
||||
//! - `MakeConnection`: A `Service` that returns a "connection", a type that
|
||||
//! implements `AsyncRead` and `AsyncWrite`.
|
||||
//!
|
||||
//! # HttpService
|
||||
//!
|
||||
//! 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
|
||||
@@ -25,11 +31,17 @@
|
||||
//! `MakeService` does.
|
||||
//!
|
||||
//! Resources that need to be shared by all `Service`s can be put into a
|
||||
//! `MakeService`, and then passed to individual `Service`s when `make_service`
|
||||
//! `MakeService`, and then passed to individual `Service`s when `call`
|
||||
//! is called.
|
||||
|
||||
mod make_service;
|
||||
mod service;
|
||||
pub use tower_service::Service;
|
||||
|
||||
pub use self::make_service::{make_service_fn, MakeService, MakeServiceRef};
|
||||
pub use self::service::{service_fn, Service};
|
||||
mod http;
|
||||
mod make;
|
||||
mod util;
|
||||
|
||||
pub(crate) use self::make::{MakeConnection, MakeServiceRef};
|
||||
pub(crate) use self::http::HttpService;
|
||||
|
||||
pub use self::make::make_service_fn;
|
||||
pub use self::util::service_fn;
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::body::Payload;
|
||||
use crate::common::{Future, Poll, task};
|
||||
use crate::{Request, Response};
|
||||
|
||||
/// An asynchronous function from `Request` to `Response`.
|
||||
pub trait Service<ReqBody>: sealed::Sealed<ReqBody> {
|
||||
/// 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<dyn StdError + Send + Sync>>;
|
||||
|
||||
/// The `Future` returned by this `Service`.
|
||||
type Future: Future<Output=Result<Response<Self::ResBody>, Self::Error>>;
|
||||
|
||||
/// Returns `Ready` when the service is able to process requests.
|
||||
///
|
||||
/// The implementation of this method is allowed to return a `Ready` even if
|
||||
/// the service is not ready to process. In this case, the future returned
|
||||
/// from `call` will resolve to an error.
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
|
||||
|
||||
/// Calls this `Service` with a request, returning a `Future` of the response.
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future;
|
||||
}
|
||||
|
||||
impl<T, B1, B2> Service<B1> for T
|
||||
where
|
||||
T: tower_service::Service<Request<B1>, Response = Response<B2>>,
|
||||
B2: Payload,
|
||||
T::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
{
|
||||
type ResBody = B2;
|
||||
|
||||
type Error = T::Error;
|
||||
type Future = T::Future;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
tower_service::Service::poll_ready(self, cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<B1>) -> Self::Future {
|
||||
tower_service::Service::call(self, req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B1, B2> sealed::Sealed<B1> for T
|
||||
where
|
||||
T: tower_service::Service<Request<B1>, Response = Response<B2>>,
|
||||
B2: Payload,
|
||||
{}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed<T> {}
|
||||
}
|
||||
|
||||
|
||||
/// 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>| async move{
|
||||
/// 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: Future,
|
||||
{
|
||||
ServiceFn {
|
||||
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, E> tower_service::Service<crate::Request<ReqBody>> for ServiceFn<F, ReqBody>
|
||||
where
|
||||
F: FnMut(Request<ReqBody>) -> Ret,
|
||||
ReqBody: Payload,
|
||||
Ret: Future<Output=Result<Response<ResBody>, E>>,
|
||||
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||
ResBody: Payload,
|
||||
{
|
||||
type Response = crate::Response<ResBody>;
|
||||
type Error = E;
|
||||
type Future = Ret;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
|
||||
(self.f)(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, R> fmt::Debug for ServiceFn<F, R> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("impl Service")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
70
src/service/util.rs
Normal file
70
src/service/util.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::body::Payload;
|
||||
use crate::common::{Future, Poll, task};
|
||||
use crate::{Request, Response};
|
||||
|
||||
/// Create a `Service` from a function.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use hyper::{Body, Request, Response, Version};
|
||||
/// use hyper::service::service_fn;
|
||||
///
|
||||
/// let service = service_fn(|req: Request<Body>| async move {
|
||||
/// 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: Future,
|
||||
{
|
||||
ServiceFn {
|
||||
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, E> tower_service::Service<crate::Request<ReqBody>> for ServiceFn<F, ReqBody>
|
||||
where
|
||||
F: FnMut(Request<ReqBody>) -> Ret,
|
||||
ReqBody: Payload,
|
||||
Ret: Future<Output=Result<Response<ResBody>, E>>,
|
||||
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||
ResBody: Payload,
|
||||
{
|
||||
type Response = crate::Response<ResBody>;
|
||||
type Error = E;
|
||||
type Future = Ret;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
|
||||
(self.f)(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, R> fmt::Debug for ServiceFn<F, R> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("impl Service")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user