From 131cd8b00e0dd47ecc783a3f2e99e5eea80c5fa0 Mon Sep 17 00:00:00 2001 From: 81reap Date: Tue, 18 Aug 2026 00:59:58 -0400 Subject: [PATCH] fix(request_body) :: return NULL when the request has no body' --- CHANGELOG.md | 1 + src/webserver/http_request_info.rs | 10 ++- tests/requests/mod.rs | 108 ++++++++++++++++------------- 3 files changed, 68 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64a4ca05..5144ae1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - `column` charts now display vertical bars instead of nothing at all. - `stacked` is now ignored on chart types that cannot stack, instead of displaying an empty chart. - Screen readers now announce the title of the modal component instead of an unnamed dialog. + - `sqlpage.request_body` and `sqlpage.request_body_base64` now return NULL when the request has no body. A body that cannot be read, such as one exceeding the payload limit, is now reported as an error instead of being silently replaced with an empty body. ## v0.45 diff --git a/src/webserver/http_request_info.rs b/src/webserver/http_request_info.rs index da628736..589603e9 100644 --- a/src/webserver/http_request_info.rs +++ b/src/webserver/http_request_info.rs @@ -183,9 +183,13 @@ async fn extract_post_data( } else { let body = actix_web::web::Bytes::from_request(http_req, payload) .await - .map(|bytes| bytes.to_vec()) - .unwrap_or_default(); - Ok((Vec::new(), Vec::new(), Some(body))) + .with_actix_error_status() + .context("could not read the request body")?; + Ok(( + Vec::new(), + Vec::new(), + (!body.is_empty()).then(|| body.to_vec()), + )) } } diff --git a/tests/requests/mod.rs b/tests/requests/mod.rs index d2b8fe75..e7883b74 100644 --- a/tests/requests/mod.rs +++ b/tests/requests/mod.rs @@ -4,37 +4,46 @@ use sqlpage::webserver::http::main_handler; use crate::common::get_request_to; -#[actix_web::test] -async fn test_request_body() -> actix_web::Result<()> { - let req = get_request_to("/tests/requests/request_body_test.sql") - .await? - .insert_header(("content-type", "text/plain")) - .set_payload("Hello, world!") - .to_srv_request(); +async fn rendered_page(req: actix_web::dev::ServiceRequest) -> actix_web::Result { let resp = main_handler(req).await?; - assert_eq!(resp.status(), StatusCode::OK); - let body = test::read_body(resp).await; - let body_str = String::from_utf8(body.to_vec()).unwrap(); + Ok(String::from_utf8(test::read_body(resp).await.to_vec()).unwrap()) +} + +#[actix_web::test] +async fn test_request_body() -> actix_web::Result<()> { + let page = rendered_page( + get_request_to("/tests/requests/request_body_test.sql") + .await? + .insert_header(("content-type", "text/plain")) + .set_payload("Hello, world!") + .to_srv_request(), + ) + .await?; assert!( - body_str.contains("Hello, world!"), - "{body_str}\nexpected to contain: Hello, world!" + page.contains("Hello, world!"), + "{page}\nexpected to contain: Hello, world!" ); - // Test with form data - should return NULL - let req = get_request_to("/tests/requests/request_body_test.sql") - .await? - .insert_header(("content-type", "application/x-www-form-urlencoded")) - .set_payload("key=value") - .to_srv_request(); - let resp = main_handler(req).await?; + let page = rendered_page( + get_request_to("/tests/requests/request_body_test.sql") + .await? + .insert_header(("content-type", "application/x-www-form-urlencoded")) + .set_payload("key=value") + .to_srv_request(), + ) + .await?; + assert!(page.contains("NULL"), "{page}\nexpected NULL for form data"); - assert_eq!(resp.status(), StatusCode::OK); - let body = test::read_body(resp).await; - let body_str = String::from_utf8(body.to_vec()).unwrap(); + let page = rendered_page( + get_request_to("/tests/requests/request_body_test.sql") + .await? + .to_srv_request(), + ) + .await?; assert!( - body_str.contains("NULL"), - "{body_str}\nexpected NULL for form data" + page.contains("NULL"), + "{page}\nexpected NULL when the request has no body" ); Ok(()) } @@ -45,35 +54,38 @@ async fn test_request_body_base64() -> actix_web::Result<()> { let expected_base64 = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &binary_data); - let req = get_request_to("/tests/requests/request_body_base64_test.sql") - .await? - .insert_header(("content-type", "application/octet-stream")) - .set_payload(binary_data) - .to_srv_request(); - let resp = main_handler(req).await?; - - assert_eq!(resp.status(), StatusCode::OK); - let body = test::read_body(resp).await; - let body_str = String::from_utf8(body.to_vec()).unwrap(); + let page = rendered_page( + get_request_to("/tests/requests/request_body_base64_test.sql") + .await? + .insert_header(("content-type", "application/octet-stream")) + .set_payload(binary_data) + .to_srv_request(), + ) + .await?; assert!( - body_str.contains(&expected_base64), - "{body_str}\nexpected to contain base64: {expected_base64}" + page.contains(&expected_base64), + "{page}\nexpected to contain base64: {expected_base64}" ); - // Test with form data - should return NULL - let req = get_request_to("/tests/requests/request_body_base64_test.sql") - .await? - .insert_header(("content-type", "application/x-www-form-urlencoded")) - .set_payload("key=value") - .to_srv_request(); - let resp = main_handler(req).await?; + let page = rendered_page( + get_request_to("/tests/requests/request_body_base64_test.sql") + .await? + .insert_header(("content-type", "application/x-www-form-urlencoded")) + .set_payload("key=value") + .to_srv_request(), + ) + .await?; + assert!(page.contains("NULL"), "{page}\nexpected NULL for form data"); - assert_eq!(resp.status(), StatusCode::OK); - let body = test::read_body(resp).await; - let body_str = String::from_utf8(body.to_vec()).unwrap(); + let page = rendered_page( + get_request_to("/tests/requests/request_body_base64_test.sql") + .await? + .to_srv_request(), + ) + .await?; assert!( - body_str.contains("NULL"), - "{body_str}\nexpected NULL for form data" + page.contains("NULL"), + "{page}\nexpected NULL when the request has no body" ); Ok(()) }