From 962973d869fe569eddea677d179d619e52004f6f Mon Sep 17 00:00:00 2001 From: Kaan Genc Date: Sat, 26 Mar 2022 15:42:27 -0400 Subject: [PATCH] add actix web --- content/actix-web-import-issue.md | 117 ++++++++++++++++++++ content/mass-batch-processing-on-the-CLI.md | 2 +- 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 content/actix-web-import-issue.md diff --git a/content/actix-web-import-issue.md b/content/actix-web-import-issue.md new file mode 100644 index 0000000..2ad7bf2 --- /dev/null +++ b/content/actix-web-import-issue.md @@ -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 Service for CheckLoginMiddleware +where + S: Service, Error = Error>, + S::Future: 'static, +{ + type Response = ServiceResponse>; + type Error = Error; + type Future = LocalBoxFuture<'static, Result>; + + 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`. +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>, +) -> Result { + 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! diff --git a/content/mass-batch-processing-on-the-CLI.md b/content/mass-batch-processing-on-the-CLI.md index 427a1a0..4797677 100644 --- a/content/mass-batch-processing-on-the-CLI.md +++ b/content/mass-batch-processing-on-the-CLI.md @@ -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 ```