Subject: [quicwg/base-drafts] First pass at REQUEST_RST (#171)

From: Mike Bishop <[email-masked]>
Date: Wed, 18 Jan 2017 09:46:43 -0800

A rough draft of a fix for #165.  Key changes:

- RST_STREAM kills only the sender's direction, not both.  (Retains the existing requirement to respond with RST_STREAM with another RST_STREAM if the stream isn't already closed, which eventually leads to the same result.)
- New REQUEST_RST frame allows peer to solicit a RST_STREAM; text states that this reflects an app recv_close, and that received data is being discarded

This should solve the scenarios outlined in #165; a server can ask a client to RST_STREAM its own direction after having sent an early response.  Feedback welcome, of course -- please point out anything I've missed.
You can view, comment on, or merge this pull request online at:

  https://github.com/quicwg/base-drafts/pull/171

-- Commit Summary --

  * First pass at REQUEST_RST

-- File Changes --

    M draft-ietf-quic-transport.md (162)

-- Patch Links --

https://github.com/quicwg/base-drafts/pull/171.patch
https://github.com/quicwg/base-drafts/pull/171.diff

-- 
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/pull/171

---

From: Mike Bishop <[email-masked]>
Date: Wed, 18 Jan 2017 09:57:58 -0800

@MikeBishop pushed 1 commit.

8451cf6  Use REQUEST_RST in HTTP/QUIC

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/171/files/3da878cdc104f7f74a939251691b4ac55f798daa..8451cf6dbb5a072518fa0d0bbd3df10ea9f691a2

---

From: Martin Thomson <[email-masked]>
Date: Wed, 18 Jan 2017 14:27:18 -0800

martinthomson requested changes on this pull request.

I think that this is an improvement, but it needs some more discussion.  I have a few open questions around both the need for a unified state machine (i.e., should be move to unidirectional streams) and the need for stream reservation that might make this a lot simpler.

> @@ -1214,6 +1216,31 @@ The fields are:
 * Error code: A 32-bit error code which indicates why the stream is being
   closed.
 
+
+
+## REQUEST_RST Frame {#frame-request-rst}
+
+An endpoint may use a REQUEST_RST frame (type=0x08) to request a peer to
+abruptly terminate transmission on a stream.  The frame is as follows:
+
+~~~
+ 0                   1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Stream ID (32)                         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Error Code (32)                        |

The offset of the error code in this any RST_STREAM could be made consistent.  Probably better off as a separate step though.

> @@ -1402,24 +1429,23 @@ shown in the following figure and described below.
     ,---| reserved |------------.    | recv data
     |   |          |             \   |
     |   +----------+              v  v
-    |               recv FIN/   +--------+ send FIN/
-    |            app read_close |        | app write_close
+    |            send FIN/RST   +--------+ recv FIN/RST
+    |           app write_close |        | app read_close

I've always wondered about this app read_|write_close thing.  Is that what triggers sending the FIN, or is it an alternative action?

I think that we need to consider removing this and using the stream close from the app as actions that drive the sending of frames.  Any action that is purely local doesn't and can't affect the protocol state machine.

> -endpoint sending an FIN flag causes the stream state to become "half-closed
-(local)".  An endpoint receiving a FIN flag causes the stream state to become
-"half-closed (remote)"; the receiving endpoint MUST NOT process the FIN flag
-until all preceding data on the stream has been received.
-
-Either endpoint can send a RST_STREAM frame from this state, causing it to
-transition immediately to "closed".
+From this state, either endpoint can send a STREAM frame with the FIN flag set
+or a RST_STREAM frame.  These indicate the clean or abrupt termination of data
+flow on the stream, respectively.  In either case, this causes the stream to
+transition into one of the "half-closed" states.  An endpoint sending an FIN
+flag or a RST_STREAM frame causes the stream state to become "half-closed
+(local)".  An endpoint receiving a FIN flag or a RST_STREAM frame causes the
+stream state to become "half-closed (remote)".  The receiving endpoint MUST NOT
+process the FIN flag until all preceding data on the stream has been received,
+but MAY process a RST_STREAM frame immediately and discard any preceding data.

