Subject: [quicwg/base-drafts] Allow not creating QPACK codec streams (#2529)

From: Alessandro Ghedini <[email-masked]>
Date: Tue, 19 Mar 2019 04:56:35 -0700

As discussed in Tokyo, we should allow endpoints to avoid creating the
QPACK control streams if they are unnecessary, but endpoints should be
allowed to create them either way.

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

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

-- Commit Summary --

  * Allow not creating QPACK codec streams

-- File Changes --

    M draft-ietf-quic-qpack.md (14)

-- Patch Links --

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

---

From: Dmitri Tikhonov <[email-masked]>
Date: Tue, 19 Mar 2019 05:19:49 -0700

dtikhonov requested changes on this pull request.

>  HTTP/3 endpoints contain a QPACK encoder and decoder. Each endpoint MUST
-initiate a single encoder stream and decoder stream. Receipt of a second
-instance of either stream type be MUST treated as a connection error of type
+initiate at most one encoder stream and one decoder stream. Receipt of a second

> Each endpoint MUST initiate at most one encoder stream and one decoder stream.

This may be interpreted as "each endpoint MUST create an encoder and a decoder stream," which is contrary to this PR.  I suggest we rephrase this.

>  HTTP_WRONG_STREAM_COUNT. These streams MUST NOT be closed. Closure of either
 unidirectional stream type MUST be treated as a connection error of type
 HTTP_CLOSED_CRITICAL_STREAM.
 
+An endpoint MAY avoid creating its own encoder stream if the maximum size of
+the dynamic table permitted by the peer is zero.
+
+An endpoint MAY avoid creating its own decoder stream if the maximum size of
+its own dynamic table is zero.
+
+An endpoint MUST allow its peer to create both encoder and decoder streams

> An endpoint MUST allow its peer to create both encoder and decoder streams

Why?

One way to prevent the peer from creating QPACK streams is to set the limit of unidirectional streams to 1 in the transport parameters.

-- 
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/2529#pullrequestreview-216117710

---

From: Lucas Pardue <[email-masked]>
Date: Tue, 19 Mar 2019 12:51:55 +0000 (UTC)

LPardue commented on this pull request.

Going back over some of the discussion thread on this topic, was the preference to do lazy loading of streams in response to SETTINGS? In which case, when an endpoint receives a SETTINGS_QPACK_MAX_TABLE_CAPACITY it should send a QUIC MAX_STREAMS to allow the peer to open the encoder stream, and hope that the peer has also given enough streams to open the decoder stream.

Its hard to tell from the different threads what the consensus was.

-- 
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/2529#discussion_r266869521

---

From: ianswett <[email-masked]>
Date: Tue, 19 Mar 2019 07:29:52 -0700

ianswett commented on this pull request.

If I don't want to use an encoder stream, I still have to open it if the table size is non-zero?  I thought the intent was to be permissive of not opening, so this seems unintentionally restrictive.

-- 
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/2529#pullrequestreview-216192821

---

From: Lucas Pardue <[email-masked]>
Date: Tue, 19 Mar 2019 15:05:04 +0000 (UTC)

I think this is telling that the current definition of signalling around QPACK is all a bit wrong. The receiver tells the sender if it is willing to support dynamic compression but there is no explicit signal it will happen until a stream is opened, are we happy with that?

For @ianswett's issue, an additional clause to this sentence could help e.g. 

"An endpoint MAY avoid creating its own encoder stream if the maximum size of the dynamic table permitted by the peer is zero, or if the endpoint doesn't want to use the dynamic table."

-- 
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/2529#discussion_r266936540

---

From: Kazuho Oku <[email-masked]>
Date: Tue, 19 Mar 2019 10:57:18 -0700

kazuho commented on this pull request.

Agrees to @dtikhonov. Maybe adding something like following immediately after could be sufficient. "Endpoints MAY avoid creating encoder streams if they are not going to be used. They MAY avoid or defer the creation of a decoder stream."

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

---

From: Alessandro Ghedini <[email-masked]>
Date: Wed, 20 Mar 2019 18:28:26 +0000 (UTC)

ghedo commented on this pull request.

Hmmm, I think I was reading https://github.com/quicwg/base-drafts/issues/2100#issuecomment-459241694 wrong.

But yeah, the intention there was to disallow setting the uni streams limit to less than 3 in order to allow the other side to open all of their streams. This is effectively what the spec says right now I think, since each side MUST create their streams and if the peer's limits prevent that, than the endpoint can't create them.

I would also be fine aloowing one side to prevent the other from opening and making that not an error, but either way I think we should specifiy what to do since the spec right now is far from clear on this.

-- 
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/2529#discussion_r267485730

---

From: Dmitri Tikhonov <[email-masked]>
Date: Wed, 20 Mar 2019 18:46:20 +0000 (UTC)

dtikhonov commented on this pull request.

> Hmmm, I think I was reading #2100 (comment) wrong.
> 
> But yeah, the **intention there was to disallow setting the uni streams limit to less than 3** in order to allow the other side to open all of their streams.

(Emphasis added)

I see how you get that from that comment by @MikeBishop:

> Discussed in Tokyo; do not require creating the streams, but creating them is not forbidden even if the setting prevents their use

But the "setting" refers to the QPACK settings -- namely, the size of the dynamic table -- and not to the stream limits.  Otherwise, how could it not be forbidden to create streams over the stream number limits specified by the peer?

I agree that more clarity is required.  My preference would be to allow the unidirectional stream limit to be set to 1 to allow just the HTTP Control Stream.

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

---

From: afrind <[email-masked]>
Date: Wed, 20 Mar 2019 13:13:32 -0700

afrind commented on this pull request.

@LPardue : The explicit signal is the arrival of a Set Dynamic Table Capacity instruction on the encoder stream with a non-zero capacity (or perhaps the arrival of a header block with a non-zero Required Insert Count, if there's reordering or loss).  I don't see any problem with this - what's the specific concern?

It's actually an improvement over HPACK, where the receiver assumes the sender will use the table by virtue of the non-zero default max capacity.  Implementations that never use the dynamic table might not be kind enough to send an Update Table Size with size=0.

For the language here, would it read better the other way around: 

"The encoder need only open the encoder stream if it wishes to use the dynamic table and is permitted to do so by the decoder" - or something to that effect?

> Both clients and servers SHOULD send a value of three or greater for the QUIC transport parameter initial_max_uni_streams.

I think the current consensus regarding QPACK streams is that you cannot prevent their creation, so clients and servers MUST give 3 or more.  If we allow the peer to limit to one unidirectional stream, there's potentially nothing from stopping an implementation from using that to create an encoder or decoder stream.

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

---

From: Dmitri Tikhonov <[email-masked]>
Date: Wed, 20 Mar 2019 13:29:34 -0700

> I think the current consensus regarding QPACK streams is that you cannot prevent their creation

What does that mean -- "[one] cannot prevent their creation?"  Surely, setting the limit low does just that.

The general MO of this WG has been to assume that the implementers are capable of making sensible choices.  Creating an unnecessary QPACK stream instead of the HTTP Control Stream when the allowance is just one stream is far from a subtle "gotcha."

-- 
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/2529#discussion_r267532197

---

From: Lucas Pardue <[email-masked]>
Date: Wed, 20 Mar 2019 20:38:22 +0000 (UTC)

The trap here is that a peer sets initial uni directional streams. And then may increase this value. 

Uni directional stream accounting needs to factor in qpack, push promise and whatever other extension mechanisms might come to pass.
The explicit signal that's missing is for QPACK off. If you dont tell the peer you wont use QPACK, and it advertised a size > 0. It will have to entertain that this could happen at any point in the connection. 

In a pathological case, you give me 3 credits and I use them to send control and 2 grease streams. What happens next?

I think these sorts of issues will come to light, the specs are not so helpful in guidance right now.

-- 
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/2529#discussion_r267535756

---

From: afrind <[email-masked]>
Date: Wed, 20 Mar 2019 15:56:34 -0700

@dtikhonov : I mean to say, that an implementation that prevents the creation of QPACK streams is not conformant, in the same way it would not be conformant to set it to 0 and block the creation of the control stream.  This is my understanding of the consensus based on the last discussion in Toyko.

@LPardue : I'm not sure that's such a big deal.  If you planned to use the dynamic table, but wasted your stream on grease instead of an encoder stream, then you can't use the table.  If you disabled the dynamic table for your peer, then you don't need to create the decoder stream anyways.  If you allowed the peer to use the dynamic table but can't create a decoder stream, then your compression will eventually degrade to ~static only but I think the connection will still work. 

-- 
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/2529#discussion_r267578761

---

From: Lucas Pardue <[email-masked]>
Date: Wed, 20 Mar 2019 23:20:54 +0000 (UTC)

>  I'm not sure that's such a big deal. If you planned to use the dynamic table, but wasted your stream on grease instead of an encoder stream, then you can't use the table.

The problem is that the sender has no intention of creating an encoder stream but the receiver doesn't know that. If the receiver is eager to use QPACK, it keeps giving back a stream credit for every unidirectional stream that is "wasted" before creating the encoder stream. Even if it didn't give back credit immediately and did let the max uni stream limit be hit, there is no guarantee that the next time the receiver gives credit (say for a push promise) that then it wouldn't spontaneously be used for QPACK. 

-- 
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/2529#discussion_r267583955

---

From: afrind <[email-masked]>
Date: Thu, 21 Mar 2019 19:08:47 +0000 (UTC)

This sounds like a case of "don't do that".  The sender gets to choose to how to use the uni stream credit it receives, and I don't think receivers should overthink this.  A receiver should choose the value based on how many concurrent uni streams they can handle, since there's no way to guaranteer the sender will use them for any particular type.  If the sender wants to use the dynamic table at any point in the connection, maybe we should recommend they open the stream as soon as possible.  

-- 
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/2529#discussion_r267915538

---

From: Lucas Pardue <[email-masked]>
Date: Thu, 21 Mar 2019 12:23:41 -0700

Thinking about this a little more, its a nice feature of HPACK that it can chose when to start using dynamic compression, with little overhead. 

My understanding though is that we can't use MAX_STREAMS to contorl concurrency like you describe (I'm probably wrong here). But from the transport document section 19.11

> Note that these frames (and the corresponding transport parameters)
   do not describe the number of streams that can be opened
   concurrently.  The limit includes streams that have been closed as
   well as those that are open.

-- 
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/2529#discussion_r267920794

---

From: afrind <[email-masked]>
Date: Thu, 21 Mar 2019 19:41:24 +0000 (UTC)

I think most implementations will take closed streams into account to know when to issue more uni stream credit.  In the case of servers, there's never a need to increase it in vanilla HTTP/3.   Clients need only increase it when they want more pushes (or grease, or extensions, etc).

> Thinking about this a little more, its a nice feature of HPACK that it can chose when to start using dynamic compression, with little overhead.

I suppose that's a feature of HPACK, but is it really all that useful?  I think of it the other way - HPACK was simpler to setup (didn't require any extra streams) but that's because the underlying transport provides the strong ordering guarantee.  If you want QPACK dynamic table then you have to open some streams.  Just do it at the beginning of the connection when you know you have credit, or risk not being able to use it later, if you consume the credit with some other stream.

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

---

From: Bence Béky <[email-masked]>
Date: Wed, 27 Mar 2019 10:21:02 -0700

bencebeky commented on this pull request.

Maybe swap the order to "one decoder stream and at most one encoder stream" to make it less likely to be misread as "at most one encoder stream and at most one decoder stream".

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

---

From: Lucas Pardue <[email-masked]>
Date: Wed, 27 Mar 2019 17:29:25 +0000 (UTC)

Thanks. FWIW. We discussed in person tying up this change and getting the PR merged soon. 

-- 
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/2529#discussion_r269683403