Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore 1xx frames #521

Merged
merged 1 commit into from
Feb 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/frame/headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ impl Headers {
&mut self.header_block.pseudo
}

/// Whether it has status 1xx
pub(crate) fn is_informational(&self) -> bool {
self.header_block.pseudo.is_informational()
}

pub fn fields(&self) -> &HeaderMap {
&self.header_block.fields
}
Expand Down Expand Up @@ -599,6 +604,12 @@ impl Pseudo {
pub fn set_authority(&mut self, authority: BytesStr) {
self.authority = Some(authority);
}

/// Whether it has status 1xx
pub(crate) fn is_informational(&self) -> bool {
self.status
.map_or(false, |status| status.is_informational())
}
}

// ===== impl EncodingHeaderBlock =====
Expand Down
22 changes: 12 additions & 10 deletions src/proto/streams/recv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ impl Recv {
counts: &mut Counts,
) -> Result<(), RecvHeaderBlockError<Option<frame::Headers>>> {
tracing::trace!("opening stream; init_window={}", self.init_window_sz);
let is_initial = stream.state.recv_open(frame.is_end_stream())?;
let is_initial = stream.state.recv_open(&frame)?;

if is_initial {
// TODO: be smarter about this logic
Expand Down Expand Up @@ -226,15 +226,17 @@ impl Recv {

let stream_id = frame.stream_id();
let (pseudo, fields) = frame.into_parts();
let message = counts
.peer()
.convert_poll_message(pseudo, fields, stream_id)?;

// Push the frame onto the stream's recv buffer
stream
.pending_recv
.push_back(&mut self.buffer, Event::Headers(message));
stream.notify_recv();
if !pseudo.is_informational() {
let message = counts
.peer()
.convert_poll_message(pseudo, fields, stream_id)?;

// Push the frame onto the stream's recv buffer
stream
.pending_recv
.push_back(&mut self.buffer, Event::Headers(message));
stream.notify_recv();
}

// Only servers can receive a headers frame that initiates the stream.
// This is verified in `Streams` before calling this function.
Expand Down
10 changes: 8 additions & 2 deletions src/proto/streams/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::io;

use crate::codec::UserError::*;
use crate::codec::{RecvError, UserError};
use crate::frame::Reason;
use crate::frame::{self, Reason};
use crate::proto::{self, PollReset};

use self::Inner::*;
Expand Down Expand Up @@ -132,10 +132,13 @@ 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, eos: bool) -> Result<bool, RecvError> {
pub fn recv_open(&mut self, frame: &frame::Headers) -> Result<bool, RecvError> {
let remote = Streaming;
let mut initial = false;
let eos = frame.is_end_stream();

self.inner = match self.inner {
Idle => {
Expand Down Expand Up @@ -172,6 +175,9 @@ impl State {
HalfClosedLocal(AwaitingHeaders) => {
if eos {
Closed(Cause::EndStream)
} else if frame.is_informational() {
tracing::trace!("skipping 1xx response headers");
HalfClosedLocal(AwaitingHeaders)
} else {
HalfClosedLocal(remote)
}
Expand Down
42 changes: 42 additions & 0 deletions tests/h2-tests/tests/client_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,48 @@ async fn allow_empty_data_for_head() {
join(srv, h2).await;
}

#[tokio::test]
async fn early_hints() {
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("GET", "https://example.com/")
.eos(),
)
.await;
srv.send_frame(frames::headers(1).response(103)).await;
srv.send_frame(frames::headers(1).response(200).field("content-length", 2))
.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::GET)
.uri("https://example.com/")
.body(())
.unwrap();
let (response, _) = client.send_request(request, true).unwrap();
let (ha, mut body) = response.await.unwrap().into_parts();
eprintln!("{:?}", ha);
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];

Expand Down