This last sentence probably belongs in the description of these frame types.

> -transition immediately to "closed".
+From this state, either endpoint can send a STREAM frame with the FIN flag set
+or a RST_STREAM frame.  These indicate the clean or abrupt termination of data
+flow on the stream, respectively.  In either case, this causes the stream to
+transition into one of the "half-closed" states.  An endpoint sending an FIN
+flag or a RST_STREAM frame causes the stream state to become "half-closed
+(local)".  An endpoint receiving a FIN flag or a RST_STREAM frame causes the
+stream state to become "half-closed (remote)".  The receiving endpoint MUST NOT
+process the FIN flag until all preceding data on the stream has been received,
+but MAY process a RST_STREAM frame immediately and discard any preceding data.
+
+Either endpoint can send a REQUEST_RST frame in this state to request closure of
+the stream in the opposite direction.  This typically indicates that the
+receiving application is no longer reading from the stream and all future data
+will be discarded upon receipt.  An endpoint SHOULD respond to a REQUEST_RST
+with a RST_STREAM of type QUIC_RECEIVED_RST.

Including this in this section implies an effect on stream state.  That's not the case though.

 An endpoint can receive any type of frame in this state.  Providing flow-control
 credit using WINDOW_UPDATE frames is necessary to continue receiving
 flow-controlled frames.  In this state, a receiver MAY ignore WINDOW_UPDATE
 frames for this stream, which might arrive for a short period after a frame
 bearing the FIN flag is sent.
 
+If an endpoint is no longer interested in the data being received in this state,
+it MAY send a REQUEST_RST frame to request that the peer close the stream.
+STREAM frames received after sending REQUEST_RST are still counted toward the
+connection and stream flow-control windows.  Even though these frames might be
+ignored, because they are sent before their sender receives the REQUEST_RST, the
+sender will consider the frames to count against its flow-control windows.
+
+If a REQUEST_RST frame is received on a stream that is already in the
+"half-closed (local)" state, a RST_STREAM frame SHOULD still be sent,
+so that no STREAM frames previously sent will be retransmitted.

RST_STREAM isn't what causes retransmissions.  You send it so that you can stop retransmissions, which is the opposite, causally.  Of course, you need to retransmit RST_STREAM, so you only save bytes.

> @@ -1709,10 +1740,12 @@ controller.
 ### Response to a RST_STREAM
 
 Since streams are bidirectional, a sender of a RST_STREAM needs to know how many
-bytes the peer has sent on the stream.  If an endpoint receives a RST_STREAM
-frame and has sent neither a FIN nor a RST_STREAM, it MUST send a RST_STREAM in
-response, bearing the offset of the last byte sent on this stream as the final
-offset.
+bytes the peer has sent on the stream.   An endpoint that receives a RST_STREAM
+frame (and which has not sent a FIN or a RST_STREAM) MUST immediately respond
+with a RST_STREAM frame bearing the offset of the last byte sent on this stream
+as the final offset, and MUST NOT send any more data on the stream.  This
+RST_STREAM SHOULD contain the error code QUIC_RECEIVED_RST.

So this seems wrong if the intent is to address the one-way stream use case.  Given that you have REQUEST_RST, that should be enough to cause the other side to abort.  Then your only obligation should be to end your side of the stream on your own time frame.  If that means that a hard abort needs both RST_STREAM and REQUEST_RST, I think that's OK.

> @@ -1691,13 +1722,13 @@ waiting for a WINDOW_UPDATE which will never come.
 ### Mid-stream RST_STREAM
 
 On receipt of an RST_STREAM frame, an endpoint will tear down state for the

for the what?

I think that you can:

1. free any buffers allocated to incoming data
2. discard any data that hasn't been delivered yet

But you have to:

