Skip to content

Commit dbc204e

Browse files
authored
fix: check final response in poll_informational (#889)
* add failed testcase for poll_informational * check final response in poll_informational
1 parent ac5cdd0 commit dbc204e

2 files changed

Lines changed: 81 additions & 0 deletions

File tree

src/proto/streams/recv.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,13 @@ impl Recv {
377377
// If it's not, we put it back
378378
if let Some(event) = stream.pending_recv.pop_front(&mut self.buffer) {
379379
match event {
380+
Event::Headers(Client(response)) => {
381+
// Final response
382+
stream
383+
.pending_recv
384+
.push_front(&mut self.buffer, Event::Headers(Client(response)));
385+
return Poll::Ready(None);
386+
}
380387
Event::InformationalHeaders(Client(response)) => {
381388
// Found an informational response, return it
382389
return Poll::Ready(Some(Ok(response)));

tests/h2-tests/tests/informational_responses.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,80 @@ async fn invalid_informational_status_returns_error() {
233233
join(client, srv).await;
234234
}
235235

236+
#[tokio::test]
237+
async fn client_poll_informational_responses_none() {
238+
h2_support::trace_init!();
239+
let (io, mut srv) = mock::new();
240+
241+
let (sync_sender, sync_receiver) = tokio::sync::oneshot::channel::<()>();
242+
243+
let srv = async move {
244+
let recv_settings = srv.assert_client_handshake().await;
245+
assert_default_settings!(recv_settings);
246+
247+
srv.recv_frame(
248+
frames::headers(1)
249+
.request("GET", "https://example.com/")
250+
.eos(),
251+
)
252+
.await;
253+
254+
// Send final response directly
255+
srv.send_frame(frames::headers(1).response(StatusCode::OK))
256+
.await;
257+
258+
// The server may not close the stream immediately.
259+
// Let's simulate this by waiting from client.
260+
// Continue after the client received the response headers
261+
tokio::time::timeout(Duration::from_secs(4), sync_receiver)
262+
.await
263+
.expect("Client blocked on informational headers")
264+
.unwrap();
265+
srv.send_frame(frames::data(1, b"request body").eos()).await;
266+
};
267+
268+
let client = async move {
269+
let (client, connection) = client::handshake(io).await.expect("handshake");
270+
271+
let request = Request::builder()
272+
.method("GET")
273+
.uri("https://example.com/")
274+
.body(())
275+
.unwrap();
276+
277+
let (mut response_future, _) = client
278+
.ready()
279+
.await
280+
.unwrap()
281+
.send_request(request, true)
282+
.unwrap();
283+
284+
tokio::spawn(async move {
285+
connection.await.expect("connection error");
286+
});
287+
288+
// Poll for informational responses
289+
loop {
290+
match poll_fn(|cx| response_future.poll_informational(cx)).await {
291+
Some(Ok(rsp)) => panic!("Unexpected informational response {:?}", rsp),
292+
Some(Err(e)) => panic!("Error polling informational: {:?}", e),
293+
None => break,
294+
}
295+
}
296+
// Let the server continue sending responses
297+
sync_sender.send(()).unwrap();
298+
299+
// Get the final response
300+
let response = response_future.await.expect("response error");
301+
assert_eq!(response.status(), StatusCode::OK);
302+
let (_hdr, mut recv_stream) = response.into_parts();
303+
let data = recv_stream.data().await.unwrap().unwrap();
304+
assert_eq!("request body", data);
305+
};
306+
307+
join(srv, client).await;
308+
}
309+
236310
#[tokio::test]
237311
async fn client_poll_informational_responses() {
238312
h2_support::trace_init!();

0 commit comments

Comments
 (0)