Strip body and content-length on 204, body on 304.

This works-around an issue where hyper incorrectly removes the body on
204 responses without removing the content-length or setting it to zero.

Resolves #2821.
This commit is contained in:
Sergio Benitez 2024-08-23 16:18:48 -07:00
parent d3323391ab
commit 9496b70e8c
4 changed files with 47 additions and 3 deletions

View File

@ -134,12 +134,17 @@ impl Rocket<Orbit> {
// Run the response fairings. // Run the response fairings.
self.fairings.handle_response(request, &mut response).await; self.fairings.handle_response(request, &mut response).await;
// Strip the body if this is a `HEAD` request. // Strip the body if this is a `HEAD` request or a 304 response.
if was_head_request { if was_head_request || response.status() == Status::NotModified {
response.strip_body(); response.strip_body();
} }
if let Some(size) = response.body_mut().size().await { // If the response status is 204, strip the body and its size (no
// content-length header). Otherwise, check if the body is sized and use
// that size to set the content-length headr appropriately.
if response.status() == Status::NoContent {
*response.body_mut() = crate::response::Body::unsized_none();
} else if let Some(size) = response.body_mut().size().await {
response.set_raw_header("Content-Length", size.to_string()); response.set_raw_header("Content-Length", size.to_string());
} }

View File

@ -107,6 +107,14 @@ impl<'r> Body<'r> {
/// The present value is `4096`. /// The present value is `4096`.
pub const DEFAULT_MAX_CHUNK: usize = 4096; pub const DEFAULT_MAX_CHUNK: usize = 4096;
pub(crate) fn unsized_none() -> Self {
Body {
size: None,
inner: Inner::None,
max_chunk: Body::DEFAULT_MAX_CHUNK,
}
}
pub(crate) fn with_sized<T>(body: T, preset_size: Option<usize>) -> Self pub(crate) fn with_sized<T>(body: T, preset_size: Option<usize>) -> Self
where T: AsyncReadSeek + Send + 'r where T: AsyncReadSeek + Send + 'r
{ {

View File

@ -7,3 +7,4 @@ pub mod mtls;
pub mod sni_resolver; pub mod sni_resolver;
pub mod tracing; pub mod tracing;
pub mod tls; pub mod tls;
pub mod no_content;

View File

@ -0,0 +1,30 @@
//! Ensure that responses with a status of 204 or 304 do not have a body, and
//! for the former, do not have a Content-Length header.
use crate::prelude::*;
use rocket::http::Status;
#[get("/<code>")]
fn status(code: u16) -> (Status, &'static [u8]) {
(Status::new(code), &[1, 2, 3, 4])
}
pub fn test_no_content() -> Result<()> {
let server = spawn!(Rocket::default().mount("/", routes![status]))?;
let client = Client::default();
let response = client.get(&server, "/204")?.send()?;
assert_eq!(response.status(), 204);
assert!(response.headers().get("Content-Length").is_none());
assert!(response.bytes()?.is_empty());
let response = client.get(&server, "/304")?.send()?;
assert_eq!(response.status(), 304);
assert_eq!(response.headers().get("Content-Length").unwrap(), "4");
assert!(response.bytes()?.is_empty());
Ok(())
}
register!(test_no_content);