diff options
Diffstat (limited to 'third_party/rust/tower-service')
-rw-r--r-- | third_party/rust/tower-service/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/tower-service/CHANGELOG.md | 63 | ||||
-rw-r--r-- | third_party/rust/tower-service/Cargo.toml | 46 | ||||
-rw-r--r-- | third_party/rust/tower-service/LICENSE | 25 | ||||
-rw-r--r-- | third_party/rust/tower-service/README.md | 56 | ||||
-rw-r--r-- | third_party/rust/tower-service/src/lib.rs | 389 |
6 files changed, 580 insertions, 0 deletions
diff --git a/third_party/rust/tower-service/.cargo-checksum.json b/third_party/rust/tower-service/.cargo-checksum.json new file mode 100644 index 0000000000..a6a7c44486 --- /dev/null +++ b/third_party/rust/tower-service/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"961cd98312a75f437e0dcd51eeb3bb61f0bbe778d5fb9203ab8a287bd9cfc84b","Cargo.toml":"81b3940c5bd437f362f4c52d442e49750263eae37b3188202c475340b7255f36","LICENSE":"4249c8e6c5ebb85f97c77e6457c6fafc1066406eb8f1ef61e796fbdc5ff18482","README.md":"da9cb4815bf06e0991df6bf4e9f46272e26115dcc4e2c8f9ffc05e11d486179e","src/lib.rs":"0acd9a6540ea50c0c60c212ba7ef89a7dd7542e99cc6854db2fdd1d5db63a715"},"package":"b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"}
\ No newline at end of file diff --git a/third_party/rust/tower-service/CHANGELOG.md b/third_party/rust/tower-service/CHANGELOG.md new file mode 100644 index 0000000000..8bc4f6523a --- /dev/null +++ b/third_party/rust/tower-service/CHANGELOG.md @@ -0,0 +1,63 @@ +# Unreleased + +- None + +# 0.3.2 (June 17, 2022) + +## Added + +- **docs**: Clarify subtlety around cloning and readiness in the `Service` docs + ([#548]) +- **docs**: Clarify details around shared resource consumption in `poll_ready()` + ([#662]) + + +[#548]: https://github.com/tower-rs/tower/pull/548 +[#662]: https://github.com/tower-rs/tower/pull/662 + + +# 0.3.1 (November 29, 2019) + +- Improve example in `Service` docs. ([#510]) + +[#510]: https://github.com/tower-rs/tower/pull/510 + +# 0.3.0 (November 29, 2019) + +- Update to `futures 0.3`. +- Update documentation for `std::future::Future`. + +# 0.3.0-alpha.2 (September 30, 2019) + +- Documentation fixes. + +# 0.3.0-alpha.1 (Aug 20, 2019) + +* Switch to `std::future::Future` + +# 0.2.0 (Dec 12, 2018) + +* Change `Service`'s `Request` associated type to be a generic instead. + * Before: + + ```rust + impl Service for Client { + type Request = HttpRequest; + type Response = HttpResponse; + // ... + } + ``` + * After: + + ```rust + impl Service<HttpRequest> for Client { + type Response = HttpResponse; + // ... + } + ``` +* Remove `NewService`, use `tower_util::MakeService` instead. +* Remove `Service::ready` and `Ready`, use `tower_util::ServiceExt` instead. + +# 0.1.0 (Aug 9, 2018) + +* Initial release diff --git a/third_party/rust/tower-service/Cargo.toml b/third_party/rust/tower-service/Cargo.toml new file mode 100644 index 0000000000..b023dab5a4 --- /dev/null +++ b/third_party/rust/tower-service/Cargo.toml @@ -0,0 +1,46 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "tower-service" +version = "0.3.2" +authors = ["Tower Maintainers <team@tower-rs.com>"] +description = """ +Trait representing an asynchronous, request / response based, client or server. +""" +homepage = "https://github.com/tower-rs/tower" +documentation = "https://docs.rs/tower-service/0.3.2" +readme = "README.md" +categories = [ + "asynchronous", + "network-programming", +] +license = "MIT" +repository = "https://github.com/tower-rs/tower" + +[dependencies] + +[dev-dependencies.futures] +version = "0.3" + +[dev-dependencies.http] +version = "0.2" + +[dev-dependencies.tokio] +version = "1" +features = [ + "macros", + "time", +] + +[dev-dependencies.tower-layer] +version = "0.3" diff --git a/third_party/rust/tower-service/LICENSE b/third_party/rust/tower-service/LICENSE new file mode 100644 index 0000000000..b980cacc77 --- /dev/null +++ b/third_party/rust/tower-service/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2019 Tower Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/tower-service/README.md b/third_party/rust/tower-service/README.md new file mode 100644 index 0000000000..3f2766dbcb --- /dev/null +++ b/third_party/rust/tower-service/README.md @@ -0,0 +1,56 @@ +# Tower Service + +The foundational `Service` trait that [Tower] is based on. + +[![Crates.io][crates-badge]][crates-url] +[![Documentation][docs-badge]][docs-url] +[![Documentation (master)][docs-master-badge]][docs-master-url] +[![MIT licensed][mit-badge]][mit-url] +[![Build Status][actions-badge]][actions-url] +[![Discord chat][discord-badge]][discord-url] + +[crates-badge]: https://img.shields.io/crates/v/tower-service.svg +[crates-url]: https://crates.io/crates/tower-service +[docs-badge]: https://docs.rs/tower-service/badge.svg +[docs-url]: https://docs.rs/tower-service +[docs-master-badge]: https://img.shields.io/badge/docs-master-blue +[docs-master-url]: https://tower-rs.github.io/tower/tower_service +[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg +[mit-url]: LICENSE +[actions-badge]: https://github.com/tower-rs/tower/workflows/CI/badge.svg +[actions-url]:https://github.com/tower-rs/tower/actions?query=workflow%3ACI +[discord-badge]: https://img.shields.io/discord/500028886025895936?logo=discord&label=discord&logoColor=white +[discord-url]: https://discord.gg/EeF3cQw + +## Overview + +The [`Service`] trait provides the foundation upon which [Tower] is built. It is a +simple, but powerful trait. At its heart, `Service` is just an asynchronous +function of request to response. + +``` +async fn(Request) -> Result<Response, Error> +``` + +Implementations of `Service` take a request, the type of which varies per +protocol, and returns a future representing the eventual completion or failure +of the response. + +Services are used to represent both clients and servers. An *instance* of +`Service` is used through a client; a server *implements* `Service`. + +By using standardizing the interface, middleware can be created. Middleware +*implement* `Service` by passing the request to another `Service`. The +middleware may take actions such as modify the request. + +[`Service`]: https://docs.rs/tower-service/latest/tower_service/trait.Service.html +[Tower]: https://crates.io/crates/tower +## License + +This project is licensed under the [MIT license](LICENSE). + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in Tower by you, shall be licensed as MIT, without any additional +terms or conditions. diff --git a/third_party/rust/tower-service/src/lib.rs b/third_party/rust/tower-service/src/lib.rs new file mode 100644 index 0000000000..a6ef0ecec8 --- /dev/null +++ b/third_party/rust/tower-service/src/lib.rs @@ -0,0 +1,389 @@ +#![warn( + missing_debug_implementations, + missing_docs, + rust_2018_idioms, + unreachable_pub +)] +#![forbid(unsafe_code)] +// `rustdoc::broken_intra_doc_links` is checked on CI + +//! Definition of the core `Service` trait to Tower +//! +//! The [`Service`] trait provides the necessary abstractions for defining +//! request / response clients and servers. It is simple but powerful and is +//! used as the foundation for the rest of Tower. + +use std::future::Future; +use std::task::{Context, Poll}; + +/// An asynchronous function from a `Request` to a `Response`. +/// +/// The `Service` trait is a simplified interface making it easy to write +/// network applications in a modular and reusable way, decoupled from the +/// underlying protocol. It is one of Tower's fundamental abstractions. +/// +/// # Functional +/// +/// A `Service` is a function of a `Request`. It immediately returns a +/// `Future` representing the eventual completion of processing the +/// request. The actual request processing may happen at any time in the +/// future, on any thread or executor. The processing may depend on calling +/// other services. At some point in the future, the processing will complete, +/// and the `Future` will resolve to a response or error. +/// +/// At a high level, the `Service::call` function represents an RPC request. The +/// `Service` value can be a server or a client. +/// +/// # Server +/// +/// An RPC server *implements* the `Service` trait. Requests received by the +/// server over the network are deserialized and then passed as an argument to the +/// server value. The returned response is sent back over the network. +/// +/// As an example, here is how an HTTP request is processed by a server: +/// +/// ```rust +/// # use std::pin::Pin; +/// # use std::task::{Poll, Context}; +/// # use std::future::Future; +/// # use tower_service::Service; +/// use http::{Request, Response, StatusCode}; +/// +/// struct HelloWorld; +/// +/// impl Service<Request<Vec<u8>>> for HelloWorld { +/// type Response = Response<Vec<u8>>; +/// type Error = http::Error; +/// type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>; +/// +/// fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { +/// Poll::Ready(Ok(())) +/// } +/// +/// fn call(&mut self, req: Request<Vec<u8>>) -> Self::Future { +/// // create the body +/// let body: Vec<u8> = "hello, world!\n" +/// .as_bytes() +/// .to_owned(); +/// // Create the HTTP response +/// let resp = Response::builder() +/// .status(StatusCode::OK) +/// .body(body) +/// .expect("Unable to create `http::Response`"); +/// +/// // create a response in a future. +/// let fut = async { +/// Ok(resp) +/// }; +/// +/// // Return the response as an immediate future +/// Box::pin(fut) +/// } +/// } +/// ``` +/// +/// # Client +/// +/// A client consumes a service by using a `Service` value. The client may +/// issue requests by invoking `call` and passing the request as an argument. +/// It then receives the response by waiting for the returned future. +/// +/// As an example, here is how a Redis request would be issued: +/// +/// ```rust,ignore +/// let client = redis::Client::new() +/// .connect("127.0.0.1:6379".parse().unwrap()) +/// .unwrap(); +/// +/// let resp = client.call(Cmd::set("foo", "this is the value of foo")).await?; +/// +/// // Wait for the future to resolve +/// println!("Redis response: {:?}", resp); +/// ``` +/// +/// # Middleware / Layer +/// +/// More often than not, all the pieces needed for writing robust, scalable +/// network applications are the same no matter the underlying protocol. By +/// unifying the API for both clients and servers in a protocol agnostic way, +/// it is possible to write middleware that provide these pieces in a +/// reusable way. +/// +/// Take timeouts as an example: +/// +/// ```rust +/// use tower_service::Service; +/// use tower_layer::Layer; +/// use futures::FutureExt; +/// use std::future::Future; +/// use std::task::{Context, Poll}; +/// use std::time::Duration; +/// use std::pin::Pin; +/// use std::fmt; +/// use std::error::Error; +/// +/// // Our timeout service, which wraps another service and +/// // adds a timeout to its response future. +/// pub struct Timeout<T> { +/// inner: T, +/// timeout: Duration, +/// } +/// +/// impl<T> Timeout<T> { +/// pub fn new(inner: T, timeout: Duration) -> Timeout<T> { +/// Timeout { +/// inner, +/// timeout +/// } +/// } +/// } +/// +/// // The error returned if processing a request timed out +/// #[derive(Debug)] +/// pub struct Expired; +/// +/// impl fmt::Display for Expired { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "expired") +/// } +/// } +/// +/// impl Error for Expired {} +/// +/// // We can implement `Service` for `Timeout<T>` if `T` is a `Service` +/// impl<T, Request> Service<Request> for Timeout<T> +/// where +/// T: Service<Request>, +/// T::Future: 'static, +/// T::Error: Into<Box<dyn Error + Send + Sync>> + 'static, +/// T::Response: 'static, +/// { +/// // `Timeout` doesn't modify the response type, so we use `T`'s response type +/// type Response = T::Response; +/// // Errors may be either `Expired` if the timeout expired, or the inner service's +/// // `Error` type. Therefore, we return a boxed `dyn Error + Send + Sync` trait object to erase +/// // the error's type. +/// type Error = Box<dyn Error + Send + Sync>; +/// type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>; +/// +/// fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { +/// // Our timeout service is ready if the inner service is ready. +/// // This is how backpressure can be propagated through a tree of nested services. +/// self.inner.poll_ready(cx).map_err(Into::into) +/// } +/// +/// fn call(&mut self, req: Request) -> Self::Future { +/// // Create a future that completes after `self.timeout` +/// let timeout = tokio::time::sleep(self.timeout); +/// +/// // Call the inner service and get a future that resolves to the response +/// let fut = self.inner.call(req); +/// +/// // Wrap those two futures in another future that completes when either one completes +/// // +/// // If the inner service is too slow the `sleep` future will complete first +/// // And an error will be returned and `fut` will be dropped and not polled again +/// // +/// // We have to box the errors so the types match +/// let f = async move { +/// tokio::select! { +/// res = fut => { +/// res.map_err(|err| err.into()) +/// }, +/// _ = timeout => { +/// Err(Box::new(Expired) as Box<dyn Error + Send + Sync>) +/// }, +/// } +/// }; +/// +/// Box::pin(f) +/// } +/// } +/// +/// // A layer for wrapping services in `Timeout` +/// pub struct TimeoutLayer(Duration); +/// +/// impl TimeoutLayer { +/// pub fn new(delay: Duration) -> Self { +/// TimeoutLayer(delay) +/// } +/// } +/// +/// impl<S> Layer<S> for TimeoutLayer { +/// type Service = Timeout<S>; +/// +/// fn layer(&self, service: S) -> Timeout<S> { +/// Timeout::new(service, self.0) +/// } +/// } +/// ``` +/// +/// The above timeout implementation is decoupled from the underlying protocol +/// and is also decoupled from client or server concerns. In other words, the +/// same timeout middleware could be used in either a client or a server. +/// +/// # Backpressure +/// +/// Calling a `Service` which is at capacity (i.e., it is temporarily unable to process a +/// request) should result in an error. The caller is responsible for ensuring +/// that the service is ready to receive the request before calling it. +/// +/// `Service` provides a mechanism by which the caller is able to coordinate +/// readiness. `Service::poll_ready` returns `Ready` if the service expects that +/// it is able to process a request. +/// +/// # Be careful when cloning inner services +/// +/// Services are permitted to panic if `call` is invoked without obtaining `Poll::Ready(Ok(()))` +/// from `poll_ready`. You should therefore be careful when cloning services for example to move +/// them into boxed futures. Even though the original service is ready, the clone might not be. +/// +/// Therefore this kind of code is wrong and might panic: +/// +/// ```rust +/// # use std::pin::Pin; +/// # use std::task::{Poll, Context}; +/// # use std::future::Future; +/// # use tower_service::Service; +/// # +/// struct Wrapper<S> { +/// inner: S, +/// } +/// +/// impl<R, S> Service<R> for Wrapper<S> +/// where +/// S: Service<R> + Clone + 'static, +/// R: 'static, +/// { +/// type Response = S::Response; +/// type Error = S::Error; +/// type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>; +/// +/// fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { +/// Poll::Ready(Ok(())) +/// } +/// +/// fn call(&mut self, req: R) -> Self::Future { +/// let mut inner = self.inner.clone(); +/// Box::pin(async move { +/// // `inner` might not be ready since its a clone +/// inner.call(req).await +/// }) +/// } +/// } +/// ``` +/// +/// You should instead use [`std::mem::replace`] to take the service that was ready: +/// +/// ```rust +/// # use std::pin::Pin; +/// # use std::task::{Poll, Context}; +/// # use std::future::Future; +/// # use tower_service::Service; +/// # +/// struct Wrapper<S> { +/// inner: S, +/// } +/// +/// impl<R, S> Service<R> for Wrapper<S> +/// where +/// S: Service<R> + Clone + 'static, +/// R: 'static, +/// { +/// type Response = S::Response; +/// type Error = S::Error; +/// type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>; +/// +/// fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { +/// Poll::Ready(Ok(())) +/// } +/// +/// fn call(&mut self, req: R) -> Self::Future { +/// let clone = self.inner.clone(); +/// // take the service that was ready +/// let mut inner = std::mem::replace(&mut self.inner, clone); +/// Box::pin(async move { +/// inner.call(req).await +/// }) +/// } +/// } +/// ``` +pub trait Service<Request> { + /// Responses given by the service. + type Response; + + /// Errors produced by the service. + type Error; + + /// The future response value. + type Future: Future<Output = Result<Self::Response, Self::Error>>; + + /// Returns `Poll::Ready(Ok(()))` when the service is able to process requests. + /// + /// If the service is at capacity, then `Poll::Pending` is returned and the task + /// is notified when the service becomes ready again. This function is + /// expected to be called while on a task. Generally, this can be done with + /// a simple `futures::future::poll_fn` call. + /// + /// If `Poll::Ready(Err(_))` is returned, the service is no longer able to service requests + /// and the caller should discard the service instance. + /// + /// Once `poll_ready` returns `Poll::Ready(Ok(()))`, a request may be dispatched to the + /// service using `call`. Until a request is dispatched, repeated calls to + /// `poll_ready` must return either `Poll::Ready(Ok(()))` or `Poll::Ready(Err(_))`. + /// + /// Note that `poll_ready` may reserve shared resources that are consumed in a subsequent + /// invocation of `call`. Thus, it is critical for implementations to not assume that `call` + /// will always be invoked and to ensure that such resources are released if the service is + /// dropped before `call` is invoked or the future returned by `call` is dropped before it + /// is polled. + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>; + + /// Process the request and return the response asynchronously. + /// + /// This function is expected to be callable off task. As such, + /// implementations should take care to not call `poll_ready`. + /// + /// Before dispatching a request, `poll_ready` must be called and return + /// `Poll::Ready(Ok(()))`. + /// + /// # Panics + /// + /// Implementations are permitted to panic if `call` is invoked without + /// obtaining `Poll::Ready(Ok(()))` from `poll_ready`. + fn call(&mut self, req: Request) -> Self::Future; +} + +impl<'a, S, Request> Service<Request> for &'a mut S +where + S: Service<Request> + 'a, +{ + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), S::Error>> { + (**self).poll_ready(cx) + } + + fn call(&mut self, request: Request) -> S::Future { + (**self).call(request) + } +} + +impl<S, Request> Service<Request> for Box<S> +where + S: Service<Request> + ?Sized, +{ + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), S::Error>> { + (**self).poll_ready(cx) + } + + fn call(&mut self, request: Request) -> S::Future { + (**self).call(request) + } +} |