diff --git a/Cargo.lock b/Cargo.lock index 273422d0..23604016 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -407,6 +407,7 @@ dependencies = [ "tower-layer", "tower-service", "tracing", + "tracing-subscriber", "typed-json", ] diff --git a/axum-extra/Cargo.toml b/axum-extra/Cargo.toml index 5b34bd28..64b4ea95 100644 --- a/axum-extra/Cargo.toml +++ b/axum-extra/Cargo.toml @@ -38,7 +38,7 @@ multipart = ["dep:multer", "dep:fastrand"] protobuf = ["dep:prost"] scheme = [] query = ["dep:form_urlencoded", "dep:serde_html_form", "dep:serde_path_to_error"] -tracing = ["axum-core/tracing", "axum/tracing"] +tracing = ["axum-core/tracing", "axum/tracing", "dep:tracing"] typed-header = ["dep:headers"] typed-routing = ["dep:axum-macros", "dep:percent-encoding", "dep:serde_html_form", "dep:form_urlencoded"] @@ -92,6 +92,7 @@ serde_json = "1.0.71" tokio = { version = "1.14", features = ["full"] } tower = { version = "0.5.2", features = ["util"] } tower-http = { version = "0.6.0", features = ["map-response-body", "timeout"] } +tracing-subscriber = "0.3.19" [lints] workspace = true diff --git a/axum-extra/src/extract/multipart.rs b/axum-extra/src/extract/multipart.rs index 2fb5e400..21769824 100644 --- a/axum-extra/src/extract/multipart.rs +++ b/axum-extra/src/extract/multipart.rs @@ -243,12 +243,13 @@ impl MultipartError { /// Get the response body text used for this rejection. pub fn body_text(&self) -> String { + let body = self.source.to_string(); axum_core::__log_rejection!( rejection_type = Self, - body_text = self.body_text(), + body_text = body, status = self.status(), ); - self.source.to_string() + body } /// Get the status code used for this rejection. @@ -398,4 +399,43 @@ mod tests { let res = client.post("/").multipart(form).await; assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE); } + + #[tokio::test] + #[cfg(feature = "tracing")] + async fn body_too_large_with_tracing() { + const BYTES: &[u8] = "🦀".as_bytes(); + + async fn handle(mut multipart: Multipart) -> impl IntoResponse { + let result: Result<(), MultipartError> = async { + while let Some(field) = multipart.next_field().await? { + field.bytes().await?; + } + Ok(()) + } + .await; + + let subscriber = tracing_subscriber::FmtSubscriber::builder() + .with_max_level(tracing::level_filters::LevelFilter::TRACE) + .with_writer(std::io::sink) + .finish(); + + let guard = tracing::subscriber::set_default(subscriber); + let response = result.into_response(); + drop(guard); + + response + } + + let app = Router::new() + .route("/", post(handle)) + .layer(DefaultBodyLimit::max(BYTES.len() - 1)); + + let client = TestClient::new(app); + + let form = + reqwest::multipart::Form::new().part("file", reqwest::multipart::Part::bytes(BYTES)); + + let res = client.post("/").multipart(form).await; + assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE); + } }