From a1f914f46f797a13d3559a690223b732e5006b92 Mon Sep 17 00:00:00 2001 From: Anthony Ramine <123095+nox@users.noreply.github.com> Date: Wed, 24 Mar 2021 16:03:12 +0100 Subject: [PATCH] Skip 1xx frames in more states (#527) #524 Co-authored-by: Kornel --- src/proto/streams/state.rs | 25 +++++++++++---- tests/h2-tests/tests/client_request.rs | 44 ++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/proto/streams/state.rs b/src/proto/streams/state.rs index 08d4dba..3e739da 100644 --- a/src/proto/streams/state.rs +++ b/src/proto/streams/state.rs @@ -132,11 +132,8 @@ impl State { /// Opens the receive-half of the stream when a HEADERS frame is received. /// - /// is_informational: whether received a 1xx status code - /// /// Returns true if this transitions the state to Open. pub fn recv_open(&mut self, frame: &frame::Headers) -> Result { - let remote = Streaming; let mut initial = false; let eos = frame.is_end_stream(); @@ -149,7 +146,12 @@ impl State { } else { Open { local: AwaitingHeaders, - remote, + remote: if frame.is_informational() { + tracing::trace!("skipping 1xx response headers"); + AwaitingHeaders + } else { + Streaming + }, } } } @@ -158,6 +160,9 @@ impl State { if eos { Closed(Cause::EndStream) + } else if frame.is_informational() { + tracing::trace!("skipping 1xx response headers"); + ReservedRemote } else { HalfClosedLocal(Streaming) } @@ -169,7 +174,15 @@ impl State { if eos { HalfClosedRemote(local) } else { - Open { local, remote } + Open { + local, + remote: if frame.is_informational() { + tracing::trace!("skipping 1xx response headers"); + AwaitingHeaders + } else { + Streaming + }, + } } } HalfClosedLocal(AwaitingHeaders) => { @@ -179,7 +192,7 @@ impl State { tracing::trace!("skipping 1xx response headers"); HalfClosedLocal(AwaitingHeaders) } else { - HalfClosedLocal(remote) + HalfClosedLocal(Streaming) } } state => { diff --git a/tests/h2-tests/tests/client_request.rs b/tests/h2-tests/tests/client_request.rs index 7dc2680..b574df5 100644 --- a/tests/h2-tests/tests/client_request.rs +++ b/tests/h2-tests/tests/client_request.rs @@ -1257,6 +1257,50 @@ async fn early_hints() { join(srv, h2).await; } +#[tokio::test] +async fn informational_while_local_streaming() { + h2_support::trace_init!(); + let (io, mut srv) = mock::new(); + + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://example.com/")) + .await; + srv.send_frame(frames::headers(1).response(103)).await; + srv.send_frame(frames::headers(1).response(200).field("content-length", 2)) + .await; + srv.recv_frame(frames::data(1, "hello").eos()).await; + srv.send_frame(frames::data(1, "ok").eos()).await; + }; + + let h2 = async move { + let (mut client, h2) = client::Builder::new() + .handshake::<_, Bytes>(io) + .await + .unwrap(); + tokio::spawn(async { + h2.await.expect("connection failed"); + }); + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); + // don't EOS stream yet.. + let (response, mut body_tx) = client.send_request(request, false).unwrap(); + // eventual response is 200, not 103 + let resp = response.await.expect("response"); + // assert_eq!(resp.status(), 200); + // now we can end the stream + body_tx.send_data("hello".into(), true).expect("send_data"); + let mut body = resp.into_body(); + assert_eq!(body.data().await.unwrap().unwrap(), "ok"); + }; + + join(srv, h2).await; +} + const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0]; const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];