Subject: [quicwg/base-drafts] Unidirectional RESET_STREAM/STOP_SENDING semantics map poorly to proxies (#2415)

From: martinduke <[email-masked]>
Date: Mon, 04 Feb 2019 15:55:34 -0800

Apologies for the lateness of the issue, but I only recently realized I was doing it wrong.

What is the thinking behind RESET_STREAM only applying to the send side of a bidirectional stream? What use case says "never mind about what I'm sending you, but please keep sending to me" within the same stream context?

I ask because this is a break from the HTTP/2-TCP world and I'm not sure we fully understand its properties.  In particular, this divergence is uncomfortable for proxying HTTP/3 for HTTP/1 and HTTP/2 servers.

If the HTTP/3 client sends a RESET_STREAM, what is the proper semantic to translate to the HTTP/2 side? An HTTP/2 RESET_STREAM will kill both directions, which seems like a problem in the general case. We can FIN the H/2 stream, but is that communicating the correct semantic to the application?

Similarly, if we get a STOP_SENDING, what's the correct translation? Zeroing out receive flow control on the HTTP/2 stream?

More generally, a future (non-HTTP) proxy use case might map QUIC streams to some protocol over individual TCP connections. This has the same problem: FIN maps nicely to TCP FIN, but RESET_STREAM and STOP_SENDING both have to use TCP RST somehow.

Maybe the answer is that fronting non-QUIC/H3 protocols implies that STOP_SENDING and RESET_STREAM have to go together. Would that break stuff?

I can see a few cases where it might be optimal to be able to flush only one direction of queues, but wonder if this is worth the added semantic confusion, and the bandwidth cost of having to send RESET_STREAM in both directions. We need to get a "final length" in both directions to keep the flow control accounting right, but that could be declared by the receiver.

I'm not sure what specific recommendation to make here, but am curious about everyone's thoughts.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/2415

---

From: Martin Thomson <[email-masked]>
Date: Mon, 04 Feb 2019 16:09:15 -0800

This is a great example of something that would be better on the mailing list.

The reason for the split is to ensure that the state machine is consistent and synchronized.  Cancellation of a stream requires RESET_STREAM in both directions to ensure that the flow control state doesn't get out of sync.  We discussed attaching a value to STOP_SENDING, but that creates difficult conflicts if one side sends STOP_SENDING and the other sends RESET_STREAM concurrently.  We also don't insist on STOP_SENDING being transmitted reliably.  In other words, just fixing this would be a massive change.

It also exists to provide for cases in HTTP where one side of a flow (typically a request) isn't needed.  Consider a large POST to a resource that has been removed.  The server can send STOP_SENDING and a 404 response without fear that the 404 will be shredded.

All of that has been litigated at some length.

The problem here is in mapping to protocols that have inconsistently implemented or non-existent half-closed states.  That would be TCP for the former and HTTP/2 for the latter.  My inclination would be thus:

RESET_STREAM only = stop forwarding data in that direction, and record this state
STOP_SENDING only = stop forwarding data in that direction, and record this state
RESET_STREAM in both directions or RESET_STREAM and STOP_SENDING from the same peer = kill the flow, using things like h2 RST_STREAM or TCP RST

I don't think that this implies that RESET_STREAM and STOP_SENDING have to be bundled.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/2415#issuecomment-460463895

---

From: martinduke <[email-masked]>
Date: Mon, 04 Feb 2019 16:12:39 -0800

Closed #2415.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/2415#event-2117224082

---

From: Mike Bishop <[email-masked]>
Date: Wed, 06 Feb 2019 11:36:45 -0800

As to the H3 question, the spec currently says:
> When a stream is closed, this indicates the end of an HTTP message. Because some messages are large or unbounded, endpoints SHOULD begin processing partial HTTP messages once enough of the message has been received to make progress. If a client stream terminates without enough of the HTTP message to provide a complete response, the server SHOULD abort its response with the error code HTTP_INCOMPLETE_REQUEST.

The stream might have closed because of a FIN or a RESET; if the entire request arrived intact, the server can proceed to respond; the RESET just means that no attempt will be made to repair any losses.  A STOP_SENDING, however, means that you don't want the response even if the request went through.

Thus:
> Clients can cancel requests by aborting the stream (QUIC RESET_STREAM and/or STOP_SENDING frames, as appropriate) with an error code of HTTP_REQUEST_CANCELLED (Section 8.1). When the client cancels a response, it indicates that this response is no longer of interest. Implementations SHOULD cancel requests by aborting both directions of a stream.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/2415#issuecomment-461157727

---

From: MikkelFJ <[email-masked]>
Date: Wed, 06 Feb 2019 21:30:27 +0000 (UTC)

> QUIC RESET_STREAM and/or STOP_SENDING

Is that really true? RESET is the senders responsibility, and it only applies to one direction of a bidi stream. STOP_SENDING is the receivers responsibility. Or what am I missing?

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/2415#issuecomment-461195178