Subject: notes on http2 draft

From: Patrick McManus <[email-masked]>
Date: Tue, 21 May 2013 11:01:53 -0400

Hi All - While I've certainly closely followed the deltas to the http/2
draft I haven't actually sat down and read the thing end to end in a while.
As I have a conflict and won't be able to join you all in CA in June, I sat
down today  to do a full read through and have some feedback on the current
state of the draft. There is a mixture of pure editorial here as well as
some more subtstantive opinion stuff.

First, I want to say thanks to the editors - overall this document is great
progress in terms of readbility and organization.
*
*
*
*
*Section 1.
*
*Furthermore, HTTP/1.1 header fields are often repetitive and verbose,
which, in addition to generating more or larger network packets, can cause
the small initial TCP congestion window to quickly. This can result in
excessive latency when multiple requests are made on a new TCP connection.*

[editorial]
Furthermore, HTTP/1.1 header fields are often repetitive and verbose,
which, in addition to generating more or larger network packets, can cause
the small initial TCP congestion window to quickly fill. This can result in
excessive latency when multiple requests are made on a single new TCP
connection.

1.2
*
message:**A complete sequence of frames.*

I'm not sure that does a lot for me as a definition. It should at least say
something about them sharing the same stream ID.. or am I describing a
stream? Is a message a unidirectional stream? as I said.. it doesn't do a
lot as a definition. The document refers to "window update messages" and
"goaway messages" but I think it means frames in those cases.. it also
talks about "receiver of a message" sending WINDOW_UPDATE which makes it
sound like a message is any data frame (not the complete sequence of
them)... and then again we also talk about "HTTP Messages" which are
something distinct..

I suggest we scrub the term message from the document (and this section)
except when it refers to HTTP messages.

3.2 Connection Magic

Can we make that bikeshed a multiple of 4 bytes long to at least
conveniently align the SETTINGS frame that follows?

3.3.1

* Implementations MUST ignore unsupported and unrecognized frame types. *

I think invalid frame types should be session errors of the MUST NOT send
variety. I know the list has been talking about this lately and I haven't
had an opportunity to chime in. Be liberal in what you receive is overrated
and often fraught with security problems - we can rev the protocol with
ALPN at Internet scale in order to sanely guide extensions as needed. I
know design committees love open ended extensions so my view won't pervail,
but this is exactly the kind of thing that leads to interop doom.

3.3.2

*Implementations with limited resources might not be capable of processing
large frame sizes. Such implementations MAY choose to place additional
limits on the maximum frame size. However, all implementations MUST be
capable of receiving and processing frames containing at least 8192 octets
of data.
*
I'm pretty confident we are just inventing complexity here for no good
reason. The tiny universe of implementations that can cope with 8KB but not
64KB is not worth the complexity. One of the advantages of going with a 16
bit frame size is that it should be small enough for everything to handle
it.

If there is reason to believe 64KB is too big for a population large enough
to care about (remembering of course that HTTP/2 is not a ticket to
Internet admission - if you're really so small that you can't read 64KB
frames the muxing of HTTP/2 is going to be a real challenge too; you're
probably best left using a different protocol. Just coap :)), then let's
lower the max frame size instead of reinventing Path MTU Discovery
complexity. But I think we can just drop this section and require everyone
to deal with 64KB.

3.4

*A "stream" is an independent, bi-directional sequence of frames*

Due to the (expected) compression requirements the frames aren't really
independent of other streams. I know this mistake comes up pretty commonly
on spdy-dev from new implementers of that protocol, so it probably helps to
avoid saying independent here. (section 5.3 does it too).

3.4.1

*Rather, new streams are established by sending a frame whose stream
identifier field references a previously unused stream identifier. *

That's a little too loose. Streams are created by the client through
HEADERS+PRIORITY (4.2.2) and by the server through PUSH_PROMISE. The text
as-is makes it sound more free flowing than that.

