Skip to content

Commit 194e6f9

Browse files
authored
fix(client): early server response shouldn't propagate NO_ERROR (#3275)
Closes #2872
1 parent 53b8372 commit 194e6f9

File tree

3 files changed

+68
-3
lines changed

3 files changed

+68
-3
lines changed

src/body/incoming.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,16 @@ impl Body for Incoming {
201201
ping.record_data(bytes.len());
202202
return Poll::Ready(Some(Ok(Frame::data(bytes))));
203203
}
204-
Some(Err(e)) => return Poll::Ready(Some(Err(crate::Error::new_body(e)))),
204+
Some(Err(e)) => {
205+
return match e.reason() {
206+
// These reasons should cause the body reading to stop, but not fail it.
207+
// The same logic as for `Read for H2Upgraded` is applied here.
208+
Some(h2::Reason::NO_ERROR) | Some(h2::Reason::CANCEL) => {
209+
Poll::Ready(None)
210+
}
211+
_ => Poll::Ready(Some(Err(crate::Error::new_body(e)))),
212+
};
213+
}
205214
None => {
206215
*data_done = true;
207216
// fall through to trailers

src/proto/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub(crate) enum BodyLength {
5050
Unknown,
5151
}
5252

53-
/// Status of when a Disaptcher future completes.
53+
/// Status of when a Dispatcher future completes.
5454
pub(crate) enum Dispatched {
5555
/// Dispatcher completely shutdown connection.
5656
Shutdown,

tests/client.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1338,7 +1338,7 @@ mod conn {
13381338
use bytes::{Buf, Bytes};
13391339
use futures_channel::{mpsc, oneshot};
13401340
use futures_util::future::{self, poll_fn, FutureExt, TryFutureExt};
1341-
use http_body_util::{BodyExt, Empty, StreamBody};
1341+
use http_body_util::{BodyExt, Empty, Full, StreamBody};
13421342
use hyper::rt::Timer;
13431343
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
13441344
use tokio::net::{TcpListener as TkTcpListener, TcpStream};
@@ -2126,6 +2126,62 @@ mod conn {
21262126
.expect("client should be open");
21272127
}
21282128

2129+
#[tokio::test]
2130+
async fn http2_responds_before_consuming_request_body() {
2131+
// Test that a early-response from server works correctly (request body wasn't fully consumed).
2132+
// https://github.com/hyperium/hyper/issues/2872
2133+
use hyper::service::service_fn;
2134+
2135+
let _ = pretty_env_logger::try_init();
2136+
2137+
let (listener, addr) = setup_tk_test_server().await;
2138+
2139+
// Spawn an HTTP2 server that responds before reading the whole request body.
2140+
// It's normal case to decline the request due to headers or size of the body.
2141+
tokio::spawn(async move {
2142+
let sock = TokioIo::new(listener.accept().await.unwrap().0);
2143+
hyper::server::conn::http2::Builder::new(TokioExecutor)
2144+
.timer(TokioTimer)
2145+
.serve_connection(
2146+
sock,
2147+
service_fn(|_req| async move {
2148+
Ok::<_, hyper::Error>(Response::new(Full::new(Bytes::from(
2149+
"No bread for you!",
2150+
))))
2151+
}),
2152+
)
2153+
.await
2154+
.expect("serve_connection");
2155+
});
2156+
2157+
let io = tcp_connect(&addr).await.expect("tcp connect");
2158+
let (mut client, conn) = conn::http2::Builder::new(TokioExecutor)
2159+
.timer(TokioTimer)
2160+
.handshake(io)
2161+
.await
2162+
.expect("http handshake");
2163+
2164+
tokio::spawn(async move {
2165+
conn.await.expect("client conn shouldn't error");
2166+
});
2167+
2168+
// Use a channel to keep request stream open
2169+
let (_tx, recv) = mpsc::channel::<Result<Frame<Bytes>, Box<dyn Error + Send + Sync>>>(0);
2170+
let req = Request::post("/a").body(StreamBody::new(recv)).unwrap();
2171+
let resp = client.send_request(req).await.expect("send_request");
2172+
assert!(resp.status().is_success());
2173+
2174+
let mut body = String::new();
2175+
concat(resp.into_body())
2176+
.await
2177+
.unwrap()
2178+
.reader()
2179+
.read_to_string(&mut body)
2180+
.unwrap();
2181+
2182+
assert_eq!(&body, "No bread for you!");
2183+
}
2184+
21292185
#[tokio::test]
21302186
async fn h2_connect() {
21312187
let (listener, addr) = setup_tk_test_server().await;

0 commit comments

Comments
 (0)