Refactor errors internals (#556)

h2::Error now knows whether protocol errors happened because the user
sent them, because it was received from the remote peer, or because
the library itself emitted an error because it detected a protocol
violation.

It also keeps track of whether it came from a RST_STREAM or GO_AWAY
frame, and in the case of the latter, it includes the additional
debug data if any.

Fixes #530
This commit is contained in:
Anthony Ramine
2021-09-28 18:04:35 +02:00
committed by GitHub
parent cab307d2ed
commit 465f0337f8
26 changed files with 464 additions and 432 deletions

View File

@@ -2,8 +2,9 @@ use super::{
store, Buffer, Codec, Config, Counts, Frame, Prioritize, Prioritized, Store, Stream, StreamId,
StreamIdOverflow, WindowSize,
};
use crate::codec::{RecvError, UserError};
use crate::codec::UserError;
use crate::frame::{self, Reason};
use crate::proto::{Error, Initiator};
use bytes::Buf;
use http;
@@ -161,6 +162,7 @@ impl Send {
pub fn send_reset<B>(
&mut self,
reason: Reason,
initiator: Initiator,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
counts: &mut Counts,
@@ -169,14 +171,16 @@ impl Send {
let is_reset = stream.state.is_reset();
let is_closed = stream.state.is_closed();
let is_empty = stream.pending_send.is_empty();
let stream_id = stream.id;
tracing::trace!(
"send_reset(..., reason={:?}, stream={:?}, ..., \
"send_reset(..., reason={:?}, initiator={:?}, stream={:?}, ..., \
is_reset={:?}; is_closed={:?}; pending_send.is_empty={:?}; \
state={:?} \
",
reason,
stream.id,
initiator,
stream_id,
is_reset,
is_closed,
is_empty,
@@ -187,13 +191,13 @@ impl Send {
// Don't double reset
tracing::trace!(
" -> not sending RST_STREAM ({:?} is already reset)",
stream.id
stream_id
);
return;
}
// Transition the state to reset no matter what.
stream.state.set_reset(reason);
stream.state.set_reset(stream_id, reason, initiator);
// If closed AND the send queue is flushed, then the stream cannot be
// reset explicitly, either. Implicit resets can still be queued.
@@ -201,7 +205,7 @@ impl Send {
tracing::trace!(
" -> not sending explicit RST_STREAM ({:?} was closed \
and send queue was flushed)",
stream.id
stream_id
);
return;
}
@@ -371,7 +375,14 @@ impl Send {
if let Err(e) = self.prioritize.recv_stream_window_update(sz, stream) {
tracing::debug!("recv_stream_window_update !!; err={:?}", e);
self.send_reset(Reason::FLOW_CONTROL_ERROR, buffer, stream, counts, task);
self.send_reset(
Reason::FLOW_CONTROL_ERROR,
Initiator::Library,
buffer,
stream,
counts,
task,
);
return Err(e);
}
@@ -379,7 +390,7 @@ impl Send {
Ok(())
}
pub(super) fn recv_go_away(&mut self, last_stream_id: StreamId) -> Result<(), RecvError> {
pub(super) fn recv_go_away(&mut self, last_stream_id: StreamId) -> Result<(), Error> {
if last_stream_id > self.max_stream_id {
// The remote endpoint sent a `GOAWAY` frame indicating a stream
// that we never sent, or that we have already terminated on account
@@ -392,14 +403,14 @@ impl Send {
"recv_go_away: last_stream_id ({:?}) > max_stream_id ({:?})",
last_stream_id, self.max_stream_id,
);
return Err(RecvError::Connection(Reason::PROTOCOL_ERROR));
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
self.max_stream_id = last_stream_id;
Ok(())
}
pub fn recv_err<B>(
pub fn handle_error<B>(
&mut self,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
@@ -417,7 +428,7 @@ impl Send {
store: &mut Store,
counts: &mut Counts,
task: &mut Option<Waker>,
) -> Result<(), RecvError> {
) -> Result<(), Error> {
// Applies an update to the remote endpoint's initial window size.
//
// Per RFC 7540 §6.9.2:
@@ -480,7 +491,7 @@ impl Send {
// of a stream is reduced? Maybe it should if the capacity
// is reduced to zero, allowing the producer to stop work.
Ok::<_, RecvError>(())
Ok::<_, Error>(())
})?;
self.prioritize
@@ -490,7 +501,7 @@ impl Send {
store.for_each(|mut stream| {
self.recv_stream_window_update(inc, buffer, &mut stream, counts, task)
.map_err(RecvError::Connection)
.map_err(Error::library_go_away)
})?;
}
}