Rust lifetimes with closures using hyper - rust

Been learning rust and having a problem with lifetime when passing conn to the request_handler. I get an error saying
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/main.rs:33:70
|
33 | let request_handler = |req: Request<Body>| async { request_handler(conn, req).await };
| ^^^^
|
= note: ...the reference is valid for the static lifetime...
but I am not sure how to handle lifetimes with closures and why/how it is static. I have a loose understanding of lifetimes and borrowing but this seems like a more complex case. I also would just not use a closure, but the return type of one of the closures has a type that is not exported by the hyper crate, so i don't know how i would create a fn without being able to declare the return type.
Also I can confirm if i remove passing conn i can get everything to work, but I want to use the conn object in the request_handler.
use hyper::server::conn::AddrStream;
use hyper::service::make_service_fn;
use hyper::Version;
use hyper::{Body, Error, Method, Request, Response, Server};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
mod http_models;
mod utils;
use hyper::service::service_fn;
async fn request_handler(
conn: &'static AddrStream,
req: Request<Body>,
) -> Result<Response<Body>, hyper::Error> {
println!("req: {:?}", req);
if req.method() == Method::CONNECT {
println!("Connect")
}
let res: Response<Body> = Response::builder()
.status(200)
.version(Version::HTTP_11)
.body(Body::empty())
.unwrap();
return Ok(res);
}
#[tokio::main]
async fn main() -> Result<(), Error> {
let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let addr = SocketAddr::new(ip, 1337);
//let client = Client::new();
let make_service = make_service_fn(|conn: &AddrStream| async {
let request_handler = |req: Request<Body>| async { request_handler(conn, req).await };
let service = service_fn(request_handler);
Ok::<_, Error>(service)
});
let server = Server::bind(&addr).serve(make_service);
println!("Listening on http://{}", addr);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
Ok(())
}

The conn argument of the closure passed to make_service_fn only lives as long as the closure body, but the return value of the closure (Ok(service)) references it. The closure must have the type FnMut(&Target) -> impl Future, which means it sadly is not permitted to return a value that references its argument.
The only solution is to copy/clone whatever you need from conn while setting up your request handler, since you cannot keep a reference to it once the closure returns.

Related

Borrowed value does not live long enough when using async/await

I am trying to make two async api call and eventually get error E0597.
Here is a code:
async fn make_request() -> Result<()> {
.........
.........
.........
let mut result = client.get(uri).await?;
let some_key = result.headers().get("some_key");
let next_url = match some_key {
Some(url) => {
let some_result = client.get(Uri::from_static(url.to_str().unwrap())).await?
}
None => println!("....")
};
Ok(())
}
When I run this code the error "borrowed value does not live long enough argument requires that result is borrowed for `'static"
I have created a compile-able example based on your snipped to reproduce the error in the playground, and if you are able to do something like this in your question (for future reference), it usually helps you get more specific answers.
The Request passed into the function has no lifetime guarantees, so this will fail with the error you mentioned:
use http::{Request, Uri};
async fn make_request(result: &Request<()>) -> std::io::Result<()> {
match result.headers().get("some_key") {
// `url` is a reference to the string in the "some_key" header
Some(url) => {
let some_result = Uri::from_static(url.to_str().unwrap());
}
None => println!("....")
};
Ok(())
}
You can add that lifetime requirement, but that probably isn't what you need, and will likely give you the same error message, just in a different place:
async fn make_request_static(result: &'static Request<()>) -> std::io::Result<()> {
match result.headers().get("some_key") {
// because the request is static, so can be `url`
Some(url) => {
let some_result = Uri::from_static(url.to_str().unwrap());
}
None => println!("....")
};
Ok(())
}
Uri implements the FromStr trait, though, so you would be best off using that. There is no longer a lifetime requirement, so it can work with any string you pass in, even one which is currently borrowed:
// need to import the trait to use its methods
use std::str::FromStr;
async fn make_request_3(result: &Request<()>) -> std::io::Result<()> {
match result.headers().get("some_key") {
// because the request is static, so can be `url`
Some(url) => {
let some_result = Uri::from_str(url.to_str().unwrap());
}
None => println!("....")
};
Ok(())
}

Issue passing mutable Arc reference to hyper service_fn handler

