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,
}Expand description
One request and its answer, on a bidirectional stream of their own.
Draft-18 Section 3.3 carries forward draft-17’s move of 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, 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. Which code goes on the wire
depends on who opened the stream — see Drop.
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,
finish,
Connection::send_on_request_stream — is the same in both directions.
Draft-18 Section 3.3.2 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.”
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: boolWhether 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.
Implementations§
Source§impl RequestStream
impl RequestStream
Sourcepub fn request_id(&self) -> VarInt
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-18 responses carry no request id, so only this stream identifies them.
Sourcepub fn kind(&self) -> RequestKind
pub fn kind(&self) -> RequestKind
Which of the seven request types opened this stream.
Sourcepub fn stream_id(&self) -> u64
pub fn stream_id(&self) -> u64
The transport-level stream identifier, the same one
ClientEvent::StreamOpened reports for data streams.
Sourcepub fn draft(&self) -> DraftVersion
pub fn draft(&self) -> DraftVersion
The draft version this stream is framed for.
Sourcepub fn origin(&self) -> RequestOrigin
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.
Sourcepub fn responded(&self) -> bool
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.
Sourcepub fn fetch_data(&mut self) -> Option<&mut FramedSendStream>
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.
Sourcefn reset_fetch_data(&mut self)
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.
Sourcepub fn is_cancelled(&self) -> bool
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.
Sourcepub fn cancel(&mut self, code: u64) -> Result<(), ConnectionError>
pub fn cancel(&mut self, code: u64) -> Result<(), ConnectionError>
Cancel the request by resetting the stream, handing the peer code.
Draft-18 has no UNSUBSCRIBE and no FETCH_CANCEL: 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. The parameter is a plain
u64 rather than a draft enum because the registry is named differently
across these drafts — draft-17 Section 14.5.4’s “Data Stream Reset Error
Codes” against draft-18’s “Stream Reset Error Codes” — and a caller who
wants a typed value 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.
Sourcepub async fn peer_cancelled(&mut self) -> Result<Option<u64>, ConnectionError>
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.
Sourcepub async fn finish(&mut self) -> Result<(), ConnectionError>
pub async fn finish(&mut self) -> Result<(), ConnectionError>
Finish the send half cleanly, leaving the receive half open.
Whether a requester may FIN before its response arrives is not settled by anything this implementation can check, so no request helper calls this and the default is to leave the send half open for the request’s life. It is offered for a caller that knows its peer.
A finished handle, like a cancelled one, does nothing further on
Drop.
Trait Implementations§
Source§impl Drop for RequestStream
impl Drop for RequestStream
Source§fn drop(&mut self)
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.
The code says which walking away it was. A stream this endpoint opened
is cancelled — REQUEST_CANCELLED — which is the requester act
draft-18 Section 3.3.2 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.