/// Represents the state of an H2 stream /// /// ```not_rust /// +--------+ /// send PP | | recv PP /// ,--------| idle |--------. /// / | | \ /// v +--------+ v /// +----------+ | +----------+ /// | | | send H / | | /// ,------| reserved | | recv H | reserved |------. /// | | (local) | | | (remote) | | /// | +----------+ v +----------+ | /// | | +--------+ | | /// | | recv ES | | send ES | | /// | send H | ,-------| open |-------. | recv H | /// | | / | | \ | | /// | v v +--------+ v v | /// | +----------+ | +----------+ | /// | | half | | | half | | /// | | closed | | send R / | closed | | /// | | (remote) | | recv R | (local) | | /// | +----------+ | +----------+ | /// | | | | | /// | | send ES / | recv ES / | | /// | | send R / v send R / | | /// | | recv R +--------+ recv R | | /// | send R / `----------->| |<-----------' send R / | /// | recv R | closed | recv R | /// `----------------------->| |<----------------------' /// +--------+ /// /// send: endpoint sends this frame /// recv: endpoint receives this frame /// /// H: HEADERS frame (with implied CONTINUATIONs) /// PP: PUSH_PROMISE frame (with implied CONTINUATIONs) /// ES: END_STREAM flag /// R: RST_STREAM frame /// ``` #[derive(Debug, Clone)] pubstruct State {
inner: Inner,
}
#[derive(Debug, Clone)] enum Inner {
Idle, // TODO: these states shouldn't count against concurrency limits:
ReservedLocal,
ReservedRemote,
Open { local: Peer, remote: Peer },
HalfClosedLocal(Peer), // TODO: explicitly name this value
HalfClosedRemote(Peer),
Closed(Cause),
}
#[derive(Debug, Clone)] enum Cause {
EndStream,
Error(Error),
/// This indicates to the connection that a reset frame must be sent out /// once the send queue has been flushed. /// /// Examples of when this could happen: /// - User drops all references to a stream, so we want to CANCEL the it. /// - Header block size was too large, so we want to REFUSE, possibly /// after sending a 431 response frame.
ScheduledLibraryReset(Reason),
}
impl State { /// Opens the send-half of a stream if it is not already open. pubfn send_open(&mutself, eos: bool) -> Result<(), UserError> { let local = Streaming;
self.inner = matchself.inner {
Idle => { if eos {
HalfClosedLocal(AwaitingHeaders)
} else {
Open {
local,
remote: AwaitingHeaders,
}
}
}
Open {
local: AwaitingHeaders,
remote,
} => { if eos {
HalfClosedLocal(remote)
} else {
Open { local, remote }
}
}
HalfClosedRemote(AwaitingHeaders) | ReservedLocal => { if eos {
Closed(Cause::EndStream)
} else {
HalfClosedRemote(local)
}
}
_ => { // All other transitions result in a protocol error return Err(UserError::UnexpectedFrameType);
}
};
Ok(())
}
/// Opens the receive-half of the stream when a HEADERS frame is received. /// /// Returns true if this transitions the state to Open. pubfn recv_open(&mutself, frame: &frame::Headers) -> Result<bool, Error> { letmut initial = false; let eos = frame.is_end_stream();
/// Indicates that the remote side will not send more data to the local. pubfn recv_close(&mutself) -> Result<(), Error> { matchself.inner {
Open { local, .. } => { // The remote side will continue to receive data.
tracing::trace!("recv_close: Open => HalfClosedRemote({:?})", local); self.inner = HalfClosedRemote(local);
Ok(())
}
HalfClosedLocal(..) => {
tracing::trace!("recv_close: HalfClosedLocal => Closed"); self.inner = Closed(Cause::EndStream);
Ok(())
} ref state => {
proto_err!(conn: "recv_close: in unexpected state {:?}", state);
Err(Error::library_go_away(Reason::PROTOCOL_ERROR))
}
}
}
/// The remote explicitly sent a RST_STREAM. /// /// # Arguments /// - `frame`: the received RST_STREAM frame. /// - `queued`: true if this stream has frames in the pending send queue. pubfn recv_reset(&mutself, frame: frame::Reset, queued: bool) { matchself.inner { // If the stream is already in a `Closed` state, do nothing, // provided that there are no frames still in the send queue.
Closed(..) if !queued => {} // A notionally `Closed` stream may still have queued frames in // the following cases: // // - if the cause is `Cause::Scheduled(..)` (i.e. we have not // actually closed the stream yet). // - if the cause is `Cause::EndStream`: we transition to this // state when an EOS frame is *enqueued* (so that it's invalid // to enqueue more frames), not when the EOS frame is *sent*; // therefore, there may still be frames ahead of the EOS frame // in the send queue. // // In either of these cases, we want to overwrite the stream's // previous state with the received RST_STREAM, so that the queue // will be cleared by `Prioritize::pop_frame`. ref state => {
tracing::trace!( "recv_reset; frame={:?}; state={:?}; queued={:?}",
frame,
state,
queued
); self.inner = Closed(Cause::Error(Error::remote_reset(
frame.stream_id(),
frame.reason(),
)));
}
}
}
pubfn recv_eof(&mutself) { matchself.inner {
Closed(..) => {} ref state => {
tracing::trace!("recv_eof; state={:?}", state); self.inner = Closed(Cause::Error(
io::Error::new(
io::ErrorKind::BrokenPipe, "stream closed because of a broken pipe",
)
.into(),
));
}
}
}
/// Indicates that the local side will not send more data to the local. pubfn send_close(&mutself) { matchself.inner {
Open { remote, .. } => { // The remote side will continue to receive data.
tracing::trace!("send_close: Open => HalfClosedLocal({:?})", remote); self.inner = HalfClosedLocal(remote);
}
HalfClosedRemote(..) => {
tracing::trace!("send_close: HalfClosedRemote => Closed"); self.inner = Closed(Cause::EndStream);
} ref state => panic!("send_close: unexpected state {:?}", state),
}
}
/// Set the stream state to reset locally. pubfn set_reset(&mutself, stream_id: StreamId, reason: Reason, initiator: Initiator) { self.inner = Closed(Cause::Error(Error::Reset(stream_id, reason, initiator)));
}
/// Set the stream state to a scheduled reset. pubfn set_scheduled_reset(&mutself, reason: Reason) {
debug_assert!(!self.is_closed()); self.inner = Closed(Cause::ScheduledLibraryReset(reason));
}
/// Returns true when the stream is in a state to receive headers pubfn is_recv_headers(&self) -> bool {
matches!( self.inner,
Idle | Open {
remote: AwaitingHeaders,
..
} | HalfClosedLocal(AwaitingHeaders)
| ReservedRemote
)
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.