How can I pass structs from an Actix middleware to the handler? - rust

I'm trying to write an authentication middleware for my Actix application. When validating the request in the middleware, I make a call to a database to retrieve the necessary user data to validate the incoming request. Once the request has been authorised, I want to be able to pass this user data to the handler as this will allow me to avoid having the query for the same data twice.
I can't find a solution for this. The best suggestion I could find so far was to "set a request extension". There doesn't seem to be any examples for this and there is also too little documentation around this to work out what to do here.

You can pass data from middleware (service) to handler via extensions. First of all you have to insert extension (in service).
For ServiceRequest struct is implemented HttpMessage witch hase extensions_mut() function. It must be mutable becouse you will be inserting new extension. It might look something like this:
req.extensions_mut().insert(user);
Then you have to implement FromRequest trait for your data structure.
impl FromRequest for User {
type Error = actix_web::Error;
type Future = futures::future::Ready<Result<Self, Self::Error>>;
type Config = ();
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
match req.extensions().get::<User>() {
Some(user) => return ok(user.clone()),
None => return err(actix_web::error::ErrorBadRequest("ups..."))
};
}
}
Then you're ready to use it in handler.
pub async fn get_user_handler(user: User) {}

Related

How do I inspect function arguments at runtime in Rust?

Say I have a trait that looks like this:
use std::{error::Error, fmt::Debug};
use super::CheckResult;
/// A Checker is a component that is responsible for checking a
/// particular aspect of the node under investigation, be that metrics,
/// system information, API checks, load tests, etc.
#[async_trait::async_trait]
pub trait Checker: Debug + Sync + Send {
type Input: Debug;
/// This function is expected to take input, whatever that may be,
/// and return a vec of check results.
async fn check(&self, input: &Self::Input) -> anyhow::Result<Vec<CheckResult>>;
}
And say I have two implementations of this trait:
pub struct ApiData {
some_response: String,
}
pub MetricsData {
number_of_events: u64,
}
pub struct ApiChecker;
impl Checker for ApiChecker {
type Input = ApiData;
// implement check function
}
pub struct MetricsChecker;
impl Checker for MetricsChecker {
type Input = MetricsData;
// implement check function
}
In my code I have a Vec of these Checkers that looks like this:
pub struct MyServer {
checkers: Vec<Box<dyn Checker>>,
}
What I want to do is figure out, based on what Checkers are in this Vec, what data I need to fetch. For example, if it just contained an ApiChecker, I would only need to fetch the ApiData. If both ApiChecker and MetricsChecker were there, I'd need both ApiData and MetricsData. You can also imagine a third checker where Input = (ApiData, MetricsData). In that case I'd still just need to fetch ApiData and MetricsData once.
I imagine an approach where the Checker trait has an additional function on it that looks like this:
fn required_data(&self) -> HashSet<DataId>;
This could then return something like [DataId::Api, DataId::Metrics]. I would then run this for all Checkers in my vec and then I'd end up a complete list of data I need to get. I could then do some complicated set of checks like this:
let mut required_data = HashSet::new();
for checker in checkers {
required_data.union(&mut checker.required_data());
}
let api_data: Option<ApiData> = None;
if required_data.contains(DataId::Api) {
api_data = Some(get_api_data());
}
And so on for each of the data types.
I'd then pass them into the check calls like this:
api_checker.check(
api_data.expect("There was some logic error and we didn't get the API data even though a Checker declared that it needed it")
);
The reasons I want to fetch the data outside of the Checkers is:
To avoid fetching the same data multiple times.
To support memoization between unrelated calls where the arguments are the same (this could be done inside some kind of Fetcher trait implementation for example).
To support generic retry logic.
By now you can probably see that I've got two big problems:
The declaration of what data a specific Checker needs is duplicated, once in the function signature and again from the required_data function. This naturally introduces bug potential. Ideally this information would only be declared once.
Similarly, in the calling code, I have to trust that the data that the Checkers said they needed was actually accurate (the expect in the previous snippet). If it's not, and we didn't get data we needed, there will be problems.
I think both of these problems would be solved if the function signature, and specifically the Input associated type, was able to express this "required data" declaration on its own. Unfortunately I'm not sure how to do that. I see there is a nightly feature in any that implements Provider and Demand: https://doc.rust-lang.org/std/any/index.html#provider-and-demand. This sort of sounds like what I want, but I have to use stable Rust, plus I figure I must be missing something and there is an easier way to do this without going rogue with semi dynamic typing.
tl;dr: How can I inspect what types the arguments are for a function (keeping in mind that the input might be more complex than just one thing, such as a struct or tuple) at runtime from outside the trait implementer? Alternatively, is there a better way to design this code that would eliminate the need for this kind of reflection?
Your problems start way earlier than you mention:
checkers: Vec<Box<dyn Checker>>
This is an incomplete type. The associated type Input means that Checker<Input = ApiData> and Checker<Input = MetricsData> are incompatible. How would you call checkers[0].check(input)? What type would input be? If you want a collection of "checkers" then you'll need a unified API, where the arguments to .check() are all the same.
I would suggest a different route altogether: Instead of providing the input, provide a type that can retrieve the input that they ask for. That way there's no need to coordinate what type the checkers will ask for in a type-safe way, it'll be inherent to the methods the checkers themselves call. And if your primary concern is repeatedly retrieving the same data for different checkers, then all you need to do is implement caching in the provider. Same with retry logic.
Here's my suggestion:
struct DataProvider { /* cached api and metrics */ }
impl DataProvider {
fn fetch_api_data(&mut self) -> anyhow::Result<ApiData> { todo!() }
fn fetch_metrics_data(&mut self) -> anyhow::Result<MetricsData> { todo!() }
}
#[async_trait::async_trait]
trait Checker {
async fn check(&self, data: &mut DataProvider) -> anyhow::Result<Vec<CheckResult>>;
}
struct ApiAndMetricsChecker;
#[async_trait::async_trait]
impl Checker for ApiAndMetricsChecker {
async fn check(&self, data: &mut DataProvider) -> anyhow::Result<Vec<CheckResult>> {
let _api_data = data.fetch_api_data()?;
let _metrics_data = data.fetch_metrics_data()?;
// do something with api and metrics data
todo!()
}
}

