I have an issue with building a future, that will be returned to a caller and fired in run-time per each client request. The main idea is pretty simple:
- Extract a token
- Create a "connection" with a Redis instance.
- Before executing a command, probably necessary to AUTH with the Redis.
- Build a expression, so, that we will extract a "user_id" by its token, process the result and validate a passed token. Otherwise will return an error, if something goes wrong.
- Return a caller future that will return a certain result:
Ok(())
or aPathfinderError::AuthenticationError
.
In my case, I'm stuck with a 3rd point, when necessary to authenticate with Redis. After sending a command and processing an outcome, I'd like to return a connection, so that it would be reused later with chained map
/map_err
calls and so on, but getting an errors on a compiling stage.
Code:
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 = match self.redis_password {
// AUTH with Redis here, before invoking any command
Some(ref password) => {
let password_inner = password.clone();
paired_connect(&redis_socket_address, handle)
.and_then(|connection| {
connection.send::<String>(resp_array!["AUTH", password_inner])
.map(|_| connection)
})
.map_err(|_| {
let message = String::from("A technical issue with Redis...");
println!("{}", message);
PathfinderError::AuthenticationError(message)
})
.map(|connection| connection)
},
// AUTH no needed, so left it as is
_ => paired_connect(&redis_socket_address, handle)
};
let token_inner = token.clone();
let validation_struct = self.get_validation_struct();
let jwt_secret_inner = self.jwt_secret.clone();
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(|_| {
let message = String::from("Token is expired.");
PathfinderError::AuthenticationError(message)
})
// Extracted user_id used here for additional JWT validation
.map(move |user_id| {
let mut validation_struct_inner = validation_struct.clone();
validation_struct_inner.set_audience(&user_id);
validate(&token_inner, &jwt_secret_inner, &validation_struct)
})
// The passed token is expired or has an invalid data
.map_err(|_| {
let message = String::from("Token is invalid.");
PathfinderError::AuthenticationError(message)
})
// Drop the result, because everything that we need was done
.map(|_| { () })
)
}
}
Compiler errors:
error[E0308]: match arms have incompatible types
--> src/token/middleware.rs:76:32
|
76 | let redis_connection = match self.redis_password {
| ________________________________^
77 | | Some(ref password) => {
78 | | let password_inner = password.clone();
79 | | paired_connect(&redis_socket_address, handle)
... |
91 | | _ => paired_connect(&redis_socket_address, handle)
92 | | };
| |_________^ expected struct `futures::Map`, found struct `std::boxed::Box`
|
= note: expected type `futures::Map<futures::MapErr<futures::AndThen<std::boxed::Box<futures::Future<Item=redis_async::client::PairedConnection, Error=redis_async::error::Error>>, futures::Map<std::boxed::Box<futures::Future<Item=std::string::String, Error=redis_async::error::Error>>, [closure@src/token/middleware.rs:82:34: 82:48 connection:_]>, [closure@src/token/middleware.rs:80:31: 83:22 password_inner:_]>, [closure@src/token/middleware.rs:84:30: 88:22]>, [closure@src/token/middleware.rs:89:26: 89:49]>`
found type `std::boxed::Box<futures::Future<Item=redis_async::client::PairedConnection, Error=redis_async::error::Error> + 'static>`
note: match arm with an incompatible type
--> src/token/middleware.rs:91:18
|
91 | _ => paired_connect(&redis_socket_address, handle)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `futures::Future<Item=(), Error=error::PathfinderError>: std::marker::Sized` is not satisfied
--> src/token/middleware.rs:97:9
|
97 | Box::new(
| ^^^^^^^^ `futures::Future<Item=(), Error=error::PathfinderError>` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `futures::Future<Item=(), Error=error::PathfinderError>`
= note: required by `<std::boxed::Box<T>>::new`
So, as far as I understood, it impossible to "generate" a future based on the "match" expression so that it could reused later and should to write and huge match, that will return Box::new(...)
depends on the match result, correct? Maybe I have other ways to solve this particular issue?