1
0
Fork 0

add actix web

This commit is contained in:
Kaan Barmore-Genç 2022-03-26 15:42:27 -04:00
parent 2f52b9b717
commit fce83731fd
5 changed files with 220 additions and 14 deletions

View file

@ -0,0 +1,117 @@
---
title: Solving `app_data` or `ReqData` missing in requests
date: 2022-03-19
---
> This post is day 5 of me taking part in the
> [#100DaysToOffload](https://100daystooffload.com/) challenge.
I'm using `actix-web` to set up a web server, and I've been hitting a small
problem that I think other people may come across too.
To explain the problem, let me talk a bit about my setup. I have a custom
middleware that checks if a user is authorized to access a route. It looks like
this:
```rust
impl<S: 'static, B> Service<ServiceRequest> for CheckLoginMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
dev::forward_ready!(service);
fn call(&self, req: ServiceRequest) -> Self::Future {
let state = self.state.clone();
let (request, payload) = req.into_parts();
let service = self.service.clone();
let user_token = get_token_from_header(&request);
let path_token = if self.allow_path_tokens {
get_token_from_query(&request)
} else {
None
};
Box::pin(async move {
match verify_auth(state, user_token, path_token, request.path()).await {
Ok(authorized) => {
tracing::debug!("Request authorized, inserting authorization token");
// This is the "important bit" where we insert the authorization token into the request data
request.extensions_mut().insert(authorized);
let service_request =
service.call(ServiceRequest::from_parts(request, payload));
service_request
.await
.map(ServiceResponse::map_into_left_body)
}
Err(err) => {
let response = HttpResponse::Unauthorized().json(err).map_into_right_body();
Ok(ServiceResponse::new(request, response))
}
}
})
}
}
```
The `verify_auth` function is omitted, but the gist of it is that it returns an `Result<Authorized, Error>`.
If the user is authorized, the authorization token `verify_auth` returned is then attached to the request.
Then here's how I use it in a path:
```rust
#[delete("/{store}/{path:.*}")]
async fn delete_storage(
params: web::Path<(String, String)>,
// This parameter is automatically filled with the token
authorized: Option<ReqData<Authorized>>,
) -> Result<HttpResponse, StorageError> {
let (store, path) = params.as_ref();
let mut store_path = get_authorized_path(&authorized, store)?;
store_path.push(path);
if fs::metadata(&store_path).await?.is_file() {
tracing::debug!("Deleting file {:?}", store_path);
fs::remove_file(&store_path).await?;
} else {
tracing::debug!("Deleting folder {:?}", store_path);
fs::remove_dir(&store_path).await?;
}
Ok(HttpResponse::Ok().finish())
}
```
This setup worked for this path, but would absolutely not work for another path.
I inserted logs to track everything, and just found that the middleware would
insert the token, but the path would just get `None`. How‽ I tried to slowly
strip everything away from the non-functional path until it was identical to
this one, but it still would not work.
Well it turns out the solution was very simple, see this:
```rust
use my_package::storage::put_storage;
use crate::storage::delete_storage;
```
Ah! They are imported differently. I had set up my program as both a library and
a program for various reasons. However, it turns out importing the same thing
from `crate` is different from importing it from the library. Because of the
difference in import, Actix doesn't recognize that the types match, so the route
can't access the attached token.
The solution is normalizing the imports. I went with going through the library
for everything, because that's what `rust-analyzer`s automatic import seems to
prefer.
```rust
use my_package::storage::{put_storage, delete_storage};
```
Solved!

View file

@ -26,7 +26,7 @@ $ mark-list my-video.mp4 # Choose a file
Marked 1 file.
$ mark-list *.webm # Choose many files
Marked 3 files.
$ cd Downloads
$ cd Downloadsr
$ mark-list last.mpg # You can go to other directories and keep marking
```

View file

@ -0,0 +1,101 @@
~~~~~~~~
title: Solving app_data or ReqData missing in requests
## date: 2022-03-19
> This post is day 5 of me taking part in the #100DaysToOffload[a] challenge.
=> https://100daystooffload.com/ [a]
Im using actix-web to set up a web server, and Ive been hitting a small problem that I think other people may come across too.
To explain the problem, let me talk a bit about my setup. I have a custom middleware that checks if a user is authorized to access a route. It looks like this:
```impl<S: 'static, B> Service<ServiceRequest> for CheckLoginMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
dev::forward_ready!(service);
fn call(&self, req: ServiceRequest) -> Self::Future {
let state = self.state.clone();
let (request, payload) = req.into_parts();
let service = self.service.clone();
let user_token = get_token_from_header(&request);
let path_token = if self.allow_path_tokens {
get_token_from_query(&request)
} else {
None
};
Box::pin(async move {
match verify_auth(state, user_token, path_token, request.path()).await {
Ok(authorized) => {
tracing::debug!("Request authorized, inserting authorization token");
// This is the "important bit" where we insert the authorization token into the request data
request.extensions_mut().insert(authorized);
let service_request =
service.call(ServiceRequest::from_parts(request, payload));
service_request
.await
.map(ServiceResponse::map_into_left_body)
}
Err(err) => {
let response = HttpResponse::Unauthorized().json(err).map_into_right_body();
Ok(ServiceResponse::new(request, response))
}
}
})
}
}
```
The verify_auth function is omitted, but the gist of it is that it returns an Result<Authorized, Error>. If the user is authorized, the authorization token verify_auth returned is then attached to the request.
Then heres how I use it in a path:
```#[delete("/{store}/{path:.*}")]
async fn delete_storage(
params: web::Path<(String, String)>,
// This parameter is automatically filled with the token
authorized: Option<ReqData<Authorized>>,
) -> Result<HttpResponse, StorageError> {
let (store, path) = params.as_ref();
let mut store_path = get_authorized_path(&authorized, store)?;
store_path.push(path);
if fs::metadata(&store_path).await?.is_file() {
tracing::debug!("Deleting file {:?}", store_path);
fs::remove_file(&store_path).await?;
} else {
tracing::debug!("Deleting folder {:?}", store_path);
fs::remove_dir(&store_path).await?;
}
Ok(HttpResponse::Ok().finish())
}
```
This setup worked for this path, but would absolutely not work for another path. I inserted logs to track everything, and just found that the middleware would insert the token, but the path would just get None. How‽ I tried to slowly strip everything away from the non-functional path until it was identical to this one, but it still would not work.
Well it turns out the solution was very simple, see this:
```use my_package::storage::put_storage;
use crate::storage::delete_storage;
```
Ah! They are imported differently. I had set up my program as both a library and a program for various reasons. However, it turns out importing the same thing from crate is different from importing it from the library. Because of the difference in import, Actix doesnt recognize that the types match, so the route cant access the attached token.
The solution is normalizing the imports. I went with going through the library for everything, because thats what rust-analyzers automatic import seems to prefer.
```use my_package::storage::{put_storage, delete_storage};
```
Solved!

View file

@ -34,15 +34,3 @@ This page is also available on HTTP/HTML if you prefer that.
Finally, below is a list of all my blog posts. These are not sorted by date at the moment, but I'm working on fixing that soon.
=> /bash.gmi
=> /customizing-emacs.gmi
=> /duplicity.gmi
=> /emacs-as-an-os.gmi
=> /getting-deus-ex-running-on-linux.gmi
=> /mpv.gmi
=> /pass.gmi
=> /publications.gmi
=> /raid.gmi
=> /running-graphical-user-services-with-systemd.gmi
=> /rust-typesystem-tricks.gmi
=> /state-of-rust-GUIs.gmi

View file

@ -20,7 +20,7 @@ The idea is simple, you use the command to mark a bunch of files. Every file you
Marked 1 file.
$ mark-list *.webm # Choose many files
Marked 3 files.
$ cd Downloads
$ cd Downloadsr
$ mark-list last.mpg # You can go to other directories and keep marking
```