I've been trying the following
Relevant imports and code shown
use std::sync::{Arc, Mutex};
use std::thread;
use hyper::rt::{self, Future, Stream};
use hyper::service::service_fn;
use hyper::{Body, Request, Response, Server, StatusCode};
pub struct ChallengeState;
pub struct ChallengeResponse;
type BoxFut<'a> = Box<Future<Item = Response<Body>, Error = hyper::Error> + Send + 'a>;
fn handle_challengeproof<'a>(
req: Request<Body>,
challenge: &Arc<Mutex<ChallengeState>>,
) -> BoxFut<'a> {
let resp = req.into_body().concat2().map(move |body| {
let challenge_lock = challenge.lock().unwrap();
Response::builder()
.status(StatusCode::OK)
.body(Body::from("test"))
.unwrap()
});
Box::new(resp)
}
fn handle<'a>(
req: Request<Body>,
challenge: &Arc<Mutex<ChallengeState>>,
) -> BoxFut<'a> {
handle_challengeproof(req, challenge)
}
pub fn run_listener(
challenge: Arc<Mutex<ChallengeState>>,
) -> thread::JoinHandle<()> {
let addr = ([127, 0, 0, 1], 9999).into();
let listener_service = move || {
let challenge = Arc::clone(&challenge);
service_fn(move |req: Request<Body>| {
handle(req, &challenge)
})
};
let server = Server::bind(&addr)
.serve(listener_service)
.map_err(|_| () );
thread::spawn(move || {
rt::run(server);
})
}
I've been trying to avoid an extra clone of Arc by passing a reference to the handle method but can't seem to get around this. Avoiding the lifetime on handle() got a different error regarding futures asking for static lifetime.
Code updated with only relevant stuff # https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10ea31450e88a122455006760d7fcdd1
The whole point of an Arc is that it counts how many references there are, which happens when it is cloned. Passing around references to an Arc defeats the point.
Instead of passing references, pass the Arc itself. So handle's signature becomes:
fn handle<'a>(
req: Request<Body>,
challenge: Arc<Mutex<ChallengeState>>,
) -> BoxFut<'a>
Passing the Arc by references from the closure isn't possible because you would be referencing something that immediately goes out of scope. Instead, move the Arc into handle:
let listener_service = move || {
service_fn(move |req: Request<Body>| handle(req, challenge))
};

How do I structure a hyper HTTP app to handle shared state? [duplicate]

