Change:
Thanks for the review Martin. I suppose the higher fidelity version of STOPSENDING would not change the state of the stream at all, but induce the peer to send a RSTSTREAM as QUIC does. Maybe that is better.
Actually, there is worse: when I send STOP_SENDING, I no longer know when the peer has received and acted on that without an explicit stream termination signal, so I can't transition the stream to a closed state.
Wait, how is this different than QUIC? You can't close a bidirectional stream until the peer sends you a FIN or RST.
Right; I was reading your description of STOPSENDING as changing stream state, either at the point that it was sent or received. In that case, if a stream is "open", the sender of STOPSENDING might believe that all it needs to do is send RESETSTREAM to close the stream completely. If it does so, the peer will receive those frames and then both peers will believe the stream to be closed. But the sender of STOPSENDING has no way to distinguish between that set of circumstances and one where the peer takes an arbitrary amount of time to process the frames. The receiver of STOP_SENDING might just keep sending data indefinitely. How does the receiver of those frames know when the other side has truly stopped? You might say "too bad". After all, the only thing you need to do is keep counting against connection-level flow control, but it makes the "closed" state that much harder to manage.
As written, STOPSENDING does change stream state, which would allow the sender to move to closed unilaterally (for example, send WTRSTSTREAM and WTSTOPSENDING together). DATA frames may trickle in after that for the closed stream, you just keep handling them for connection flow control -- this is the same as RSTSTREAM in vanilla H2. If we make it like QUIC instead, where STOPSENDING does not affect state, the sender of the STOPSENDING has to wait for WTRSTSTREAM or a FIN to move to closed. So I think either model is fine and I don't have a stylistic preference. I'm wondering if it has an effect on the stream limit accounting, but I don't think it does? The H2 model frees the streams 1/2 RTT faster.
Ugh, I should know this stuff better, I only recently rewrote the closing bit in h2 to deal with this problem. Of course. It's bad, but no worse than status quo. Thanks for correcting me. (Full transparency: I think that the QUIC stream model is so much better than h2 that I can no longer stomach these minor infidelities in h2. This PR does manage to balance the need to provide API consistency across versions without fixing/changing h2's problems. I'm sure I'll manage to swallow my distaste eventually if we go this way.)
Heh. Since this is merging an H3 feature into H2 I feel like we could pick either model and still claim to be consistent. For now, I'll leave this H2 style.
QUIC and HTTP/3 support resetting only half of a bidirectional stream. If WebTransport requires this functionality, then this draft would need additional frames to convey those signals (WTRESETSTREAM and WTSTOPSENDING).
Fixed by
Thanks for writing this up Alan. This works. Though I'm not sure about the STOPSENDING analogue; QUIC permits the sender to choose the ultimate code for RESETSTREAM, even if it is generally copied from STOPSENDING. I know that we mostly don't need to include RESETSTREAM here as the stream size accounting isn't as critical, so maybe perfect fidelity isn't needed.Thanks NAME