Cannot solve compiler errors while extracting reqwest::Response from a reqwest::Client request

I'm having a hard time trying to figure out how to error handle this piece of code.
This is the endpoint of my actix_web api, up until now everything works:
pub async fn simple_moving_average() -> HttpResponse {
if Client::new()
.get("https://coinranking1.p.rapidapi.com/coins")
.header(
"X-RapidAPI-Key",
"MY_KEY")
.send()
.await
.is_err()
{
HttpResponse::BadRequest().finish();
}
HttpResponse::Ok().finish()
}
It's when I try to extract the data from the response that the compiler starts screaming at me:
[...]
let response = Client::new()
.get("https://coinranking1.p.rapidapi.com/coins")
.header(
"X-RapidAPI-Key",
"MY_KEY")
.send()
.await?
.json::<Root>()
.await?;
HttpResponse::Ok().finish()
}
cargo check ->
he ? operator can only be used in an async function that returns
Result or Option (or another type that implements FromResidual)
the trait FromResidual<Result<Infallible, reqwest::Error>> is not
implemented for HttpResponserustcE0277 simple_moving_average.rs(50,
65): this function should return Result or Option to accept ?
Root in .json::<Root>() is just a struct representing json payload. I generated it from this website, not sure if it's relevant
how do I solve that? My final goal here would be to return a json response containing the data I get from this request. I've tried implementing the ::thiserror crate but I cannot figure out how it works and honestly I would prefer to understand how error handling works before using some fast solution.
I've tried following what compiler suggests but I cannot seem to solve the situation.
I solved by using the dyn trait
correct code:
pub async fn simple_moving_average() ->
Result<HttpResponse, Box<dyn std::error::Error>>{ [...] }

How do I use trait bound in Rust?

I am going to use generic to pass A<B<C>>>> and Z<A<B<C>>>> to a function as one type. But I don't know how to implement trait bounds for this. The detail are as follows.
http service and https service each have listener and poll for accept. When an accept request comes in, handle_request() is called to process the request.
The http and https request processing methods are the same, but in the case of https, TlsService is additionally implemented to include tls processing. As a result, it has the following structure:
pub type RawRequest = HttpService<AccessLog<RouteService<CorsService<ProxyService>>>>;
pub struct Services {
http_service: Arc<RawRequest>,
https_service: Arc<TlsService<RawRequest>>,
http_accept_service: AcceptService,
https_accept_service: AcceptService,
}
...
fn handle_request<S>(stream: TcpStream, accesslog_sender: crossbeam_channel::Sender<String>, http_service: S) {
let connection = NewConnection {
stream,
service_builder: ServiceBuilder::new(),
};
tokio::spawn(async move {
ACCESSLOG_SENDER
.scope(accesslog_sender, async move {
if let Err(_e) = http_service.call(connection).await {
// TODO: write error
};
})
.await;
});
}
handle_request(stream, accesslog_sender, services.http_accept_service.clone());
handle_request(stream, accesslog_sender, services.https_accept_service.clone());
Question
I am going to use generic to call the same function for different types of Service.
fn handle_request<S>(
stream: TcpStream,
accesslog_sender: crossbeam_channel::Sender<String>,
http_service: S,
) {
...
}
But I get a trait bound error.
error[E0599]: no method named `call` found for type parameter `S` in the current scope
I'm new to Rust and I'm struggling with generic and trait bound. Please help me how to write
When you use a generic type like S rust will not make any assumptions about that type (expect that it has a compile time known size). Therefore you cannot really do anything on that type. For the compiler there is no way to know that a call() method exists.
This is different from how C++ handles generics where you would get long and hard to read compiler errors when using a type for S which does not have all the functionality used by the generic function.
The solution in Rust is a trait. Lets suppose both your http and https service have a call function. You can then write a trait (or more likely use one from the library your using) which says that these types have a call method. You can then use this trait as a trait bound in your function like this: handle_request<S : Name_of_the_trait>
This tells the compiler to only use types as a substitute for S which implement Name_of_the_trait and therefore you can call all the functions of this trait on S.
I guess you should read more about traits and it should become apparent what you should do in detail.
https://doc.rust-lang.org/stable/book/ch10-00-generics.html

