pub struct Reader { /* private fields */ }Expand description
The receiving half of a split request stream.
Implementations§
Source§impl Reader
impl Reader
Sourcepub async fn recv_data(&mut self) -> Result<Option<Bytes>, StreamError>
pub async fn recv_data(&mut self) -> Result<Option<Bytes>, StreamError>
Reads the next chunk of body data.
Ok(None) means the peer finished its sending side – for a CONNECT
tunnel, the client’s FIN.
Cancel-safe: every byte read is accounted for in self before this
returns, so a caller may poll it inside a select! with a timeout, which
is exactly what a UDP session does.
Sourcepub async fn reset_by_peer(&mut self) -> StreamError
pub async fn reset_by_peer(&mut self) -> StreamError
Resolves when the peer resets this stream, with the reset as an error.
Self::recv_data already reports a reset – it is the Err a read
fails with – but only to a caller that is reading. A CONNECT tunnel
spends much of its life not reading: with a chunk in hand and a target
that has stopped taking bytes, the pump is parked in a write instead,
and nothing there watches the request stream. This is what such a write
can select on.
It reports only a reset, and never resolves for any other ending. A clean FIN and a stream this endpoint stopped both belong to the reading half, which is where a caller meets them; ending the wait here would turn one of them into an abort.
§Why not quinn::RecvStream::received_reset
Because it borrows a slot it never gives back, and quinn asserts on that
slot being empty. quinn keeps one waker per stream for whoever is
waiting to read, and exactly three things take an entry out of it: a
StreamEvent::Readable for that stream, a RecvStream::stop while the
stream still exists at the protocol layer, and RecvStream::drop while
all_data_read is still false. A read that succeeds does not, so an
entry outlives every later read of the bytes it was waiting for. That
call writes into the slot on every poll that finds no reset, and a
select! which drops the arm leaves it behind.
In release builds that is a leak – one waker per tunnel, held until the
connection closes, keeping the finished task’s cell alive with it. In
debug builds it is a panic, because RecvStream::drop debug-asserts the
slot is empty once the stream has been read to its end. An ordinary
upload trips it: the client sends chunk after chunk with its FIN behind
them, the pump is parked writing chunk k while k+1 onwards are
already buffered, and every one of those iterations leaves another waker
behind. Nothing becomes readable after the FIN, so the last one is never
cleared, and the clean end of the stream then panics the tunnel’s task
inside a destructor.
So the wait is built on the zero-length read alone, which registers only through quinn’s own read path. That kind of entry is safe to abandon: quinn takes it out again the moment the stream becomes readable, and a stream cannot reach its end without becoming readable first, so there is never one left when the reading half meets the FIN.
§What that costs
A zero-length read parks only while the stream has nothing to give. With
bytes already buffered it returns at once – which is precisely the state
a stalled upload sits in – so the wait there is a poll on a timer rather
than a wake-up, and RESET_PEEK_INTERVAL – 250 ms, documented where it
is defined – bounds how late the reset is noticed. A reset that arrives
while nothing is buffered needs no timer: it wakes the parked read at
once, as any read error would.
Cancel-safe, so a select! may poll it and drop it repeatedly.
Sourcepub fn stop_receiving(&mut self, code: Code)
pub fn stop_receiving(&mut self, code: Code)
Asks the peer to stop sending on this stream.