Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
10 changes: 7 additions & 3 deletions src/webserver/http_request_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
))
}
}

Expand Down
108 changes: 60 additions & 48 deletions tests/requests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
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(())
}
Expand All @@ -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(())
}
Expand Down