Skip to content

Commit 84b78b6

Browse files
authoredDec 6, 2021
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.
1 parent ce82425 commit 84b78b6

File tree

4 files changed

+41
-5
lines changed

4 files changed

+41
-5
lines changed
 

‎src/body/body.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,14 @@ impl Body {
204204
#[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
205205
pub(crate) fn h2(
206206
recv: h2::RecvStream,
207-
content_length: DecodedLength,
207+
mut content_length: DecodedLength,
208208
ping: ping::Recorder,
209209
) -> Self {
210+
// If the stream is already EOS, then the "unknown length" is clearly
211+
// actually ZERO.
212+
if !content_length.is_exact() && recv.is_end_stream() {
213+
content_length = DecodedLength::ZERO;
214+
}
210215
let body = Body::new(Kind::H2 {
211216
ping,
212217
content_length,

‎src/body/length.rs

+10
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ impl DecodedLength {
6868
}
6969
}
7070
}
71+
72+
/// Returns whether this represents an exact length.
73+
///
74+
/// This includes 0, which of course is an exact known length.
75+
///
76+
/// It would return false if "chunked" or otherwise size-unknown.
77+
#[cfg(feature = "http2")]
78+
pub(crate) fn is_exact(&self) -> bool {
79+
self.0 <= MAX_LEN
80+
}
7181
}
7282

7383
impl fmt::Debug for DecodedLength {

‎src/proto/h2/server.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -484,12 +484,13 @@ where
484484
}
485485
}
486486

487-
// automatically set Content-Length from body...
488-
if let Some(len) = body.size_hint().exact() {
489-
headers::set_content_length_if_missing(res.headers_mut(), len);
490-
}
491487

492488
if !body.is_end_stream() {
489+
// automatically set Content-Length from body...
490+
if let Some(len) = body.size_hint().exact() {
491+
headers::set_content_length_if_missing(res.headers_mut(), len);
492+
}
493+
493494
let body_tx = reply!(me, res, false);
494495
H2StreamState::Body {
495496
pipe: PipeToSendStream::new(body, body_tx),

‎tests/server.rs

+20
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,26 @@ mod response_body_lengths {
361361
assert_eq!(res.headers().get("content-length").unwrap(), "10");
362362
assert_eq!(res.body().size_hint().exact(), Some(10));
363363
}
364+
365+
#[tokio::test]
366+
async fn http2_implicit_empty_size_hint() {
367+
use http_body::Body;
368+
369+
let server = serve();
370+
let addr_str = format!("http://{}", server.addr());
371+
server.reply();
372+
373+
let client = Client::builder()
374+
.http2_only(true)
375+
.build_http::<hyper::Body>();
376+
let uri = addr_str
377+
.parse::<hyper::Uri>()
378+
.expect("server addr should parse");
379+
380+
let res = client.get(uri).await.unwrap();
381+
assert_eq!(res.headers().get("content-length"), None);
382+
assert_eq!(res.body().size_hint().exact(), Some(0));
383+
}
364384
}
365385

366386
#[test]

0 commit comments

Comments
 (0)
Please sign in to comment.