Warp filter moving variable out of its environment [duplicate]

点点圈 提交于 2021-02-05 12:19:19

问题


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 implements FnOnce closure is FnOnce because it moves the variable tmp 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!