问题
I am trying to implement a filter that sits in all my routes and extracts a header and matches a possible token to what is stored on my system.
I want to implement something like the warp rejection example but I get the error
expected a closure that implements the
Fn
trait, but this closure only implementsFnOnce
closure isFnOnce
because it moves the variabletmp
out of its environment
I kind of get what the compiler saying, but don't know how to solve it.
I thought doing let tmp = store.clone()
would.
I have the filter:
pub fn haystack_auth_header(store: Store) -> impl Filter<Extract = (Store,), Error = Rejection> + Clone {
let tmp = store.clone();
warp::header("Authorization").and_then (|auth_header: String| async move {
// Authorization: BEARER authToken=xxxyyyzzz
let result = auth_token(&auth_header); //-> IResult<&'a str, (&'a str, &'a str), (&'a str, ErrorKind)> {
if result.is_err() {
return Err(reject::custom(HayStackAuthToken));
}
let (_, key_value) = result.unwrap();
let auth_token_result = tmp.read().get_authtoken();
if auth_token_result.is_err() {
return Err(reject::custom(HayStackAuthToken));
}
let auth_token_option = auth_token_result.unwrap();
if auth_token_option.is_none() {
return Err(reject::custom(HayStackAuthToken));
}
let auth_token = auth_token_option.unwrap();
if auth_token != key_value.1 {
return Err(reject::custom(HayStackAuthToken));
}
Ok(tmp)
})
}
store
is type Store = Arc<RwLock<Box<dyn UserAuthStore>>>
and UserAuthStore
is trait UserAuthStore: fmt::Debug + Send + Sync
.
UserAuthStore is defined as
pub trait UserAuthStore: fmt::Debug + Send + Sync {
// Return handshake token for username. If user has no handshake token generate one
fn get_handshake_token(&self, username: &str) -> HaystackResult<String>;
fn get_username(&self, handshake_token: &str) -> HaystackResult<String>;
fn set_temporary_value(&mut self, k: &str, v: &str) -> HaystackResult<()>;
fn get_temporary_value(&self, k: &str) -> HaystackResult<Option<&String>>;
fn set_authtoken(&mut self, s: String) -> HaystackResult<()>;
/// returns a base64 encoded sha256 salt of password.
fn get_password_salt(&self) -> HaystackResult<String>;
fn get_salted_password(&self) -> HaystackResult<String>;
fn get_authtoken(&self) -> HaystackResult<Option<String>>;
}
Why does clone
not work here?
The full error is
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server/mod.rs:997:45
|
997 | warp::header("Authorization").and_then (|auth_header: String| async move {
| ___________________________________--------__^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^_-
| | | |
| | | this closure implements `FnOnce`, not `Fn`
| | the requirement to implement `Fn` derives from here
998 | |
999 | | // Authorization: BEARER authToken=xxxyyyzzz
1000 | | let result = auth_token(&auth_header); //-> IResult<&'a str, (&'a str, &'a str), (&'a str, ErrorKind)> {
... |
1026 | | Ok(tmp.clone())
1027 | | })
| |_____- closure is `FnOnce` because it moves the variable `tmp` out of its environment
Simplified test case can be seen at
https://github.com/glennpierce/warp-test
Thanks
回答1:
After creating a simple test case I managed to get it going with the following function.
pub fn haystack_auth_header(store: Store) -> impl Filter<Extract = (Store,), Error = Rejection> + Clone {
warp::header("Authorization").and_then (
move |auth_header: String|
{
let tmp = store.clone();
async move {
let tmp = tmp.clone();
if tmp.read().get_authtoken().is_none() {
return Err(reject::custom(HayStackAuthToken));
}
Ok(tmp.clone())
}
}
)
}
So in the end just needed clone in the correct place.
回答2:
This question suggests that you don't have yet a clear distinction about the fn
hierarchy:
taken from here.
Warp's filter function need to implement the Fn trait. This means that you cannot access the outer context(read: environment).
The first time AndThen call your closure, the function will consume the "only" tmp variable available, thus it won't be available for future calls. Since the closure it's taking ownership of its context, it means it's implementing FnOnce.
As suggested by @msrd0 in the comment, probably this is solvable by moving that call in the function, but I would need an MRE to be certain about it.
来源:https://stackoverflow.com/questions/63132362/warp-filter-moving-variable-out-of-its-environment