質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

1回答

1202閲覧

Actix-webのミドルウェア内のメソッドが二度発火してしまう。

HalsekiRaika

総合スコア6

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

1グッド

0クリップ

投稿2021/12/31 08:44

編集2022/01/01 06:56

前提・実現したいこと

初質問で、文章がおかしい部分があると思います。申し訳ないです。

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 }
equal-l2👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

equal-l2

2022/01/01 02:13

actix_middleware_test を実行してみましたが、middlewareのコードが2度呼ばれる不具合は再現できませんでした stableで実行しています
guest

回答1

0

自己解決

二重で発火する原因が分かりました。
今回レスポンスを確認するためのデバッグにGoogleChromeを使用していたのですが、今回はそれが原因でした。
Chromeでリクエストする際に通常のリソースリクエスト一緒にfavicon.icoを入手するためにもう一度リクエストを送信している故に二重で発火しているように見えているということが今回原因でした。
WebAPI開発用のツールで著名なPostmanを使用しデバッグした際、二重で発火するということは起こらず、正しい処理が走りました。

イメージ説明

テストする際のツールが原因というのは完全に見逃していました。
今後開発する際の反省点とさせて頂きます。

投稿2022/01/01 12:04

HalsekiRaika

総合スコア6

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問