*The identifier of a newly established stream MUST be numerically greater
than all previously established streams from that endpoint within the
HTTP/2.0 connection, unless the identifier has been reserved using a
PUSH_PROMISE (Section 3.8.5<http://http2.github.io/http2-spec/#PUSH_PROMISE>)
frame.*

Likewise, PUSH_PROMISE really creates the stream (4.3.1) from the server..
I don't see a reason for the caveat here.

3.4.2

I thought one of the takeaways at Tokyo was to define a change-priority
frame.. Can we do that now? is the intention to use a H+P without any
headers at any point in the stream? If so, I think that should be called
out so that server implementation's don't freak out at seeing H+P at
strange points in the stream.. and I think there is some language in 4.2.2
that could be interpreted as meaning certain colon headers are required to
be in every H+P

3.8.5

*The PUSH_PROMISE frame (type=0x5) is used to notify the peer endpoint in
advance of streams the sender intends to initiate.*

I think I must have missed something on list about this. Why is the
language here general (peer, sender).. why define this so that clients can
generate PUSH_PROMISE ?

3.8.9.4

*After a receiver reads in a frame that marks the end of a stream (for
example, a data stream with a FINAL flag set), it ceases transmission of
WINDOW_UPDATE frames. A sender is not required to maintain the available
flow control window for streams that it is no longer sending on. *

First - since this is a spec, that should be "it MAY/SHOULD/MUST cease
transmission of WINDOW_UPDATE frames". I suggest MUST.

Second - The language isn't clear if this applies to session windows or
just the stream window. I think it is just the stream window and the
receiver still needs to ack^H^H^HWINDOW_UPDATE the session window after the
FINAL flag, but we should say explicitly.

4.2.2

*User-agents MUST support gzip compression. Regardless of the
Accept-Encoding sent by the user-agent, the server may always send content
encoded with gzip or deflate encoding.
[rfc.comment.11<http://http2.github.io/http2-spec/#rfc.comment.11>:
Still valid?]*

I support that still being valid. This is an important performance property
to rely on.

also 4.2.2

*The client initiates a request by sending a HEADERS+PRIORITY frame [...]
if the server receives a data frame prior to a HEADERS or HEADERS+PRIORITY
frame
*

I think that means just "prior to a HEADERS+PRIORITY frame" which is what
is required to open the stream...
4.2.3

*The server responds to a client request using the same stream identifier
that was used by the request. An HTTP response begins with a HEADERS frame.
[...] If the client receives a data frame prior to a HEADERS or
HEADERS+PRIORITY

*
Again, I think that means "prior to a HEADERS" [NOT "or H+P"]. Can server's
send H+P frames? If so, why? and if there is a compelling why, why can't
the response begin with H+P?

hope that helps.

---

From: James M Snell <[email-masked]>
Date: Tue, 21 May 2013 09:00:01 -0700

Excellent review... a few brief responses...

On Tue, May 21, 2013 at 8:01 AM, Patrick McManus <[email-masked]> wrote:
>[snip]
> 1.2
>
> message:A complete sequence of frames.
>
> "goaway messages" but I think it means frames in those cases.. it also talks
> about "receiver of a message" sending WINDOW_UPDATE which makes it sound
> like a message is any data frame (not the complete sequence of them)... and
> then again we also talk about "HTTP Messages" which are something distinct..
>

Agree. The term "message" used in the framing layer is confusing and
meaningless and ought to be reserved specifically for the HTTP
semantic layer discussion.

>[snip[
> Implementations MUST ignore unsupported and unrecognized frame types.
>
> and often fraught with security problems - we can rev the protocol with ALPN
> at Internet scale in order to sanely guide extensions as needed. I know
> design committees love open ended extensions so my view won't pervail, but
> this is exactly the kind of thing that leads to interop doom.
>

There are generally three categories here depending on how you
characterize the framing layer...

1. Malformed/Invalid Frames ... which yes, ought to be rejected with a
connection error
2. Unknown protocol frames (hop-by-hop frames). Given that these may
or may not affect the connection state, it would likely be better to
reject these rather than silently ignore them and go with Roberto's
option of requiring a new protocol version id when they're used.
3. Unknown data frames (end-to-end frames). These do not affect the
connection state at all. These are safe for an endpoint to ignore and
pass thru with the assumption that the a higher layer will deal with
them.

Right now, the spec draws absolutely no distinction between 2 and 3
and just says to ignore everything you don't understand.

Alternatively, if you view the framing layer as being essentially a
black box to the higher application levels and require that any and
all application data be transferred using DATA frames (ruling out the
use of framing for anything other than hop-by-hop protocol level
stuff) then I agree that MUST NOT send is significantly safer than
MUST IGNORE.

> 3.3.2
>
> Implementations with limited resources might not be capable of processing
> to care about (remembering of course that HTTP/2 is not a ticket to Internet
> admission - if you're really so small that you can't read 64KB frames the
> muxing of HTTP/2 is going to be a real challenge too; you're probably best
> left using a different protocol. Just coap :)), then let's lower the max
> frame size instead of reinventing Path MTU Discovery complexity. But I think
> we can just drop this section and require everyone to deal with 64KB.
>

After experimenting with this further, I'm +1 on this for now but I
want to see a lot more experimentation with flow control. Any
configurable limits that we place on frame size is going to introduce
a Path MTU problem and could force "reframing" to occur at various
points in the message flow.

> 3.4
>
> A "stream" is an independent, bi-directional sequence of frames
>

Well... streams themselves are independent of one another, it's the
serialization of the individual frames that is dependent on a shared
context state. I agree that this ought to be explained better.

> 3.4.1
>
> Rather, new streams are established by sending a frame whose stream
> identifier field references a previously unused stream identifier.
>
> The identifier of a newly established stream MUST be numerically greater
> PUSH_PROMISE (Section 3.8.5) frame.
>
> Likewise, PUSH_PROMISE really creates the stream (4.3.1) from the server.. I
> don't see a reason for the caveat here.
>

This is up for debate, apparently.

I agree with you that H, H+P and P_P ought to be the only ways to
create new streams but others (such as Roberto) have argued that any
time you send any frame with an unused stream identifier, it
initializes that stream.

> headers at any point in the stream? If so, I think that should be called out
> so that server implementation's don't freak out at seeing H+P at strange
> points in the stream.. and I think there is some language in 4.2.2 that
> could be interpreted as meaning certain colon headers are required to be in
> every H+P
>

Truthfully, I'd rather see us define a distinct PRIORITY frame and
remove H+P from the picture altogether.

To create the stream, I would send a HEADERS frame. Then to set the
priority for the stream, I would send a separate PRIORITY frame. This
would resolve any possible ambiguity and confusion with regards to
when to use HEADERS vs HEADERS+PRIORITIES vs any new CHANGE-PRIORITY
frame.

Yes, I get the argument that sending HEADERS+PRIORITY vs. HEADERS and
a PRIORITY frame allows creation of the stream and setting of the
priority in a single operation, but the added complexity of having
separate HEADERS, HEADERS+PRIORITY, CHANGE-PRIORITY frames just
doesn't seem like a worth while tradeoff to me.

>
> 3.8.5
>
> The PUSH_PROMISE frame (type=0x5) is used to notify the peer endpoint in
> advance of streams the sender intends to initiate.
>
> I think I must have missed something on list about this. Why is the language
> here general (peer, sender).. why define this so that clients can generate
> PUSH_PROMISE ?
>

This also appears to be up for debate. There is a theoretical use case
for clients sending PUSH_PROMISE frames and sending multiple
associated streams to a server.. but, it's purely theoretical. For
now, I think using more specific language that restricts PUSH_PROMISE
use to the server is more than appropriate.

>[snip]
> 4.2.2
>
> User-agents MUST support gzip compression. Regardless of the Accept-Encoding
> sent by the user-agent, the server may always send content encoded with gzip
> or deflate encoding. [rfc.comment.11: Still valid?]
>

Actually, this bit needs to be clarified, and I believe we ought to be
able to simplify this significantly.

For instance, for all DATA frames, we can introduce a GZIP flag (0x2)
that indicates that the data contained in that frame has been
compressed. Doing so really eliminates the need to specify anything at
the HTTP semantic layer and eliminates the need for the
accept-/transfer-/content-encoding header mechanism completely.

- James

---

From: William Chan (陈智昌) <[email-masked]>
Date: Tue, 21 May 2013 14:15:44 -0300

On Tue, May 21, 2013 at 1:00 PM, James M Snell <[email-masked]> wrote:

> talks
> ALPN
> Internet

I'm +1 on this too. What experimentation would you like to see James? We've
been using flow control for awhile now in SPDY land.

> time you send any frame with an unused stream identifier, it

Yes, this is up for debate. I agree with you guys too.

> out

I don't view it as much added complexity. I think it just may be confusing
to people why you might have a combined HEADERS+PRIORITY frame vs separate
ones. It wasn't a problem earlier until we renamed the frames :P

> language

+1, restrict to server.

---

From: Martin Thomson <[email-masked]>
Date: Tue, 21 May 2013 14:45:48 -0700

On 21 May 2013 08:01, Patrick McManus <[email-masked]> wrote:
> Section 1. [...] [editorial]

Thanks.  Fixed.

> message: [...]

Scrubbing done.  Turns out that "message" was misused almost
everywhere it appeared :)

> 3.2 Connection Magic [...] a multiple of 4 bytes

I don't know how to do that, except by trimming "BAR" down to "BA" (or
"GO" or some other shed-tone).  That might need to be tested.

