Subject: Review of draft-ietf-quic-transport-00

From: Mirja Kühlewind <[email-masked]>
Date: Mon, 5 Dec 2016 18:02:52 +0100

Hi all,

I reviewed draft-ietf-quic-transport-00 as an individual. I have quite a few comments but I would like to raise three high-level design questions which I will send separate mails for next with more explanation to keep the discussion separated. I do have more comments but I might bring them up later or just send a PR for some of them. Excuse me if parts of these issues have been discussed earlier; it’s not unlikely that I’ve missed parts of previous discussions given the amount of mail we currently have on this list. 

The three high level points, which are more on the transporty side of life, are (see further explanation in following mails):

1. Per-stream vs. per-connection information: They way per-stream and per-connection information are mixed up in frames (or not) is really confusing to me. I have some concrete examples and proposal in the next mail, and this is a little related to my next point.

2. Encryption: The split between the crypto and the transport handshake is confusing and, as I believe was discussed to some extend already, it’s not clear which information will be encrypted and which will be visible.

3. Do we really need to close streams? Why not just leave them open and have them inactive, e.g. with a time-out and/or advertising a window of zero…? Especially reading section 8, I think that would simplify things enormously.

I think some of these questions are connected to the split of documents, especially regarding the tls-doc but also the cc/recovery-doc. I only reviewed in detail this doc, the quic-transport draft, and only skimmed the other docs, however, to my understanding as it should be possible to replace the crypto-negotiation as well as recovery mechanism with other mechanisms,  and as such this draft must be stand-alone to a certain extend and therefore a few things are missing.

Please see next mails!
Mirja

P.S.: One nit I would like to mention here:
Section 4.2 (Regular Packets) page 10 says: "The first packet send by an endpoint MUST have a packet number of 1."
And section 7 (Packetization and Reliability) page 28 says: "That is, the first packet in both directions of the connection MUST have a packet number of 0."

---

From: Mike Bishop <[email-masked]>
Date: Mon, 5 Dec 2016 18:57:24 +0000

Having an infinite number of streams open and idle seems like a good way to waste application resources tracking state.  Speaking from an application perspective, I'm reading from a stream and would like a way to know when I've reached the end (rather than just having not received it yet).  Of course, I could define that myself with an EOF-indicator on the stream data if there's a good reason for the transport *not* to provide that functionality.  Other than simplifying the stream state machine, why wouldn't you support it?

-----Original Message-----
From: QUIC [mailto:[email-masked]] On Behalf Of Mirja Kühlewind
Sent: Monday, December 5, 2016 9:03 AM
To: [email-masked]
Subject: Review of draft-ietf-quic-transport-00

Hi all,

Please see next mails!
Mirja

---

From: Ian Swett <[email-masked]>
Date: Mon, 5 Dec 2016 15:43:44 -0500

In regards to your PS, I added a note to fix this as part Issue #35(
https://github.com/quicwg/base-drafts/issues/35)

On Mon, Dec 5, 2016 at 1:57 PM, Mike Bishop <[email-masked]>
wrote:

> Having an infinite number of streams open and idle seems like a good way
> to waste application resources tracking state.  Speaking from an
> application perspective, I'm reading from a stream and would like a way to
> know when I've reached the end (rather than just having not received it
> yet).  Of course, I could define that myself with an EOF-indicator on the
> stream data if there's a good reason for the transport *not* to provide
> that functionality.  Other than simplifying the stream state machine, why
> wouldn't you support it?
>
> I reviewed draft-ietf-quic-transport-00 as an individual. I have quite a
> few comments but I would like to raise three high-level design questions
> which I will send separate mails for next with more explanation to keep the
> discussion separated. I do have more comments but I might bring them up
> later or just send a PR for some of them. Excuse me if parts of these
> issues have been discussed earlier; it’s not unlikely that I’ve missed
> parts of previous discussions given the amount of mail we currently have on
> this list.
>
> The three high level points, which are more on the transporty side of
> life, are (see further explanation in following mails):
>
> 1. Per-stream vs. per-connection information: They way per-stream and
> per-connection information are mixed up in frames (or not) is really
> confusing to me. I have some concrete examples and proposal in the next
> mail, and this is a little related to my next point.
>
> 2. Encryption: The split between the crypto and the transport handshake is
> confusing and, as I believe was discussed to some extend already, it’s not
> clear which information will be encrypted and which will be visible.
>
> 3. Do we really need to close streams? Why not just leave them open and
> have them inactive, e.g. with a time-out and/or advertising a window of
> zero…? Especially reading section 8, I think that would simplify things
> enormously.
>
> I think some of these questions are connected to the split of documents,
> especially regarding the tls-doc but also the cc/recovery-doc. I only
> reviewed in detail this doc, the quic-transport draft, and only skimmed the
> other docs, however, to my understanding as it should be possible to
> replace the crypto-negotiation as well as recovery mechanism with other
> mechanisms,  and as such this draft must be stand-alone to a certain extend
> and therefore a few things are missing.
>
> Please see next mails!
> Mirja
>
>
> Section 4.2 (Regular Packets) page 10 says: "The first packet send by an
> endpoint MUST have a packet number of 1."
> And section 7 (Packetization and Reliability) page 28 says: "That is, the
> first packet in both directions of the connection MUST have a packet number
> of 0."
>
>
>

