I have the following code snippet:
async fn server(config: crate::Config) {
println!("Building server");
let key = hmac::Key::new(hmac::HMAC_SHA256, config.docusign.hmac_key.as_bytes());
let webhook = warp::path("webhook")
.and(warp::post())
.and(warp::body::content_length_limit(4194304))
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.then(|headers: HeaderMap, bytes: Bytes| async move {
match verify_msg(&key, &headers, &bytes) {
Ok(_) => {
println!("Message is Valid!");
process_msg(bytes).await.into_response()
}
Err(string) => {
println!("{string}");
warp::reply::with_status(warp::reply(), http::StatusCode::UNAUTHORIZED)
.into_response()
}
}
});
warp::serve(webhook)
.tls()
.cert_path("cert/cert.pem")
.key_path("cert/key.pem")
.run(([0, 0, 0, 0], 443))
.await;
println!("Shutting down Server");
}
This gives me an error:
expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
this closure implements `FnOnce`, not `Fn`rustc(E0525)
server.rs(20, 4): the requirement to implement `Fn` derives from here
server.rs(20, 9): this closure implements `FnOnce`, not `Fn`
server.rs(21, 22): closure is `FnOnce` because it moves the variable `key` out of its environment
This makes sense, I am using the key variable and thus moving it out of the environment. What I can't figure out is how can I get this async closure to work without moving the key? I've tried cloning it like this: match verify_msg(&key.clone(), &headers, &bytes)
but it still doesn't work. I guess that makes sense, since the variable is still referenced inside the closure. So, how do I clone the key before it gets moved?
I was able to get it working with .map() and a regular (non-async) closure, but the process_msg() function is async, so I don't think that would work.
Edit: The answer from @t56k got me on the right track, but didn't quite work. Going in the direction of putting async blocks inside of a closure and following the compiler's recommendations eventually got me this:
async fn server(config: crate::Config) {
println!("Building server");
let key = hmac::Key::new(hmac::HMAC_SHA256, config.docusign.hmac_key.as_bytes());
let webhook = warp::path("webhook")
.and(warp::post())
.and(warp::body::content_length_limit(4194304))
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.then(move |headers: HeaderMap, bytes: Bytes| {
let key = key.clone();
async move {
match verify_msg(&key, &headers, &bytes) {
Ok(_) => {
println!("Message is Valid!");
process_msg(bytes).await.into_response()
}
Err(string) => {
println!("{string}");
warp::reply::with_status(warp::reply(), http::StatusCode::UNAUTHORIZED)
.into_response()
}
}
}
});
warp::serve(webhook)
.tls()
.cert_path("cert/cert.pem")
.key_path("cert/key.pem")
.run(([0, 0, 0, 0], 443))
.await;
println!("Shutting down Server");
}
which works perfectly for some reason even though I'm using the move
keyword. I guess i'm only allowed to move key
if it isn't inside of an async
block? In any case, my problem is solved, but if anyone could explain why this works I would gladly accept it.
This is untested for your use case, but you can .clone()
things before the move
to allow them access.
.and_then(|headers: HeaderMap, bytes: Bytes| async {
let key = key.clone();
move {
match verify_msg(key, &headers, &bytes) {
// ...
}
}
});