Skip to content
Open
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
21 changes: 21 additions & 0 deletions neqo-http3/src/connection_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5596,6 +5596,27 @@ mod tests {
assert_closed(&client, &Error::HttpId);
}

// A PUSH_PROMISE whose Push ID exceeds the maximum is an H3_ID_ERROR even when
// its header block blocks the QPACK decoder. max_push_id is 4, so push_id 5 is
// out of range; the header block refers to the dynamic table, so the client
// cannot decode it until the (withheld) encoder instructions arrive.
#[test]
fn exceed_max_push_id_promise_while_blocked() {
let (mut client, mut server, request_stream_id) = connect_and_send_request(true);

setup_server_side_encoder(&mut client, &mut server);

// The encoder instructions are withheld, so this push promise stays blocked.
drop(send_push_promise_using_encoder(
&mut client,
&mut server,
request_stream_id,
PushId::new(5),
));

assert_closed(&client, &Error::HttpId);
}

// Test that max_push_id is enforced when a push stream is received.
#[test]
fn exceed_max_push_id_push_stream() {
Expand Down
2 changes: 1 addition & 1 deletion neqo-http3/src/push_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ impl PushController {
)
}

fn check_push_id(&self, push_id: PushId) -> Res<()> {
pub(crate) fn check_push_id(&self, push_id: PushId) -> Res<()> {
// Check if push id is greater than what we allow.
if push_id > self.current_max_push_id {
qerror!("Push id is greater than current_max_push_id");
Expand Down
13 changes: 10 additions & 3 deletions neqo-http3/src/recv_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,16 @@ impl RecvMessage {
}

fn handle_push_promise(&mut self, push_id: PushId, header_block: Vec<u8>) -> Res<()> {
if self.push_handler.is_none() {
return Err(Error::HttpFrameUnexpected);
}
// Validate the Push ID before decoding or buffering. new_push_promise checks
// it too, but only after a successful decode; a header block that blocks the
// QPACK decoder defers that check indefinitely, so a Push ID above the maximum
// gets buffered instead of being the H3_ID_ERROR that RFC 9114, Section 7.2.5
// requires.
self.push_handler
.as_ref()
.ok_or(Error::HttpFrameUnexpected)?
.borrow()
.check_push_id(push_id)?;

if !self.blocked_push_promise.is_empty() {
self.blocked_push_promise.push_back(PushInfo {
Expand Down
Loading