> [...] this is exactly the kind of thing that leads to interop doom.

I think that we plan to discuss this at the interim.  I don't plan to
take a position on this, except that whatever we choose needs to be
consistently applied and motivated, so maybe I can do a (poor) Patrick
impersonation for you.

> 3.3.2 [...]

I would be perfectly happy with this position, if only because a) it
resolves the issue, and b) ends up with less text in the draft.

> avoid saying independent here. (section 5.3 does it too).

This is an interesting one.  The view that I think that James has
taken is that the compression context is evaluated at a lower layer of
the logical stack than streaming.  That would allow this to remain
true, at least superficially.

I get the point though.  Do you think that qualifying the statement to
say that streams are subject to independent flow control and
life-cycles would be enough?

> as-is makes it sound more free flowing than that.

I think that the intent is to avoid precluding any chance of
multiplexing websockets in here.  HEADERS+PRIORITY might be used
there, but it might not be necessary.

> don't see a reason for the caveat here.

PUSH_PROMISE doesn't create a stream.  It promises to.  This is
important because it allows a pushing server to promise more streams
that the maximum stream concurrency limit set by the client.  Hence
the caveat.

> could be interpreted as meaning certain colon headers are required to be in

We haven't had a proposal for a change-priority frame yet.  I'm not
sure that H+P is going to be optimal for this case.

