Subject: Re: [quicwg/base-drafts] Unidirectional Streams (#643)

From: Martin Thomson <[email-masked]>
Date: Mon, 19 Jun 2017 23:23:56 -0700

@martinthomson pushed 1 commit.

f9231fe  Editorial tweaks, add HTTP_WRONG_STREAM

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/643/files/fa43b3b03e65bcc7bef9cdef8bec262688c1a2fb..f9231fe89eaa6b9de01d2e2b0e6a7a571d16839e

---

From: Martin Thomson <[email-masked]>
Date: Mon, 19 Jun 2017 23:23:56 -0700

This is a fairly fundamental change.  Let me try to describe what this does, and what it doesn't do.

## Transport Changes

Streams are unidirectional.  Each has three states: idle, open, and closed.  I separated the transitions for sending and receiving because that turned out to be easier to explain.  The reordering thing makes them different in subtle ways.

That's all.  The changes in transport are relatively small and they simplify streams a lot.  That it also makes the transport more generic is a nice bonus.

## HTTP Changes

This is where the bulk of the changes are.

### Stream Correlation

Previously request and response were implicitly correlated, as was the data correlated with the request or response headers.  With this change, that correlation each stream has a header that explicitly correlates these.

There are 5 types of stream: connection control, request, response, data, and push.  The first two have a simple header that has a type.  The next two reference another stream in their header, which ties request to response and data to request or response.  Push references a response stream and a PUSH_PROMISE on that stream.

I chose backward references rather than forward references for two reasons.  First, request and response correlation can't use forward references because that would mean the client would be exerting (uncoordinated) control over server streams.  Second, that gives the server the most flexibility in terms of how it answers requests (see #281).  The cost of using backward references is that endpoints have to check that the backward references aren't bad, either referencing the wrong type of stream, or with multiple references to the same stream.

The presence or absence of a message body is signaled after the initial header block using a HAS_BODY frame.  This empty frame indicates that another stream will include the body of the message - or a promise for a body.  I would like to eliminate this stream split.  See #245 and #557 for more details on that, though we need to fix #176 first, which brings us back to QPACK/QCRAM again.

### Prioritization

This changes prioritization so that it identifies requests.  I only made the minimal changes here, which means that this doesn't fix #441 at the same time, I've left that for later (see below).

### Cancelling Pushes

Because server push doesn't create streams with PUSH_PROMISE, I had to create a way to cancel them between the time that the PUSH_PROMISE is sent and when the push stream is created.  That's called RST_PUSH.

## Things That Need Improvement

I haven't based this on #171.  I think that functionality is good, but the last time I looked at the PR I didn't like some of the changes Mike made there.  (That's a taste thing.)  This really assumes that we accept something very much like #171.

This really needs QPACK/QCRAM.  Right now, it is impossible to cancel some types of request using RST_STREAM because not all messages will have a body (#176 again).  On balance, given that bodies are what you really want to kill, it's not disastrous, but it's a problem nonetheless.  I think that we all agree that this is something that we need to fix, but we haven't reached the point where we agree on the details of the fix.

## Things That Want Improvement

This also really wants headers and data on the same stream .  It doesn't exactly need that, but merging the streams would make things a lot easy, both conceptually and structurally.

I chose the simplest possible encoding for all of the fields that I touched.  That means that stream IDs are all 32 bits in size and the HAS_BODY frame takes an entire 9 octets.  For things that are that common, there are many ways in which byte efficiency could be improved.

I chose to use stream ID (and PUSH_PROMISE indexes) to correlate things rather than mint a new identifier.  It's tempting to move to an explicit identifier for correlation, which might contribute to fixing other issues (see discussion on priority and #441 above).  This would simplify RST_PUSH as well.

# Fixes

Closes #515, #240, #281, #175.
You can view, comment on, or merge this pull request online at:

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

-- Commit Summary --

  * WIP
  * Initial cut at unidirectional streams
  * Fix PRIORITY to refer to requests
  * RST_PUSH frame for early cancellation of pushes
  * This was a common misunderstanding with h2
  * Lazy

-- File Changes --

    M draft-ietf-quic-http.md (493)
    M draft-ietf-quic-transport.md (290)

-- Patch Links --

https://github.com/quicwg/base-drafts/pull/643.patch
https://github.com/quicwg/base-drafts/pull/643.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/643

---

From: MikkelFJ <[email-masked]>
Date: Tue, 20 Jun 2017 00:14:42 -0700

mikkelfj commented on this pull request.

> -Sending or receiving a STREAM or RST_STREAM frame causes the identified stream
-to become "open".  The stream identifier for a new stream is selected as
-described in {{stream-id}}.  A RST_STREAM frame, or a STREAM frame with the FIN
-flag set also causes a stream to become "half-closed".
-
-An endpoint might receive MAX_STREAM_DATA or STREAM_BLOCKED frames on
-peer-initiated streams that are "idle" if there is loss or reordering of
-packets.  Receiving these frames also causes the stream to become "open".
-
-An endpoint MUST NOT send a STREAM or RST_STREAM frame for a stream ID that is
-higher than the peers advertised maximum stream ID (see
-{{frame-max-stream-id}}).
-
+Sending a STREAM or RST_STREAM frame causes the identified stream to become
+"open" for a sending endpoint.  New streams use the next stream available
+identifier, as described in {{stream-id}}.  An endpoint MUST NOT send a STREAM

next available stream identifier?

-- 
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/643#pullrequestreview-45049191

---

From: MikkelFJ <[email-masked]>
Date: Tue, 20 Jun 2017 00:18:28 -0700

-### half-closed (remote) {#state-hc-remote}
+A stream becomes "closed" at the receiving endpoint when all data is received
+and a STREAM frame with a FIN flag is received or when a RST_STREAM frame is
+received.

It is not clear if data must be send/received up to the RST_STREAM offset.

-- 
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/643#pullrequestreview-45049877

---

From: MikkelFJ <[email-masked]>
Date: Tue, 20 Jun 2017 00:24:10 -0700

-### closed {#state-closed}
-
-The "closed" state is the terminal state for a stream.
-
-Once a stream reaches this state, no frames can be sent that mention the stream.
-Reordering might cause frames to be received after closing, see
-{{state-hc-remote}}.
+An endpoint could continue receiving frames for the stream even after the stream
+is closed for sending if packets are reordered.  At the receiving endpoint,
+retransmissions of data in STREAM frames, or retransmissions of STREAM_BLOCKED
+and RST_STREAM frames might arrive after the stream becomes "closed".  At the
+sending endpoint, MAX_STREAM_DATA frames might be received after the stream
+becomes "closed".  Frames received on a "closed" stream SHOULD be discarded.  An
+endpoint MAY choose to limit the period over which it ignores frames and treat
+frames that arrive after this time as being in error.

Perhaps a note on fatal connection error if receiving frames at later offset than close - otherwise a transmitter might abuse flow control limits - though the benefit of that is unclear. Perhaps also a note here stating the both RST_STREAM and FIN defines a specific end offset - even if discussed in more detail in specific frames.

-- 
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/643#pullrequestreview-45050993

---

From: MikkelFJ <[email-masked]>
Date: Tue, 20 Jun 2017 00:52:41 -0700

I can't speak for HTTP/2 that I have not studied in detail, but I believe this is a very significant improvement for the transport layer.

As mentioned above, DISINTEREST (#171) or some other mechanism for receiver to cancel is needed, and because it can cause some race conditions on stream state, I think it would be good to consider in more detail before settling on a unidirectional solution.

I am concerned about the STREAM_BLOCKED being able to force the receivers stream in open state if it is above the stream limit, or did I miss something. I know it is also there to hint at a need for more streams, but I think that should be an application level issue. Regardless - if naively implemented, a transmitter could force a lot of undesired resource usage at receiver by allocating stream state. I suggest that any stream related transmission above MAX_STREAM_LIMIT is a fatal error.

As to MAX_STREAM_LIMIT - this was a great improvement to avoid race conditions in the bi-directional design, and I do immediately suggest removing it. However, I think it might be worthwhile to consider alternatives for a better insight into the problem. For example: assume that we do not require incremental stream identifiers: there is no internal gain in incremental order such as keeping a simple stream table because old streams can randomly linger (e.g. stream 0), so open streams must be tracked via a map anyway. With a unidirectional design there is no ambigouty of the senders view of how many streams are at most open at the receiving end. It is the difference between received ACKS for RST_STREAM and FIN STREAMS and streams placed in senders open state.

Unless there is a flaw in the above argument, this suggests that streams can be opened with arbitrary identifiers. If we permit that, applications could use their own identifiers. However, it becomes intractable to make sure that a stream identifier is not being reused. Thus, the next question is: is it a problem to reuse stream identifiers once they have been closed? A sender cannot start reusing the stream before receiving an ACK for RST_STREAM or FIN STREAM of the old version of the stream and in this case the receiver cannot have many doubts about a new stream arriving being new. However, there may still be spurious retransmissions. This can be managed by looking at packet numbers but requires to remember the packet number of a closed stream, which is complicated.

Thus, I believe the MAX_STREAM_LIMIT is still the simplest approach.

-- 
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/643#issuecomment-309673734

---

From: Martin Thomson <[email-masked]>
Date: Tue, 20 Jun 2017 15:56:33 -0700

04a7cde  Add push ID for identifying pushes

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/643/files/f9231fe89eaa6b9de01d2e2b0e6a7a571d16839e..04a7cde17e2aec34e249ccac2f6ecd102fef0478

---

From: Martin Thomson <[email-masked]>
Date: Tue, 20 Jun 2017 19:16:23 -0700

You can't open a stream that exceeds the value your peer sent in MAX_STREAM_ID, STREAM_BLOCKED included.

@mikkelfj, your suggestion to allow use of arbitrary stream identifiers is counter to something we've discussed at length.  The current design allows an endpoint to maintain a map of open streams.  Then the state of other streams is easily determined.  Streams lower than the largest open stream ID are either in the map or are closed, streams with higher numbers are idle.

This is why I think that "fixing" #634 is not a good idea because it means additional state to track closed vs. idle on streams below the largest open stream ID.  And because there is nothing to be gained from opening out of order.  That's even more true with with this change, which removes any vestigial semantics attached to stream identifiers.

-- 
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/643#issuecomment-309943197

---

From: Martin Thomson <[email-masked]>
Date: Tue, 20 Jun 2017 19:17:46 -0700

martinthomson commented on this pull request.

Seems clear to me: 
1. when all data is received and a STREAM frame with a FIN flag is received, or
2. when a RST_STREAM frame is received.

-- 
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/643#discussion_r123140418

---

From: Martin Thomson <[email-masked]>
Date: Tue, 20 Jun 2017 19:18:24 -0700

c4f9a8b  Grammo

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/643/files/04a7cde17e2aec34e249ccac2f6ecd102fef0478..c4f9a8b212b37a05dadf485a880e446fb95db54f

---

From: MikkelFJ <[email-masked]>
Date: Tue, 20 Jun 2017 23:06:42 -0700

It'ts not clear to me if the semantics are that the receiver can expect data to be delivered up the RST_FRAME offset, or the transmitter can just decide to cancel at that offset whether data before that was ACK'ed. The formulation of the RST_STREAM suggests the receiver should not wait around, but RST_STREAM could also be used to send half a file and use RST_STREAM instead of FIN, even if this is probably not the design intent.

-- 
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/643#discussion_r123160693

---

From: MikkelFJ <[email-masked]>
Date: Tue, 20 Jun 2017 23:10:37 -0700

> @mikkelfj, your suggestion to allow use of arbitrary stream identifiers

I would call it a suggestion - I just wanted to ensure the previous discussion still holds up in the new configuration, and based on what I wrote, and what you have added, it seems to be the case.

-- 
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/643#issuecomment-309973467

---

From: MikkelFJ <[email-masked]>
Date: Tue, 20 Jun 2017 23:21:31 -0700

The formulation "all data received" on the FIN flag, implies that will not be the case for RST_STREAM, so I agree, it is covered in a terse form.

-- 
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/643#discussion_r123162289

---

From: Lucas Pardue <[email-masked]>
Date: Wed, 21 Jun 2017 04:31:11 -0700

LPardue commented on this pull request.

-##  Stream 1: Connection Control Stream
+~~~~~
+ 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
++-+-+-+-+-+-+-+-+
+|   Type (8)    |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                         Push ID (32)                          |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+~~~~~

It seems this diagram is missing the 16-bit index value described in the text.

> +The stream header for a response stream contains the type octet, which is set to
+0x01, and the stream ID of the request stream that this response is for as a
+32-bit value.
+
+~~~~~
+ 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
++-+-+-+-+-+-+-+-+
+|   Type (8)    |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Stream ID (32)                         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+~~~~~
+
+After the stream header, response streams contain a sequence of frames (see
+{{http-framing-layer}}).

Not sure where the `http-framing-layer` link is supposed to take us to.

 HTTP/QUIC uses the priority scheme described in {{!RFC7540}} Section 5.3. In
-this priority scheme, a given stream can be designated as dependent upon another
-stream, which expresses the preference that the latter stream (the "parent"
-stream) be allocated resources before the former stream (the "dependent"
-stream). Taken together, the dependencies across all streams in a connection
-form a dependency tree. The structure of the dependency tree changes as PRIORITY
-frames add, remove, or change the dependency links between streams.
+this priority scheme, a given request can be designated as dependent upon
+another request, which expresses the preference that the latter stream (the
+"parent" request) be allocated resources before the former stream (the
+"dependent" request). Taken together, the dependencies across all request in a

nit: the dependencies across all request**s**

 HTTP/QUIC uses the priority scheme described in {{!RFC7540}} Section 5.3. In
-this priority scheme, a given stream can be designated as dependent upon another
-stream, which expresses the preference that the latter stream (the "parent"
-stream) be allocated resources before the former stream (the "dependent"
-stream). Taken together, the dependencies across all streams in a connection
-form a dependency tree. The structure of the dependency tree changes as PRIORITY
-frames add, remove, or change the dependency links between streams.
+this priority scheme, a given request can be designated as dependent upon
+another request, which expresses the preference that the latter stream (the
+"parent" request) be allocated resources before the former stream (the
+"dependent" request). Taken together, the dependencies across all request in a
+connection form a dependency tree. The structure of the dependency tree changes
+as PRIORITY frames add, remove, or change the dependency links between request.

nit: the dependency links between request**s**

-The data stream MUST be half-closed immediately after the transfer of the body.
-If the message does not contain a body, the corresponding data stream MUST still
-be half-closed without transferring any data. The "chunked" transfer encoding
-defined in Section 4.1 of {{!RFC7230}} MUST NOT be used.
+response may contain zero or more header blocks on the response stream
+containing the message headers of informational (1xx) HTTP responses (see
+{{!RFC7230}}, Section 3.2 and {{!RFC7231}}, Section 6.2).
+
+A message that contains a payload body includes a HAS_BODY frame immediately
+after the first header block.  This indicates that another stream will include
+the payload body.  A data stream is opened to carry the payload body.  The data
+stream MUST be closed immediately after sending the body.  If the message does
+not contain a body, a data stream MUST NOT be opened and the HAS_BODY message
+MUST NOT be sent.  The "chunked" transfer encoding defined in Section 4.1 of
+{{!RFC7230}} MUST NOT be used.
 
 Trailing header fields are carried in an additional header block on the message
 control stream. Such a header block is a sequence of HEADERS frames with End

Sending trailing header fields on the control stream seems like a hangover from earlier design.

-The server push response is conveyed in the same way as a non-server-push
-response, with response headers and (if present) trailers carried by HEADERS
-frames sent on the control stream, and response body (if any) sent via the
-corresponding data stream.
+The server push response is conveyed on a push stream.  Aside from the stream
+header, which identifies the response stream and PUSH_PROMISE frame, a push
+stream is identical to a regular response stream ({{stream-response}}), with
+response headers and (if present) trailers carried by HEADERS frames sent on the
+control stream, and response body (if any) indicated with a HAS_BODY frame and

My reading of the design is that trailing header fields would be carried on the push stream, not the control stream.

> @@ -401,6 +547,14 @@ All frames have the following format:
 
 ## Frame Definitions {#frames}
 
+### HAS_BODY {#frame-has-body}
+
+The HAS_BODY frame indicates that the HTTP message contains a payload body.  It

Is it missing a frame type?

> @@ -712,6 +936,21 @@ HTTP_MULTIPLE_SETTINGS (0x10):
 HTTP_RST_CONTROL_STREAM (0x11):
 : A message control stream closed abruptly.
 
+HTTP_INVALID_STREAM_HEADER (0x12):
+: A stream header contained an unknown type or referenced an invalid stream.
+
+HTTP_UNMATCHED_RESPONSE (0x13):
+: A response stream referenced an invalid stream.
+
+HTTP_UNMATCHED_DATA (0x14):
+: A data stream referenced an invalid stream.
+
+HTTP_UNMATCHED_PUSH (0x15):
+: A push stream referenced an server push request.

nit: **a** server push request

> @@ -758,8 +997,8 @@ QUIC simply by replacing Stream 0 in HTTP/2 with Stream 1 in HTTP/QUIC.
 Below is a listing of how each HTTP/2 frame type is mapped:
 
 DATA (0x0):
-: Instead of DATA frames, HTTP/QUIC uses a separate data stream.  See
-  {{stream-mapping}}.
+: Instead of DATA frames, HTTP/QUIC uses a separate data stream.  The code point
+  for DATA frames is used for the HAS_BODY frame. See {{frame-has-body}}.

Maybe this answers my earlier question about HAS_BODY type?

-- 
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/643#pullrequestreview-45396112

---

From: Lucas Pardue <[email-masked]>
Date: Wed, 21 Jun 2017 06:30:29 -0700

> @@ -2755,10 +2722,10 @@ stream.  For a stream that is reset, the final offset is carried explicitly in
 the RST_STREAM frame.  Otherwise, the final offset is the offset of the end of
 the data carried in STREAM frame marked with a FIN flag.
 
-An endpoint will know the final offset for a stream when the stream enters the
-"half-closed (remote)" state.  However, if there is reordering or loss, an
-endpoint might learn the final offset prior to entering this state if it is
-carried on a STREAM frame.
+An endpoint will know the final offset for a stream when it receives a STREAM
+frame with a FIN flag or a RST_STREAM frame.  If there is reordering or loss,
+an endpoint receive a STREAM frame with a FIN flag prior to the stream entering

Is a word is missing here?

-- 
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/643#pullrequestreview-45431879

---

From: Martin Thomson <[email-masked]>
Date: Wed, 21 Jun 2017 17:40:43 -0700

@martinthomson pushed 2 commits.

28aa006  Change RST_STREAM to CANCEL_REQUEST
8b561ef  Cleanup error codes

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/643/files/c4f9a8b212b37a05dadf485a880e446fb95db54f..8b561efda4a8e73a71ca72f62b5ea7abaed60b09

---

From: Mark Nottingham <[email-masked]>
Date: Wed, 21 Jun 2017 17:51:06 -0700

**Reminder** -- substantive discussion / feedback needs to happen on issues and/or the mailing list, not on PRs. Editorial-level discussion only, please.

-- 
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/643#issuecomment-310244786

---

From: Mike Bishop <[email-masked]>
Date: Wed, 21 Jun 2017 23:24:12 -0700

MikeBishop approved this pull request.

As we've discussed, I highly approve moving in this direction, and this looks like a solid way of doing it.  Side-note for the transport draft:  In my version of this, I found it useful to subdivide the frame types into three categories:

  - Frames about streams you're sending
  - Frames about streams you're receiving
  - Frames about the connection

...and then be clear anywhere there's a stream reference whether that refers to the sender of the frame's stream or the receiver of the frame's stream.

 ## HTTP Message Exchanges
 
-A client sends an HTTP request on a new pair of QUIC streams. A server sends an
-HTTP response on the same streams as the request.
+A client sends an HTTP request on a new QUIC request stream

Nit:  a "QUIC request stream" implies that request streams are a QUIC-layer concept, not an HTTP-layer one.  Maybe a "new request stream," possibly augmented by some reinforcement that all stream types are QUIC streams?

> @@ -320,39 +449,45 @@ A proxy that supports CONNECT establishes a TCP connection ({{!RFC0793}}) to the
 server identified in the ":authority" pseudo-header field. Once this connection
 is successfully established, the proxy sends a HEADERS frame containing a 2xx
 series status code to the client, as defined in {{!RFC7231}}, Section 4.3.6, on
-the message control stream.
-
-All QUIC STREAM frames on the message data stream correspond to data sent on the
-TCP connection. Any QUIC STREAM frame sent by the client is transmitted by the
-proxy to the TCP server; data received from the TCP server is written to the
-data stream by the proxy. Note that the size and number of TCP segments is not
-guaranteed to map predictably to the size and number of QUIC STREAM frames.
-
-The TCP connection can be closed by either peer. When the client half-closes the
-data stream, the proxy will set the FIN bit on its connection to the TCP server.
-When the proxy receives a packet with the FIN bit set, it will half-close the
-corresponding data stream. TCP connections which remain half-closed in a single
+the response stream.
+
+The data received on the data streams associated with this exchange after the

Might want to be explicit that the request/response streams will need to have HAS_BODY, but I know that's considered vestigial.

Agreed.  Any mention of a request having a control stream is leftover from my old terminology -- "on the push stream."

+associated data stream.

 # HTTP Framing Layer

I think the {{http-framing-layer}} link is intended to point here, but it doesn't have an anchor.

>  The semantics of the Stream Dependency, Weight, and E flag are the same as in
 HTTP/2.
 
 The flags defined are:
 
+  PUSH (0x04):

For consistency, this might be PUSH_PRIORITIZED.  Not stuck on that, though.

   Stream Dependency:
-  : A 32-bit stream identifier for the stream that this stream depends on (see
-    {{priority}} and {!RFC7540}} Section 5.3).
+  : A 32-bit stream identifier for a dependent request.  This contains the
+    stream ID of a request stream when the PUSH flag is clear, or a push ID when
+    the PUSH flag is set.  A request stream ID of 0 indicates a dependency on
+    the root stream. For details of dependencies, see{{priority}} and

Missing a space.

> +   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                      Error Code (32)                          |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+~~~~~~~~~~
+{: #fig-cancel-request title="CANCEL_REQUEST frame payload"}
+
+The CANCEL_REQUEST frame payload has the following fields:
+
+  Request Stream/Push ID:
+  : A 32-bit stream ID or Push ID that identifies the request.  If the PUSH flag
+    is set, this refers to a Push ID included in a PUSH_PROMISE frame (see
+    {{frame-push-promise}}); if the PUSH flag is cleared, this refers to a
+    request by its stream ID.
+
+  Error Code:
+  : A 32-bit error code indicating why the response is not desired.

Feels a little weird lifting the QUIC error code space into the HTTP frames as well.  Are we sure we need the error code at this level?  "I no longer want it" is already implied by the frame.

> +| Connection Control | n/a  | {{stream-control}}  |
+| Request            | 0x00 | {{stream-request}}  |
+| Response           | 0x01 | {{stream-response}} |
+| Data               | 0x02 | {{stream-data}}     |
+| Push               | 0x03 | {{stream-push}}     |
+{: #stream-type-table title="Stream Types"}
+
+An endpoint MUST terminate the connection with an HTTP_INVALID_STREAM_HEADER
+error if it receives a stream header that uses an unknown or unsupported type.
+
+
+### Stream 1: Connection Control Stream {#stream-control}
+
+Stream 1 is opened by both client and server and is used for the SETTINGS frame
+immediately after the connection opens.  After the SETTINGS frame has been sent,
+this stream is used for PRIORITY frames.

And CANCEL_REQUEST frames

> @@ -648,7 +889,9 @@ entire connection when an error is encountered.  These are referred to as
 [QUIC-TRANSPORT].
 
 HTTP/QUIC requires that only data streams be terminated abruptly.  Terminating a
-message control stream will result in an error of type HTTP_RST_CONTROL_STREAM.
+request, response or push stream will result in an error of type
+HTTP_RST_CONTROL_STREAM.  \[Editor's note: this is clearly busted right now,
+because data streams don't always exist.]

But not so badly, since you can always use CANCEL_REQUEST instead of telling QUIC to reset the stream.

It's the associatives that are hard to parse, I think.  ("all data" AND ("STREAM with FIN" OR "RST_STREAM")) versus (("all data" AND "STREAM with FIN") OR "RST_STREAM")

I know which one you mean, and probably everyone in the WG does, but someone reading this document to understand QUIC might get confused.  The "whens" do delineate two cases, but I can understand that being glossed over.

-- 
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/643#pullrequestreview-45627079

---

From: Mark Nottingham <[email-masked]>
Date: Wed, 21 Jun 2017 23:25:56 -0700

@MikeBishop could you please summarise your support on-list, so it's not lost here?

-- 
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/643#issuecomment-310287671

---

From: Mike Bishop <[email-masked]>
Date: Wed, 21 Jun 2017 23:27:17 -0700

@mnot, that started as an e-mail, but I decided I'd better lift out the specific feedback into a GitHub review first.  ;-)  You'll see the remaining e-mail momentarily.

-- 
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/643#issuecomment-310287887

---

From: ekr <[email-masked]>
Date: Thu, 22 Jun 2017 12:09:19 -0700

ekr commented on this pull request.

This seems like a good start

>  # Streams: QUIC's Data Structuring Abstraction {#streams}
 
-Streams in QUIC provide a lightweight, ordered, and bidirectional byte-stream
-abstraction modeled closely on HTTP/2 streams {{?RFC7540}}.
+Streams in QUIC provide a lightweight, ordered, and unidirectional byte-stream.

nit: "and" is unnecessary here.

-Stream ID 0 (0x0) is reserved for the cryptographic handshake.  Stream 0 MUST
-NOT be used for application data, and is the first client-initiated stream.
+Stream ID 0 (0x0) in both directions is reserved for the cryptographic

Nit: "0x0" is unnecessary here. People know what 0 is.

-Stream ID 0 (0x0) is reserved for the cryptographic handshake.  Stream 0 MUST
-NOT be used for application data, and is the first client-initiated stream.
+Stream ID 0 (0x0) in both directions is reserved for the cryptographic
+handshake.  Both client and server send cryptographic handshake messages on
+their stream 0, which is bound into a bidirectional channel.  Stream 0 MUST NOT

I'm not sure you need to say "bound into a bidirectional channel". you just send on one and receive on the other.

> @@ -2337,13 +2338,21 @@ Stream IDs are usually encoded as a 32-bit integer, though the STREAM frame
 stream ID are zero.

-## Life of a Stream
+## Stream States
+
+Endpoints do not coordinate the creation of streams; they are created
+unilaterally by either endpoint.

by the endpoint which will be sending on them. I know you say that below.

> @@ -2337,13 +2338,21 @@ Stream IDs are usually encoded as a 32-bit integer, though the STREAM frame
 stream ID are zero.

-## Life of a Stream
+## Stream States
+
+Endpoints do not coordinate the creation of streams; they are created
+unilaterally by either endpoint.
+
+A stream sender controls the state of its streams; the state of a stream is only
+affected by frames that are sent by the sending endpoint.  However,

Isn't this going to be wrong if you are going to allow receiver RST?

> @@ -2337,13 +2338,21 @@ Stream IDs are usually encoded as a 32-bit integer, though the STREAM frame
 stream ID are zero.

-## Life of a Stream
+## Stream States
+
+Endpoints do not coordinate the creation of streams; they are created
+unilaterally by either endpoint.
+
+A stream sender controls the state of its streams; the state of a stream is only
+affected by frames that are sent by the sending endpoint.  However,

And in any case, isn't the window part of the state?

>                              |        |
                             +--------+
+                                 |
+                      send STREAM with FIN flag
+                         send RST_STREAM
+                                 |
+                                 v
+                            +--------+
+                            |        |
+                            | closed |
+                            |        |
+                            +--------+
+~~~

This seems like it's a bug in the current state machine too, but there's clearly a difference between FIN and RST because the sender still has to retransmit when it has sent FIN. I guess you could claim that these are substates, but now that you've simplified this piece of the state machine, I think adding them explicitly here would make sense.

-### idle
+### The "idle" State

I think I would write separate state descriptions for each side. Let me know if you want help.

-flag set also causes a stream to become "half-closed".
-
-An endpoint might receive MAX_STREAM_DATA or STREAM_BLOCKED frames on
-peer-initiated streams that are "idle" if there is loss or reordering of
-packets.  Receiving these frames also causes the stream to become "open".
-
-An endpoint MUST NOT send a STREAM or RST_STREAM frame for a stream ID that is
-higher than the peers advertised maximum stream ID (see
-{{frame-max-stream-id}}).
-
+Sending a STREAM or RST_STREAM frame causes the identified stream to become
+"open" for a sending endpoint.  New streams use the next available stream
+identifier, as described in {{stream-id}}.  An endpoint MUST NOT send a STREAM
+or RST_STREAM frame for a stream ID that is higher than the peers advertised
+maximum stream ID (see {{frame-max-stream-id}}).

Can you say what the other side does in this case?

I think it would be helpful to reorder these, i.e., 
"A stream becomes "closed" at the receiving endpoint 
when an RST_STREAM frame is received. when all data is received
and a STREAM frame with a FIN flag is received"

Because as-is it is ambiguous if you just read this section in isolation

This graf is a bit confusing, because you first say that you should discard frames on a closed stream and then you say that you may instead ignore them only after some time. Why would I want to do that? What would I do if I didn't ignore them?

> -treated as a connection error of type HTTP_RST_CONTROL_STREAM.  When a message
-control stream terminates cleanly, if the last frame on the stream was
-truncated, this MUST be treated as a connection error (see HTTP_MALFORMED_* in
-{{http-error-codes}}).
-
-Pairs of streams must be utilized sequentially, with no gaps.  The data stream
-is opened at the same time as the message control stream is opened and is closed
-after transferring the body.  The data stream is closed immediately after
-sending the request headers if there is no body.
+the stream management. An HTTP request/response consumes a pair of streams in
+each direction.
+
+Aside from the connection control stream, the beginning of each stream starts
+with a short stream header.  This stream header is used to identify the type of
+stream and to link the stream to another stream as necessary.  See
+{{stream-header}} for more details on the stream header.

Nit: I would put this cite after the first sentence.

> -sending the request headers if there is no body.
+the stream management. An HTTP request/response consumes a pair of streams in
+each direction.
+
+Aside from the connection control stream, the beginning of each stream starts
+with a short stream header.  This stream header is used to identify the type of
+stream and to link the stream to another stream as necessary.  See
+{{stream-header}} for more details on the stream header.
+
+\[Editor's Note: the following is clearly busted.  Requests and responses can't
+reliably be cancelled as a result of this change.  We will need QPACK/QCRAM for
+this to work.] Request, response and push streams contain HPACK data which
+manipulates connection-level state.  Therefore, these streams MUST NOT be closed
+with a stream-level error.
+
+Streams must be opened sequentially, with no gaps.  Data streams SHOULD be

Isn't this already a QUIC invariant?

> -sending the request headers if there is no body.
+the stream management. An HTTP request/response consumes a pair of streams in
+each direction.
+
+Aside from the connection control stream, the beginning of each stream starts
+with a short stream header.  This stream header is used to identify the type of
+stream and to link the stream to another stream as necessary.  See
+{{stream-header}} for more details on the stream header.
+
+\[Editor's Note: the following is clearly busted.  Requests and responses can't
+reliably be cancelled as a result of this change.  We will need QPACK/QCRAM for
+this to work.] Request, response and push streams contain HPACK data which
+manipulates connection-level state.  Therefore, these streams MUST NOT be closed
+with a stream-level error.
+
+Streams must be opened sequentially, with no gaps.  Data streams SHOULD be

"New streams use the next available stream"

+
+After the stream header, response streams contain a sequence of frames (see
+{{http-framing-layer}}).
+
+If a response stream header identifies a stream that is not a request stream, or
+it references a request stream that already has an associated response stream,
+the connection MUST be terminated with an HTTP_UNMATCHED_RESPONSE error.
+
+
+### Data Streams {#stream-data}
+
+Data streams are opened by both client and server.  Data streams carry the
+payload body of requests or responses.
+
+The stream header for a response stream contains the type octet, which is set to

I think you mean "data stream"

++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Stream ID (32)                         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+~~~~~
+
+After the stream header, data streams contain the octets of the payload body.
+Data streams do not carry frames.
+
+Data streams MUST reference a request or response stream.  The referenced stream
+MUST include a HAS_BODY frame.  If a data stream is received that references a
+stream of a different type, the referenced stream does not include a HAS_BODY
+frame, or another data stream already references the identified steam, then the
+connection MUST be terminated with a HTTP_UNMATCHED_BODY error.  Note that due
+to reordering of packets, the referenced stream or HAS_BODY frame might not have
+been received when the data stream is opened.

So, the implementation here is:

- If has_body frame received, then process stream
- Else, block until either

  (a) has_body frame received, in which case process stream
  (b) fin/rst received in which case send error

-An HTTP request/response exchange fully consumes a pair of streams. After
-sending a request, a client closes the streams for sending; after sending a
-response, the server closes its streams for sending and the QUIC streams are
-fully closed.
+response may contain zero or more header blocks on the response stream
+containing the message headers of informational (1xx) HTTP responses (see
+{{!RFC7230}}, Section 3.2 and {{!RFC7231}}, Section 6.2).
+
+A message that contains a payload body includes a HAS_BODY frame immediately
+after the first header block.  This indicates that another stream will include
+the payload body.  A data stream is opened to carry the payload body.  The data
+stream MUST be closed immediately after sending the body.  If the message does
+not contain a body, a data stream MUST NOT be opened and the HAS_BODY message
+MUST NOT be sent.  The "chunked" transfer encoding defined in Section 4.1 of
+{{!RFC7230}} MUST NOT be used.

I wonder if it would make more sense to have the pointer to the body stream here rather than a back pointer for the dependency

> @@ -401,6 +538,17 @@ All frames have the following format:

+### HAS_BODY {#frame-has-body}
+
+The HAS_BODY frame (type=0x0) indicates that the HTTP message contains a payload
+body.  It is sent after the initial header block on a request, response, or push
+stream.
+
+The HAS_BODY frame has no flags and is always empty.  A HAS_BODY frame with a
+non-empty payload or flags that are set MUST be treated as a
+HTTP_MALFORMED_HAS_BODY error.

Might it be easier to just put this field in the header block

+  Request Stream/Push ID:
+  : A 32-bit stream ID or Push ID that identifies the request.  If the PUSH flag
+    is set, this refers to a Push ID included in a PUSH_PROMISE frame (see
+    {{frame-push-promise}}); if the PUSH flag is cleared, this refers to a
+    request by its stream ID.
+
+  Error Code:
+  : A 32-bit error code indicating why the response is not desired.
+
+A CANCEL_REQUEST frame always contains an 8 octet payload.  A server MUST treat
+a CANCEL_REQUEST frame payload that is other than 8 octets in length, or a
+CANCEL_REQUEST frame that identifies a stream that doesn't carry a valid request
+as an HTTP_MALFORMED_CANCEL_REQUEST error.  Note however that due to packet
+reordering, a CANCEL_REQUEST frame could arrive before the request stream that
+it references is opened.

This is weird text. Isn't it always the case that if you receive an unexpected frame structure you are sad?

> +ID MUST be treated as a HTTP_MALFORMED_PRIORITY error.
+
+The length of a PRIORITY frame is 9 octets.  A PRIORITY frame with any other
+length MUST be treated as a connection error of type HTTP_MALFORMED_PRIORITY.
+
+
+### CANCEL_REQUEST {#frame-cancel-request}
+
+The CANCEL_REQUEST frame (type=0x3) is used to request cancellation of a request
+prior to the response stream being created.  This frame can be used to cancel
+both outstanding requests initiated by a client and server push requests that
+are initiated with a PUSH_PROMISE.
+
+The frame identifies a client-initiated request by the stream ID that carried
+the request; a server push request is identified by Push ID (see
+{{frame-push-promise}}).

Don't you mean "request ID" that's what you are using for dependencies

-- 
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/643#pullrequestreview-45794120

---

From: Christian Huitema <[email-masked]>
Date: Thu, 22 Jun 2017 20:20:51 -0700

huitema commented on this pull request.

-### idle
+### The "idle" State

I am not sure whether RST_STREAM means "I won't send anymore on that stream" or "I won't receive anymore on that stream". Don't we need both?

-- 
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/643#discussion_r123664999

---

From: Mike Bishop <[email-masked]>
Date: Thu, 22 Jun 2017 20:25:02 -0700

MikeBishop commented on this pull request.

-### idle
+### The "idle" State

It means "I won't send," I'm fairly sure.  That's the allusion to #171, which defines a separate "I won't receive any more" frame.

-- 
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/643#discussion_r123665290

---

From: Christian Huitema <[email-masked]>
Date: Thu, 22 Jun 2017 21:03:21 -0700

Looking at this, it seems that the transport advantage is to not require both ends to send FIN or RST for each stream. Of course, if the streams are in practice bilateral, the total number of messages will be exactly the same.

There are some loose ends in the documentation. The text on RST_STREAM needs to be updated, as well as the text of GO_AWAY. Plus, we need something like GO_AWAY_FRAME.

+                            |  open  |
+                            |        |
+                            +--------+
+                                 |
+                     receive STREAM with FIN flag
+                        receive RST_STREAM
+                                 |
+                                 v
+                            +--------+
+                            |        |
+                            | closed |
+                            |        |
+                            +--------+
+~~~
+{: #stream-state-recv title="Stream States for Receipt"}

This new diagram shows an undocumented change in the meaning of RST_STREAM. In the current version, RST_STREAM is bilateral. "After sending a RST_STREAM, an endpoint ceases transmission of STREAM frames on
the identified stream.  A receiver of RST_STREAM can discard any data that it
already received on that stream.  An endpoint sends a RST_STREAM in response to
a RST_STREAM unless the stream is already closed." Obviously, this "send an RST back" will not work in an unilateral setup, and at a minimum that text should be updated. But if we want to keep a "right of refusal", we need a new frame, maybe "GOAWAY_STREAM", to indicate that the receiver is now discarding that stream.

> @@ -2291,10 +2291,10 @@ Too Big messages.
 provisional until QUIC's loss detection algorithm determines that the packet is

I would like to see some discussion of unilateral and GOAWAY frame. The current text describing that frame assumes bilateral streams.

-- 
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/643#pullrequestreview-45893072

---

From: Subodh Iyengar <[email-masked]>
Date: Fri, 23 Jun 2017 12:55:47 -0700

siyengar commented on this pull request.

-Due to reordering, an endpoint could continue receiving frames for the stream
-even after the stream is closed for sending.  Frames received after a peer
-closes a stream SHOULD be discarded.  An endpoint MAY choose to limit the period
-over which it ignores frames and treat frames that arrive after this time as
-being in error.
+In the "closed" state, a sending endpoint can retransmit data that has already
+been sent in STREAM frames, or retransmit a RST_STREAM frame.  Retransmission of
+data is permitted if the packets carrying the original data are determined to
+have been lost.

how would this be possible if we only transition to closed state only when either:

* We get the ACK for all data until FIN
* We get ACK for RST?

-- 
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/643#pullrequestreview-46056036

---

From: MikkelFJ <[email-masked]>
Date: Fri, 23 Jun 2017 13:09:54 -0700

> how would this be possible if we only transition to closed state only when either:

The sender does not wait for ACK before entering closed state.

-- 
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/643#discussion_r123834654

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 15:50:55 -0700

+   |                      Error Code (32)                          |

I've removed it.  I almost didn't include this in the first 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/643#discussion_r124141442

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 15:56:58 -0700

9ea5911  Review comments from @MikeBishop

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/643/files/a4e2528fa7a1f0d824637a12aef92a243c2431e3..9ea5911331d5b79f512710202f725da1c6f57f3d

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 17:01:16 -0700

+--------+
+                                 |

@janaiyengar and I agreed to make the draft consistent about closing when the frame is first sent.  Based on reading #572, I think that we should revert that change.  The states are basically advisory only and the degree to which the documentation manages to capture these important notions makes it more useful to enclose rather than to leave retransmission up to the "transport".

-- 
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/643#discussion_r124150858

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 17:06:00 -0700

-### idle
+### The "idle" State

I considered splitting the descriptions, but they are really still the same states.

-- 
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/643#discussion_r124151380

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 17:10:08 -0700

If a peer closes the stream and continues to send frames on that stream long after closing, it's a way to generate unaccounted traffic.

This is a case where I would reference the ENHANCE_YOUR_CALM error, but apparently we can't have nice things.

-- 
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/643#discussion_r124151810

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 17:13:05 -0700

Yeah, you can error out earlier if you receive another header block as well in the final clause.

-- 
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/643#discussion_r124152133

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 17:13:52 -0700

I wanted to be able to create the body at any time, without knowing what stream might be used.  It's a minor thing, but it also keeps all pointers as back references.

-- 
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/643#discussion_r124152211

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 17:15:57 -0700

I considered that, and it's definitely an optimization that we can talk about later, so I went with the simplest option I could think of. The problem with the header block is that there are potentially multiple places you would put this.  The easiest approach is to grab a flag, but those appear in multiple places, so does this have to be consistent, or do you only allowing if END_HEADERS is also set, or can it appear on any HEADERS frame?

-- 
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/643#discussion_r124152411

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 17:17:43 -0700

+

I should make that more consistent, but I had more room here to break the two out.  I think that I'm starting to warm to Christian's notion of this needing to draw from the same 32-bit space with a bit stolen to allow discrimination rather than rely on external signals.

-- 
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/643#discussion_r124152573

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 17:30:18 -0700

I'm operating here on the assumption that we are going to execute on the "application-layer shutdown" notion.  It's not at all clear to me that GOAWAY in its current form works as intended and this certainly messes up GOAWAY pretty badly.  My belief is that GOAWAY is much easier to implement at the HTTP layer where context is better understood.   You only need to concern yourself with requests and the pushes they trigger, which can simplify things a lot.  A fully generic graceful shutdown at the transport layer might not be achievable, despite the lofty goals of #604.

-- 
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/643#discussion_r124153941

---

From: Martin Thomson <[email-masked]>
Date: Mon, 26 Jun 2017 17:33:16 -0700

+                            |  open  |
+                            |        |
+                            +--------+
+                                 |

This is #171, I explicitly mentioned this in the writeup.

-- 
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/643#discussion_r124154277

---

From: Mike Bishop <[email-masked]>
Date: Tue, 27 Jun 2017 11:25:15 -0700

+An endpoint MUST terminate the connection with an HTTP_INVALID_STREAM_HEADER
+error if it receives a stream header that uses an unknown or unsupported type.
+
+
+### Stream 1: Connection Control Stream {#stream-control}
+
+Stream 1 is opened by both client and server and is used for the SETTINGS frame
+immediately after the connection opens.  After the SETTINGS frame has been sent,
+this stream is used for PRIORITY and CANCEL_REQUEST frames.
+
+There is no stream header for a connection control stream; the stream is
+identified by its stream number.  The connection control stream contains a
+sequence of frames, starting with the SETTINGS frame.
+
+If an endpoint receives a stream type of 0xff on any stream other than stream 1,

I think two versions collided here.  Stream type 0xff is undefined, so it's also invalid on Stream 1.

-- 
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/643#pullrequestreview-46638462

---

From: Martin Thomson <[email-masked]>
Date: Tue, 27 Jun 2017 12:27:03 -0700

1a62a5c  Fixup dead reference to stream type 0xff

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/643/files/9ea5911331d5b79f512710202f725da1c6f57f3d..1a62a5cc02e8c8a537d761f275e3fd34ce91e78c

---

From: Mike Bishop <[email-masked]>
Date: Wed, 28 Jun 2017 14:55:16 -0700

++-+-+-+-+-+-+-+-+
+|   Type (8)    |

HTTP_UNMATCHED_DATA

-- 
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/643#pullrequestreview-46979459

---

From: Martin Thomson <[email-masked]>
Date: Wed, 28 Jun 2017 23:33:27 +0000 (UTC)

aa78b14  UNMATCHED_DATA

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/643/files/1a62a5cc02e8c8a537d761f275e3fd34ce91e78c..aa78b14f5d8df206f22594cbb3a6f355463d32e8