/// `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<Header>,
},
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: u64,
}
/// Returns None if a stream has been closed already. pubfn get_mut(
&mutself,
push_id: u64,
) -> Option<&mut <usize as SliceIndex<[PushState]>>::Output> { if push_id < self.first_push_id { return None;
}
let inx = usize::try_from(push_id - self.first_push_id).unwrap(); if inx >= self.push_streams.len() { self.push_streams.resize(inx + 1, PushState::Init);
} matchself.push_streams.get_mut(inx) {
Some(PushState::Closed) => None,
e => e,
}
}
/// Returns None if a stream has been closed already. pubfn get(&mutself, push_id: u64) -> Option<&mut PushState> { self.get_mut(push_id)
}
/// Returns the State of a closed push stream or None for already closed streams. pubfn close(&mutself, push_id: u64) -> Option<PushState> { matchself.get_mut(push_id) {
None | Some(PushState::Closed) => None,
Some(s) => { let res = mem::replace(s, PushState::Closed); whileself.push_streams.front() == Some(&PushState::Closed) { self.push_streams.pop_front(); self.first_push_id += 1;
}
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 steams 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: u64, // 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,
}
impl PushController { /// 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: u64,
ref_stream_id: StreamId,
new_headers: Vec<Header>,
) -> Res<()> {
qtrace!(
[self], "New push promise push_id={} headers={:?} max_push={}",
push_id,
new_headers, self.max_concurent_push
);
for e in events.drain(..) { self.conn_events.insert(e);
}
*push_state = PushState::Active {
stream_id: stream_id_tmp,
headers: new_headers,
};
Ok(())
}
PushState::Closed => unreachable!("This is only internal; it is transfer to None"),
},
}
}
pubfn add_new_push_stream(&mutself, push_id: u64, stream_id: StreamId) -> Res<bool> {
qtrace!( "A new push stream with push_id={} stream_id={}",
push_id,
stream_id
);
self.check_push_id(push_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: // PushState::OnlyPushStream | PushState::Active
_ => {
qerror!("Duplicate push stream.");
Err(Error::HttpId)
}
},
)
}
fn check_push_id(&self, push_id: u64) -> 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 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.
mem::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: u64, 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.conn_events.push_reset(push_id, app_error);
} else { self.conn_events.push_canceled(push_id);
}
}
_ => {
debug_assert!( false, "Reset cannot actually happen because we do not have a stream."
);
}
}
}
}
pubfn new_stream_event(&mutself, push_id: u64, 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: u64,
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.