quic: fix readable stream truncation on stop-sending, abort & timeout#63967
quic: fix readable stream truncation on stop-sending, abort & timeout#63967pimterry wants to merge 1 commit into
Conversation
Signed-off-by: Tim Perry <pimterry@gmail.com>
|
Review requested:
|
|
hmm... "This PR changes the iterable to throw in any scenario where you don't receive the full stream" I'm not sure this should be a blanket default behavior in every scenario. A closed stream in not really no-notice as the I wonder if it is reasonable to make this a configuration option. |
You get notice that the stream is closed, but you're just not directly told that it's been truncated & failed to reach the end of the data, as opposed to cleanly completing. Both return
Do you have any examples where the stream ends abruptly without the remote peer sending FIN, but a read should resolve successfully? I'd be interested in protocol flows where unclear shutdown like that is common. For actual errors, I think this change is clear enough and hopefully unambiguous. I can see how the cleaner shutdown cases (idle timeout/remote & local cancel) are more debatable, I did think about the same thing for a good while. I've end up convincing myself that the iterator should always treat not reaching FIN as an error though. Some more detailed reasons:
I'm not saying this should be an error emit or something similar that everybody needs to always carefully look for & handle. Just that if you're actively reading, and the stream ends without the remote peer sending a FIN, the read should fail (reject with an error). If you never read the stream, or if you've started but stopped now (not awaiting As above, I haven't changed |
Currently in ~all cases where a QUIC stream is closed abruptly, we still exposed the data as a successfully completed stream on the iterable - in effect truncating it without any notice. This includes:
destroy()orstopSending(). This is a no-error abort, but that doesn't mean we should expose the received data as a successfully received & complete stream.This PR changes the iterable to throw in any scenario where you don't receive the full stream. If the remote peer doesn't send a happy FIN successfully for some reason, you haven't received the full data, and you should know that.
This matches the behaviour for Node streams in all our other APIs, including TCP sockets, which expose various PREMATURE_CLOSE and similar errors when you try to read a stream that is closed but hasn't actually received a clean end signal. All data up to the stream abort is delivered successfully, it's just the final read at the end that exposes the error.
This modifies blob.js generally, but only
createBlobReaderIterablewhich is exclusively used by QUIC, and I think the behaviour is correct for an iterable reader in any scenarios.This change doesn't touch
closed, which I did consider for a while. The docs say:I'm treating that as "is there an actual definite error" as opposed to "the readable or the writable actually completed successfully". This diverges from our other stream semantics, but I think it's reasonable. Just might be worth considering if we want a separate way to wait for "readable ended OK" later on, currently those aren't part of the stream API so only the consumer itself knows this and there's no way to check the stream result externally. I'd rather leave that till later and just fix the clear bugs for now.