I'm attempting to learn Rust by implementing a simple in-memory URL shortener with Hyper 0.10. I'm running into an issue that I think is caused by trying to close over a mutable HashMap in my handler:
fn post(mut req: Request, mut res: Response, short_uris: &mut HashMap<&str, &str>) {
let mut body = String::new();
match req.read_to_string(&mut body) {
Ok(_) => {
let key = short_uris.len();
short_uris.insert(&key.to_string(), &body.to_string());
*res.status_mut() = StatusCode::Created;
res.start().unwrap().write(&key.to_string().into_bytes());
},
Err(_) => *res.status_mut() = StatusCode::BadRequest
}
}
fn get(req: Request, mut res: Response, short_uris: &HashMap<&str, &str>) {
match req.uri.clone() {
AbsolutePath(path) => {
match short_uris.get::<str>(&path) {
Some(short_uri) => {
*res.status_mut() = StatusCode::MovedPermanently;
res.headers_mut().set(Location(short_uri.to_string()));
},
None => *res.status_mut() = StatusCode::NotFound
}
},
_ => *res.status_mut() = StatusCode::BadRequest
}
}
fn main() {
let mut short_uris: HashMap<&str, &str> = HashMap::new();
short_uris.insert("/example", "http://www.example.com");
Server::http("0.0.0.0:3001").unwrap().handle(move |req: Request, mut res: Response| {
match req.method {
hyper::Post => post(req, res, &mut short_uris),
hyper::Get => get(req, res, &short_uris),
_ => *res.status_mut() = StatusCode::MethodNotAllowed
}
}).unwrap();
}
src/main.rs:42:40: 42:46 error: the trait bound `for<'r, 'r, 'r> [closure#src/main.rs:42:47: 48:3 short_uris:std::collections::HashMap<&str, &str>]: std::ops::Fn<(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>)>` is not satisfied [E0277]
src/main.rs:42 Server::http("0.0.0.0:3001").unwrap().handle(move |req: Request, mut res: Response| {
Do I need to use an Arc to share the HashMap between threads? If so, what would that look like? Also, I could be totally wrong about the issue. The error message is very cryptic to me.
Please include all the necessary use declarations next time, thanks!
If you're using nightly Rust, the error message is a less cryptic:
expected a closure that implements the Fntrait, but this closure only implements FnMut
That means that Hyper needs the closure to be shared between threads, so the closure needs to use its environment only via immutable or shared methods – so the usage of &mut short_uris is the offender here. To provide shared threadsafe mutability in Rust, you should use Mutex or RwLock.
Please note that you don't need Arc here – Hyper manages the ownership of the closure itself (probably by wrapping the closure in Arc under the hood, or using something like scoped-threads).
There's also second issue with your code – you use HashMap<&str, &str>. &str is a borrowed reference. Each time when you have something borrowed in Rust, you should ask yourself – from where? Here you try to borrow from really short-lived strings – key.to_string() and body.to_string(). It just can't work. Just make your hashmap fully owned – HashMap<String, String>. Here's the version of your code which compiles:
extern crate hyper;
use hyper::server::{Request, Response, Server};
use std::collections::HashMap;
use hyper::status::StatusCode;
use hyper::uri::RequestUri::AbsolutePath;
use hyper::header::Location;
use std::io::prelude::*;
fn post(mut req: Request, mut res: Response, short_uris: &mut HashMap<String, String>) {
let mut body = String::new();
match req.read_to_string(&mut body) {
Ok(_) => {
let key = short_uris.len();
short_uris.insert(key.to_string(), body);
*res.status_mut() = StatusCode::Created;
res.start()
.unwrap()
.write(&key.to_string().into_bytes())
.unwrap();
}
Err(_) => *res.status_mut() = StatusCode::BadRequest,
}
}
fn get(req: Request, mut res: Response, short_uris: &HashMap<String, String>) {
match req.uri {
AbsolutePath(ref path) => match short_uris.get(path) {
Some(short_uri) => {
*res.status_mut() = StatusCode::MovedPermanently;
res.headers_mut().set(Location(short_uri.to_string()));
}
None => *res.status_mut() = StatusCode::NotFound,
},
_ => *res.status_mut() = StatusCode::BadRequest,
}
}
fn main() {
let mut short_uris: HashMap<String, String> = HashMap::new();
short_uris.insert("/example".into(), "http://www.example.com".into());
let short_uris = std::sync::RwLock::new(short_uris);
Server::http("0.0.0.0:3001")
.unwrap()
.handle(move |req: Request, mut res: Response| match req.method {
hyper::Post => post(req, res, &mut short_uris.write().unwrap()),
hyper::Get => get(req, res, &short_uris.read().unwrap()),
_ => *res.status_mut() = StatusCode::MethodNotAllowed,
})
.unwrap();
}
I've also got rid of the unnecessary .clone() in the get function.
Please note that this code, while compiles, is not perfect yet – the RwLock locks should last shorter (get and post should take &RwLock<HashMap<String,String>> as an argument and perform the locking by themselves). The .unwrap() also may be handled in a better way. You can also consider using some lockless concurrent hashmap, there should be some crates for that, but I'm not into the topic, so I won't recommend any.

Why need static variable delivery between method but need not in same one method?

I want to start a Hyper server in a function with port and dao parameters provided by main(), but the function only works after I explicitly indicate the 'static lifetime. This confused me a lot.
extern crate futures;
extern crate hyper;
use futures::future::Future;
use hyper::header::ContentLength;
use hyper::server::{Http, Request, Response, Service};
use std::net::SocketAddr;
trait Dao {}
struct MysqlDao;
impl Dao for MysqlDao {}
struct HelloWorld<'a> {
dao: &'a Dao,
}
const PHRASE: &'static str = "Hello, World!";
impl<'a> Service for HelloWorld<'a> {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, _req: Request) -> Self::Future {
Box::new(futures::future::ok(
Response::new()
.with_header(ContentLength(PHRASE.len() as u64))
.with_body(PHRASE),
))
}
}
fn main() {
let addr = "127.0.0.1:3000".parse().unwrap();
let dao = MysqlDao;
let server = Http::new()
.bind(&addr, move || Ok(HelloWorld { dao: &dao }))
.unwrap();
server.run().unwrap();
}
The Http::new().bind API documententation said it needs a NewService + 'static, so I think the compiler would infer the dao variant is 'static, but when I move the last three statements out of main, it can't infer!
fn main() {
let addr = "127.0.0.1:3000".parse().unwrap();
let dao: MysqlDao = MysqlDao;
web_startup(&addr, &dao);
}
fn web_startup<T: Dao>(addr: &SocketAddr, dao: &T) {
let server = Http::new()
.bind(addr, move || Ok(HelloWorld { dao }))
.unwrap();
server.run().unwrap();
}
I get the error:
error[E0477]: the type `[closure#src/main.rs:44:21: 44:51 dao:&T]` does not fulfill the required lifetime
--> src/main.rs:44:10
|
44 | .bind(addr, move || Ok(HelloWorld { dao }))
| ^^^^
|
= note: type must satisfy the static lifetime
So I fixed it:
fn main() {
let addr = "127.0.0.1:3000".parse().unwrap();
static DAO: MysqlDao = MysqlDao;
web_startup(&addr, &DAO);
}
fn web_startup<T: Dao>(addr: &SocketAddr, dao: &'static T) {
let server = Http::new()
.bind(addr, move || Ok(HelloWorld { dao }))
.unwrap();
server.run().unwrap();
}
I don't understand why I should use the static keyword for static DAO: MysqlDao = MysqlDao; statement but need not before change the code. The compiler couldn't infer it or am I thinking about things incorrectly?
The reason the compiler cannot infer that the only time the web_startup function will be called it's called with a 'static is because that's not guaranteed. What if the function were public and it was called by a third-party module? The compiler would have to tell the end-user to use a 'static on a function that doesn't seem to require one. What if some time in the future eval() is added to Rust (e.g. for a REPL), so that even your private function could be called with unexpected function parameters?
You're asking for an inference that should not happen.

