[SOLVED] Converting an existing future to an another one


#1

I’m trying to process the result of a redis future, generated by redis-async:

pub type MiddlewareFuture = Box<Future<Item=(), Error=PathfinderError> + 'static>;

impl Middleware for JwtTokenMiddleware {
    fn process_request(&self, message: &JsonMessage, handle: &Handle) -> MiddlewareFuture {
        let token = match message["token"].as_str() {
            Some(token) => String::from(token),
            None => {
                return Box::new(lazy(move || {
                    let message = String::from("Token was not specified.");
                    Err(PathfinderError::AuthenticationError(message))
                }))
            }
        };

        let redis_socket_address = self.redis_address.parse().unwrap();
        let redis_connection = paired_connect(&redis_socket_address, handle);
        Box::new(
            redis_connection
                // Get the User ID from Redis by the token
                .and_then(move |connection| {
                    connection.send::<String>(resp_array!["GET", token])
                })
                // Connection issue or token is already deleted
                .map_err(|err| {
                    let message = String::from("Token is expired.");
                    Err(PathfinderError::AuthenticationError(message))
                })
                // Extracted user_id used here for additional JWT validation
                .map(|user_id| {
                    let validation_struct = self.get_validation_struct(&user_id);
                    validate_token(&token, &self.jwt_secret, &validation_struct)
                })
                .map_err(|_| {
                    let message = String::from("Token is invalid.");
                    Err(PathfinderError::AuthenticationError(message))
                })
                .map(|_| { Ok(()) })
        )
    }
}

This future will be fired and an execution result will be processing further, during processing client requests:

let ws_reader = stream.for_each(move |message: Message| {
    // ...

    // Apply a middleware to each incoming message
    let auth_future = auth_middleware_local.borrow()
        .process_request(&json_message, &handle_inner)
        .then(move |result| {
            if result.is_err() {
                let tx_nested = &connections_nested.borrow_mut()[&addr];
                let formatted_error = format!("{}", result.unwrap_err());
                let error_message = engine_nested.borrow().wrap_an_error(formatted_error.as_str());
                tx_nested.unbounded_send(error_message).unwrap();
            };
            Ok(())
        });

    handle_inner.spawn(auth_future);
    Ok(())
});

But any of my attempts in conversion (like dropping a result, because I don’t care about if, if the token is valid) are leads to errors and warnings on the compiler step:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
   --> src/token/middleware.rs:91:9
    |
91  | /         Box::new(
92  | |             redis_connection
93  | |                 // Get the User ID from Redis by the token
94  | |                 .and_then(move |connection| {
...   |
111 | |                 .map(|_| ())
112 | |         )
    | |_________^
    |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 78:5...
   --> src/token/middleware.rs:78:5
    |
78  | /     fn process_request(&self, message: &JsonMessage, handle: &Handle) -> MiddlewareFuture {
79  | |         let token = match message["token"].as_str() {
80  | |             Some(token) => String::from(token),
81  | |             None => {
...   |
112 | |         )
113 | |     }
    | |_____^
note: ...so that the type `futures::Map<futures::MapErr<futures::Map<futures::MapErr<futures::AndThen<std::boxed::Box<futures::Future<Item=redis_async::client::PairedConnection, Error=redis_async::error::Error>>, std::boxed::Box<futures::Future<Item=std::string::String, Error=redis_async::error::Error>>, [closure@src/token/middleware.rs:94:27: 96:18 token:std::string::String]>, [closure@src/token/middleware.rs:98:26: 101:18]>, [closure@src/token/middleware.rs:103:22: 106:18 self:&&token::middleware::JwtTokenMiddleware, token:&std::string::String]>, [closure@src/token/middleware.rs:107:26: 110:18]>, [closure@src/token/middleware.rs:111:22: 111:28]>` will meet its required lifetime bounds
   --> src/token/middleware.rs:91:9
    |
91  | /         Box::new(
92  | |             redis_connection
93  | |                 // Get the User ID from Redis by the token
94  | |                 .and_then(move |connection| {
...   |
111 | |                 .map(|_| ())
112 | |         )
    | |_________^
    = note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected std::boxed::Box<futures::Future<Item=(), Error=error::PathfinderError> + 'static>, found std::boxed::Box<futures::Future<Item=(), Error=error::PathfinderError>>)
   --> src/token/middleware.rs:91:9
    |
91  | /         Box::new(
92  | |             redis_connection
93  | |                 // Get the User ID from Redis by the token
94  | |                 .and_then(move |connection| {
...   |
111 | |                 .map(|_| ())
112 | |         )
    | |_________^

Any ideas how it could be fixed? Perhaps for futures already exists any convention about processing different future types and transforming it to an another types?


#2

Your MiddlewareFuture is a type alias for a future that’s 'static but you’re actually borrowing self there from the looks of it.


#3

I’ve tried to change the code inside of process_request method, to this one:

fn process_request(&self, message: &JsonMessage, handle: &Handle) -> MiddlewareFuture {
    let token = match message["token"].as_str() {
        Some(token) => String::from(token),
        None => {
            return Box::new(lazy(move || {
                let message = String::from("Token was not specified.");
                Err(PathfinderError::AuthenticationError(message))
            }))
        }
    };

    let redis_socket_address = self.redis_address.parse().unwrap();
    let redis_connection = paired_connect(&redis_socket_address, handle);

    Box::new(
        redis_connection
            // Get the User ID from Redis by the token
            .and_then(move |connection| {
                connection.send::<String>(resp_array!["GET", token])
            })
            // Connection issue or token is already deleted
            .map_err(|err| {
                let message = String::from("Token is expired.");
                PathfinderError::AuthenticationError(message)
            })
            // Extracted user_id used here for additional JWT validation
            .map(|user_id| {
                let mut validation_struct = Validation {
                    leeway: 0,
                    validate_exp: true,
                    validate_iat: true,
                    validate_nbf: true,
                    iss: Some(String::from(DEFAULT_ISSUER)),
                    sub: None,
                    aud: None,
                    algorithms: Some(vec![Algorithm::HS512]),
                };
                validation_struct.set_audience(&user_id);
                validate(&token, &self.jwt_secret.clone(), &validation_struct)
            })
            .map_err(|_| {
                let message = String::from("Token is invalid.");
                PathfinderError::AuthenticationError(message)
            })
            .map(|_| ())
    )
}

But still getting the same error. Also my attempts to remove 'static lifetime didn’t help (but I presuming that nonetheless it necessary here).


#4
validate(&token, &self.jwt_secret.clone(), &validation_struct)

You still have token there, which is a reference tied to &JsonMessage. You may also want to clone the jwt_secret outside the closures.


#5

Thank you so much! It helped me and now it works as expected :slight_smile: