Skip to main content

RequestStream

Struct RequestStream 

Source
pub struct RequestStream {
    send: FramedSendStream,
    recv: FramedRecvStream,
    request_id: VarInt,
    kind: RequestKind,
    fetch_data: Option<FramedSendStream>,
    draft: DraftVersion,
    stream_id: u64,
    cancelled: bool,
    finished: bool,
    origin: RequestOrigin,
    responded: bool,
    owes_publish_done: bool,
}
Expand description

One request and its answer, on a bidirectional stream of their own.

Draft-19 Section 3.3 keeps requests off the control plane: each request is the first message on a bidirectional stream it opens, and the response comes back on that same stream. Responses carry no request id on this draft — the stream is the correlation, which is why this handle exists and why a bare request id is no longer enough to find an answer.

§Reading and writing go through the connection

This handle owns both halves of the stream but not the session, so the endpoint state machine and the observer stay where they were. Read a response with Connection::recv_on_request_stream, write a follow-up with Connection::send_on_request_stream, and cancel with Connection::cancel_request_stream.

cancel and peer_cancelled are on the handle because they touch the stream and nothing else, and Drop needs the first of them. Neither moves the endpoint’s record of the request, which is why the connection carries a pair of its own.

§Dropping this cancels the request

A dropped handle resets the send half and sends STOP_SENDING on the receive half, both with REQUEST_CANCELLED, unless the stream was already cancelled or finished. Letting the default drop stand would send a FIN instead, telling the peer the request ended cleanly when it was abandoned.

The consequence is sharp and worth stating: a live subscription’s request stream must be held for the subscription’s life, because PUBLISH_DONE arrives on it. Keeping only request_id and letting the handle fall out of scope cancels the subscription.

What a drop cannot do is say so at the endpoint. Drop holds the stream and not the session, so the request stays where it was in the endpoint’s record while the stream it travelled on is gone. Call Connection::cancel_request_stream wherever that record matters.

§Which side opened it changes what this handle does

origin says whether this endpoint opened the stream or accepted it, and three behaviours turn on it: reads dispatch as responses or as follow-ups to the peer’s request, the respond_* helpers refuse a stream this endpoint opened, and Drop resets with REQUEST_UNANSWERED rather than REQUEST_CANCELLED. Everything else — cancel, peer_cancelled, Connection::send_on_request_stream — is the same in both directions. Draft-19 Section 3.3.3 is explicit that a cancel is available to both: “Senders cancel requests if the response is no longer of interest; Receivers cancel requests if they are unable to or choose not to respond.”

finish is the one act that is constrained rather than merely different: Section 3.3.2 forbids a FIN before the messages that direction still owes.

All fields are private so the shape can grow without breaking callers.

Fields§

§send: FramedSendStream§recv: FramedRecvStream§request_id: VarInt§kind: RequestKind§fetch_data: Option<FramedSendStream>

The unidirectional stream this request’s objects are being served on.

Only a FETCH has one, and only once the caller has opened it through Connection::open_fetch_stream_on. Held here rather than in a table on the connection because the request stream is already the thing that knows what this request still owes, and because a handle kept beside the request cannot outlive it.

§draft: DraftVersion§stream_id: u64§cancelled: bool§finished: bool§origin: RequestOrigin§responded: bool

Whether a respond_* helper has written a response on this stream. True on a RequestOrigin::Peer stream from the response written on it, and on a RequestOrigin::Local one from the answer to an update the peer sent, which is the only response this endpoint writes on a stream of its own.

§owes_publish_done: bool

Whether this endpoint is the publisher of a subscription established on this stream and has not yet sent PUBLISH_DONE.

True from the PUBLISH this endpoint sent, and from the SUBSCRIBE_OK it wrote answering a peer’s SUBSCRIBE; false again once PUBLISH_DONE is out. It is what draft-19 Section 3.3.2’s second clause is checked against in finish.

Implementations§

Source§

impl RequestStream

Source

pub fn request_id(&self) -> VarInt

The request id the endpoint allocated for this request.

Useful for logging and for endpoint calls that still take one. It is not enough to find the response: draft-19 responses carry no request id, so only this stream identifies them.

Source

pub fn kind(&self) -> RequestKind

Which of the seven request types opened this stream.

Source

pub fn stream_id(&self) -> u64

The transport-level stream identifier, the same one ClientEvent::StreamOpened reports for data streams.

Source

pub fn draft(&self) -> DraftVersion

The draft version this stream is framed for.

Source

pub fn origin(&self) -> RequestOrigin

Which side opened this stream.

RequestOrigin::Peer means this endpoint owes a response and the respond_* helpers apply; RequestOrigin::Local means it is waiting for one.

Source

pub fn responded(&self) -> bool

Whether a response has been written on this stream by one of the respond_* helpers.

On a RequestOrigin::Local stream this says an update the peer sent was answered here, not that the request itself was: that one is answered by the peer.

Source

pub fn owes_publish_done(&self) -> bool

Whether this endpoint still owes the peer a PUBLISH_DONE on this stream.

True for a PUBLISH this endpoint sent and for a peer’s SUBSCRIBE it answered with SUBSCRIBE_OK — the two ways draft-19 Section 3.3.2’s “publisher of an Established subscription” is reached — until Connection::publish_done or Connection::publish_done_on has run. While it is true, finish refuses.

Source

pub fn fetch_data(&mut self) -> Option<&mut FramedSendStream>

The stream this request’s objects are being served on, if one is open.

Only a FETCH answered through Connection::open_fetch_stream_on has one. Writing objects goes through this rather than through a handle the caller keeps, so that the connection can still reach the stream when a rule says to reset it.

Source

fn reset_fetch_data(&mut self)

Reset the fetch data stream, if one was opened, and forget it.

The sentence that requires this names no error code, so the code comes from the registry rather than from here: CANCELLED is “the stream was cancelled by either endpoint”, which is what a publisher abandoning the objects it was serving has done.

Source

pub fn is_cancelled(&self) -> bool

Whether cancel has already run on this handle.

Says nothing about the peer: a peer reset is learned from peer_cancelled or from the next read.

Source

pub fn cancel(&mut self, code: u64) -> Result<(), ConnectionError>

Cancel the request by resetting the stream, handing the peer code.

Draft-17 removed UNSUBSCRIBE and FETCH_CANCEL and draft-19 has not brought them back: resetting the request stream is how a request is withdrawn. Both halves are shut — a QUIC bidirectional stream has two independent halves, so resetting only the send half would leave the peer free to keep writing a response nobody will read. The send half is reset with code and the receive half is stopped with the same value.

REQUEST_CANCELLED is the ordinary choice. Draft-19 Section 3.3.4 says an application SHOULD take the code from the Stream Reset Error Codes registry when resetting, or sending STOP_SENDING on, any stream — request streams included — so StreamResetErrorCode is where a value other than the default should come from. The parameter is a plain u64 because the registry reserves greasing code points that have no variant; a caller with a named code has StreamResetErrorCode::as_u64.

This is the stream and nothing else. The endpoint’s record of the request does not move, so a response already in flight is still accepted after this returns. Connection::cancel_request_stream does both and is what a caller holding a connection should reach for; this stays because Drop has no connection to reach.

Idempotent, and it retires the Drop behaviour: a cancelled handle does nothing further when it goes out of scope. Errors from a stream that was already reset or stopped are swallowed for the same reason — the request is cancelled either way.

§Errors

ConnectionError::Transport carrying TransportError::Write if code is outside the QUIC varint range (0..2^62). Nothing is sent in that case, and the handle is not marked cancelled, so a caller can retry with a representable code.

Source

pub async fn peer_cancelled(&mut self) -> Result<Option<u64>, ConnectionError>

Wait for the peer to cancel this request, consuming nothing.

A caller applying backpressure is deliberately not calling Connection::recv_on_request_stream, which is the only other place a peer reset surfaces — so without this the abandonment goes unobserved for as long as the backpressure lasts. This grants no flow-control credit and is cancel-safe.

Returns Ok(Some(code)) with the peer’s application error code, or Ok(None) meaning no reset is observable, now or ever — stop asking. A caller that re-polls after Ok(None) spins.

Like cancel, this records nothing at the endpoint. Connection::peer_cancelled_on_request_stream is the same wait with the record attached.

On WebTransport this always answers Ok(None): wtransport exposes no reset-only observable, so a WebTransport caller learns of a peer cancel on its next read and not before.

Source

pub async fn finish(&mut self) -> Result<(), ConnectionError>

Finish the send half cleanly, leaving the receive half open.

Draft-19 Section 3.3.2 settles what a FIN means, which earlier drafts left open: “A FIN only indicates that an endpoint will send no further messages in that direction; it is not a request cancellation.” A requester may therefore FIN before its response arrives — “A requester, with the exception of the sender of PUBLISH, MAY FIN immediately after sending a message if it will not send a REQUEST_UPDATE” — and the receive half stays open to carry the response, which is why this touches only the send half.

What the same section forbids is finishing early: “An endpoint MUST NOT send a FIN on a direction of a request stream until it has sent all required messages on that direction for its request type”, and “An endpoint that receives a FIN before all required messages have arrived treats the request as failed.” The section names the two messages that are always required — “an endpoint sending a response to a request MUST send the corresponding response message, and the publisher of an Established subscription MUST send PUBLISH_DONE” — and both are things this handle knows about itself, so both are checked here and nothing is written when either is outstanding.

A responder that owes a response has not sent one: ConnectionError::FinBeforeResponse. A publisher that owes a PUBLISH_DONE, whether from a PUBLISH it sent or a peer’s SUBSCRIBE it accepted, gets ConnectionError::FinBeforePublishDone — which is why Connection::publish_done and Connection::publish_done_on are the routes that end a publication, rather than a bare finish.

What is not checked is everything the caller alone knows: a SUBSCRIBE_NAMESPACE responder deciding it has no more namespaces to announce, or a requester deciding it will send no REQUEST_UPDATE. The section leaves those to the endpoint, and so does this.

Drafts 17 and 18 have no Section 3.3.2 and no guard: their finish is unconditional. A porter must not carry this one back to them.

A finished handle, like a cancelled one, does nothing further on Drop. That matters here: the default drop resets both halves, which Section 3.3.2 contrasts with a FIN as the abrupt close, so a request that ended cleanly must come through this to avoid being reported as cancelled.

Trait Implementations§

Source§

impl Drop for RequestStream

Source§

fn drop(&mut self)

Reset the request unless it was already cancelled or finished.

See the type-level note: the default drop would FIN the send half, which claims a clean end for a request the caller walked away from — and on draft-19 a FIN is a stronger claim than on earlier drafts, since Section 3.3.2 has the receiver of one treat the request as failed only when messages are still owed and complete otherwise.

The code says which walking away it was. A stream this endpoint opened is cancelled — REQUEST_CANCELLED — which is the requester act Section 3.3.3 describes. A stream the peer opened is reset with REQUEST_UNANSWERED whether or not a response was already written: before one, the request was never served; after one, the obligations that follow it are still outstanding.

Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more