How to pass a variable to actix-web guard() in Rust? - rust

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
let token = env::var("TOKEN").expect("Set TOKEN");
HttpServer::new(|| {
App::new()
.wrap(middleware::Logger::default())
.service(
web::resource("/")
.guard(guard::Header("TOKEN", &token))
.route(web::post().to(index))
)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
The error is:
error[E0597]: `token` does not live long enough
I saw .data() in actix docs but that is for passing variables inside routes functions.
UPD:
If I add "move":
HttpServer::new(move || {
then just error changes:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:50:58
|
50 | .guard(guard::Header("TOKEN", &token))
| ^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the body at 42:21...
--> src/main.rs:42:21
|
42 | HttpServer::new(move || {
| ^^^^^^^
note: ...so that closure can access `token`
--> src/main.rs:50:58
|
50 | .guard(guard::Header("TOKEN", &token))
| ^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that reference does not outlive borrowed content
--> src/main.rs:50:58
|
50 | .guard(guard::Header("TOKEN", &token))
| ^^^^^^
error: aborting due to previous error

actix-web creates many threads and each worker (in each thread) must get a copy of a variable. So using let token = token.clone(); and move.
After that each of these variables goes into fn_guard function. So once again move.
let token = env::var("TOKEN").expect("You must set TOKEN");
HttpServer::new(move || {
let token = token.clone();
App::new()
.wrap(middleware::Logger::default())
.service(
web::resource("/")
.guard(guard::fn_guard(
move |req| match req.headers().get("TOKEN") {
Some(value) => value == token.as_str(),
None => false,
}))
.route(web::post().to(index))
)
})
.bind("127.0.0.1:8080")?
.run()
.await
This works.
Can't get it working with only .guard(guard::Header("TOKEN", &token))

Related

Error on Future generator closure: Captured variable cannot escape `FnMut` closure body

I want to create a simple websocket server. I want to process the incoming messages and send a response, but I get an error:
error: captured variable cannot escape `FnMut` closure body
--> src\main.rs:32:27
|
32 | incoming.for_each(|m| async {
| _________________________-_^
| | |
| | inferred to be a `FnMut` closure
33 | | match m {
34 | | // Error here...
35 | | Ok(message) => do_something(message, db, &mut outgoing).await,
36 | | Err(e) => panic!(e)
37 | | }
38 | | }).await;
| |_____^ returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
This gives a few hits on Stack Overflow but I don't see anywhere in my code where a variable is escaping. The async block won't run concurrently, so I don't see any problem. Furthermore, I feel like I am doing something very simple: I get a type which allows me to send data back to the client, but when using a reference to it in the async block, it gives a compile error. The error only occurs when I use the outgoing or db variable in the async code.
This is my code (error is in the handle_connection function):
main.rs
use tokio::net::{TcpListener, TcpStream};
use std::net::SocketAddr;
use std::sync::Arc;
use futures::{StreamExt, SinkExt};
use tungstenite::Message;
use tokio_tungstenite::WebSocketStream;
struct DatabaseConnection;
#[tokio::main]
async fn main() -> Result<(), ()> {
listen("127.0.0.1:3012", Arc::new(DatabaseConnection)).await
}
async fn listen(address: &str, db: Arc<DatabaseConnection>) -> Result<(), ()> {
let try_socket = TcpListener::bind(address).await;
let mut listener = try_socket.expect("Failed to bind on address");
while let Ok((stream, addr)) = listener.accept().await {
tokio::spawn(handle_connection(stream, addr, db.clone()));
}
Ok(())
}
async fn handle_connection(raw_stream: TcpStream, addr: SocketAddr, db: Arc<DatabaseConnection>) {
let db = &*db;
let ws_stream = tokio_tungstenite::accept_async(raw_stream).await.unwrap();
let (mut outgoing, incoming) = ws_stream.split();
// Adding 'move' does also not work
incoming.for_each(|m| async {
match m {
// Error here...
Ok(message) => do_something(message, db, &mut outgoing).await,
Err(e) => panic!(e)
}
}).await;
}
async fn do_something(message: Message, db: &DatabaseConnection, outgoing: &mut futures_util::stream::SplitSink<WebSocketStream<TcpStream>, Message>) {
// Do something...
// Send some message
let _ = outgoing.send(Message::Text("yay".to_string())).await;
}
Cargo.toml
[dependencies]
futures = "0.3.*"
futures-channel = "0.3.*"
futures-util = "0.3.*"
tokio = { version = "0.2.*", features = [ "full" ] }
tokio-tungstenite = "0.10.*"
tungstenite = "0.10.*"
When using async move, I get the following error:
code
incoming.for_each(|m| async move {
let x = &mut outgoing;
let b = db;
}).await;
error
error[E0507]: cannot move out of `outgoing`, a captured variable in an `FnMut` closure
--> src\main.rs:33:38
|
31 | let (mut outgoing, incoming) = ws_stream.split();
| ------------ captured outer variable
32 |
33 | incoming.for_each(|m| async move {
| ______________________________________^
34 | | let x = &mut outgoing;
| | --------
| | |
| | move occurs because `outgoing` has type `futures_util::stream::stream::split::SplitSink<tokio_tungstenite::WebSocketStream<tokio::net::tcp::stream::TcpStream>, tungstenite::protocol::message::Message>`, which does not implement the `Copy` trait
| | move occurs due to use in generator
35 | | let b = db;
36 | | }).await;
| |_____^ move out of `outgoing` occurs here
FnMut is an anonymous struct, since FnMutcaptured the &mut outgoing, it becomes a field inside of this anonymous struct and this field will be used on each call of FnMut , it can be called multiple times. If you lose it somehow (by returning or moving into another scope etc...) your program will not able to use that field for further calls, due to safety Rust Compiler doesn't let you do this(for your both case).
In your case instead of capturing the &mut outgoing we can use it as argument for each call, with this we'll keep the ownership of outgoing. You can do this by using fold from futures-rs:
incoming
.fold(outgoing, |mut outgoing, m| async move {
match m {
// Error here...
Ok(message) => do_something(message, db, &mut outgoing).await,
Err(e) => panic!(e),
}
outgoing
})
.await;
This may seem a bit tricky but it does the job, we are using constant accumulator(outgoing) which will be used as an argument for our FnMut.
Playground (Thanks #Solomon Ucko for creating reproducible example)
See also :
How to return the captured variable from `FnMut` closure, which is a captor at the same time
How can I move a captured variable into a closure within a closure?

Cannot share Arc variables when spawning threads

I have the following struct to represent the server object:
pub struct Server {
client_managers: Arc<ClientManager>,
listener: Option<TcpListener>,
}
Here is the code that receives a client's connection and handles it in a new thread:
fn serve(&self) {
for stream in self.listener.as_ref().unwrap().incoming() {
match stream {
Ok(stream) => {
let client_manager = &mut self.client_managers.clone();
// let client_manager = Arc.new(self.client_managers);
thread::spawn(move || {
client_manager.do_something();
});
}
Err(e) => {
println!("connection error: {}", e);
}
}
}
}
However, I get the following error when compiling:
error[E0716]: temporary value dropped while borrowed
--> server/src/server.rs:37:47
|
37 | let client_manager = &mut self.client_managers.clone();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
38 | // let client_manager = Arc.new(self.client_managers);
39 | / thread::spawn(move || {
40 | | client_manager.nothing();
41 | | });
| |______________________- argument requires that borrow lasts for `'static`
42 | }
| - temporary value is freed at the end of this statement
I understood why this error happened. My question is:
1) I use Arc by following some tutorials online. (Example) But why do their examples work but not mine?
2) How can I fix this error in my situation? (I still want to share the object client_manager).
thread::spawn takes a closure that is 'static, meaning that it cannot borrow data from outside the thread. However, this line will clone the Arc and borrow it, and passing the borrowed reference into the thread:
let client_manager = &mut self.client_managers.clone();
thread::spawn(move || {
client_manager.do_something();
// ^-- client_manager is a `&mut Arc<_>` borrowed from outside the thread
});
Instead, what you want is to just clone the Arc, not borrow it in any way before its passed into the thread:
let client_manager = self.client_managers.clone();
thread::spawn(move || {
client_manager.do_something();
// ^-- client_manager is a `Arc<_>` owned by the new thread
});
The Arc has shared ownership over the value, so it is only destroyed once all Arc pointers referring to it falls out of scope, even across threads.

Accessing a method of self inside a thread in Rust

I want to propagate the self struct object into a thread and then call its time_tick() method for increasing the HMS time.
pub fn start(&mut self) {
self.acti = true; // the time tick is activated now...
thread::spawn(move || {
let local_self: *mut Self = self; // this self live in the thread
loop {
thread::sleep(Duration::from_secs(1)); // wait for 1 sec
if (*local_self).acti == true { (*local_self).time_tick(); }
(*local_self).print_time(); // for debug
}
});
}
I get the error message:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/hmstimer/mod.rs:42:17
|
42 | thread::spawn(move || {
| _______________________^
43 | | let local_self: *mut Self = self; // this self live in the thread
44 | | loop {
45 | | thread::sleep(Duration::from_secs(1)); // wait for 1 sec
... |
48 | | }
49 | | });
| |_________^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 40:2...
--> src/hmstimer/mod.rs:40:2
|
40 | pub fn start(&mut self) {
| _____^
41 | | self.acti = true; // the time tick is activated now...
42 | | thread::spawn(move || {
43 | | let local_self: *mut Self = self; // this self live in the thread
... |
49 | | });
50 | | }
| |_____^
= note: ...so that the types are compatible:
expected &mut hmstimer::HMSTimer
found &mut hmstimer::HMSTimer
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure#src/hmstimer/mod.rs:42:17: 49:7 self:&mut hmstimer::HMSTimer]` will meet its required lifetime bounds
But it seems that the about method is inappropriate. What is the best practice for doing the task?
You can't pass a closure that captures a mutable reference to thread::spawn. thread::spawn needs the function to be 'static, which means that either it captures no borrows, or that all borrows are 'static. That's because the thread can continue running after the referent has been dropped.
If you don't need to use self in the original thread after calling start, then you can just pass self by value.
pub fn start(self) {
self.acti = true;
thread::spawn(move || {
loop {
thread::sleep(Duration::from_secs(1));
if self.acti == true { self.time_tick(); }
self.print_time();
}
});
}
Otherwise, you'll need to use Arc to get the two threads to share ownership of Self, as well as Mutex or RwLock to synchronize reads and writes across threads.
// note: this is not a method anymore;
// invoke as `HMSTimer::start(arc.clone());`
pub fn start(this: Arc<Mutex<Self>>) {
this.lock().expect("mutex is poisoned").acti = true;
thread::spawn(move || {
loop {
thread::sleep(Duration::from_secs(1));
let lock = this.lock().expect("mutex is poisoned");
if lock.acti == true { lock.time_tick(); }
lock.print_time();
// `lock` is dropped here, unlocking the mutex
}
});
}

References to captured variables in an Actix server causes "argument requires that it must outlive 'static"

In the below example, I have parameters verbose and data_source that are set by command line parameters. verbose is a boolean flag, but data_source is used to select a default from a set of available functions that can be used depending on the data source for the application.
Actix uses a closure to set up a server, so I need to get these parameters into the closure. I added move for the bool, but I'm having trouble passing in the function to be used for the index and am getting lifetime errors. I've tried boxing the function, but that doesn't seem to help.
If I'm understanding the error message correctly, it's actually the closure itself that is failing to outlive 'static.
What should I do to solve this issue?
extern crate actix;
extern crate actix_web;
extern crate env_logger;
use actix_web::http::Method;
use actix_web::{middleware, server, App, HttpRequest, HttpResponse};
enum DataSource {
Postgres,
HDF5,
}
fn index_postgres(req: &HttpRequest) -> HttpResponse {
HttpResponse::Ok().body("not implemented")
}
fn index_hdf5(req: &HttpRequest) -> HttpResponse {
HttpResponse::Ok().body("not implemented")
}
fn main() {
let mut verbose = false;
verbose = true;
let mut data_source = DataSource::Postgres;
data_source = DataSource::HDF5;
let index = match data_source {
DataSource::Postgres => index_postgres,
DataSource::HDF5 => index_hdf5,
};
::std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
let sys = actix::System::new("test");
server::new(move || {
if verbose {
App::new()
.middleware(middleware::Logger::default())
.resource("/", |r| r.method(Method::GET).f(index))
} else {
App::new().resource("/", |r| r.method(Method::GET).f(index))
}
})
.bind("127.0.0.1:8080")
.unwrap()
.start();
println!("Started http server: 127.0.0.1:8080");
let _ = sys.run();
}
error: unsatisfied lifetime constraints
--> src\main.rs:50:13
|
48 | server::new(move || {
| ------- lifetime `'1` represents this closure's body
49 | if verbose {
50 | / App::new()
51 | | .middleware(middleware::Logger::default())
52 | | .resource("/", |r| r.method(Method::GET).f(index))
| |__________________________________________________________________^ argument requires that `'1` must outlive `'static`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
I don't understand what compiler is trying to say but it obvious that you need move in handler closures:
if verbose {
App::new()
.middleware(middleware::Logger::default())
.resource("/", move |r| r.method(Method::GET).f(index))
} else {
App::new().resource("/", move |r| r.method(Method::GET).f(index))
}
This is because you need to move index from the outer closure.

Rust closure errors -> ...which is owned by the current function | capture of moved value:

This code:
//let seen_cell = std::cell::RefCell::new(window_0);
window_0.connect_delete_event(|_, _| {
//window_0.destroy();
window.hide();
Inhibit(true)
});
button_0.connect_clicked(|_|{
window.show_all();
}
);
Produces the errors:
error[E0373]: closure may outlive the current function, but it borrows `window`, which is owned by the current function
--> src/main.rs:192:36
|
192 | window_0.connect_delete_event( |_, _| {
| ^^^^^^ may outlive borrowed value `window`
...
195 | window.hide();
| ------ `window` is borrowed here
|
help: to force the closure to take ownership of `window` (and any other referenced variables), use the `move` keyword, as shown:
| window_0.connect_delete_event( move |_, _| {
error[E0373]: closure may outlive the current function, but it borrows `window`, which is owned by the current function
--> src/main.rs:199:30
|
199 | button_0.connect_clicked(|_|{
| ^^^ may outlive borrowed value `window`
200 | window.show_all();
| ------ `window` is borrowed here
|
help: to force the closure to take ownership of `window` (and any other referenced variables), use the `move` keyword, as shown:
| button_0.connect_clicked(move |_|{
If I try this:
//let seen_cell = std::cell::RefCell::new(window_0);
window_0.connect_delete_event(move |_, _| {
//window_0.destroy();
window.hide();
Inhibit(true)
});
button_0.connect_clicked(|_|{
window.show_all();
}
);
I get the errors:
error[E0373]: closure may outlive the current function, but it borrows `window`, which is owned by the current function
--> src/main.rs:199:30
|
199 | button_0.connect_clicked(|_|{
| ^^^ may outlive borrowed value `window`
200 | window.show_all();
| ------ `window` is borrowed here
|
help: to force the closure to take ownership of `window` (and any other referenced variables), use the `move` keyword, as shown:
| button_0.connect_clicked(move |_|{
error[E0382]: capture of moved value: `window`
--> src/main.rs:199:30
|
192 | window_0.connect_delete_event(move |_, _| {
| ----------- value moved (into closure) here
...
199 | button_0.connect_clicked(|_|{
| ^^^ value captured here after move
|
= note: move occurs because `window` has type `gtk::Window`, which does not implement the `Copy` trait
If I try this:
//let seen_cell = std::cell::RefCell::new(window_0);
window_0.connect_delete_event(move |_, _| {
//window_0.destroy();
window.hide();
Inhibit(true)
});
button_0.connect_clicked(move|_|{
window.show_all();
}
);
I get the errors:
error[E0382]: capture of moved value: `window`
--> src/main.rs:200:9
|
192 | window_0.connect_delete_event(move |_, _| {
| ----------- value moved (into closure) here
...
200 | window.show_all();
| ^^^^^^ value captured here after move
|
= note: move occurs because `window` has type `gtk::Window`, which does not implement the `Copy` trait
I have read similar questions but I have not been able to solve this case. How can I solve this in the best way, perhaps by using Arc or similar?
I have solved the above using a macro that I have drawn from some sample projects of gtk-rs
macro_rules! clone {
(#param _) => ( _ );
(#param $x:ident) => ( $x );
($($n:ident),+ => move || $body:expr) => (
{
$( let $n = $n.clone(); )+
move || $body
}
);
($($n:ident),+ => move |$($p:tt),+| $body:expr) => (
{
$( let $n = $n.clone(); )+
move |$(clone!(#param $p),)+| $body
}
);
}
Taking the case described I have used it this way:
window_0.connect_delete_event(clone!(window => move |_, _| {
//window_0.destroy();
window.hide();
Inhibit(true)
}));
button_0.connect_clicked(clone!(window => move |_|{
window.show_all();
}));
This is the relevant part window_0.connect_delete_event(clone!(window => move. In my case it also applies to button_0.connect_clicked because window Is used later in a similar place

Resources