cloudflare / quiche

šŸ„§ Savoury implementation of the QUIC transport protocol and HTTP/3

Home Page:https://docs.quic.tech/quiche/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Connection::stream_send() reports Err::Done for finished streams

bwoebi opened this issue Ā· comments

This case occurs with streams closed with STOP_SENDING, then a fin bit being received too, then the data being read via Connection::stream_recv().

In high-level terms:

server opens stream - sends some data
client reads some data and writes data
server reads that data, sends STOP_SENDING, and writes some data, then sends the fin bit in a separate empty message of length 0
client sees stream as readable, then reads the data
client tries to write some data ... gets Err::Done from stream_send().

As far as I gathered from the documentation, Err::Done is supposed to be returned when no data could be written because of no capacity and Err::StreamStopped returned when the stream has been closed by the peer.

In this specific case though, the stream seems to already have been freed by quiche, thus StreamMap::get_or_create() returns Err::Done, which Connection::stream_send() simply forwards.

I would expect an Err::StreamStopped, or at least an Err::InvalidStreamState (signaling that a stream with that id could not be created, in that case, obviously, because the stream id is not matching what's allowed for the client/server).

Currently I'm working around it with an extra check of Connection::stream_writable() for Err::InvalidStreamState whenever Err::Done is reported, but that seems unnecessary/wrong to me, why would that be needed?

In case this is the expected behaviour though, I would like to ask for a note in the documentation to check stream_writable() explicitly if Err::Done is returned.

Thanks for the detailed report. Thatspecific sequence would lead to the stream being collected, and hence always get_or_create will return Done.

For stream_recv we return a InvalidStreamState if the stream doesn't exist

.ok_or(Error::InvalidStreamState(stream_id))?;

We could do similar for stream_send, capture the Done and convert it to InvalidStreamState at

let stream = self.get_or_create_stream(stream_id, true)?;
. Using StopSending for the collected streams seems like it would be confusing two different stream states.

Yes, an InvalidStreamState would be probably most appropriate here with the current architecture.

It would be great if there were a solution for #1299 too, but I suppose quiche has no mode where we can instruct it to explicitly keep streams alive until they are manually collected with a method call (Connection::stream_free()) or such.

Oh thanks for the reminder on that. I guess if we're considering g changing the API, we might want to follow through with a solution that can satisfy both.