> here general (peer, sender).. why define this so that clients can generate

No particular reason.  Other than it being easier.  Switching to

> 3.8.9.4
>
> After a receiver reads in a frame that marks the end of a stream (for
> flow control window for streams that it is no longer sending on.
>

Righto.  MUST.

> Second - The language isn't clear if this applies to session windows or just
> the stream window. I think it is just the stream window and the receiver
> still needs to ack^H^H^HWINDOW_UPDATE the session window after the FINAL
> flag, but we should say explicitly.

Yes.  Express > implied.

> I support that still being valid. This is an important performance property

I think that this was marked as so when the COMPRESSED bit was
removed.  I don't think that this proscription on the upper layer is
unreasonable, even if it is a bit of a layering violation that this
specification doesn't strictly need to address.

I've moved this to the response processing side and clarified the
statement.  If there are objections to this, the paragraph is easy
enough to remove.

> also 4.2.2
>
> The client initiates a request by sending a HEADERS+PRIORITY frame [...] if
> the server receives a data frame prior to a HEADERS or HEADERS+PRIORITY
> frame
>
> I think that means just "prior to a HEADERS+PRIORITY frame" which is what is
> required to open the stream...

Gotcha, and 4.2.3 fixed (to the converse) too.  Servers have no real
business setting priority (for HTTP).

---

From: Martin Thomson <[email-masked]>
Date: Tue, 21 May 2013 14:49:06 -0700

> accept-/transfer-/content-encoding header mechanism completely.

HTTP/2.0 originally had exactly this feature and it was removed.  It
was removed from SPDY shortly after the two specifications diverged.

https://github.com/http2/http2-spec/issues/46

It turns out that a compression bit is a) not especially useful
without knowledge of what the payload is going to be, and b)
confusing.

---

From: Patrick McManus <[email-masked]>
Date: Tue, 21 May 2013 20:16:00 -0400

> consistently applied and motivated, so maybe I can do a (poor) Patrick
Can't wait for the summary notes :)

> life-cycles would be enough?

sgtm