use std::cmp::Ordering; use std::io; use std::task::{Context, Poll, Waker};
/// Manages state transitions related to outbound frames. #[derive(Debug)] pub(super) struct Send { /// Stream identifier to use for next initialized stream.
next_stream_id: Result<StreamId, StreamIdOverflow>,
/// Any streams with a higher ID are ignored. /// /// This starts as MAX, but is lowered when a GOAWAY is received. /// /// > After sending a GOAWAY frame, the sender can discard frames for /// > streams initiated by the receiver with identifiers higher than /// > the identified last stream.
max_stream_id: StreamId,
/// Initial window size of locally initiated streams
init_window_sz: WindowSize,
/// Prioritization layer
prioritize: Prioritize,
is_push_enabled: bool,
/// If extended connect protocol is enabled.
is_extended_connect_protocol_enabled: bool,
}
/// A value to detect which public API has called `poll_reset`. #[derive(Debug)] pub(crate) enum PollReset {
AwaitingHeaders,
Streaming,
}
// Queue the frame for sending // // This call expects that, since new streams are in the open queue, new // streams won't be pushed on pending_send. self.prioritize
.queue_frame(frame.into(), buffer, stream, task);
// Need to notify the connection when pushing onto pending_open since // queue_frame only notifies for pending_send. if pending_open { iflet Some(task) = task.take() {
task.wake();
}
}
Ok(())
}
/// Send an explicit RST_STREAM frame pubfn send_reset<B>(
&mutself,
reason: Reason,
initiator: Initiator,
buffer: &mut Buffer<Frame<B>>,
stream: &mut store::Ptr,
counts: &mut Counts,
task: &mut Option<Waker>,
) { 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;
if is_reset { // Don't double reset
tracing::trace!( " -> not sending RST_STREAM ({:?} is already reset)",
stream_id
); return;
}
// Transition the state to reset no matter what.
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. if is_closed && is_empty {
tracing::trace!( " -> not sending explicit RST_STREAM ({:?} was closed \
and send queue was flushed)",
stream_id
); return;
}
// Clear all pending outbound frames. // Note that we don't call `self.recv_err` because we want to enqueue // the reset frame before transitioning the stream inside // `reclaim_all_capacity`. self.prioritize.clear_queue(buffer, stream);
pub(super) fn recv_go_away(&mutself, 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 // of previous `GOAWAY` frame. In either case, that is illegal. // (When sending multiple `GOAWAY`s, "Endpoints MUST NOT increase // the value they send in the last stream identifier, since the // peers might already have retried unprocessed requests on another // connection.")
proto_err!(conn: "recv_go_away: last_stream_id ({:?}) > max_stream_id ({:?})",
last_stream_id, self.max_stream_id,
); return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
// Applies an update to the remote endpoint's initial window size. // // Per RFC 7540 §6.9.2: // // In addition to changing the flow-control window for streams that are // not yet active, a SETTINGS frame can alter the initial flow-control // window size for streams with active flow-control windows (that is, // streams in the "open" or "half-closed (remote)" state). When the // value of SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST adjust // the size of all stream flow-control windows that it maintains by the // difference between the new value and the old value. // // A change to `SETTINGS_INITIAL_WINDOW_SIZE` can cause the available // space in a flow-control window to become negative. A sender MUST // track the negative flow-control window and MUST NOT send new // flow-controlled frames until it receives WINDOW_UPDATE frames that // cause the flow-control window to become positive. iflet Some(val) = settings.initial_window_size() { let old_val = self.init_window_sz; self.init_window_sz = val;
match val.cmp(&old_val) {
Ordering::Less => { // We must decrease the (remote) window on every open stream. let dec = old_val - val;
tracing::trace!("decrementing all windows; dec={}", dec);
// TODO: this decrement can underflow based on received frames!
stream
.send_flow
.dec_send_window(dec)
.map_err(proto::Error::library_go_away)?;
// It's possible that decreasing the window causes // `window_size` (the stream-specific window) to fall below // `available` (the portion of the connection-level window // that we have allocated to the stream). // In this case, we should take that excess allocation away // and reassign it to other streams. let window_size = stream.send_flow.window_size(); let available = stream.send_flow.available().as_size(); let reclaimed = if available > window_size { // Drop down to `window_size`. let reclaim = available - window_size;
stream
.send_flow
.claim_capacity(reclaim)
.map_err(proto::Error::library_go_away)?;
total_reclaimed += reclaim;
reclaim
} else { 0
};
// TODO: Should this notify the producer when the capacity // of a stream is reduced? Maybe it should if the capacity // is reduced to zero, allowing the producer to stop work.
Ok::<_, proto::Error>(())
})?;
self.prioritize
.assign_connection_capacity(total_reclaimed, store, counts);
}
Ordering::Greater => { let inc = val - old_val;
pubfn ensure_not_idle(&self, id: StreamId) -> Result<(), Reason> { iflet Ok(next) = self.next_stream_id { if id >= next { return Err(Reason::PROTOCOL_ERROR);
}
} // if next_stream_id is overflowed, that's ok.
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.