How can I make a postgres pool connection global in rocket.rs and make FromRequest custom guards asyncable?

I am making an authorization system which will implement the FromRequest trait from the web framework rocket, which is needed to make custom guards.
The first problem I have gone into is how I have to make the connection global. Should I cast it as a constant, so it can be accessed from the functions in the implementation, or is there a cache in rocket or some form of storage where the PgPool connection (since I am using sqlx) can be accessed & queries can be made.
The second problem I have gone through is making the FromRequest functions asynchronous. Since sqlx is natively async afaik, I do not know if rocket has yet to support this. I was thinking of either making a tokio thread, or if there is a version of .then() in Rust, but I do not know
#[derive(Debug)]
struct User {
username: String,
password: String,
user_id: i16,
phone_number: String,
display_name: String,
//other fields omitted
}
impl<'a, 'r> FromRequest<'a, 'r> for &'a User {
type Error = !;
fn from_request(request: &'a Request<'r>) -> request::Outcome<&'a User, !> {
let user_result = request.local_cache(|| {
//..need to fetch user with sqlx [but sqlx is async]
//and later get cookies and check
});
user_result.as_ref().or_forward(())
}
}
Async support for Rocket lands in Version 0.5. For now, you can use the master branch: https://github.com/SergioBenitez/Rocket
The idiomatic way to handle this use case in Rocket would be to use rocket-contrib adapter for databases: https://docs.rs/rocket_contrib/0.4.7/rocket_contrib/databases/index.html#provided
You'll need to implement the Poolable Trait for your database. Here are the docs for that: https://docs.rs/rocket_contrib/0.4.7/rocket_contrib/databases/trait.Poolable.html
Rocket already provides the trait for the following:
diesel::MysqlConnection
diesel::PgConnection
diesel::SqliteConnection
postgres::Connection
mysql::Conn
rusqlite::Connection
rusted_cypher::GraphClient
redis::Connection
You can then use the database macro like so:
use rocket_contrib::databases::postgres;
#[database("db_name")]
struct MyPgDatabase(postgres::Connection);
fn main() {
rocket::custom(config)
.attach(MyPgDatabase::fairing())
.launch();
}
You can then just use this type as a request guard and then retrieve the database connection since the macro above automatically generates the FromRequest implementation for you
#[get("/")]
fn my_handler(conn: MyPgDatabase) {
// ...
}
The macro also generates a Deref implementation allowing you access to the inner connection type.
For Reference:
https://github.com/TatriX/realworld-rust-rocket: A great code example showing this

Rust handling error response bodies with Reqwest

I'm using the reqwest (version 0.10.4) crate for the HTTP calls in my Rust application but can't find any examples of how to handle APIs calls that could return more than one possible response body, mainly for error handling.
For instance, an API call could respond with a success JSON structure, or an error structure of format:
{
"errors": ["..."]
}
Currently I have this code for the function, but can't seem to figure out how to determine which struct I need to deserialize the response buffer into based on whether the HTTP request was successful or not.
use super::responses::{Error, Response};
use crate::clients::HttpClient;
use crate::errors::HttpError;
use reqwest::header;
pub fn call() -> Result<Response, HttpError> {
let url = format!("{}/auth/userpass/login/{}", addr, user);
let response = HttpClient::new()
.post(&url)
.header(header::ACCEPT, "application/json")
.header(header::CONTENT_TYPE, "application/json")
.json(&serde_json::json!({ "password": pass }))
.send();
match response {
Ok(res) => {
let payload = res.json(); // could be `Error` or `Response` but only parses to `Response`
match payload {
Ok(j) => Ok(j),
Err(e) => Err(HttpError::JsonParse(e)),
}
}
Err(e) => Err(HttpError::RequestFailed(e)),
}
}
Did I miss something in the documentation for reqwest or is this a common issue?
Internally, res.json() uses the serde_json crate to deserialize the from JSON to your Rust object.
In Rust, when you want a type that have multiple different variants, you use an enumeration. serde implements this behavior for you, which allows you to deserialize to an enumeration, based on the format deserialized from. For example, you might define your response enumeration as follows:
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum ResponseType {
Ok(/* fields */),
Err(/* fields */),
}
There is a lot going on there, but here are the highlights: #[serde(untagged)] tells serde that the enumeration should only be differentiated by the fields in Ok and Err. In your Rust code, you can differentiate by variant, using the full range of pattern matching, etc.
For your specific use case, it looks like the standard Result<V, E> enumeration should be good enough.

Resources