Subject: [quicwg/base-drafts] Adding unidirectional streams (#656)

From: ianswett <[email-masked]>
Date: Thu, 22 Jun 2017 17:11:10 -0700

You can view, comment on, or merge this pull request online at:

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

-- Commit Summary --

  * Adding unidirectional streams

-- File Changes --

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

-- Patch Links --

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

---

From: ekr <[email-masked]>
Date: Thu, 22 Jun 2017 17:34:11 -0700

ekr commented on this pull request.

>  These bits are parsed as follows:
 
-* The first two bits must be set to 11, indicating that this is a STREAM frame.
+* The first bit must be set to 1, indicating that this is a STREAM frame.
+
+* 'U' is the UNI bit, which is used to indicate this is a unidirectional
+  stream, and no stream frames may be sent in response.

This seems to break the implicit stream opening feature that we currently have, because if the maximum stream open is X and then you get a STREAM frame for Y >> X, you don't know if the other side is going to open W where X < W < Y unidirectional or bidirectional

-- 
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/656#pullrequestreview-45877919

---

From: ianswett <[email-masked]>
Date: Thu, 22 Jun 2017 18:04:04 -0700

@ianswett pushed 1 commit.

d254f76  Update

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

---

From: ianswett <[email-masked]>
Date: Thu, 22 Jun 2017 18:06:50 -0700

ianswett commented on this pull request.

I don't believe it matters.  A unidirectional stream is just a normal bidirectional stream that is created in a half-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/656#discussion_r123654861

---

From: ekr <[email-masked]>
Date: Thu, 22 Jun 2017 18:08:28 -0700

Well, it matters in the sense that the peer doesn't know the semantics.

-- 
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/656#discussion_r123654990

---

From: janaiyengar <[email-masked]>
Date: Thu, 22 Jun 2017 18:10:46 -0700

janaiyengar commented on this pull request.

Nope, since implicit open always leaves the stream in "open" state, and then when UNI bit is sent/received, the stream transitions to half-closed state. Though -- I wonder if we need implicit open anymore, since we don't count max open streams anymore.

-- 
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/656#discussion_r123655152

---

From: janaiyengar <[email-masked]>
Date: Thu, 22 Jun 2017 18:15:12 -0700

>  STREAM frames is permitted.  An endpoint MAY also send MAX_STREAM_DATA and
-RST_STREAM in this state.
+RST_STREAM in this state.  A stream immediately transitions to
+"half-closed (local)" if it is created with the UNI flag set.

I would suggest changing this to "A stream immediately transitions to "half-closed (local)" if it is created as unidirectional or when a STREAM frame is received with the UNI bit set."

-- 
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/656#pullrequestreview-45881796

---

From: ekr <[email-masked]>
Date: Thu, 22 Jun 2017 18:15:13 -0700

Yes it transitions to a half-closed state *after* you've sent data and the other side has ignored it (or, in Ian's new PR, it's sent some error). That's really not a very good semantic

-- 
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/656#discussion_r123655450

---

From: janaiyengar <[email-masked]>
Date: Thu, 22 Jun 2017 18:17:38 -0700

> @@ -2757,7 +2766,8 @@ is increased.
 The final offset is the count of the number of octets that are transmitted on a
 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.
+the data carried in STREAM frame marked with a FIN flag, or 0 in the case of
+received streams with the UNI flag set.

nit: "or 0 in the case of incoming unidirectional 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/656#pullrequestreview-45882008

---

From: janaiyengar <[email-masked]>
Date: Thu, 22 Jun 2017 18:22:50 -0700

Huh? You're not making sense. Can you give an example? The client initiates odd stream IDs, and the server initiates even stream IDs, as usual. What's the confusion again?

-- 
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/656#discussion_r123655997

---

From: ekr <[email-masked]>
Date: Thu, 22 Jun 2017 18:25:28 -0700

The client hasn't created any streams and then sends:

P10: [Stream=1, UNI=1]
P11: [Stream=3, UNI=Whatever]

But P10 gets lost, so the server just sees P11, and now it knows that Stream 1 is open, but it doesn't know what its state is. in QUIC currently, it can start sending, but now it can't because if it does, it creates an error when the client gets the server's 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/656#discussion_r123656209

---

From: ianswett <[email-masked]>
Date: Thu, 22 Jun 2017 18:48:36 -0700

I'm still confused.  Why would the server send on a stream the peer implicitly opened before receiving any data?  This seems like a non-problem.

-- 
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/656#discussion_r123658033

---

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

Why wouldn't it? Consider the strawman WebRTC structure I proposed where you just transmit packets on whatever stream is currently idle.

Another example would be any protocol where the server speaks first, like, say IMAP. If you just think of each QUIC stream as like a TCP connection, then the server might send the server greeting upon initial connection, in which case it wouldn't need to wait for any data on implicit streams.

I'm not saying you can't prohibit this in a protocol mapping, but it's a new restriction you've created that has to be dealt with

-- 
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/656#discussion_r123659161

---

From: ianswett <[email-masked]>
Date: Thu, 22 Jun 2017 19:15:56 -0700

I think you have a different mental model of how this might work, or possibly you're commenting that I'm not solving an existing problem you think should be solved?

I'm not familiar with the WebRTC structure, but presumably for WebRTC each side would send RTP on unidirectional streams created by the appropriate side(odd for initiator and even for the other).

For IMAP, if the server speaks first, then I would expect it to also create an even stream ID and send communications on that.

-- 
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/656#discussion_r123660171

---

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

No, I think you've invented a new problem.

Consider how IMAP works now: the client pulls up a TCP connection to the server and then the server's first message is the advertisement.

Now consider a hypothetical new version of IMAP which runs over QUIC but allows (for some reason) multiple concurrent IMAP sessions from the client. In this model, the client knows how many sessions it wants (the server has no idea) and it opens up a new stream for each of them. In the current QUIC model, the server can start sending the banner on each one of those connections immediately as soon as it knows it exists, whether it has received a STREAM frame or not. However, in your new model, if there is any way to have a unidirectional stream, then the server can't know whether a given stream is bidirectional and represents a new IMAP session or unidirectional. Now, you can say that the IMAP binding forbids unidirectional streams, but that doesn't stop them from being tagged that way in the STREAM frame and now you have an anomaly.

-- 
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/656#discussion_r123660509

---

From: ianswett <[email-masked]>
Date: Thu, 22 Jun 2017 19:27:17 -0700

Yes, given your hypothetical mapping, that creates a 1RTT delay.  However, one could forbid unidirectional streams as you recommend or potentially do other things to mitigate that issue.

Support for both multidirectional and unidirectional streams does not mean the application can't forbid one or the other.  It just means the application can use whatever stream style is most natural.

-- 
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/656#discussion_r123661062

---

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

Sure, I'm not saying it's the worst thing in the world, I'm just saying it creates an edge case which we need to make sure we document correctly.

-- 
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/656#discussion_r123661223

---

From: Igor Lubashev <[email-masked]>
Date: Thu, 22 Jun 2017 19:48:04 -0700

igorlord commented on this pull request.

UNI invents a new state -- "implicit".  That state is between "idle" and "open".  Unlike "idle", it counts for the stream max.  Unlike "open", it does not allow an endpoint to write to it.

-- 
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/656#discussion_r123662701

---

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

huitema commented on this pull request.

One thing that I like in Ian's proposal is that there is only one stream number space. I am a bit concerned with having the same stream number used by different streams in both directions. The potential for coding errors is quite large.

-- 
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/656#discussion_r123668434

---

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

I like this proposal slightly better than #643, because it keeps the feature of having only one stream number space. Both PR will end up causing the same number of control messages.

Like #643, I would like to see the text on RST_STREAM frame updated. Today it says, "reply to RST_STREAM with RST_STREAM". Is that still true?

> @@ -1607,7 +1610,7 @@ entropy on demand, which should be adequate protection against most
 opportunistic acknowledgement attacks.
 
 The type byte for a ACK frame contains embedded flags, and is formatted as
-`101NLLMM`.  These bits are parsed as follows:
+`011NLLMM`.  These bits are parsed as follows:
 
 * The first three bits must be set to 101 indicating that this is an ACK frame.

101 should be 011, I suppose...

I like the idea that there is just one stream number space. For example, it makes RST_STREAM cleaner. Also, I am concerned with implementation bugs if the same stream number can designate either a client stream or a server stream depending on which frame we look at.

But EKR is right, there is an edge case with the "implicit open". There is also an easy fix -- consider streams as not writeable if implicit open + no data received yet.

-- 
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/656#pullrequestreview-45895531

---

From: MikkelFJ <[email-masked]>
Date: Thu, 22 Jun 2017 23:45:00 -0700

mikkelfj commented on this pull request.

> I'm still confused. Why would the server send on a stream the peer implicitly opened before receiving any data? This seems like a non-problem.

A lot of the complexity with bi-directional streams is that this is possible even if unintended. Adding uni-directional next to bi-directional streams does not simplify things as you now how to check if a reverse frame is valid or not. Uni-directional streams do handle the implicit stream close problem.

-- 
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/656#discussion_r123681834

---

From: Lucas Pardue <[email-masked]>
Date: Fri, 23 Jun 2017 07:20:34 -0700

LPardue commented on this pull request.

There's something about this that seems odd. An endpoint opens a stream with UNI, transitions to half-closed (local) and the MUST NOT send new STREAM 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/656#discussion_r123759217

---

From: Lucas Pardue <[email-masked]>
Date: Fri, 23 Jun 2017 07:27:29 -0700

To address Jana's comment, I think this note just belongs with the similar note immediately beneath the Statechart.

-- 
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/656#discussion_r123760876

---

From: Lucas Pardue <[email-masked]>
Date: Fri, 23 Jun 2017 08:15:32 -0700

> @@ -2571,6 +2577,9 @@ the data limits set by its peer.  The cryptographic handshake stream, Stream 0,
 is exempt from the connection-level data limits established by MAX_DATA.  Stream
 0 is still subject to stream-level data limits and MAX_STREAM_DATA.
 
+If a STREAM frame is received for a stream that was previously specified as
+unidirectional, the connection MUST be closed with QUIC_INVALID_FRAME_DATA.

I don't follow this. Surely the initiator of a unidirectional stream can send STREAM frames on it (and hence the receivin end is expected to receive frames). Are you trying to say that reception of a STREAM frame, by the initiator of the unidirectional stream, on that stream, is forbidden and should result in a closed connection?

-- 
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/656#pullrequestreview-46011736

---

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

siyengar commented on this pull request.

>                                   |
                                  v
-                 recv FIN/  +--------+    send FIN/
-                 recv RST   |        |    send RST
+              recv FIN/     +--------+     send FIN/
+         recv RST/send UNI  |        |  send RST/recv UNI

after send UNI should really be a new state completely, the problem is that in a normal rcv FIN/ rcv RST state it is absolutely expected to get stream frame retransmissions, however on a unidirectional stream it is illegal and should be a transport error.

-- 
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/656#pullrequestreview-46073763

---

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

v

also if a RST and STREAM frame for a unidirectional stream are reordered so that RST is received first, the receiver will go into half closed remote state and have no idea that they can't send data in this 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/656#discussion_r123829813

---

From: Subodh Iyengar <[email-masked]>
Date: Fri, 23 Jun 2017 15:22:16 -0700

> @@ -2483,8 +2489,8 @@ after a frame bearing the FIN flag is sent.
 
 A stream is "half-closed (remote)" when the stream is no longer being used by
 the peer to send any data.  An endpoint will have either received all data that
-a peer has sent or will have received a RST_STREAM frame and discarded any
-received data.
+a peer has sent, will have sent an UNI flag on the stream, or will have

I guess this is more of a meta question, at a high level do we need to negotiate unidirectionality on a stream level or would it be sufficient to apply it at a connection level.

-- 
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/656#pullrequestreview-46103106

---

From: krasic <[email-masked]>
Date: Fri, 23 Jun 2017 16:27:00 -0700

krasic commented on this pull request.

Consider HTTP.   Client-initiated streams are bidirectional.  Server-initiated (i.e. server push) are unidirectional.   So, I don't think application/connection level makes sense.

-- 
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/656#discussion_r123861511

---

From: Subodh Iyengar <[email-masked]>
Date: Fri, 23 Jun 2017 18:04:35 -0700

Client initiated streams can also be unidirectional as well if mapped out that way, why would a mapping want to specify a mix like that? The PR doesn't have details about how the http mapping changes, would love to see that as well.

-- 
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/656#discussion_r123866572

---

From: ianswett <[email-masked]>
Date: Wed, 28 Jun 2017 17:38:58 -0700

fee594a  Update draft-ietf-quic-transport.md

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

---

From: ianswett <[email-masked]>
Date: Thu, 29 Jun 2017 00:43:28 +0000 (UTC)

0dbd1f8  Trailing whitespace

-- 
You are receiving this because you are subscribed to this thread.
View it on GitHub:
https://github.com/quicwg/base-drafts/pull/656/files/fee594a3d1bcc0f3876401bc2fd96db4a6866ac3..0dbd1f805c412cb694c26a3b50bc7b3e6b647666

---

From: ianswett <[email-masked]>
Date: Wed, 28 Jun 2017 17:46:59 -0700

Thanks for all the comments.  I tried to resolve them in my updated PR, but a few questions may need further clarification or tweaks.

                                  v

I clarified the text to indicate only the peer the stream is reserved for can send the first STREAM frame.  In regards to RST, I added text on that as well, but I suspect it needs a state transition change as well, which I haven't fully thought through.  Feel free to offer a suggestion.

I attempted to clarify the text largely as Jana suggested to clarify this is when one receives a STREAM frame with the UNI bit set.

Possibly you or Buck have suggestions on how the HTTP mapping should change?  Except for server push, I don't think it needs to change at all, and that change should be as minor as specifying that server push streams should be initiated with the unidirectional bit set to true.

Yes, this was worded terribly, I attempted to fix it.  Please tell me if it makes sense now.

Done

-- 
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/656#pullrequestreview-47000249

---

From: Kazuho Oku <[email-masked]>
Date: Thu, 29 Jun 2017 13:43:23 +0000 (UTC)

kazuho commented on this pull request.

Can we use a bit of the stream ID to indicate whether if a stream is uni-directional or not?

The state machine already relies on the least significant bit to indicate the direction of the stream, and using the next bit for indicating whether if a stream is uni-directional or not makes sense to me.

-- 
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/656#discussion_r124802810

---

From: Ryan Hamilton <[email-masked]>
Date: Thu, 29 Jun 2017 07:04:59 -0700

RyanAtGoogle commented on this pull request.

One complication with using Stream ID is how it relates to stream limits. Using stream ID would probably imply that there be different limits for unidirectional streams vs bidirectional streams since a given application may well have an asymmetric stream distribution.

-- 
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/656#discussion_r124808703

---

From: Kazuho Oku <[email-masked]>
Date: Thu, 29 Jun 2017 18:17:18 +0000 (UTC)

@RyanAtGoogle Ah. I missed that point. Thank you.

I think that it would be possible to monotonically increment 30 bits of the Stream ID while keeping the remaining two bits as flags indicating the originator of the stream and whether if its uni-directional. But I agree that it might not be worth doing.

-- 
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/656#discussion_r124875703

---

From: Ryan Hamilton <[email-masked]>
Date: Thu, 29 Jun 2017 18:54:02 +0000 (UTC)

If we went this route, I think the best way to do it would be to think of the stream ID space as being divided into 4 distinct spaces, as opposed to the two we have today.

Today: client | server
Future: client uni | client bidi | server uni | server bidi

In this case, each one of those 4 spaces could have different steam limits (aka, max stream ID).

-- 
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/656#discussion_r124884649

---

From: ianswett <[email-masked]>
Date: Thu, 29 Jun 2017 19:46:53 +0000 (UTC)

@kazuho Ryan's reasoning is the reason I didn't go with that approach, along with the fact that even though determining if a number is even or odd is easy, determining if it's mod 4 value is 1 or 3 is not as simple, so I thought it would increase the complexity of debugging.

-- 
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/656#discussion_r124895813

---

From: Igor Lubashev <[email-masked]>
Date: Thu, 29 Jun 2017 22:19:52 +0000 (UTC)

Breaking up endpoint's stream ID space into two parts (and managing max stream ID for each part separately) may not work too well, if you care about the max number of open streams and do not know what kind of streams the peer may like to open next.

-- 
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/656#discussion_r124926280

---

From: Lucas Pardue <[email-masked]>
Date: Fri, 30 Jun 2017 04:02:17 -0700

> @@ -2571,6 +2584,10 @@ the data limits set by its peer.  The cryptographic handshake stream, Stream 0,
 
+If a STREAM frame is received and the UNI bit does not match the UNI bit of
+previous STREAM frames for that stream id, the connection MUST be closed with
+QUIC_INVALID_FRAME_DATA.

This addresses my previous comment.

nit: stream ID 

However,  I wonder if a stronger statement could be made. I interpret this new text to effectively say that "A stream MUST be unidirectional or bidirectional for its complete lifetime. An endpoint MUST send all STREAM frames for a stream ID with the UNI bit set to the same vale. An endpoint that receives a STREAM frame with a non-matching UNI bit MUST close the connection with QUIC_INVALID_FRAME_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/656#pullrequestreview-47363246

---

From: Lucas Pardue <[email-masked]>
Date: Fri, 30 Jun 2017 07:09:01 -0700

> @@ -2955,7 +2973,7 @@ QUIC_INVALID_NEGOTIATED_VALUE (0x80000017):
 
 QUIC_DECOMPRESSION_FAILURE (0x80000018):
 : There was an error decompressing data.
-
+pu

???

-- 
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/656#pullrequestreview-47400659

---

From: Subodh Iyengar <[email-masked]>
Date: Sat, 01 Jul 2017 00:09:15 +0000 (UTC)

v

maybe uni bit on RST stream frame as well?
ya I think it needs an additional state after send UNI technically.

-- 
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/656#discussion_r125148914