Discussion:
4.2. <ins> The RST_STREAM frame defined in HTTP/2 moves a stream to the "closed" state, but WebTransport streams can be reset unidirectionally, as in QUIC and HTTP/3.  A new HTTP/2 frame called WT_RST_STREAM is introduced for an endpoint to unidirectionally reset a stream for writing.  WT_RST_STREAM frames can be sent on a stream in the "open", or "half-closed (remote)" state. The WT_RST_STREAM frame contains a single unsigned, 32-bit integer identifying the error code.  The error code indicates why the stream is being abruptly terminated for writing. The WT_RST_STREAM frame does not define any flags. The WT_RST_STREAM frame half-closes the referenced stream and effects the same state machine transitions as sending or receiving the END_STREAM flag.  If the sender is in the "open" state, it transitions to "half-closed (local)".  If the sender is in the "half- closed (remote)" state, it transitions to "closed".  If the receiver is in the "open" state, it transitions to "half-closed (remote)".  If the receiver is in the "half-closed (local)" state, it transitions to "closed". After sending a WT_RST_STREAM on a stream, the sender MUST NOT send additional DATA frames for that stream.  If another frame is received on a stream after receiving a WT_RST_STREAM, the recipient MUST treat this as a connection error of type PROTOCOL_ERROR. WT_RST_STREAM frames MUST be associated with a WebTransport stream. If a WT_RST_STREAM frame is received with a stream identifier of 0x0, or a request or push stream, the recipient MUST treat this as a connection error of type PROTOCOL_ERROR. WT_RST_STREAM frames MUST NOT be sent for a stream in any state other than "open" or "half-closed (remote)".  If a WT_RST_STREAM frame identifying a stream in the "idle", "reserved (local)", "reserved (remote)" state is received, the recipient MUST treat this as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.  Because of race conditions with WT_STOP_SENDING, it is possible to receive a WT_RST_STREAM in the "half-closed (remote)" or "closed" state.  The recipient should ignore the frame in this case. A WT_RST_STREAM frame with a length other than 4 octets MUST be treated as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR. 4.3. The RST_STREAM frame defined in HTTP/2 moves a stream to the "closed" state, but WebTransport streams can be reset unidirectionally, as in QUIC and HTTP/3.  A new HTTP/2 frame called WT_STOP_SENDING is introduced for an endpoint to unidirectionally reset a stream for reading.  WT_STOP_SENDING frames can be sent on a stream in the "open", or "half-closed (local)" state. The WT_STOP_SENDING frame contains a single unsigned, 32-bit integer identifying the error code.  The error code indicates why the reading the stream is being abandoned. The WT_STOP_SENDING frame does not define any flags. The WT_STOP_SENDING frame half-closes the referenced stream and effects the same state machine transitions as sending or receiving the END_STREAM flag.  If the sender is in the open state, it transitions to "half-closed (remote)".  If the sender is in the "half-closed (local)" state, it transitions to "closed".  If the receiver is in the "open" state, it transitions to "half-closed (local)".  If the receiver is in the "half-closed (remote)" state, it transitions to "closed". After receiving a WT_STOP_SENDING on a stream, the sender MUST NOT send additional frames for that stream.  After sending the WT_STOP_SENDING, the sending endpoint MUST be prepared to receive and handle additional frames sent on the stream that might have been sent by the peer prior to the arrival of the WT_STOP_SENDING. WT_STOP_SENDING frames MUST be associated with a WebTransport stream. If a WT_STOP_SENDING frame is received with a stream identifier of 0x0, or a request or push stream, the recipient MUST treat this as a connection error of type PROTOCOL_ERROR. WT_STOP_SENDING frames MUST NOT be sent for a stream in any state other than "open" or "half-closed (local)".  It is possible to receive a WT_STOP_SENDING in another state however, because the sender might have closed or reset the stream while the WT_STOP_SENDING was in flight.  If a WT_STOP_SENDING frame identifying a stream that is already "closed" or "half-closed (local)", the recipient SHOULD ignore the frame. It is also possible to receive DATA frames on a WebTransport stream in the "half-closed (remote)" or "closed" states if the stream transitioned there via WT_STOP_SENDING.  If a DATA frame is received on a WebTransport stream in one of these states, the recipient MUST account for its contribution against the connection flow-control window and MUST NOT treat it as an error. A WT_STOP_SENDING frame with a length other than 4 octets MUST be treated as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR. 4.4. </ins> A new HTTP/2 frame called WT_DATAGRAM is introduced for either endpoint to transmit a datagram.  WT_DATAGRAM frames are sent with Stream Identifier 0.

This document <ins> The "WT_RST_STREAM" frame allows HTTP/2 WebTransport streams to be unidirectionally reset for writing: 0xTBD WEBTRANSPORT_RST_STREAM This document The "WT_STOP_SENDING" frame allows HTTP/2 WebTransport streams to be unidirectionally reset for reading: 0xTBD WEBTRANSPORT_STOP_SENDING This document </ins> The "WT_DATAGRAM" frame allows HTTP/2 client and server to exchange datagrams used by WebTransport: