fix(server): use EmptyWriter for status codes that have no body
Previously, hyper was defaulting to Chunked which adds a Transfer-Encoding header, whenever there was no Content-Length header. RFC 7230 section 3.3.1 reads: ... A server MUST NOT send a Transfer-Encoding header field in any response with a status code of 1xx (Informational) or 204 (No Content). A server MUST NOT send a Transfer-Encoding header field in any 2xx (Successful) response to a CONNECT request ... This commit fixes the cases of 1xx (Informational), 204 (No Content) by using the EmptyWriter. It also uses EmptyWriter for 304 (NotModified) which should not have a body. It does NOT address the case of responses to CONNECT requests, or to HEAD requests which do not send a body. These cases cannot be determined using the data available in the response, and are left for future work.
This commit is contained in:
@@ -12,7 +12,7 @@ use time::now_utc;
|
|||||||
|
|
||||||
use header;
|
use header;
|
||||||
use http::h1::{CR, LF, LINE_ENDING, HttpWriter};
|
use http::h1::{CR, LF, LINE_ENDING, HttpWriter};
|
||||||
use http::h1::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter};
|
use http::h1::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter, EmptyWriter};
|
||||||
use status;
|
use status;
|
||||||
use net::{Fresh, Streaming};
|
use net::{Fresh, Streaming};
|
||||||
use version;
|
use version;
|
||||||
@@ -88,11 +88,14 @@ impl<'a, W: Any> Response<'a, W> {
|
|||||||
self.headers.set(header::Date(header::HttpDate(now_utc())));
|
self.headers.set(header::Date(header::HttpDate(now_utc())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let body_type = match self.status {
|
||||||
let mut body_type = Body::Chunked;
|
status::StatusCode::NoContent | status::StatusCode::NotModified => Body::Empty,
|
||||||
|
c if c.class() == status::StatusClass::Informational => Body::Empty,
|
||||||
if let Some(cl) = self.headers.get::<header::ContentLength>() {
|
_ => if let Some(cl) = self.headers.get::<header::ContentLength>() {
|
||||||
body_type = Body::Sized(**cl);
|
Body::Sized(**cl)
|
||||||
|
} else {
|
||||||
|
Body::Chunked
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// can't do in match above, thanks borrowck
|
// can't do in match above, thanks borrowck
|
||||||
@@ -177,7 +180,8 @@ impl<'a> Response<'a, Fresh> {
|
|||||||
let (version, body, status, headers) = self.deconstruct();
|
let (version, body, status, headers) = self.deconstruct();
|
||||||
let stream = match body_type {
|
let stream = match body_type {
|
||||||
Body::Chunked => ChunkedWriter(body.into_inner()),
|
Body::Chunked => ChunkedWriter(body.into_inner()),
|
||||||
Body::Sized(len) => SizedWriter(body.into_inner(), len)
|
Body::Sized(len) => SizedWriter(body.into_inner(), len),
|
||||||
|
Body::Empty => EmptyWriter(body.into_inner()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// "copy" to change the phantom type
|
// "copy" to change the phantom type
|
||||||
@@ -227,6 +231,7 @@ impl<'a> Write for Response<'a, Streaming> {
|
|||||||
enum Body {
|
enum Body {
|
||||||
Chunked,
|
Chunked,
|
||||||
Sized(u64),
|
Sized(u64),
|
||||||
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Any> Drop for Response<'a, T> {
|
impl<'a, T: Any> Drop for Response<'a, T> {
|
||||||
@@ -235,6 +240,7 @@ impl<'a, T: Any> Drop for Response<'a, T> {
|
|||||||
let mut body = match self.write_head() {
|
let mut body = match self.write_head() {
|
||||||
Ok(Body::Chunked) => ChunkedWriter(self.body.get_mut()),
|
Ok(Body::Chunked) => ChunkedWriter(self.body.get_mut()),
|
||||||
Ok(Body::Sized(len)) => SizedWriter(self.body.get_mut(), len),
|
Ok(Body::Sized(len)) => SizedWriter(self.body.get_mut(), len),
|
||||||
|
Ok(Body::Empty) => EmptyWriter(self.body.get_mut()),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("error dropping request: {:?}", e);
|
debug!("error dropping request: {:?}", e);
|
||||||
return;
|
return;
|
||||||
@@ -361,4 +367,23 @@ mod tests {
|
|||||||
"" // empty zero body
|
"" // empty zero body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_no_content() {
|
||||||
|
use std::io::Write;
|
||||||
|
use status::StatusCode;
|
||||||
|
let mut headers = Headers::new();
|
||||||
|
let mut stream = MockStream::new();
|
||||||
|
{
|
||||||
|
let mut res = Response::new(&mut stream, &mut headers);
|
||||||
|
*res.status_mut() = StatusCode::NoContent;
|
||||||
|
res.start().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
lines! { stream =
|
||||||
|
"HTTP/1.1 204 No Content",
|
||||||
|
_date,
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user