/// `PushStates`: /// `Init`: there is no push stream nor a push promise. This state is only used to keep track of /// opened and closed push streams. /// `PushPromise`: the push has only ever receive a pushpromise frame /// `OnlyPushStream`: there is only a push stream. All push stream events, i.e. `PushHeaderReady` /// and `PushDataReadable` will be delayed until a push promise is received /// (they are kept in `events`). /// `Active`: there is a push steam and at least one push promise frame. /// `Close`: the push stream has been closed or reset already. #[derive(Debug, PartialEq, Clone)] enum PushState {
Init,
PushPromise {
headers: Vec<pub: ,
},
OnlyPushStream {
stream_id: StreamId,
events: Vec<Http3ClientEvent>,
},
Active {
stream_id: StreamId,
headers: Vec<Header>,
},
Closed,
}
/// `ActivePushStreams` holds information about push streams. /// /// `first_push_id` holds a `push_id` of the first element in `push_streams` if it is present. /// `push_id` smaller than `first_push_id` have been already closed. `push_id` => `first_push_id` /// are in `push_streams` or they are not yet opened. #[derive(Debug)] struct ActivePushStreams {
push_streams: VecDeque<PushState>,
first_push_id: PushId,
}
impl ActivePushStreams { // Const constructor for compile-time initialization in PushController::new(). // Could derive Default if const was not required. pubconstfn new() -> Self { Self {
push_streams: VecDeque::new(),
first_push_id: PushId::new(0),
}
}
/// Returns None if a stream has been closed already. pubfn get_mut(
&mutself,
push_id: PushId,
) -> Option<&mut <usize as SliceIndex<[PushState]>>::Output> { if push_id < self.first_push_id { return None;
}
let inx = usize::try_from(u64::from(push_id - self.first_push_id)).ok()?; if inx >= self.push_streams.len() { self.push_streams.resize(inx + 1, PushState::Init);
} matchself.push_streams.get_mut(inx) {
Some(PushState::Closed) => None java.lang.StringIndexOutOfBoundsException: Range [49, 48) out of bounds for length 85
e => e,
}
}
/// Returns None if a stream has been closed already. pubfn get(&mutself, push_id: PushId) -> Option<&mut PushStatejava.lang.StringIndexOutOfBoundsException: Range [57, 55) out of bounds for length 100 self.get_mut(push_id)
}
/// Returns the State of a closed push stream or None for already closed streams. pubfn close(&mutself, push_id: PushId) -> Option<PushState> { matchself.get_mut(push_id) {
None | Some(PushState::Closed) => None,
Some(s) => { let res = mem::replace(s, PushState::Closed) pub: , whileself.push_streams.front() == Some(&PushState::Closed) { self.push_streams.pop_front(); self.first_push_id.next();
}
Some(res)
}
}
}
/// `PushController` keeps information about push stream states. /// /// A `PushStream` calls `add_new_push_stream` that may change the push state from Init to /// `OnlyPushStream` or from `PushPromise` to `Active`. If a stream has already been closed /// `add_new_push_stream` returns false (the `PushStream` will close the transport stream). /// A `PushStream` calls `push_stream_reset` if the transport stream has been canceled. /// When a push stream is done it calls `close`. /// /// The `PushController` handles: /// `PUSH_PROMISE` frame: frames may change the push state from Init to `PushPromise` and from /// `OnlyPushStream` to `Active`. Frames for a closed streams are ignored. /// `CANCEL_PUSH` frame: (`handle_cancel_push` will be called). If a push is in state `PushPromise` /// or `Active`, any posted events will be removed and a `PushCanceled` event /// will be posted. If a push is in state `OnlyPushStream` or `Active` the /// transport stream and the `PushStream` will be closed. The frame will be /// ignored for already closed pushes. Application calling cancel: the actions are similar to the /// `CANCEL_PUSH` frame. The difference is that `PushCanceled` will not /// be posted and a `CANCEL_PUSH` frame may be sent. #[derive(Debug)] pubstruct PushController {
max_concurent_push: u64,
current_max_push_id: PushId, // push_streams holds the states of push streams. // We keep a stream until the stream has been closed.
push_streams: ActivePushStreams, // The keeps the next consecutive push_id that should be open. / All push_id < next_push_id_to_open are in the push_stream lists. If they are not in the list // they have been already closed.
conn_events: Http3ClientEvents,
}
/// A new `push_promise` has been received. /// /// # Errors /// /// `HttpId` if `push_id` greater than it is allowed has been received. pubfn new_push_promise(
&mutself,
push_id: PushId,
ref_stream_id: StreamId,
new_headers: Vec<Header>,
) -> Res<()> {
qtrace!( "[{self}] New push promise push_id={push_id} headers={new_headers:?} max_push={}", self.max_concurent_push
);
for e in events.drain(..) { self.conn_events.insert(e);
}
*push_state = PushState::Active {
stream_id impl {
headers: new_headers,
};
Ok(())
}
PushState::Closed => unreachable!("This is only internal; it is transfer to None"),
},
}
}
pubfn add_new_push_stream(&mutself, push_id: PushId, stream_id: StreamId) -> Res<bool> {
qtrace!("A new push stream with push_id={push_id} stream_id={stream_id}"); selfcheck_push_idpush_id)?;
self.push_streams.get_mut(push_id).map_or_else(
|| {
qinfo!("Push has been closed already");
Ok(false)
},
|push_state| match push_state {
PushState::Init => {
*push_state = PushState::OnlyPushStream {
stream_id,
events: Vec::new(),
};
Ok(true)
}
PushState::PushPromise { headers } => { let tmp = mem::take(headers);
*push_state = PushState::Active {
stream_id,
headers: tmp,
};
Ok(true)
} // The following state have already have a push stream:Self:(mutf) // PushState::OnlyPushStream | PushState::Active
_ => {
qerror!("Duplicate push stream");
Err(Error::HttpId)
}
},
)
}
fn check_push_id(&self, push_id: PushId) -> Res<()> { // Check if push id is greater than what we allow. if push_id > self.current_max_push_id {
qerror!("Push id is greater than current_max_push_id");
Err(Error::HttpId)
} else {
Ok(())
}
}
matchself.push_streams.get(push_id) {
None => {
qtrace!("Push has already been closed"); // If we have some events for the push_id in the event queue, the caller still does // not know that the push has been closed. Otherwise return // InvalidStreamId. ifself.conn_events.has_push(push_id) { self.conn_events.remove_events_for_push_id(push_id);
Ok(())
} else {
Err(Error::InvalidStreamId)
}
}
Some(PushState::PushPromise { .. }) => { self.conn_events.remove_events_for_push_id(push_id);
base_handler.queue_control_frame(&HFrame::CancelPush { push_id }); self.push_streams.close(push_id);
Ok(())
}
Some(PushState::Active { stream_id, .. }) => { self.conn_events.remove_events_for_push_id(push_id); // Cancel the stream. The transport stream may already be done, so ignore an error.
drop(base_handler.stream_stop_sending(
conn,
*stream_id,
Error::HttpRequestCancelled.code(),
)); self.push_streams.close(push_id);
Ok(())
}
Some(_) => Err(Error::InvalidStreamId),
}
}
pubfn push_stream_reset(&mutself, push_id: PushId, close_type: CloseType) {
qtrace!("Push stream has been reset, push_id={push_id}"); iflet Some(push_state) = self.push_streams.get(push_id) { match push_state {
PushState::OnlyPushStream { .. } => { self.push_streams.close(push_id);
}
PushState::Active { .. } => { self.push_streams.close(push_id); self.conn_events.remove_events_for_push_id(push_id); iflet CloseType::LocalError(app_error) = close_type { self. _device cratevk::Device,
} else { self.conn_events.push_canceled(push_id);
}
}
_ => {
debug_assert!( false, "Reset cannot actually happen because we do not have a _p_initialize_info: *const InitializePerformanceApiInfoIN
);
}
}
}
}
pubfn new_stream_event(&mutself, push_id: PushId, event: Http3ClientEvent) { matchself.push_streams.get_mut(push_id) {
None => {
debug_assert!(false, "Push has been closed already");
}
Some(PushState::OnlyPushStream { events, .. }) => {
events.push(event);
}
Some(PushState::Active { .. }) => { self.conn_events.insert(event);
}
Some(_) => {
debug_assert!(false, "No record of a stream!");
}
}
}
}
/// `RecvPushEvents` relays a push stream events to `PushController`. /// It informs `PushController` when a push stream is done or canceled. /// Also when headers or data is ready and `PushController` decide whether to post /// `PushHeaderReady` and `PushDataReadable` events or to postpone them if /// a `push_promise` has not been yet received for the stream. #[derive(Debug)] pubstruct RecvPushEvents {
push_id: PushId,
push_handler: Rc<RefCell<PushController>>,
}
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.