Skip to content

Commit 8099b44

Browse files
committed
Ignore 1xx frames
#515
1 parent 30ca832 commit 8099b44

File tree

4 files changed

+73
-12
lines changed

4 files changed

+73
-12
lines changed

src/frame/headers.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,11 @@ impl Headers {
254254
&mut self.header_block.pseudo
255255
}
256256

257+
/// Whether it has status 1xx
258+
pub(crate) fn is_informational(&self) -> bool {
259+
self.header_block.pseudo.is_informational()
260+
}
261+
257262
pub fn fields(&self) -> &HeaderMap {
258263
&self.header_block.fields
259264
}
@@ -599,6 +604,12 @@ impl Pseudo {
599604
pub fn set_authority(&mut self, authority: BytesStr) {
600605
self.authority = Some(authority);
601606
}
607+
608+
/// Whether it has status 1xx
609+
pub(crate) fn is_informational(&self) -> bool {
610+
self.status
611+
.map_or(false, |status| status.is_informational())
612+
}
602613
}
603614

604615
// ===== impl EncodingHeaderBlock =====

src/proto/streams/recv.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl Recv {
161161
counts: &mut Counts,
162162
) -> Result<(), RecvHeaderBlockError<Option<frame::Headers>>> {
163163
tracing::trace!("opening stream; init_window={}", self.init_window_sz);
164-
let is_initial = stream.state.recv_open(frame.is_end_stream())?;
164+
let is_initial = stream.state.recv_open(&frame)?;
165165

166166
if is_initial {
167167
// TODO: be smarter about this logic
@@ -226,15 +226,17 @@ impl Recv {
226226

227227
let stream_id = frame.stream_id();
228228
let (pseudo, fields) = frame.into_parts();
229-
let message = counts
230-
.peer()
231-
.convert_poll_message(pseudo, fields, stream_id)?;
232-
233-
// Push the frame onto the stream's recv buffer
234-
stream
235-
.pending_recv
236-
.push_back(&mut self.buffer, Event::Headers(message));
237-
stream.notify_recv();
229+
if !pseudo.is_informational() {
230+
let message = counts
231+
.peer()
232+
.convert_poll_message(pseudo, fields, stream_id)?;
233+
234+
// Push the frame onto the stream's recv buffer
235+
stream
236+
.pending_recv
237+
.push_back(&mut self.buffer, Event::Headers(message));
238+
stream.notify_recv();
239+
}
238240

239241
// Only servers can receive a headers frame that initiates the stream.
240242
// This is verified in `Streams` before calling this function.

src/proto/streams/state.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::io;
22

33
use crate::codec::UserError::*;
44
use crate::codec::{RecvError, UserError};
5-
use crate::frame::Reason;
5+
use crate::frame::{self, Reason};
66
use crate::proto::{self, PollReset};
77

88
use self::Inner::*;
@@ -132,10 +132,13 @@ impl State {
132132

133133
/// Opens the receive-half of the stream when a HEADERS frame is received.
134134
///
135+
/// is_informational: whether received a 1xx status code
136+
///
135137
/// Returns true if this transitions the state to Open.
136-
pub fn recv_open(&mut self, eos: bool) -> Result<bool, RecvError> {
138+
pub fn recv_open(&mut self, frame: &frame::Headers) -> Result<bool, RecvError> {
137139
let remote = Streaming;
138140
let mut initial = false;
141+
let eos = frame.is_end_stream();
139142

140143
self.inner = match self.inner {
141144
Idle => {
@@ -172,6 +175,9 @@ impl State {
172175
HalfClosedLocal(AwaitingHeaders) => {
173176
if eos {
174177
Closed(Cause::EndStream)
178+
} else if frame.is_informational() {
179+
tracing::trace!("skipping 1xx response headers");
180+
HalfClosedLocal(AwaitingHeaders)
175181
} else {
176182
HalfClosedLocal(remote)
177183
}

tests/h2-tests/tests/client_request.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,48 @@ async fn allow_empty_data_for_head() {
12151215
join(srv, h2).await;
12161216
}
12171217

1218+
#[tokio::test]
1219+
async fn early_hints() {
1220+
h2_support::trace_init!();
1221+
let (io, mut srv) = mock::new();
1222+
1223+
let srv = async move {
1224+
let settings = srv.assert_client_handshake().await;
1225+
assert_default_settings!(settings);
1226+
srv.recv_frame(
1227+
frames::headers(1)
1228+
.request("GET", "https://example.com/")
1229+
.eos(),
1230+
)
1231+
.await;
1232+
srv.send_frame(frames::headers(1).response(103)).await;
1233+
srv.send_frame(frames::headers(1).response(200).field("content-length", 2))
1234+
.await;
1235+
srv.send_frame(frames::data(1, "ok").eos()).await;
1236+
};
1237+
1238+
let h2 = async move {
1239+
let (mut client, h2) = client::Builder::new()
1240+
.handshake::<_, Bytes>(io)
1241+
.await
1242+
.unwrap();
1243+
tokio::spawn(async {
1244+
h2.await.expect("connection failed");
1245+
});
1246+
let request = Request::builder()
1247+
.method(Method::GET)
1248+
.uri("https://example.com/")
1249+
.body(())
1250+
.unwrap();
1251+
let (response, _) = client.send_request(request, true).unwrap();
1252+
let (ha, mut body) = response.await.unwrap().into_parts();
1253+
eprintln!("{:?}", ha);
1254+
assert_eq!(body.data().await.unwrap().unwrap(), "ok");
1255+
};
1256+
1257+
join(srv, h2).await;
1258+
}
1259+
12181260
const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
12191261
const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
12201262

0 commit comments

Comments
 (0)