3

In hyper 0.12.33, how do I implement hyper::service::Service for a struct ?

I have tried the following but it is not sufficient as it seems that in 0.12 the Future trait is not provided automatically anymore for a struct that implements Service:

use futures::future::Future;
use hyper::{Body, Request, Response};

struct MyStruct;

impl MyStruct {
    pub fn new() -> Self {
        MyStruct
    }
}

impl hyper::service::Service for MyStruct {
    type ReqBody = Body;
    type ResBody = Body;
    type Error = hyper::Error;
    type Future = Box<Future<Item = Response<Body>, Error = hyper::Error>>;

    fn call(&mut self, req: Request<Body>) -> Self::Future {
        unimplemented!()
    }
}

fn main() {
    let addr = "0.0.0.0:8080".parse().unwrap();
    let server = hyper::Server::bind(&addr)
        .serve(|| MyStruct::new())
        .map_err(|e| eprintln!("server error: {}", e));

    hyper::rt::run(server);
}

gives me the build error message:

Standard Error

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `MyStruct: futures::future::Future` is not satisfied
  --> src/main.rs:26:10
   |
26 |         .serve(|| MyStruct::new())
   |          ^^^^^ the trait `futures::future::Future` is not implemented for `MyStruct`
   |
   = note: required because of the requirements on the impl of `hyper::service::make_service::MakeServiceRef<hyper::server::tcp::addr_stream::AddrStream>` for `[closure@src/main.rs:26:16: 26:34]`

error[E0599]: no method named `map_err` found for type `hyper::server::Server<hyper::server::tcp::AddrIncoming, [closure@src/main.rs:26:16: 26:34]>` in the current scope
  --> src/main.rs:27:10
   |
27 |         .map_err(|e| eprintln!("server error: {}", e));
   |          ^^^^^^^
   |
   = note: the method `map_err` exists but the following trait bounds were not satisfied:
           `&mut hyper::server::Server<hyper::server::tcp::AddrIncoming, [closure@src/main.rs:26:16: 26:34]> : futures::future::Future`
           `hyper::server::Server<hyper::server::tcp::AddrIncoming, [closure@src/main.rs:26:16: 26:34]> : futures::future::Future`
hellow
  • 12,430
  • 7
  • 56
  • 79
Nicolas Marshall
  • 4,186
  • 9
  • 36
  • 54
  • Please try to create a [mcve] next time, e.g. it is not necessary to define the `call` body. Just use `unimplemented!()`. Also make sure "it compiles", e.g. there are no other errors (in your case it was that `Future` was imported twice). Also please use `rustfmt` to make sure, that your code is properly formatted. Last but not least, include the **full** error message, not just a snippet. But apart from that, well done ;) – hellow Aug 06 '19 at 07:49
  • 1
    Will pay more attention next time. I noticed the double import and was editing but you were quicker. Thanks for the edit – Nicolas Marshall Aug 06 '19 at 07:52
  • 1
    It seems that [all examples](https://github.com/hyperium/hyper/tree/master/examples) are now using `async fn` and not a struct. Also [the doc](https://docs.rs/hyper/0.12.33/hyper/service/index.html) states: *"While it's possible to implement Service for a type manually, the helpers service_fn and service_fn_ok should be sufficient for most cases."* and *"Resources that need to be shared by all Services can be put into a MakeService, and then passed to individual Services when make_service is called."* – hellow Aug 06 '19 at 07:59

1 Answers1

1

This example gives one way. It compiles and runs with v0.14.12

#![deny(warnings)]

use std::task::{Context, Poll};

use futures_util::future;
use hyper::service::Service;
use hyper::{Body, Request, Response, Server};

const ROOT: &str = "/";

#[derive(Debug)]
pub struct Svc;

impl Service<Request<Body>> for Svc {
    type Response = Response<Body>;
    type Error = hyper::Error;
    type Future = future::Ready<Result<Self::Response, Self::Error>>;

    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Ok(()).into()
    }

    fn call(&mut self, req: Request<Body>) -> Self::Future {
        let rsp = Response::builder();

        let uri = req.uri();
        if uri.path() != ROOT {
            let body = Body::from(Vec::new());
            let rsp = rsp.status(404).body(body).unwrap();
            return future::ok(rsp);
        }

        let body = Body::from(Vec::from(&b"heyo!"[..]));
        let rsp = rsp.status(200).body(body).unwrap();
        future::ok(rsp)
    }
}

pub struct MakeSvc;

impl<T> Service<T> for MakeSvc {
    type Response = Svc;
    type Error = std::io::Error;
    type Future = future::Ready<Result<Self::Response, Self::Error>>;

    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Ok(()).into()
    }

    fn call(&mut self, _: T) -> Self::Future {
        future::ok(Svc)
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // pretty_env_logger::init();

    let addr = "127.0.0.1:1337".parse().unwrap();

    let server = Server::bind(&addr).serve(MakeSvc);

    println!("Listening on http://{}", addr);

    server.await?;

    Ok(())
}

The indirection (MakeSvc -> Src) appears to follow from the architecture of Hyper, as described in this issue:

There's two steps involved here, and both make use of Service:

  1. The MakeSvc is a Service that creates Svcs for each connection.
  2. The Svc is a Service to handle requests on a single connection.
Rich Apodaca
  • 28,316
  • 16
  • 103
  • 129