1. account for the octets consumed from the connection flow control window (those that aren't already accounted for)
2. keep the octet offset so that you can reject overflows
3. keep the state for outgoing data in place

-- 
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/pull/171#pullrequestreview-17334289

---

From: Mike Bishop <[email-masked]>
Date: Mon, 30 Jan 2017 06:39:05 -0800

MikeBishop commented on this pull request.

+
+

Entered as #249 for the general case; updating here to match.

---

From: Mike Bishop <[email-masked]>
Date: Mon, 30 Jan 2017 06:42:37 -0800

I tried to make this change, but couldn't find a place that made sense.  This is talking about the logic of stream state transitions, not about the parsing of the frame itself.  Technically, the "MUST NOT process" really means "don't complete this state transition with outstanding data."  I reworded this section to talk about the transitions and not the processing of the frame, which I think works better.

---

From: Mike Bishop <[email-masked]>
Date: Mon, 30 Jan 2017 06:49:27 -0800

Moved to its own section.

---

From: Mike Bishop <[email-masked]>
Date: Mon, 30 Jan 2017 06:52:46 -0800

I'm not sure what would be clearer.  The spec does say:

> When an endpoint sends a RST_STREAM frame, data outstanding on that stream SHOULD NOT be retransmitted, since subsequent data on this stream is expected to not be delivered by the receiver.

Maybe just "and"?

---

From: Mike Bishop <[email-masked]>
Date: Mon, 30 Jan 2017 07:18:58 -0800

@MikeBishop pushed 3 commits.

8bc87e6  Merge remote-tracking branch 'origin/master' into request_rst
636d8d0  Martin's feedback on REQUEST_RST
4d7c4f6  Wrong word form

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/171/files/8451cf6dbb5a072518fa0d0bbd3df10ea9f691a2..4d7c4f60dc44f0a53ba2c20c0414afc05db74680

---

From: Mike Bishop <[email-masked]>
Date: Mon, 30 Jan 2017 07:20:27 -0800

Agreed, but that's separate.  #182 covers this.

---

From: Martin Thomson <[email-masked]>
Date: Mon, 30 Jan 2017 16:47:16 -0800

martinthomson commented on this pull request.

> -Since streams are bidirectional, a sender of a RST_STREAM needs to know how many
-bytes the peer has sent on the stream.  If an endpoint receives a RST_STREAM
-frame and has sent neither a FIN nor a RST_STREAM, it MUST send a RST_STREAM in
-response, bearing the offset of the last byte sent on this stream as the final
-offset.
+RST_STREAM terminates a stream abruptly.  Whether any action or response can
+or should be taken on the data already received is an application-specific
+issue.  If the sender of a RST_STREAM wishes to explicitly state that no future
+data will be processed, that endpoint MAY send a REQUEST_RST frame at the same
+time.
+
+Regardless, it will often be the case that upon receipt of a RST_STREAM, an
+endpoint will choose to stop sending data in its own direction. An endpoint that
+receives a RST_STREAM frame MUST still close the stream for sending in the other
+direction, either with a STREAM frame containing the FIN bit or a RST_STREAM
+frame of its own, but MAY choose not to do so immediately.

This doesn't really work for me.  The "MUST still close" clause is toothless in light of the "MAY choose not to" that follows it.  We should keep things like the MUST out of this.  There are many cases where it is appropriate to close your writer when your reader aborts, but that will depend a great deal on context.

I would avoid normative language in this paragraph and instead note that aborting a read might trigger the need for an abort on the write.

-- 
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/pull/171#pullrequestreview-19223659

---

From: Martin Thomson <[email-masked]>
Date: Mon, 30 Jan 2017 20:06:46 -0800

Another thing to ponder.  RST (aka reset) has all of the wrong connotations.  I think that this needs a better name.  Maybe we can finally divorce ourselves from all of the badly fitting TCP connotations.

-- 
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/pull/171#issuecomment-276270122

---

From: Mike Bishop <[email-masked]>
Date: Mon, 30 Jan 2017 20:11:21 -0800

I meant it in the sense of it eventually will have to close, because all streams do in the end.  (For all streams are as grass, and all their transferred data as the flower of grass....)  But sure, we can incorporate the first sentence into the previous paragraph and drop the rest.