diff options
Diffstat (limited to 'third_party/rust/hyper/src/service')
-rw-r--r-- | third_party/rust/hyper/src/service/http.rs | 58 | ||||
-rw-r--r-- | third_party/rust/hyper/src/service/make.rs | 187 | ||||
-rw-r--r-- | third_party/rust/hyper/src/service/mod.rs | 55 | ||||
-rw-r--r-- | third_party/rust/hyper/src/service/oneshot.rs | 73 | ||||
-rw-r--r-- | third_party/rust/hyper/src/service/util.rs | 84 |
5 files changed, 457 insertions, 0 deletions
diff --git a/third_party/rust/hyper/src/service/http.rs b/third_party/rust/hyper/src/service/http.rs new file mode 100644 index 0000000000..81a20c80b5 --- /dev/null +++ b/third_party/rust/hyper/src/service/http.rs @@ -0,0 +1,58 @@ +use std::error::Error as StdError; + +use crate::body::HttpBody; +use crate::common::{task, Future, Poll}; +use crate::{Request, Response}; + +/// An asynchronous function from `Request` to `Response`. +pub trait HttpService<ReqBody>: sealed::Sealed<ReqBody> { + /// The `HttpBody` body of the `http::Response`. + type ResBody: HttpBody; + + /// 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: HttpBody, + 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: HttpBody, +{ +} + +mod sealed { + pub trait Sealed<T> {} +} diff --git a/third_party/rust/hyper/src/service/make.rs b/third_party/rust/hyper/src/service/make.rs new file mode 100644 index 0000000000..63e6f298f1 --- /dev/null +++ b/third_party/rust/hyper/src/service/make.rs @@ -0,0 +1,187 @@ +use std::error::Error as StdError; +use std::fmt; + +use tokio::io::{AsyncRead, AsyncWrite}; + +use super::{HttpService, Service}; +use crate::body::HttpBody; +use crate::common::{task, Future, Poll}; + +// 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>>; + + fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>; + fn make_connection(&mut self, target: Target) -> Self::Future; +} + +impl<S, Target> self::sealed::Sealed<(Target,)> for S where S: Service<Target> {} + +impl<S, Target> MakeConnection<Target> for S +where + S: Service<Target>, + S::Response: AsyncRead + AsyncWrite, +{ + type Connection = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> { + Service::poll_ready(self, cx) + } + + fn make_connection(&mut self, target: Target) -> Self::Future { + Service::call(self, target) + } +} + +// Just a sort-of "trait alias" of `MakeService`, not to be implemented +// by anyone, only used as bounds. +pub trait MakeServiceRef<Target, ReqBody>: self::sealed::Sealed<(Target, ReqBody)> { + type ResBody: HttpBody; + type Error: Into<Box<dyn StdError + Send + Sync>>; + type Service: HttpService<ReqBody, ResBody = Self::ResBody, Error = Self::Error>; + type MakeError: Into<Box<dyn StdError + Send + Sync>>; + type Future: Future<Output = Result<Self::Service, Self::MakeError>>; + + // Acting like a #[non_exhaustive] for associated types of this trait. + // + // Basically, no one outside of hyper should be able to set this type + // or declare bounds on it, so it should prevent people from creating + // trait objects or otherwise writing code that requires using *all* + // of the associated types. + // + // Why? So we can add new associated types to this alias in the future, + // if necessary. + type __DontNameMe: self::sealed::CantImpl; + + fn poll_ready_ref(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::MakeError>>; + + fn make_service_ref(&mut self, target: &Target) -> Self::Future; +} + +impl<T, Target, E, ME, S, F, IB, OB> MakeServiceRef<Target, IB> for T +where + 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: HttpService<IB, ResBody = OB, Error = E>, + F: Future<Output = Result<S, ME>>, + IB: HttpBody, + OB: HttpBody, +{ + type Error = E; + type Service = S; + type ResBody = OB; + type MakeError = ME; + type Future = F; + + type __DontNameMe = self::sealed::CantName; + + fn poll_ready_ref(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::MakeError>> { + self.poll_ready(cx) + } + + fn make_service_ref(&mut self, target: &Target) -> Self::Future { + self.call(target) + } +} + +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: HttpBody, + B2: HttpBody, +{ +} + +/// Create a `MakeService` from a function. +/// +/// # Example +/// +/// ``` +/// # #[cfg(feature = "runtime")] +/// # 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}; +/// +/// let addr = ([127, 0, 0, 1], 3000).into(); +/// +/// let make_svc = make_service_fn(|socket: &AddrStream| { +/// let remote_addr = socket.remote_addr(); +/// async move { +/// Ok::<_, Infallible>(service_fn(move |_: Request<Body>| async move { +/// Ok::<_, Infallible>( +/// Response::new(Body::from(format!("Hello, {}!", remote_addr))) +/// ) +/// })) +/// } +/// }); +/// +/// // Then bind and serve... +/// let server = Server::bind(&addr) +/// .serve(make_svc); +/// +/// // Finally, spawn `server` onto an Executor... +/// if let Err(e) = server.await { +/// eprintln!("server error: {}", e); +/// } +/// # } +/// # fn main() {} +/// ``` +pub fn make_service_fn<F, Target, Ret>(f: F) -> MakeServiceFn<F> +where + F: FnMut(&Target) -> Ret, + Ret: Future, +{ + MakeServiceFn { f } +} + +/// `MakeService` returned from [`make_service_fn`] +#[derive(Clone, Copy)] +pub struct MakeServiceFn<F> { + f: 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>>, + MkErr: Into<Box<dyn StdError + Send + Sync>>, +{ + type Error = MkErr; + type Response = Svc; + type Future = Ret; + + fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, target: &'t Target) -> Self::Future { + (self.f)(target) + } +} + +impl<F> fmt::Debug for MakeServiceFn<F> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MakeServiceFn").finish() + } +} + +mod sealed { + pub trait Sealed<X> {} + + #[allow(unreachable_pub)] // This is intentional. + pub trait CantImpl {} + + #[allow(missing_debug_implementations)] + pub enum CantName {} + + impl CantImpl for CantName {} +} diff --git a/third_party/rust/hyper/src/service/mod.rs b/third_party/rust/hyper/src/service/mod.rs new file mode 100644 index 0000000000..22f850ca47 --- /dev/null +++ b/third_party/rust/hyper/src/service/mod.rs @@ -0,0 +1,55 @@ +//! Asynchronous Services +//! +//! 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>`. +//! +//! 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: +//! +//! - `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 +//! connection will receive. +//! +//! The helper [`service_fn`](service_fn) should be sufficient for most cases, but +//! if you need to implement `Service` for a type manually, you can follow the example +//! in `service_struct_impl.rs`. +//! +//! # MakeService +//! +//! Since a `Service` is bound to a single connection, a [`Server`](crate::Server) +//! needs a way to make them as it accepts connections. This is what a +//! `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 `call` +//! is called. + +pub use tower_service::Service; + +mod http; +mod make; +#[cfg(all(any(feature = "http1", feature = "http2"), feature = "client"))] +mod oneshot; +mod util; + +pub(super) use self::http::HttpService; +#[cfg(all(any(feature = "http1", feature = "http2"), feature = "client"))] +pub(super) use self::make::MakeConnection; +#[cfg(all(any(feature = "http1", feature = "http2"), feature = "server"))] +pub(super) use self::make::MakeServiceRef; +#[cfg(all(any(feature = "http1", feature = "http2"), feature = "client"))] +pub(super) use self::oneshot::{oneshot, Oneshot}; + +pub use self::make::make_service_fn; +pub use self::util::service_fn; diff --git a/third_party/rust/hyper/src/service/oneshot.rs b/third_party/rust/hyper/src/service/oneshot.rs new file mode 100644 index 0000000000..2697af8f4c --- /dev/null +++ b/third_party/rust/hyper/src/service/oneshot.rs @@ -0,0 +1,73 @@ +// TODO: Eventually to be replaced with tower_util::Oneshot. + +use pin_project_lite::pin_project; +use tower_service::Service; + +use crate::common::{task, Future, Pin, Poll}; + +pub(crate) fn oneshot<S, Req>(svc: S, req: Req) -> Oneshot<S, Req> +where + S: Service<Req>, +{ + Oneshot { + state: State::NotReady { svc, req }, + } +} + +pin_project! { + // A `Future` consuming a `Service` and request, waiting until the `Service` + // is ready, and then calling `Service::call` with the request, and + // waiting for that `Future`. + #[allow(missing_debug_implementations)] + pub struct Oneshot<S: Service<Req>, Req> { + #[pin] + state: State<S, Req>, + } +} + +pin_project! { + #[project = StateProj] + #[project_replace = StateProjOwn] + enum State<S: Service<Req>, Req> { + NotReady { + svc: S, + req: Req, + }, + Called { + #[pin] + fut: S::Future, + }, + Tmp, + } +} + +impl<S, Req> Future for Oneshot<S, Req> +where + S: Service<Req>, +{ + type Output = Result<S::Response, S::Error>; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { + let mut me = self.project(); + + loop { + match me.state.as_mut().project() { + StateProj::NotReady { ref mut svc, .. } => { + ready!(svc.poll_ready(cx))?; + // fallthrough out of the match's borrow + } + StateProj::Called { fut } => { + return fut.poll(cx); + } + StateProj::Tmp => unreachable!(), + } + + match me.state.as_mut().project_replace(State::Tmp) { + StateProjOwn::NotReady { mut svc, req } => { + me.state.set(State::Called { fut: svc.call(req) }); + } + _ => unreachable!(), + } + } + } +} diff --git a/third_party/rust/hyper/src/service/util.rs b/third_party/rust/hyper/src/service/util.rs new file mode 100644 index 0000000000..7cba1206f1 --- /dev/null +++ b/third_party/rust/hyper/src/service/util.rs @@ -0,0 +1,84 @@ +use std::error::Error as StdError; +use std::fmt; +use std::marker::PhantomData; + +use crate::body::HttpBody; +use crate::common::{task, Future, Poll}; +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, + } +} + +/// Service returned by [`service_fn`] +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: HttpBody, + Ret: Future<Output = Result<Response<ResBody>, E>>, + E: Into<Box<dyn StdError + Send + Sync>>, + ResBody: HttpBody, +{ + 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() + } +} + +impl<F, R> Clone for ServiceFn<F, R> +where + F: Clone, +{ + fn clone(&self) -> Self { + ServiceFn { + f: self.f.clone(), + _req: PhantomData, + } + } +} + +impl<F, R> Copy for ServiceFn<F, R> where F: Copy {} |