Skip to content

do not send body content in reply to HEAD requests #124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 30, 2020
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
29 changes: 19 additions & 10 deletions src/server/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use async_std::io;
use async_std::io::prelude::*;
use async_std::task::{Context, Poll};
use http_types::headers::{CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
use http_types::Response;
use http_types::{Method, Response};

use crate::chunked::ChunkedEncoder;
use crate::date::fmt_http_date;
Expand Down Expand Up @@ -36,6 +36,8 @@ pub(crate) struct Encoder {
body_bytes_written: usize,
/// An encoder for chunked encoding.
chunked: ChunkedEncoder,
/// the http method that this response is in reply to
method: Method,
}

#[derive(Debug)]
Expand Down Expand Up @@ -69,7 +71,7 @@ impl Read for Encoder {

impl Encoder {
/// Create a new instance of Encoder.
pub(crate) fn new(res: Response) -> Self {
pub(crate) fn new(res: Response, method: Method) -> Self {
Self {
res,
depth: 0,
Expand All @@ -80,6 +82,7 @@ impl Encoder {
body_len: 0,
body_bytes_written: 0,
chunked: ChunkedEncoder::new(),
method,
}
}

Expand All @@ -97,7 +100,7 @@ impl Encoder {
match self.state {
Start => assert!(matches!(state, ComputeHead)),
ComputeHead => assert!(matches!(state, EncodeHead)),
EncodeHead => assert!(matches!(state, EncodeChunkedBody | EncodeFixedBody)),
EncodeHead => assert!(matches!(state, EncodeChunkedBody | EncodeFixedBody | End)),
EncodeFixedBody => assert!(matches!(state, End)),
EncodeChunkedBody => assert!(matches!(state, End)),
End => panic!("No state transitions allowed after the ServerEncoder has ended"),
Expand Down Expand Up @@ -176,14 +179,20 @@ impl Encoder {
// If we've read the total length of the head we're done
// reading the head and can transition to reading the body
if self.head_bytes_written == head_len {
// The response length lets us know if we are encoding
// our body in chunks or not
match self.res.len() {
Some(body_len) => {
self.body_len = body_len;
self.dispatch(State::EncodeFixedBody, cx, buf)
if self.method == Method::Head {
// If we are responding to a HEAD request, we MUST NOT send
// body content
self.dispatch(State::End, cx, buf)
Comment on lines +183 to +185
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

} else {
// The response length lets us know if we are encoding
// our body in chunks or not
match self.res.len() {
Some(body_len) => {
self.body_len = body_len;
self.dispatch(State::EncodeFixedBody, cx, buf)
}
None => self.dispatch(State::EncodeChunkedBody, cx, buf),
}
None => self.dispatch(State::EncodeChunkedBody, cx, buf),
}
} else {
// If we haven't read the entire header it means `buf` isn't
Expand Down
4 changes: 3 additions & 1 deletion src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@ where
}
};

let method = req.method();
// Pass the request to the endpoint and encode the response.
let res = endpoint(req).await?;
let mut encoder = Encoder::new(res);

let mut encoder = Encoder::new(res, method);

// Stream the response to the writer.
io::copy(&mut encoder, &mut io).await?;
Expand Down
4 changes: 4 additions & 0 deletions tests/fixtures/head_request.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
HEAD / HTTP/1.1
host: example.com
user-agent: curl/7.54.0

5 changes: 5 additions & 0 deletions tests/fixtures/head_response.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
HTTP/1.1 200 OK
content-length: 5
date: {DATE}
content-type: text/plain;charset=utf-8

11 changes: 11 additions & 0 deletions tests/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,14 @@ async fn test_invalid_trailer() {

assert!(case.read_result().await.is_empty());
}
#[async_std::test]
async fn empty_body_for_head_requests() {
let case =
TestCase::new_server("fixtures/head_request.txt", "fixtures/head_response.txt").await;

async_h1::accept(case.clone(), |_| async { Ok("hello".into()) })
.await
.unwrap();

case.assert().await;
}