fix(http2): received Body::size_hint() now return 0 if implicitly empty (#2715)
An HTTP/2 stream may include a set of headers, and a flag signalling END-STREAM, even if a `content-length` isn't included. hyper wouldn't notice, and so the `Body` would report a size-hint of `0..MAX`. hyper now notices that the stream is ended, and couldn't possibly include any bytes for the body, and thus will give a size-hint of `0` exactly.
This commit is contained in:
@@ -204,9 +204,14 @@ impl Body {
|
|||||||
#[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
|
#[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
|
||||||
pub(crate) fn h2(
|
pub(crate) fn h2(
|
||||||
recv: h2::RecvStream,
|
recv: h2::RecvStream,
|
||||||
content_length: DecodedLength,
|
mut content_length: DecodedLength,
|
||||||
ping: ping::Recorder,
|
ping: ping::Recorder,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
// If the stream is already EOS, then the "unknown length" is clearly
|
||||||
|
// actually ZERO.
|
||||||
|
if !content_length.is_exact() && recv.is_end_stream() {
|
||||||
|
content_length = DecodedLength::ZERO;
|
||||||
|
}
|
||||||
let body = Body::new(Kind::H2 {
|
let body = Body::new(Kind::H2 {
|
||||||
ping,
|
ping,
|
||||||
content_length,
|
content_length,
|
||||||
|
|||||||
@@ -68,6 +68,16 @@ impl DecodedLength {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether this represents an exact length.
|
||||||
|
///
|
||||||
|
/// This includes 0, which of course is an exact known length.
|
||||||
|
///
|
||||||
|
/// It would return false if "chunked" or otherwise size-unknown.
|
||||||
|
#[cfg(feature = "http2")]
|
||||||
|
pub(crate) fn is_exact(&self) -> bool {
|
||||||
|
self.0 <= MAX_LEN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for DecodedLength {
|
impl fmt::Debug for DecodedLength {
|
||||||
|
|||||||
@@ -484,12 +484,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// automatically set Content-Length from body...
|
|
||||||
if let Some(len) = body.size_hint().exact() {
|
|
||||||
headers::set_content_length_if_missing(res.headers_mut(), len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !body.is_end_stream() {
|
if !body.is_end_stream() {
|
||||||
|
// automatically set Content-Length from body...
|
||||||
|
if let Some(len) = body.size_hint().exact() {
|
||||||
|
headers::set_content_length_if_missing(res.headers_mut(), len);
|
||||||
|
}
|
||||||
|
|
||||||
let body_tx = reply!(me, res, false);
|
let body_tx = reply!(me, res, false);
|
||||||
H2StreamState::Body {
|
H2StreamState::Body {
|
||||||
pipe: PipeToSendStream::new(body, body_tx),
|
pipe: PipeToSendStream::new(body, body_tx),
|
||||||
|
|||||||
@@ -361,6 +361,26 @@ mod response_body_lengths {
|
|||||||
assert_eq!(res.headers().get("content-length").unwrap(), "10");
|
assert_eq!(res.headers().get("content-length").unwrap(), "10");
|
||||||
assert_eq!(res.body().size_hint().exact(), Some(10));
|
assert_eq!(res.body().size_hint().exact(), Some(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn http2_implicit_empty_size_hint() {
|
||||||
|
use http_body::Body;
|
||||||
|
|
||||||
|
let server = serve();
|
||||||
|
let addr_str = format!("http://{}", server.addr());
|
||||||
|
server.reply();
|
||||||
|
|
||||||
|
let client = Client::builder()
|
||||||
|
.http2_only(true)
|
||||||
|
.build_http::<hyper::Body>();
|
||||||
|
let uri = addr_str
|
||||||
|
.parse::<hyper::Uri>()
|
||||||
|
.expect("server addr should parse");
|
||||||
|
|
||||||
|
let res = client.get(uri).await.unwrap();
|
||||||
|
assert_eq!(res.headers().get("content-length"), None);
|
||||||
|
assert_eq!(res.body().size_hint().exact(), Some(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user