Enforce monotonic stream IDs for push promises (#275)

Previously, monotonic stream IDs (spec 5.1.1) for push promises were not
enforced. This was due to push promises going through an entirely
separate code path than normally initiated streams.

This patch unifies the code path for initializing streams via both
HEADERS and PUSH_PROMISE. This is done by first calling `recv.open` in
both cases.

Closes #272
This commit is contained in:
Carl Lerche
2018-05-14 10:20:57 -07:00
committed by GitHub
parent 173f9a67e7
commit bb454e017c
7 changed files with 227 additions and 108 deletions

View File

@@ -1,7 +1,7 @@
use {client, proto, server};
use codec::{Codec, RecvError, SendError, UserError};
use frame::{self, Frame, Reason};
use proto::{peer, Peer, WindowSize};
use proto::{peer, Peer, Open, WindowSize};
use super::{Buffer, Config, Counts, Prioritized, Recv, Send, Stream, StreamId};
use super::recv::RecvHeaderBlockError;
use super::store::{self, Entry, Resolve, Store};
@@ -127,6 +127,8 @@ where
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
// The GOAWAY process has begun. All streams with a greater ID than
// specified as part of GOAWAY should be ignored.
if id > me.actions.recv.max_stream_id() {
trace!("id ({:?}) > max_stream_id ({:?}), ignoring HEADERS", id, me.actions.recv.max_stream_id());
return Ok(());
@@ -134,7 +136,7 @@ where
let key = match me.store.find_entry(id) {
Entry::Occupied(e) => e.key(),
Entry::Vacant(e) => match me.actions.recv.open(id, &mut me.counts)? {
Entry::Vacant(e) => match me.actions.recv.open(id, Open::Headers, &mut me.counts)? {
Some(stream_id) => {
let stream = Stream::new(
stream_id,
@@ -182,7 +184,9 @@ where
Reason::REFUSED_STREAM,
counts,
&mut actions.task);
actions.recv.enqueue_reset_expiration(stream, counts);
Ok(())
} else {
Err(RecvError::Stream {
@@ -215,6 +219,8 @@ where
let stream = match me.store.find_mut(&id) {
Some(stream) => stream,
None => {
// The GOAWAY process has begun. All streams with a greater ID
// than specified as part of GOAWAY should be ignored.
if id > me.actions.recv.max_stream_id() {
trace!("id ({:?}) > max_stream_id ({:?}), ignoring DATA", id, me.actions.recv.max_stream_id());
return Ok(());
@@ -254,6 +260,8 @@ where
return Err(RecvError::Connection(Reason::PROTOCOL_ERROR));
}
// The GOAWAY process has begun. All streams with a greater ID than
// specified as part of GOAWAY should be ignored.
if id > me.actions.recv.max_stream_id() {
trace!("id ({:?}) > max_stream_id ({:?}), ignoring RST_STREAM", id, me.actions.recv.max_stream_id());
return Ok(());
@@ -402,44 +410,66 @@ where
let id = frame.stream_id();
let promised_id = frame.promised_id();
let res = {
let stream = match me.store.find_mut(&id) {
Some(stream) => stream.key(),
None => return Err(RecvError::Connection(Reason::PROTOCOL_ERROR)),
};
// First, ensure that the initiating stream is still in a valid state.
let parent_key = match me.store.find_mut(&id) {
Some(stream) => {
// The GOAWAY process has begun. All streams with a greater ID
// than specified as part of GOAWAY should be ignored.
if id > me.actions.recv.max_stream_id() {
trace!("id ({:?}) > max_stream_id ({:?}), ignoring PUSH_PROMISE", id, me.actions.recv.max_stream_id());
return Ok(());
}
if me.counts.peer().is_server() {
// The remote is a client and cannot reserve
trace!("recv_push_promise; error remote is client");
return Err(RecvError::Connection(Reason::PROTOCOL_ERROR));
// The stream must be receive open
stream.state.ensure_recv_open()?;
stream.key()
}
me.actions.recv.recv_push_promise(frame,
&me.actions.send,
stream,
&mut me.store)
None => return Err(RecvError::Connection(Reason::PROTOCOL_ERROR)),
};
if let Err(err) = res {
if let Some(ref mut new_stream) = me.store.find_mut(&promised_id) {
// Ensure that we can reserve streams
me.actions.recv.ensure_can_reserve()?;
// Next, open the stream.
//
// If `None` is returned, then the stream is being refused. There is no
// further work to be done.
if me.actions.recv.open(promised_id, Open::PushPromise, &mut me.counts)?.is_none() {
return Ok(());
}
// Create a scope
let child_key = {
// Create state for the stream
let stream = me.store.insert(promised_id, {
Stream::new(
promised_id,
me.actions.send.init_window_sz(),
me.actions.recv.init_window_sz())
});
let actions = &mut me.actions;
me.counts.transition(stream, |counts, stream| {
let res = actions.recv.recv_push_promise(frame, stream);
let mut send_buffer = self.send_buffer.inner.lock().unwrap();
me.actions.reset_on_recv_stream_err(
&mut *send_buffer,
new_stream,
&mut me.counts,
Err(err))
} else {
// If there was a stream error, the stream should have been stored
// so we can track sending a reset.
//
// Otherwise, this MUST be an connection error.
assert!(!err.is_stream_error());
Err(err)
}
} else {
res
}
actions.reset_on_recv_stream_err(&mut *send_buffer, stream, counts, res)
.map(|_| stream.key())
})?
};
// Push the stream... this requires a bit of indirection to make
// the borrow checker happy.
let mut ppp = me.store[parent_key].pending_push_promises.take();
ppp.push(&mut me.store.resolve(child_key));
let parent = &mut me.store[parent_key];
parent.pending_push_promises = ppp;
parent.notify_recv();
Ok(())
}
pub fn next_incoming(&mut self) -> Option<StreamRef<B>> {
@@ -633,7 +663,7 @@ where
let key = match me.store.find_entry(id) {
Entry::Occupied(e) => e.key(),
Entry::Vacant(e) => match me.actions.recv.open(id, &mut me.counts) {
Entry::Vacant(e) => match me.actions.recv.open(id, Open::Headers, &mut me.counts) {
Ok(Some(stream_id)) => {
let stream = Stream::new(stream_id, 0, 0);