Calling an FnMut callback from another thread

I am writing a Phoenix client library for Rust, taking advantage of the async websocket client from rust-websockets. Right now I am having trouble figuring out how to pass callback functions into the thread that is handling the websocket traffic. I have a simplified struct:
pub struct Socket {
endpoint: String,
connected: Arc<AtomicBool>,
state_change_close: Option<Box<FnMut(String)>>,
}
This struct has a connect function laid out as follows:
pub fn connect(&mut self) -> Result<(), String> {
if self.connected.load(Ordering::Relaxed) {
return Ok(())
}
// Copy endpoint string, otherwise we get an error on thread::spawn
let connection_string = self.endpoint.clone();
let (usr_msg, stdin_ch) = mpsc::channel(0);
let connection_thread = thread::spawn(move || {
// tokio core for running event loop
let mut core = Core::new().unwrap();
let runner = ClientBuilder::new(&connection_string)
.unwrap()
.add_protocol("rust-websocket")
.async_connect_insecure(&core.handle())
.and_then(|(duplex, _)| {
let (sink, stream) = duplex.split();
stream.filter_map(|message| {
println!("Received Message: {:?}", message);
match message {
OwnedMessage::Close(e) => {
// This is the line where I am trying to call the callback
if let Some(ref mut func) = self.state_change_close {
(func)(e.unwrap().reason);
}
Some(OwnedMessage::Close(e))
},
_ => None,
}
})
.select(stdin_ch.map_err(|_| WebSocketError::NoDataAvailable))
.forward(sink)
});
// Start the event loop
core.run(runner).unwrap();
});
self.connected.store(true, Ordering::Relaxed);
return Ok(())
}
When I try to compile this code I get the following error:
error[E0277]: the trait bound `std::ops::FnMut(std::string::String) + 'static: std::marker::Send` is not satisfied
--> src\socket.rs:99:29
|
99 | let connection_thread = thread::spawn(move || {
| ^^^^^^^^^^^^^ the trait `std::marker::Send` is not implemented for `std::ops::FnMut(std::string::String) + 'static`
|
I have tried changing the type of state_change_close to a Mutex<Option<...>> to avoid thread safety issues, but that did not help with this problem. Is what I'm trying to do possible?
After doing some more research I realized that I just had to modify Option<Box<FnMut(String)>> to be Option<Box<FnMut(String) + Send>> and copy that around my code to everywhere that the callback might be set. Learning more about trait objects!

Resources