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:
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user