Passing additional state to rust `hyper::service::service_fn`
Asked Answered
P

1

8

I am having trouble passing additional state to my service function, but I can't sort out the lifetimes in the closures. None of the tutorials seem to address it:

https://hyper.rs/

https://docs.rs/hyper/0.13.4/hyper/server/index.html

https://docs.rs/hyper/0.13.4/hyper/service/fn.make_service_fn.html

Relevant code:

#[tokio::main]                                                                       
async fn main() {                                                               
    let addr = ([127, 0, 0, 1], 8080).into();                                   
    let db = Arc::new(Mutex::new(Slab::new()));                                 
    let server = Server::bind(&addr).serve(make_service_fn(|_conn| async {      
        let db = db.clone();                                                         
        Ok::<_, Infallible>(service_fn(move |req| serve_req(req, &db)))         
    }));                                                                             
    if let Err(e) = server.await {                                                   
        eprintln!("server error: {}", e)                                             
    }                                                                                
}

Errors:

   Compiling hyper-microservice v0.1.0 (/data/repos/rust/hyper-microservice)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
   --> src/main.rs:121:66
    |
121 |         Ok::<_, Infallible>(service_fn(move |req| serve_req(req, &db)))
    |                                                                  ^^^
    |
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the body at 121:40...
   --> src/main.rs:121:40
    |
121 |         Ok::<_, Infallible>(service_fn(move |req| serve_req(req, &db)))
    |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `db`
   --> src/main.rs:121:66
    |
121 |         Ok::<_, Infallible>(service_fn(move |req| serve_req(req, &db)))
    |                                                                  ^^^
note: but, the lifetime must be valid for the expression at 121:29...
   --> src/main.rs:121:29
    |
121 |         Ok::<_, Infallible>(service_fn(move |req| serve_req(req, &db)))
    |                             ^^^^^^^^^^

My understanding is that make_service_fn is responsible for returning a ServiceFn that can be used to serve requests for a given connection, and it creates a new ServiceFn for every connection. Now, service_fn takes a function/closure that that takes a request and returns a Future that returns a response (or error). And, as I understand it, the ServiceFn may be executed multiple times per connection. So roughly, each client gets it's own thread, which executes an async service function for each request on that connection (so the same client could make multiple requests on the same connection and be served concurrently within the same thread).

I believe that what might be happening is that the compiler thinks that one of the closures might outlive db?

Preece answered 2/4, 2020 at 16:31 Comment(1)
|_conn| async => move |_conn| async?Legitimize
E
4

The correct code should be:

#[tokio::main]
async fn main() {
    let addr = ([127, 0, 0, 1], 8080).into();
    let db = Arc::new(Mutex::new(Slab::new()));
    let server = Server::bind(&addr).serve(make_service_fn(move |_conn| {
        let db = db.clone();
        async move { Ok::<_, Infallible>(service_fn(move |req| serve_req(req, db.clone()))) }
    }));

    if let Err(e) = server.await {
        eprintln!("server error: {}", e)
    }
}
Ecumenicist answered 27/4, 2021 at 21:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.