Skip to content

Commit

Permalink
fix: Make content_length optional
Browse files Browse the repository at this point in the history
To make it possible to distinguish whether the reponse has no content or
contains an empty content, wraps `content_length` with `Option`. Without
having 'Content-Length: 0' for an empty content, a client may wait for
the server to close the connection or for a timeout to occur.

Signed-off-by: Takahiro Itazuri <itazur@amazon.com>
  • Loading branch information
zulinx86 committed Feb 20, 2024
1 parent e75dfa1 commit 1f9a265
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 6 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Added

- Implemented `Eq` for `common::headers::Encoding`, `common::headers::MediaType`,
- Implemented `Eq` for `common::headers::Encoding`, `common::headers::MediaType`,
`common::headers::Headers`, `common::HttpHeaderError`, `common::Body`, `common::Version`,
`common::RequestError`, `request::Uri`, `request::RequestLine`, `response::StatusCode`,
`response::ResponseHeaders`
Expand All @@ -11,6 +11,7 @@

- Mark `HttpServer::new_from_fd` as `unsafe` as the correctness of the unsafe code
in this method relies on an invariant the caller has to uphold
- Set `Content-Length: 0` when setting an empty body in the response.

# v0.1.0

Expand Down
26 changes: 21 additions & 5 deletions src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl StatusLine {
/// The content type can be updated with a call to `set_content_type`.
#[derive(Debug, PartialEq, Eq)]
pub struct ResponseHeaders {
content_length: i32,
content_length: Option<i32>,
content_type: MediaType,
deprecation: bool,
server: String,
Expand Down Expand Up @@ -151,15 +151,15 @@ impl ResponseHeaders {
self.write_allow_header(buf)?;
self.write_deprecation_header(buf)?;

if self.content_length != 0 {
if let Some(content_length) = self.content_length {
buf.write_all(Header::ContentType.raw())?;
buf.write_all(&[COLON, SP])?;
buf.write_all(self.content_type.as_str().as_bytes())?;
buf.write_all(&[CR, LF])?;

buf.write_all(Header::ContentLength.raw())?;
buf.write_all(&[COLON, SP])?;
buf.write_all(self.content_length.to_string().as_bytes())?;
buf.write_all(content_length.to_string().as_bytes())?;
buf.write_all(&[CR, LF])?;

if self.accept_encoding {
Expand All @@ -175,7 +175,7 @@ impl ResponseHeaders {

// Sets the content length to be written in the HTTP response.
fn set_content_length(&mut self, content_length: i32) {
self.content_length = content_length;
self.content_length = Some(content_length);
}

/// Sets the HTTP response header server.
Expand Down Expand Up @@ -296,7 +296,7 @@ impl Response {

/// Returns the Content Length of the response.
pub fn content_length(&self) -> i32 {
self.headers.content_length
self.headers.content_length.unwrap_or(0)
}

/// Returns the Content Type of the response.
Expand Down Expand Up @@ -480,4 +480,20 @@ mod tests {
let another_response = Response::new(Version::Http10, StatusCode::BadRequest);
assert_ne!(response, another_response);
}

#[test]
fn test_content_length() {
let mut response = Response::new(Version::Http10, StatusCode::OK);

// When not called `set_body()`, the content_length must be 0 and the body must be None.
assert_eq!(response.content_length(), 0);
assert_eq!(response.body(), None);

// With called `set_body()` with an empty content, the content_length must also be 0
// and the body is an empty content.
let body = Body::new("");
response.set_body(body.clone());
assert_eq!(response.content_length(), 0);
assert_eq!(response.body(), Some(body));
}
}

0 comments on commit 1f9a265

Please sign in to comment.