---

From: Mirja Kühlewind <[email-masked]>
Date: Wed, 7 Dec 2016 10:46:54 +0100

Hi Mike, hi all,

see below.

> Am 05.12.2016 um 19:57 schrieb Mike Bishop <[email-masked]>:
> 

The idea is really not to have an infinite number of streams open. You still have an maximum number of open stream and as Ian pointed out if you open them in order, you probably just keep the last X open. My point is that closing a stream can be a local decision. That doesn’t mean that quic couldn’t tell the higher layers about it. It’s again just a local interface question. 

And yes if you send one object over a stream and you want to let the other end know that you are not planning to send any additional data, there should probably be a EOF signal at the layer that knows about the objects. Again, in this case there could be a local interface to the lower layer telling quic that you do expect any data anymore on that stream or that you are not will to accept any data any more on that stream, so quic could send the right error message if it anyway receives data on that stream.

Mirja

---

From: Kyle Rose <[email-masked]>
Date: Mon, 12 Dec 2016 21:26:22 -0500

On Wed, Dec 7, 2016 at 4:46 AM, Mirja Kühlewind <
[email-masked]> wrote:

>
> The idea is really not to have an infinite number of streams open. You
> still have an maximum number of open stream and as Ian pointed out if you
> open them in order, you probably just keep the last X open. My point is
> that closing a stream can be a local decision. That doesn’t mean that quic
> couldn’t tell the higher layers about it. It’s again just a local interface
> question.
>

The question here seems to reduce to a design choice regarding the degree
to which we want to bake application-level semantics into the transport. If
QUIC is going to be used primarily for protocols like HTTP in which stream
data begins in a way and at a time that the receiver understands (e.g., on
a specific application-defined stream ID right after the headers come in on
an associated stream), is often of a predetermined length, and is logically
dead for the remainder of the connection after that number of bytes has
been transferred, then baking the EOF into the transport probably makes
sense.

If OTOH some application uses the stream ID as a generic mechanism for
serialization of data, but without any way for the receiver to predict
stream IDs, nor any way for it to know when the last possible data has been
sent on that stream, then EOF in the transport is vestigal. I'm struggling
to think of a not-crazy example here. Even in this case, its presence
doesn't do any damage, as an application could simply not employ it.

And yes if you send one object over a stream and you want to let the other
> end know that you are not planning to send any additional data, there
> should probably be a EOF signal at the layer that knows about the objects.
> Again, in this case there could be a local interface to the lower layer
> telling quic that you do expect any data anymore on that stream or that you
> are not will to accept any data any more on that stream, so quic could send
> the right error message if it anyway receives data on that stream.
>

Is this really that different from the existing EOF? The receiver can
already choose when to close the stream by telling the stack to send a FIN.
Under this proposal, the application would have to send an app-level EOF
(other than cases in which it is implicit, e.g., via Content-Length) and
also tell the stack to reject future data.

Baking the EOF signal into the transport seems entirely appropriate if we
decide the stack has to know when not to accept any more data on that
stream, anyway.

I don't think this particular signal has the potential to be like URG,
which turned out to be mostly useless at the transport level (I imagine
largely because of the strict ordering of TCP and the not-really-OOB-data
semantics) and is a good illustration of the #allthefeatures approach of
over-designing an API. FIN, by contrast, seems integral to the lifecycle of
a stream.

It also seems like the stack's state maintenance is similar in both cases:
as long as the stack needs to know when not to accept any more data, it
needs to retain information about which streams have been closed or put
into a "reject" state. The application, of course, is going to need to
decide independently which streams it's going to retain state for,
regardless of which lever the stack supports (close or reject).

Kyle