Shared data in hyper

I'm not very experienced with async and I'm trying to understand passing of shared data to a handler in hyper.

After searching for examples and some fiddling I got the following to work:

use std::{
    convert::Infallible,
    net::SocketAddr,
    sync::{Arc, Mutex},
};

use hyper::{
    header,
    service::{make_service_fn, service_fn},
    Body, Method, Request, Response, Server,
};

// I'm only using a usize here for demonstration purposes.
async fn handle(data: Arc<Mutex<usize>>, req: Request<Body>) -> Result<Response<Body>, Infallible> {
    let data = *data.lock().unwrap();
    Ok(Response::new(data.to_string().into()))
}

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

I'm trying to understand why two separate calls to clone are needed and if there is a way to simplify this example. Also, when running, the second clone gets called on each request as I would expect, but the first clone gets called on some requests and not others, seemingly at random.

1 Like

I don't think two clones are required. Moreover, the first clone to data can be moved outside the closure, and renamed to something else. Look at this comment.

I've tried moving it outside, but can't make it work that way.

Why? What happens? What is the error message?

Two clones are needed here. The reason is that you can reuse the same connection to make multiple HTTP requests. The first clone happens once per new connection, the second clone happens once per request on the same connection.

2 Likes

As Alice said, it seems they are both necessary.

When I tried, if I had data captured directly it would complain about not being able to move into a FnMut, while trying to pass a reference would complain about data not living long enough.

Yep, I was wrong.

Thanks.

That makes sense I guess. Intuitvely it still feels like only one clone should be needed but I guess that's the way Hyper is architected.

It was weird though that refreshing my browser would sometimes trigger a new connection and sometimes not with no discernable pattern.

I was able to remove the move modifiers from a couple places and it still works. I understand how move affects captured variables, but the combination of things with async thrown in is too much for me to process, so I resorted to randomly applying and removing modifiers.

That line of code has so much going on: There's a closure used as an argument to service_fn, which is called in an async block, which is part of another closure, which is an argument to make_service_fn.

I have a headache. :upside_down_face:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.