/// Connection level flow control governing received data
flow: FlowControl,
/// Amount of connection window capacity currently used by outstanding streams.
in_flight_data: WindowSize,
/// The lowest stream ID that is still idle
next_stream_id: Result<StreamId, StreamIdOverflow>,
/// The stream ID of the last processed stream
last_processed_id: StreamId,
/// 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,
/// Streams that have pending window updates
pending_window_updates: store::Queue<stream::NextWindowUpdate>,
/// New streams to be accepted
pending_accept: store::Queue<stream::NextAccept>,
/// Locally reset streams that should be reaped when they expire
pending_reset_expired: store::Queue<stream::NextResetExpire>,
/// How long locally reset streams should ignore received frames
reset_duration: Duration,
/// Holds frames that are waiting to be read
buffer: Buffer<Event>,
/// Refused StreamId, this represents a frame that must be sent out.
refused: Option<StreamId>,
/// If push promises are allowed to be received.
is_push_enabled: bool,
/// If extended connect protocol is enabled.
is_extended_connect_protocol_enabled: bool,
}
/// Returns the ID of the last processed stream pubfn last_processed_id(&self) -> StreamId { self.last_processed_id
}
/// Update state reflecting a new, remotely opened stream /// /// Returns the stream state if successful. `None` if refused pubfn open(
&mutself,
id: StreamId,
mode: Open,
counts: &mut Counts,
) -> Result<Option<StreamId>, Error> {
assert!(self.refused.is_none());
counts.peer().ensure_can_open(id, mode)?;
let next_id = self.next_stream_id()?; if id < next_id {
proto_err!(conn: "id ({:?}) < next_id ({:?})", id, next_id); return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
self.next_stream_id = id.next_id();
if !counts.can_inc_num_recv_streams() { self.refused = Some(id); return Ok(None);
}
Ok(Some(id))
}
/// Transition the stream state based on receiving headers /// /// The caller ensures that the frame represents headers and not trailers. pubfn recv_headers(
&mutself,
frame: frame::Headers,
stream: &mut store::Ptr,
counts: &mut Counts,
) -> Result<(), RecvHeaderBlockError<Option<frame::Headers>>> {
tracing::trace!("opening stream; init_window={}", self.init_window_sz); let is_initial = stream.state.recv_open(&frame)?;
if is_initial { // TODO: be smarter about this logic if frame.stream_id() > self.last_processed_id { self.last_processed_id = frame.stream_id();
}
// Increment the number of concurrent streams
counts.inc_num_recv_streams(stream);
}
if !stream.content_length.is_head() { usesuper::stream::ContentLength; use http::header;
iflet Some(content_length) = frame.fields().get(header::CONTENT_LENGTH) { let content_length = match frame::parse_u64(content_length.as_bytes()) {
Ok(v) => v,
Err(_) => {
proto_err!(stream: "could not parse content-length; stream={:?}", stream.id); return Err(Error::library_reset(stream.id, Reason::PROTOCOL_ERROR).into());
}
};
if frame.is_over_size() { // A frame is over size if the decoded header block was bigger than // SETTINGS_MAX_HEADER_LIST_SIZE. // // > A server that receives a larger header block than it is willing // > to handle can send an HTTP 431 (Request Header Fields Too // > Large) status code [RFC6585]. A client can discard responses // > that it cannot process. // // So, if peer is a server, we'll send a 431. In either case, // an error is recorded, which will send a REFUSED_STREAM, // since we don't want any of the data frames either.
tracing::debug!( "stream error REQUEST_HEADER_FIELDS_TOO_LARGE -- \
recv_headers: frame is over size; stream={:?}",
stream.id
); returnif counts.peer().is_server() && is_initial { letmut res = frame::Headers::new(
stream.id,
frame::Pseudo::response(::http::StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE),
HeaderMap::new(),
);
res.set_end_stream();
Err(RecvHeaderBlockError::Oversize(Some(res)))
} else {
Err(RecvHeaderBlockError::Oversize(None))
};
}
let stream_id = frame.stream_id(); let (pseudo, fields) = frame.into_parts();
if pseudo.protocol.is_some()
&& counts.peer().is_server()
&& !self.is_extended_connect_protocol_enabled
{
proto_err!(stream: "cannot use :protocol if extended connect protocol is disabled; stream={:?}", stream.id); return Err(Error::library_reset(stream.id, Reason::PROTOCOL_ERROR).into());
}
if pseudo.status.is_some() && counts.peer().is_server() {
proto_err!(stream: "cannot use :status header for requests; stream={:?}", stream.id); return Err(Error::library_reset(stream.id, Reason::PROTOCOL_ERROR).into());
}
if !pseudo.is_informational() { let message = counts
.peer()
.convert_poll_message(pseudo, fields, stream_id)?;
// Push the frame onto the stream's recv buffer
stream
.pending_recv
.push_back(&mutself.buffer, Event::Headers(message));
stream.notify_recv();
// Only servers can receive a headers frame that initiates the stream. // This is verified in `Streams` before calling this function. if counts.peer().is_server() { // Correctness: never push a stream to `pending_accept` without having the // corresponding headers frame pushed to `stream.pending_recv`. self.pending_accept.push(stream);
}
}
Ok(())
}
/// Called by the server to get the request /// /// # Panics /// /// Panics if `stream.pending_recv` has no `Event::Headers` queued. /// pubfn take_request(&mutself, stream: &mut store::Ptr) -> Request<()> { usesuper::peer::PollMessage::*;
match stream.pending_recv.pop_front(&mutself.buffer) {
Some(Event::Headers(Server(request))) => request,
_ => unreachable!("server stream queue must start with Headers"),
}
}
/// Called by the client to get pushed response pubfn poll_pushed(
&mutself,
cx: &Context,
stream: &mut store::Ptr,
) -> Poll<Option<Result<(Request<()>, store::Key), proto::Error>>> { usesuper::peer::PollMessage::*;
letmut ppp = stream.pending_push_promises.take(); let pushed = ppp.pop(stream.store_mut()).map(|mut pushed| { match pushed.pending_recv.pop_front(&mutself.buffer) {
Some(Event::Headers(Server(headers))) => (headers, pushed.key()), // When frames are pushed into the queue, it is verified that // the first frame is a HEADERS frame.
_ => panic!("Headers not set on pushed stream"),
}
});
stream.pending_push_promises = ppp; iflet Some(p) = pushed {
Poll::Ready(Some(Ok(p)))
} else { let is_open = stream.state.ensure_recv_open()?;
/// Called by the client to get the response pubfn poll_response(
&mutself,
cx: &Context,
stream: &mut store::Ptr,
) -> Poll<Result<Response<()>, proto::Error>> { usesuper::peer::PollMessage::*;
// If the buffer is not empty, then the first frame must be a HEADERS // frame or the user violated the contract. match stream.pending_recv.pop_front(&mutself.buffer) {
Some(Event::Headers(Client(response))) => Poll::Ready(Ok(response)),
Some(_) => panic!("poll_response called after response returned"),
None => { if !stream.state.ensure_recv_open()? {
proto_err!(stream: "poll_response: stream={:?} is not opened;", stream.id); return Poll::Ready(Err(Error::library_reset(
stream.id,
Reason::PROTOCOL_ERROR,
)));
}
/// Transition the stream based on receiving trailers pubfn recv_trailers(
&mutself,
frame: frame::Headers,
stream: &mut store::Ptr,
) -> Result<(), Error> { // Transition the state
stream.state.recv_close()?;
if stream.ensure_content_length_zero().is_err() {
proto_err!(stream: "recv_trailers: content-length is not zero; stream={:?};", stream.id); return Err(Error::library_reset(stream.id, Reason::PROTOCOL_ERROR));
}
let trailers = frame.into_fields();
// Push the frame onto the stream's recv buffer
stream
.pending_recv
.push_back(&mutself.buffer, Event::Trailers(trailers));
stream.notify_recv();
/// Releases capacity back to the connection & stream pubfn release_capacity(
&mutself,
capacity: WindowSize,
stream: &mut store::Ptr,
task: &mut Option<Waker>,
) -> Result<(), UserError> {
tracing::trace!("release_capacity; size={}", capacity);
if capacity > stream.in_flight_recv_data { return Err(UserError::ReleaseCapacityTooBig);
}
self.release_connection_capacity(capacity, task);
// Decrement in-flight data
stream.in_flight_recv_data -= capacity;
// Assign capacity to stream // TODO: proper error handling let _res = stream.recv_flow.assign_capacity(capacity);
debug_assert!(_res.is_ok());
if stream.recv_flow.unclaimed_capacity().is_some() { // Queue the stream for sending the WINDOW_UPDATE frame. self.pending_window_updates.push(stream);
iflet Some(task) = task.take() {
task.wake();
}
}
Ok(())
}
/// Release any unclaimed capacity for a closed stream. pubfn release_closed_capacity(&mutself, stream: &mut store::Ptr, task: &mut Option<Waker>) {
debug_assert_eq!(stream.ref_count, 0);
/// Set the "target" connection window size. /// /// By default, all new connections start with 64kb of window size. As /// streams used and release capacity, we will send WINDOW_UPDATEs for the /// connection to bring it back up to the initial "target". /// /// Setting a target means that we will try to tell the peer about /// WINDOW_UPDATEs so the peer knows it has about `target` window to use /// for the whole connection. /// /// The `task` is an optional parked task for the `Connection` that might /// be blocked on needing more window capacity. pubfn set_target_connection_window(
&mutself,
target: WindowSize,
task: &mut Option<Waker>,
) -> Result<(), Reason> {
tracing::trace!( "set_target_connection_window; target={}; available={}, reserved={}",
target, self.flow.available(), self.in_flight_data,
);
// The current target connection window is our `available` plus any // in-flight data reserved by streams. // // Update the flow controller with the difference between the new // target and the current target. let current = self
.flow
.available()
.add(self.in_flight_data)?
.checked_size(); if target > current { self.flow.assign_capacity(target - current)?;
} else { self.flow.claim_capacity(current - target)?;
}
// If changing the target capacity means we gained a bunch of capacity, // enough that we went over the update threshold, then schedule sending // a connection WINDOW_UPDATE. ifself.flow.unclaimed_capacity().is_some() { iflet Some(task) = task.take() {
task.wake();
}
}
Ok(())
}
// 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.
match target.cmp(&old_sz) {
Ordering::Less => { // We must decrease the (local) window on every open stream. let dec = old_sz - target;
tracing::trace!("decrementing all windows; dec={}", dec);
store.try_for_each(|mut stream| {
stream
.recv_flow
.dec_recv_window(dec)
.map_err(proto::Error::library_go_away)?;
Ok::<_, proto::Error>(())
})?;
}
Ordering::Greater => { // We must increase the (local) window on every open stream. let inc = target - old_sz;
tracing::trace!("incrementing all windows; inc={}", inc);
store.try_for_each(|mut stream| { // XXX: Shouldn't the peer have already noticed our // overflow and sent us a GOAWAY?
stream
.recv_flow
.inc_window(inc)
.map_err(proto::Error::library_go_away)?;
stream
.recv_flow
.assign_capacity(inc)
.map_err(proto::Error::library_go_away)?;
Ok::<_, proto::Error>(())
})?;
}
Ordering::Equal => (),
}
}
// This should have been enforced at the codec::FramedRead layer, so // this is just a sanity check.
assert!(sz <= MAX_WINDOW_SIZE as usize);
let sz = sz as WindowSize;
let is_ignoring_frame = stream.state.is_local_error();
if !is_ignoring_frame && !stream.state.is_recv_streaming() { // TODO: There are cases where this can be a stream error of // STREAM_CLOSED instead...
// Receiving a DATA frame when not expecting one is a protocol // error.
proto_err!(conn: "unexpected DATA frame; stream={:?}", stream.id); return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
if is_ignoring_frame {
tracing::trace!( "recv_data; frame ignored on locally reset {:?} for some time",
stream.id,
); returnself.ignore_data(sz);
}
// Ensure that there is enough capacity on the connection before acting // on the stream. self.consume_connection_window(sz)?;
if stream.recv_flow.window_size() < sz { // http://httpwg.org/specs/rfc7540.html#WINDOW_UPDATE // > A receiver MAY respond with a stream error (Section 5.4.2) or // > connection error (Section 5.4.1) of type FLOW_CONTROL_ERROR if // > it is unable to accept a frame. // // So, for violating the **stream** window, we can send either a // stream or connection error. We've opted to send a stream // error. return Err(Error::library_reset(stream.id, Reason::FLOW_CONTROL_ERROR));
}
if frame.is_end_stream() { if stream.ensure_content_length_zero().is_err() {
proto_err!(stream: "recv_data: content-length underflow; stream={:?}; len={:?}",
stream.id,
frame.payload().len(),
); return Err(Error::library_reset(stream.id, Reason::PROTOCOL_ERROR));
}
if stream.state.recv_close().is_err() {
proto_err!(conn: "recv_data: failed to transition to closed state; stream={:?}", stream.id); return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
}
// Received a frame, but no one cared about it. fix issue#648 if !stream.is_recv {
tracing::trace!( "recv_data; frame ignored on stream release {:?} for some time",
stream.id,
); self.release_connection_capacity(sz, &mut None); return Ok(());
}
// Update stream level flow control
stream
.recv_flow
.send_data(sz)
.map_err(proto::Error::library_go_away)?;
// Track the data as in-flight
stream.in_flight_recv_data += sz;
let event = Event::Data(frame.into_payload());
// Push the frame onto the recv buffer
stream.pending_recv.push_back(&mutself.buffer, event);
stream.notify_recv();
Ok(())
}
pubfn ignore_data(&mutself, sz: WindowSize) -> Result<(), Error> { // Ensure that there is enough capacity on the connection... self.consume_connection_window(sz)?;
// Since we are ignoring this frame, // we aren't returning the frame to the user. That means they // have no way to release the capacity back to the connection. So // we have to release it automatically. // // This call doesn't send a WINDOW_UPDATE immediately, just marks // the capacity as available to be reclaimed. When the available // capacity meets a threshold, a WINDOW_UPDATE is then sent. self.release_connection_capacity(sz, &mut None);
Ok(())
}
// Update connection level flow control self.flow.send_data(sz).map_err(Error::library_go_away)?;
// Track the data as in-flight self.in_flight_data += sz;
Ok(())
}
pubfn recv_push_promise(
&mutself,
frame: frame::PushPromise,
stream: &mut store::Ptr,
) -> Result<(), Error> {
stream.state.reserve_remote()?; if frame.is_over_size() { // A frame is over size if the decoded header block was bigger than // SETTINGS_MAX_HEADER_LIST_SIZE. // // > A server that receives a larger header block than it is willing // > to handle can send an HTTP 431 (Request Header Fields Too // > Large) status code [RFC6585]. A client can discard responses // > that it cannot process. // // So, if peer is a server, we'll send a 431. In either case, // an error is recorded, which will send a REFUSED_STREAM, // since we don't want any of the data frames either.
tracing::debug!( "stream error REFUSED_STREAM -- recv_push_promise: \
headers frame is over size; promised_id={:?};",
frame.promised_id(),
); return Err(Error::library_reset(
frame.promised_id(),
Reason::REFUSED_STREAM,
));
}
let promised_id = frame.promised_id(); let (pseudo, fields) = frame.into_parts(); let req = crate::server::Peer::convert_poll_message(pseudo, fields, promised_id)?;
iflet Err(e) = frame::PushPromise::validate_request(&req) { use PushPromiseHeaderError::*; match e {
NotSafeAndCacheable => proto_err!(
stream: "recv_push_promise: method {} is not safe and cacheable; promised_id={:?}",
req.method(),
promised_id,
),
InvalidContentLength(e) => proto_err!(
stream: "recv_push_promise; promised request has invalid content-length {:?}; promised_id={:?}",
e,
promised_id,
),
} return Err(Error::library_reset(promised_id, Reason::PROTOCOL_ERROR));
}
/// Ensures that `id` is not in the `Idle` state. pubfn ensure_not_idle(&self, id: StreamId) -> Result<(), Reason> { iflet Ok(next) = self.next_stream_id { if id >= next {
tracing::debug!( "stream ID implicitly closed, PROTOCOL_ERROR; stream={:?}",
id
); return Err(Reason::PROTOCOL_ERROR);
}
} // if next_stream_id is overflowed, that's ok.
Ok(())
}
/// Handle remote sending an explicit RST_STREAM. pubfn recv_reset(
&mutself,
frame: frame::Reset,
stream: &mut Stream,
counts: &mut Counts,
) -> Result<(), Error> { // Reseting a stream that the user hasn't accepted is possible, // but should be done with care. These streams will continue // to take up memory in the accept queue, but will no longer be // counted as "concurrent" streams. // // So, we have a separate limit for these. // // See https://github.com/hyperium/hyper/issues/2877 if stream.is_pending_accept { if counts.can_inc_num_remote_reset_streams() {
counts.inc_num_remote_reset_streams();
} else {
tracing::warn!( "recv_reset; remotely-reset pending-accept streams reached limit ({:?})",
counts.max_remote_reset_streams(),
); return Err(Error::library_go_away_data(
Reason::ENHANCE_YOUR_CALM, "too_many_resets",
));
}
}
// Notify the stream
stream.state.recv_reset(frame, stream.is_pending_send);
stream.notify_send();
stream.notify_recv();
Ok(())
}
/// Handle a connection-level error pubfn handle_error(&mutself, err: &proto::Error, stream: &mut Stream) { // Receive an error
stream.state.handle_error(err);
// If a receiver is waiting, notify it
stream.notify_send();
stream.notify_recv();
}
pub(super) fn clear_recv_buffer(&mutself, stream: &mut Stream) { while stream.pending_recv.pop_front(&mutself.buffer).is_some() { // drop it
}
}
/// Get the max ID of streams we can receive. /// /// This gets lowered if we send a GOAWAY frame. pubfn max_stream_id(&self) -> StreamId { self.max_stream_id
}
pubfn may_have_created_stream(&self, id: StreamId) -> bool { iflet Ok(next_id) = self.next_stream_id { // Peer::is_local_init should have been called beforehand
debug_assert_eq!(id.is_server_initiated(), next_id.is_server_initiated(),);
id < next_id
} else { true
}
}
pub(super) fn maybe_reset_next_stream_id(&mutself, id: StreamId) { iflet Ok(next_id) = self.next_stream_id { // !Peer::is_local_init should have been called beforehand
debug_assert_eq!(id.is_server_initiated(), next_id.is_server_initiated()); if id >= next_id { self.next_stream_id = id.next_id();
}
}
}
/// Returns true if the remote peer can reserve a stream with the given ID. pubfn ensure_can_reserve(&self) -> Result<(), Error> { if !self.is_push_enabled {
proto_err!(conn: "recv_push_promise: push is disabled"); return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
}
Ok(())
}
/// Add a locally reset stream to queue to be eventually reaped. pubfn enqueue_reset_expiration(&mutself, stream: &mut store::Ptr, counts: &mut Counts) { if !stream.state.is_local_error() || stream.is_pending_reset_expiration() { return;
}
// Create the RST_STREAM frame let frame = frame::Reset::new(stream_id, Reason::REFUSED_STREAM);
// Buffer the frame
dst.buffer(frame.into()).expect("invalid RST_STREAM frame");
}
self.refused = None;
Poll::Ready(Ok(()))
}
pubfn clear_expired_reset_streams(&mutself, store: &='color:red'>mut Store, counts: &mut Counts) { if !self.pending_reset_expired.is_empty() { let now = Instant::now(); let reset_duration = self.reset_duration; whilelet Some(stream) = self.pending_reset_expired.pop_if(store, |stream| { let reset_at = stream.reset_at.expect("reset_at must be set if in queue"); // rust-lang/rust#86470 tracks a bug in the standard library where `Instant` // subtraction can panic (because, on some platforms, `Instant` isn't actually // monotonic). We use a saturating operation to avoid this panic here.
now.saturating_duration_since(reset_at) > reset_duration
}) {
counts.transition_after(stream, true);
}
}
}
if !stream.state.is_recv_streaming() { // No need to send window updates on the stream if the stream is // no longer receiving data. // // TODO: is this correct? We could possibly send a window // update on a ReservedRemote stream if we already know // we want to stream the data faster... return;
}
// TODO: de-dup iflet Some(incr) = stream.recv_flow.unclaimed_capacity() { // Create the WINDOW_UPDATE frame let frame = frame::WindowUpdate::new(stream.id, incr);
// Buffer it
dst.buffer(frame.into())
.expect("invalid WINDOW_UPDATE frame");
// Update flow control
stream
.recv_flow
.inc_window(incr)
.expect("unexpected flow control state");
}
})
}
}
// Notify the recv task. This is done just in case // `poll_trailers` was called. // // It is very likely that `notify_recv` will just be a no-op (as // the task will be None), so this isn't really much of a // performance concern. It also means we don't have to track // state to see if `poll_trailers` was called before `poll_data` // returned `None`.
stream.notify_recv();
// No more data frames
Poll::Ready(None)
}
None => self.schedule_recv(cx, stream),
}
}
pubfn poll_trailers(
&mutself,
cx: &Context,
stream: &mut Stream,
) -> Poll<Option<Result<HeaderMap, proto::Error>>> { match stream.pending_recv.pop_front(&mutself.buffer) {
Some(Event::Trailers(trailers)) => Poll::Ready(Some(Ok(trailers))),
Some(event) => { // Frame is not trailers.. not ready to poll trailers yet.
stream.pending_recv.push_front(&mutself.buffer, event);
fn schedule_recv<T>(
&mutself,
cx: &Context,
stream: &mut Stream,
) -> Poll<Option<Result<T, proto::Error>>> { if stream.state.ensure_recv_open()? { // Request to get notified once more frames arrive
stream.recv_task = Some(cx.waker().clone());
Poll::Pending
} else { // No more frames will be received
Poll::Ready(None)
}
}
}
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.