前提・実現したいこと
初質問で、文章がおかしい部分があると思います。申し訳ないです。
Rustを最近学び、自プロジェクトで実際に利用したいと思い立ち、actix-webを使用しWebAPIを作成しています。
その中でヘッダー情報のToken情報を認証サーバーにgrpc(tonicを使用)経由で送信し、バリデーションを行うミドルウェアを現在製作しているのですが、いざ実装して動作確認をしたところ自作したLoggerのメソッドが二度発火してしまうということが起きてしまいました。
このままではLoggerが見づらくなってしまう他、この後実装予定のgrpcの送受信メソッドが二重に発火してしまうのではないかと考え、原因を探ろうとしたのですがFutures等を利用した非同期処理などの勉強が不足しているせいか全く分かりませんでした。
「なぜ二回発火しているのか」
「どう回避すればいいのか」
「現在の実装が完全に間違っている、もしくはバグを生む懸念がある場所があるのか」
この三点を知りたいです。
これは我儘ですが「Rustのデバッグ方法」も教えてくださるとありがたいです…
発生している問題・エラーメッセージ
TestMiddlewareProcess内のcallメソッドにあるLoggerのメソッドにて表示されるメッセージが二度流れている。
rust
1 // auth.rs LINE: 43 2 fn call(&self, req: ServiceRequest) -> Self::Future { 3 let logger = Rc::new(Logger::new(Some("TestMiddleware"))); 4 5 // Why is this method firing twice... 6 logger.debug("task call from middleware start."); 7 // 以下略 8 }
該当のソースコード
コードはActix-webに標準で実装されているLoggerを参考に実装しました。
自プロジェクトをGithubにプッシュしようにも原因が分からないバグ(?)を残したまましたくないので、該当部分を切り出したプロジェクトを製作し、テストしました。
actix_middleware_test
ミドルウェアのソースコード
rust
1use std::cell::RefCell; 2use std::pin::Pin; 3use std::rc::Rc; 4use std::task::{Context, Poll}; 5use actix_web::dev::{Service, Transform, ServiceRequest, ServiceResponse}; 6use futures::Future; 7use futures::future::{ok, Ready}; 8use crate::logger::Logger; 9 10pub struct TestMiddleware; 11pub struct TestMiddlewareProcess<S> { service: Rc<RefCell<S>> } 12 13impl<S: 'static, B> Transform<S, ServiceRequest> for TestMiddleware 14 where S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>, 15 S::Future: 'static, 16 B: 'static 17{ 18 //type Request = ServiceRequest; 19 type Response = ServiceResponse<B>; 20 type Error = actix_web::Error; 21 type Transform = TestMiddlewareProcess<S>; 22 type InitError = (); 23 type Future = Ready<Result<Self::Transform, Self::InitError>>; 24 25 fn new_transform(&self, service: S) -> Self::Future { 26 ok(TestMiddlewareProcess { service: Rc::new(RefCell::new(service)) }) 27 } 28} 29 30impl<S, B> Service<ServiceRequest> for TestMiddlewareProcess<S> 31 where S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error> + 'static, 32 S::Future: 'static, 33 B: 'static 34{ 35 type Response = ServiceResponse<B>; 36 type Error = actix_web::Error; 37 type Future = S::Future;//Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>; 38 39 fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { 40 self.service.poll_ready(ctx) 41 } 42 43 fn call(&self, req: ServiceRequest) -> Self::Future { 44 let logger = Rc::new(Logger::new(Some("TestMiddleware"))); 45 46 // Why is this method firing twice... 47 logger.debug("task call from middleware start."); 48 49 // Todo: Write Token Validator Task (2021/12/30 23:54) 50 51 self.service.call(req) 52 // Box::pin(async move { 53 // let res = serv.call(req).await?; 54 // println!("headers: {:?}", res.headers()); 55 // logger.debug("cage task call from middleware response"); 56 // 57 // Ok(res) 58 // }) 59 } 60}
試したこと
・actix-webのバージョンを3.3.2から4.0.0-beta.13にアップデートした。
・LoggerをRc<T>でラップした。(Rcをよく理解できていない)
・env_loggerを利用し、どの様にコードが実行されているのか観測しようとしたがenv_logger自体の発火しているタイミングなどが分からなかった。
補足情報
・CLion 2021.2.3 (Build #CL-212.5457.51, built on October 14, 2021)
・IntellijRust plugin (0.4.160.4261-212)
・cargo 1.58.0-nightly (2e2a16e98 2021-11-08)
・rustup 1.24.3 (ce5817a94 2021-05-31)
・rustc 1.58.0-nightly (82af160c2 2021-11-10)
Cargo.toml
toml
1[package] 2name = "actix_middleware_test" 3version = "0.1.0" 4edition = "2021" 5 6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 8[dependencies] 9actix-web = "4.0.0-beta.13" 10env_logger = "0.6.2" 11futures = "0.3.18" 12tokio = { version = "1.12.0", features = ["full"] } 13yansi = "0.5.0" 14chrono = "0.4.19" 15serde = "1.0.130" 16serde_json = "1.0.68" 17serde_derive = "1.0.130"
追加情報
・rustup override set
を使用してrustc 1.56.0 (09c42c458 2021-10-18)
に切り替えて実行したところ特に解決するということは起こりませんでした。
・Cargo.toml
内のEdition
を2021から2018に変更しても同じ事が起きます。
・ブラウザ等の環境問題も疑い、こちらのコードを拝借して実行したところ、二重で発火するということは起こりませんでした。
なお、このコードの依存関係のバージョンを今のプロジェクトに合わせると変更が大きく加わるのでアップデート等の操作は行っていません。
コードの変更箇所
rust
1 // main.rs LINE: 82 2 fn call(&mut self, mut service_request: Self::Request) -> Self::Future { 3 println!("task start!"); // デバッグ用に追加 4 5 // if Request path is "/hack_secret", then overwrite truly secret uri. 6 if service_request.path() == "/hack_secret" { 7 let secret_uri = "/f18b211dd1744570bb643e800308b1e4" 8 .parse::<http::Uri>() 9 .unwrap(); 10 service_request 11 .match_info_mut() 12 .get_mut() 13 .update(&secret_uri); 14 } 15 Box::new(self.service.call(service_request).map(|mut res| { 16 // When this middleware is set, it append my custom header to received response. 17 let header_name = http::HeaderName::from_lowercase(b"hacker-code").unwrap(); 18 let header_value = 19 http::HeaderValue::from_str("69de96e2-d5b0-41d4-89b8-864222140e24").unwrap(); 20 res.headers_mut().append(header_name, header_value); 21 res 22 })) 23 } 24}
・また、テストに使用しているLoggerをprintln!()
に置き換えても該当部分の二重発火は観測できました。
コードの変更箇所
rust
1 // auth.rs LINE: 43 2 fn call(&self, req: ServiceRequest) -> Self::Future { 3 let logger = Rc::new(Logger::new(Some("TestMiddleware"))); 4 //let serv = self.service.clone(); 5 6 // Why is this method firing twice... 7 //logger.debug("task call from middleware start."); 8 println!("task call from middleware start."); // printlnマクロに変更 9 10 // Todo: Write Token Validator Task (2021/12/30 23:54) 11 12 self.service.call(req) 13 // Box::pin(async move { 14 // let res = serv.call(req).await?; 15 // println!("headers: {:?}", res.headers()); 16 // // ここにあるメソッドも二度実行された。 17 // logger.debug("cage task call from middleware response"); 18 // 19 // Ok(res) 20 // }) 21 }
回答1件
あなたの回答
tips
プレビュー