Notify RecvStream tasks if SendStream sends a local reset
This commit is contained in:
@@ -671,15 +671,9 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
let stream = me.store.resolve(key);
|
let stream = me.store.resolve(key);
|
||||||
let actions = &mut me.actions;
|
|
||||||
let mut send_buffer = self.send_buffer.inner.lock().unwrap();
|
let mut send_buffer = self.send_buffer.inner.lock().unwrap();
|
||||||
let send_buffer = &mut *send_buffer;
|
let send_buffer = &mut *send_buffer;
|
||||||
|
me.actions.send_reset(stream, reason, &mut me.counts, send_buffer);
|
||||||
me.counts.transition(stream, |counts, stream| {
|
|
||||||
actions.send.send_reset(
|
|
||||||
reason, send_buffer, stream, counts, &mut actions.task);
|
|
||||||
actions.recv.enqueue_reset_expiration(stream, counts)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_go_away(&mut self, last_processed_id: StreamId) {
|
pub fn send_go_away(&mut self, last_processed_id: StreamId) {
|
||||||
@@ -848,14 +842,10 @@ impl<B> StreamRef<B> {
|
|||||||
let me = &mut *me;
|
let me = &mut *me;
|
||||||
|
|
||||||
let stream = me.store.resolve(self.opaque.key);
|
let stream = me.store.resolve(self.opaque.key);
|
||||||
let actions = &mut me.actions;
|
|
||||||
let mut send_buffer = self.send_buffer.inner.lock().unwrap();
|
let mut send_buffer = self.send_buffer.inner.lock().unwrap();
|
||||||
let send_buffer = &mut *send_buffer;
|
let send_buffer = &mut *send_buffer;
|
||||||
|
|
||||||
me.counts.transition(stream, |counts, stream| {
|
me.actions.send_reset(stream, reason, &mut me.counts, send_buffer);
|
||||||
actions.send.send_reset(
|
|
||||||
reason, send_buffer, stream, counts, &mut actions.task)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_response(
|
pub fn send_response(
|
||||||
@@ -1178,6 +1168,22 @@ impl<B> SendBuffer<B> {
|
|||||||
// ===== impl Actions =====
|
// ===== impl Actions =====
|
||||||
|
|
||||||
impl Actions {
|
impl Actions {
|
||||||
|
fn send_reset<B>(
|
||||||
|
&mut self,
|
||||||
|
stream: store::Ptr,
|
||||||
|
reason: Reason,
|
||||||
|
counts: &mut Counts,
|
||||||
|
send_buffer: &mut Buffer<Frame<B>>,
|
||||||
|
) {
|
||||||
|
counts.transition(stream, |counts, stream| {
|
||||||
|
self.send.send_reset(
|
||||||
|
reason, send_buffer, stream, counts, &mut self.task);
|
||||||
|
self.recv.enqueue_reset_expiration(stream, counts);
|
||||||
|
// if a RecvStream is parked, ensure it's notified
|
||||||
|
stream.notify_recv();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn reset_on_recv_stream_err<B>(
|
fn reset_on_recv_stream_err<B>(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: &mut Buffer<Frame<B>>,
|
buffer: &mut Buffer<Frame<B>>,
|
||||||
|
|||||||
@@ -321,6 +321,11 @@ impl Mock<frame::Reset> {
|
|||||||
let id = self.0.stream_id();
|
let id = self.0.stream_id();
|
||||||
Mock(frame::Reset::new(id, frame::Reason::INTERNAL_ERROR))
|
Mock(frame::Reset::new(id, frame::Reason::INTERNAL_ERROR))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reason(self, reason: frame::Reason) -> Self {
|
||||||
|
let id = self.0.stream_id();
|
||||||
|
Mock(frame::Reset::new(id, reason))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Mock<frame::Reset>> for SendFrame {
|
impl From<Mock<frame::Reset>> for SendFrame {
|
||||||
|
|||||||
@@ -360,6 +360,91 @@ fn send_request_poll_ready_when_connection_error() {
|
|||||||
h2.join(srv).wait().expect("wait");
|
h2.join(srv).wait().expect("wait");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn send_reset_notifies_recv_stream() {
|
||||||
|
let _ = ::env_logger::try_init();
|
||||||
|
let (io, srv) = mock::new();
|
||||||
|
|
||||||
|
|
||||||
|
let srv = srv.assert_client_handshake()
|
||||||
|
.unwrap()
|
||||||
|
.recv_settings()
|
||||||
|
.recv_frame(
|
||||||
|
frames::headers(1)
|
||||||
|
.request("POST", "https://example.com/")
|
||||||
|
)
|
||||||
|
.send_frame(frames::headers(1).response(200))
|
||||||
|
.recv_frame(frames::reset(1).refused())
|
||||||
|
.recv_frame(
|
||||||
|
frames::headers(3)
|
||||||
|
.request("POST", "https://example.com/")
|
||||||
|
.eos()
|
||||||
|
)
|
||||||
|
.send_frame(
|
||||||
|
frames::headers(3)
|
||||||
|
.response(200)
|
||||||
|
.eos()
|
||||||
|
)
|
||||||
|
.close();
|
||||||
|
|
||||||
|
let client = client::handshake(io)
|
||||||
|
.expect("handshake")
|
||||||
|
.and_then(|(mut client, conn)| {
|
||||||
|
let request = Request::builder()
|
||||||
|
.method(Method::POST)
|
||||||
|
.uri("https://example.com/")
|
||||||
|
.body(())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// first request is allowed
|
||||||
|
let (resp1, tx) = client.send_request(request, false).unwrap();
|
||||||
|
|
||||||
|
conn.drive(resp1)
|
||||||
|
.map(move |(conn, res)| (client, conn, tx, res))
|
||||||
|
})
|
||||||
|
.and_then(|(client, conn, mut tx, res)| {
|
||||||
|
let tx = futures::future::poll_fn(move || {
|
||||||
|
tx.send_reset(h2::Reason::REFUSED_STREAM);
|
||||||
|
Ok(().into())
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let rx = res
|
||||||
|
.into_body()
|
||||||
|
.for_each(|_| -> Result<(), _> {
|
||||||
|
unreachable!("no response body expected")
|
||||||
|
});
|
||||||
|
// a FuturesUnordered is used on purpose!
|
||||||
|
//
|
||||||
|
// We don't want a join, since any of the other futures notifying
|
||||||
|
// will make the rx future polled again, but we are
|
||||||
|
// specifically testing that rx gets notified on its own.
|
||||||
|
let mut unordered = futures::stream::FuturesUnordered::<Box<Future<Item=(), Error=()>>>::new();
|
||||||
|
unordered.push(Box::new(rx.expect_err("RecvBody").then(|_| Ok(()))));
|
||||||
|
unordered.push(Box::new(tx));
|
||||||
|
|
||||||
|
conn.drive(unordered.for_each(|_| Ok(())))
|
||||||
|
.map(move |(conn, _)| (client, conn))
|
||||||
|
})
|
||||||
|
.and_then(|(mut client, conn)| {
|
||||||
|
// send a second request just to keep the connection alive until
|
||||||
|
// we know the previous `RecvStream` was notified about the reset.
|
||||||
|
let request = Request::builder()
|
||||||
|
.method(Method::POST)
|
||||||
|
.uri("https://example.com/")
|
||||||
|
.body(())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (resp2, _) = client.send_request(request, true).unwrap();
|
||||||
|
let fut = resp2.map(|_res| ());
|
||||||
|
|
||||||
|
conn.drive(fut)
|
||||||
|
.and_then(|(conn, _)| conn.expect("client"))
|
||||||
|
});
|
||||||
|
|
||||||
|
client.join(srv).wait().expect("wait");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn http_11_request_without_scheme_or_authority() {
|
fn http_11_request_without_scheme_or_authority() {
|
||||||
let _ = ::env_logger::try_init();
|
let _ = ::env_logger::try_init();
|
||||||
|
|||||||
Reference in New Issue
Block a user