From 55ba000746fa150dbb36a0f28a8d7e93885fff9a Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 15 Jun 2020 12:01:04 -0700 Subject: [PATCH] docs(service): add example of `impl Service` (#2209) Add `examples/service_struct_impl.rs`, which provides an up-to-date example of implementing MakeService and Service on custom types. The `Svc` struct has a Counter, which demonstrates how to instantiate shared resources in the `MakeSvc` and pass the resource to the services. Updates the `examples/README.md` and the doc in `src/service/mod.rs`. Closes #1691 --- examples/README.md | 2 + examples/service_struct_impl.rs | 76 +++++++++++++++++++++++++++++++++ src/service/mod.rs | 5 ++- 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 examples/service_struct_impl.rs diff --git a/examples/README.md b/examples/README.md index 012e2bb0..7071bd7b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -41,6 +41,8 @@ pretty_env_logger = "0.4" * [`send_file`](send_file.rs) - A server that sends back content of files using tokio-util to read the files asynchronously. +* [`service_struct_impl`](service_struct_impl.rs) - A struct that manually implements the `Service` trait and uses a shared counter across requests. + * [`single_threaded`](single_threaded.rs) - A server only running on 1 thread, so it can make use of `!Send` app state (like an `Rc` counter). * [`state`](state.rs) - A webserver showing basic state sharing among requests. A counter is shared, incremented for every request, and every response is sent the last count. diff --git a/examples/service_struct_impl.rs b/examples/service_struct_impl.rs new file mode 100644 index 00000000..8bc3f10f --- /dev/null +++ b/examples/service_struct_impl.rs @@ -0,0 +1,76 @@ +use hyper::service::Service; +use hyper::{Body, Request, Response, Server}; + +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +type Counter = i32; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let addr = ([127, 0, 0, 1], 3000).into(); + + let server = Server::bind(&addr).serve(MakeSvc { counter: 81818 }); + println!("Listening on http://{}", addr); + + server.await?; + Ok(()) +} + +struct Svc { + counter: Counter, +} + +impl Service> for Svc { + type Response = Response; + type Error = hyper::Error; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: Request) -> Self::Future { + fn mk_response(s: String) -> Result, hyper::Error> { + Ok(Response::builder().body(Body::from(s)).unwrap()) + } + + let res = match req.uri().path() { + "/" => mk_response(format!("home! counter = {:?}", self.counter)), + "/posts" => mk_response(format!("posts, of course! counter = {:?}", self.counter)), + "/authors" => mk_response(format!( + "authors extraordinare! counter = {:?}", + self.counter + )), + // Return the 404 Not Found for other routes, and don't increment counter. + _ => return Box::pin(async { mk_response("oh no! not found".into()) }), + }; + + if req.uri().path() != "/favicon.ico" { + self.counter += 1; + } + + Box::pin(async { res }) + } +} + +struct MakeSvc { + counter: Counter, +} + +impl Service for MakeSvc { + type Response = Svc; + type Error = hyper::Error; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _: T) -> Self::Future { + let counter = self.counter.clone(); + let fut = async move { Ok(Svc { counter }) }; + Box::pin(fut) + } +} diff --git a/src/service/mod.rs b/src/service/mod.rs index bb5a7740..6310a10f 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -21,8 +21,9 @@ //! 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 helper -//! [`service_fn`](service_fn) should be sufficient for most cases. +//! 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 //!