Subject: legality of Transfer-Encoding: chunked bodies in HTTP/2

From: Johnny Graettinger <[email-masked]>
Date: Tue, 5 Aug 2014 14:39:40 -0400

I'm not sure this has been explicitly discussed (couldn't find a related
thread).

What's the WG's view on the legality of chunked bodies in HTTP/2? Is a
HTTP/1 <=> HTTP/2 intermediary obligated to transform an upstream HTTP/1
chunked response into a non-chunked HTTP/2 stream?

Section 8.1.3 "Examples" provides guidance on how an intermediary *should *do
so, however section 8.1.2.2 "Hop-by-Hop Header Fields" says that stripping
Transfer-Encoding is a SHOULD, not a MUST.

cheers,
-johnny

---

From: Martin Thomson <[email-masked]>
Date: Tue, 5 Aug 2014 13:09:28 -0700

On 5 August 2014 11:39, Johnny Graettinger <[email-masked]> wrote:
> Section 8.1.3 "Examples" provides guidance on how an intermediary should do

Yeah, so the only reason that it's not a MUST is that some
intermediaries don't look and we don't want to force them to look.
That's all.  Transfer-Encoding has no meaning in HTTP/2 (unless an
extension restores that meaning).

Seeing the chunked framing in DATA frames would be wrong.

---

From: Zhong Yu <[email-masked]>
Date: Tue, 5 Aug 2014 15:29:34 -0500

> Seeing the chunked framing in DATA frames would be wrong.

That is quite confusing and dangerous. Should we require that anyone
*generating* the initial HTTP2 message MUST NOT include the
Transfer-Encoding header?

Zhong Yu
bayou.io

---

From: Osama Mazahir <[email-masked]>
Date: Tue, 5 Aug 2014 21:00:56 +0000

+1 for allowing Chunked.

Windows machines are going to end up output chunked responses (well...technically anything built on top of the Windows HTTP networking components).  We cannot rewrite all the Windows applications to avoid chunking and having some magical translation layer doing unbounded buffering to convert into Content-Length prefixed form is bad.  Stripping the chunk boundaries when encapsulating into DATA frames requires a reparser in the data send path, which is merely swapping one problem for another (and may require the peer to do the opposite if converting back into 1.1 form).

It may be _ineffecient_ to have chunks but it should be legal, to maximize 1.1<-->2.0 fidelity.  

We can probably address this via editorial changes indicating that in pure HTTP2, using END_STREAM only is the preferred way to mark end-of-body (or, of course, trailers).  Along with all the normal caveats of smuggling bytes in DATA frames after the final chunk, etc.

-----Original Message-----
From: Zhong Yu [mailto:[email-masked]] 
Sent: Tuesday, August 5, 2014 1:30 PM
To: Martin Thomson
Cc: Johnny Graettinger; HTTP Working Group
Subject: Re: legality of Transfer-Encoding: chunked bodies in HTTP/2

On Tue, Aug 5, 2014 at 3:09 PM, Martin Thomson <[email-masked]> wrote:
>> Section 8.1.3 "Examples" provides guidance on how an intermediary 
>> should do so, however section 8.1.2.2 "Hop-by-Hop Header Fields" says 
>> that stripping Transfer-Encoding is a SHOULD, not a MUST.
>

That is quite confusing and dangerous. Should we require that anyone
*generating* the initial HTTP2 message MUST NOT include the Transfer-Encoding header?

Zhong Yu
bayou.io

---

From: Martin Thomson <[email-masked]>
Date: Tue, 5 Aug 2014 14:26:46 -0700

> Transfer-Encoding header?

I re-read, and I figured that it could be made even clearer:

https://github.com/http2/http2-spec/pull/583

Does that help?

---

From: Martin Thomson <[email-masked]>
Date: Tue, 5 Aug 2014 14:35:10 -0700

> +1 for allowing Chunked.

HTTP/2 is inherently chunked.  You don't have to send Content-Length,
though we do like to encourage it.  Stripping the decoration (the
chunking) is necessary if you want to make DATA frames.  Otherwise we
have HTTP/2 implementations needing to know about the details (and
corresponding mess) of HTTP/1.1 framing.

When it comes to reforming into 1.1, you can make your own assessment
about how to translate.  If you have content-length, then chunked
encoding isn't neccessary, even if it was originally chunked.

---

From: Ryan Hamilton <[email-masked]>
Date: Tue, 5 Aug 2014 15:41:04 -0700

> https://github.com/http2/http2-spec/pull/583

​Seems nice and unambiguous.​

---

From: Ryan Hamilton <[email-masked]>
Date: Tue, 5 Aug 2014 15:41:41 -0700

> corresponding mess) of HTTP/1.1 framing.

​Completely agree.
​

---

From: Greg Wilkins <[email-masked]>
Date: Wed, 6 Aug 2014 08:53:55 +1000

On 6 August 2014 07:26, Martin Thomson <[email-masked]> wrote:

> +1

-- 
Greg Wilkins <[email-masked]>
http://eclipse.org/jetty HTTP, SPDY, Websocket server and client that scales
http://www.webtide.com  advice and support for jetty and cometd.

---

From: Zhong Yu <[email-masked]>
Date: Tue, 5 Aug 2014 18:00:26 -0500

> https://github.com/http2/http2-spec/pull/583

in this text

> any request containing connection-specific header fields...

did you mean

> any *message* containing connection-specific header fields...

Zhong Yu
bayou.io

---

From: Greg Wilkins <[email-masked]>
Date: Wed, 6 Aug 2014 09:03:47 +1000

On 6 August 2014 07:00, Osama Mazahir <[email-masked]> wrote:

> Windows machines are going to end up output chunked responses
> (well...technically anything built on top of the Windows HTTP networking
> components).

Wow, didn't know that they delegated chunking generation to the
application!

>  We cannot rewrite all the Windows applications to avoid chunking and
> having some magical translation layer

Well there has to be some layer to at least do the h2 framing.... be it
magical or otherwise :)

> doing unbounded buffering to convert into Content-Length prefixed form is
> bad.

Content-Length is not necessary.

>  Stripping the chunk boundaries when encapsulating into DATA frames
> requires a reparser in the data send path,

I don't think this is too onerous.  If Windows insists on generating the
chunk boundaries, then something has to eventually parse them.       Not
parsing them out in the sender is just shifting the burden of the strange
technical choice of Windows HTTP networking components to the receiver.

cheers

---

From: Michael Sweet <[email-masked]>
Date: Tue, 05 Aug 2014 20:32:36 -0400

DATA frames are already equivalent to HTTP/1.1 chunking.  Why do we need to chunk twice?

> *generating* the initial HTTP2 message MUST NOT include the Transfer-Encoding header?

_________________________________________________________
Michael Sweet, Senior Printing System Engineer, PWG Chair

---

From: Mark Nottingham <[email-masked]>
Date: Wed, 6 Aug 2014 14:25:20 +1000

Just to make sure we're all on the same page -- *logically* HTTP/2 is still chunked, because you still split things up into DATA frames. However, the "chunk" format specified in <http://httpwg.github.io/specs/rfc7230.html#chunked.encoding> will NOT be considered part of the message framing in HTTP/2; if that shows up, it'll be considered payload (and therefore visible to the application as content, which is probably NOT what you want).

Cheers,

--
Mark Nottingham   https://www.mnot.net/

---

From: Mike Bishop <[email-masked]>
Date: Wed, 6 Aug 2014 13:56:01 +0000

Mostly for the case of 1.1->2 intermediaries.  If you receive a 1.1 message, you have two options:  Encode the headers and encapsulate the payload (if you know the payload will be valid), or perform transform work on the headers and payload.  The more work that has to be done in that transform, the more throughput suffers.  The ability to simply encapsulate the body rather than transforming it is less work for the intermediary.

---

From: Greg Wilkins <[email-masked]>
Date: Thu, 7 Aug 2014 00:27:00 +1000

On 6 August 2014 23:56, Mike Bishop <[email-masked]> wrote:

> The ability to simply encapsulate the body rather than transforming it is
> less work for the intermediary.

But by sending the http1 chunked body over http2 does not avoid the need to
transform it.  Somebody has to unchunk it and I'd really rather it not be
the server.  Now one might argue that those lazy browsers have heaps of
available CPU, but I really don't think exporting data transform duty in
either direction is a good idea.

Besides, if it is an intermediary and not the source of the data, it has to
parse the chunks anyway so it can identify the http1 end of message.

cheers

---

From: Jason Greene <[email-masked]>
Date: Wed, 6 Aug 2014 09:44:54 -0500

On Aug 6, 2014, at 9:27 AM, Greg Wilkins <[email-masked]> wrote:

> The ability to simply encapsulate the body rather than transforming it is less work for the intermediary.
> 
> But by sending the http1 chunked body over http2 does not avoid the need to transform it.  Somebody has to unchunk it and I'd really rather it not be the server.  Now one might argue that those lazy browsers have heaps of available CPU, but I really don't think exporting data transform duty in either direction is a good idea.
> 
> Besides, if it is an intermediary and not the source of the data, it has to parse the chunks anyway so it can identify the http1 end of message.

It sounds like equivalent work to me. You read the chunk header, then write a data frame header. Then you use an efficient copy mechanism for the data block, repeat. If you want to reframe the chunk on different data frame boundaries you do have to buffer, but thats the same whether you encapsulate or transform the chunk stream.

--
Jason T. Greene
WildFly Lead / JBoss EAP Platform Architect
JBoss, a division of Red Hat

---

From: <[email-masked]>
Date: Wed, 6 Aug 2014 14:55:08 +0000

Hi Michael-

On Wednesday,06 August 2014 15:56, [email-masked]<mailto:[email-masked]> wrote:

> [Option 1:]

> Encode the headers and encapsulate the payload (if you

> know the payload will be valid)

if (length(chunk_hdr) + length(chunk) <= MAX_FRAME_SIZE)

  create h2 frame where frame_len == length(chunk_hdr) + length(chunk)

  add chunk_hdr

  add chunk

else

  remove chunk header

  split chunk into n smaller chunks <= MAX_FRAME_SIZE (including chunk header len!)

  for each of the n smaller chunks

    create h2 frame where frame_len = length(chunk_hdr[n]) + length(chunk[n])

    add chunk_hdr[n]

    add chunk[n]

> [Option 2:]

> , or perform transform work on the headers and

> payload.

strip chunk header

if (length(chunk) <= MAX_FRAME_SIZE)

  create h2 frame where frame_len == length(chunk)

  add chunk

else

  split chunk into n smaller chunks <= MAX_FRAME_SIZE (excluding chunk header len)

  for each of the n smaller chunks

    create h2 frame where frame_len = length(chunk[n])

    add chunk[n]

> The more work that has to be done in that transform,

> the more throughput suffers.

Seems to me that option 2 is less work.

-keith

P.S.  Not to mention Greg's point that the server has less work with option 2 (i.e. it doesn't have to "de-frame" and "de-chunk").

This email message is intended only for the use of the named recipient. Information contained in this email message and its attachments may be privileged, confidential and protected from disclosure. If you are not the intended recipient, please do not read, copy, use or disclose this communication to others. Also please notify the sender by replying to this message and then delete it from your system.

---

From: Osama Mazahir <[email-masked]>
Date: Thu, 7 Aug 2014 00:34:30 +0000

So we are forcing 1.1<-->2.0 intermediaries to transform the body by stripping & adding chunked encoding instead of merely encapsulating the body into DATA frames?

1. Intermediary receives HTTP/1.1 chunked request body.  Intermediary strips "Transfer-Encoding: chunked".
2. Intermediary strips chunk headers as it constructs the DATA frames.  The final 0-chunk maps to an END_STREAM.
3. Intermediary receives HTTP/2 response body consisting of DATA frames (no "Transfer-Encoding: chunked").
4. Intermediary adds chunk boundaries as it constructs the DATA frames.  The END_STREAM maps to the final 0-chunk.

I'm confused.  Either "Transfer-Encoding: chunked" is illegal and intermediaries MUST convert or it's legal and HTTP2 receivers have to be able to handle "Transfer-Encoding: chunked".

-----Original Message-----
From: Mark Nottingham [mailto:[email-masked]] 
Sent: Tuesday, August 5, 2014 9:25 PM
To: Michael Sweet
Cc: Osama Mazahir; Zhong Yu; Martin Thomson; Johnny Graettinger; HTTP Working Group
Subject: Re: legality of Transfer-Encoding: chunked bodies in HTTP/2

Cheers,

On 6 Aug 2014, at 10:32 am, Michael Sweet <[email-masked]> wrote:

>>>> should do so, however section 8.1.2.2 "Hop-by-Hop Header Fields" 
>>>> says that stripping Transfer-Encoding is a SHOULD, not a MUST.
>>>

---

From: Mike Bishop <[email-masked]>
Date: Thu, 7 Aug 2014 01:15:56 +0000

Or 3b, Intermediary receives HTTP/2 response headers containing a Content-Length, and it just dumps the DATA payload into the TCP connection as it arrives.  If the CL is incorrect, that's the origin's problem/fault.  But yes, at the transition point, prohibiting Transfer-Encoding strongly suggests that the absence of Content-Length implies creating the chunks off the DATA frames.

-----Original Message-----
From: Mark Nottingham [mailto:[email-masked]]
Sent: Tuesday, August 5, 2014 9:25 PM
To: Michael Sweet
Cc: Osama Mazahir; Zhong Yu; Martin Thomson; Johnny Graettinger; HTTP Working Group
Subject: Re: legality of Transfer-Encoding: chunked bodies in HTTP/2

Cheers,

---

From: Martin Thomson <[email-masked]>
Date: Wed, 6 Aug 2014 21:10:59 -0700

On 6 August 2014 17:34, Osama Mazahir <[email-masked]> wrote:
> "Transfer-Encoding: chunked" is illegal and intermediaries MUST convert

That.

---

From: Osama Mazahir <[email-masked]>
Date: Thu, 7 Aug 2014 21:48:30 +0000

Ok...so it appears we actually do want intermediaries to transform [1]...and the current spec text is incorrect.

If so, we need to add some inescapably obvious editorial text into the spec stating that "Transfer-Encoding: chunked" is illegal and intermediaries MUST convert (in both directions) along with guidance of how to do it (e.g. presence/absence of Content-Length, 0-chunk <--> END_STREAM, etc).  Currently, there are only 2 references to the string "Transfer-Encoding" and one of them is just an itemization of headers that "SHOULD" be removed, as part of a blurb about removing connection specific headers.

[1] http://lists.w3.org/Archives/Public/ietf-http-wg/2014JulSep/1635.html

-----Original Message-----
From: Martin Thomson [mailto:[email-masked]] 
Sent: Wednesday, August 6, 2014 9:11 PM
To: Osama Mazahir
Cc: Mark Nottingham; Michael Sweet; Zhong Yu; Johnny Graettinger; HTTP Working Group
Subject: Re: legality of Transfer-Encoding: chunked bodies in HTTP/2

That.

---

From: Martin Thomson <[email-masked]>
Date: Thu, 7 Aug 2014 14:52:37 -0700

On 7 August 2014 14:48, Osama Mazahir <[email-masked]> wrote:
> If so, we need to add some inescapably obvious editorial text into the spec stating that "Transfer-Encoding: chunked" is illegal

http://http2.github.io/http2-spec/#rfc.section.8.1.2.2

The first paragraph is unequivocal enough, in my opinion.  But I wrote
that, so I'll defer to others on this point.

---

From: Mike Bishop <[email-masked]>
Date: Thu, 7 Aug 2014 22:19:14 +0000

I think part of the problem is the definition of a "hop-by-hop" header.  Transfer-Encoding isn't removed and reapplied by each hop, and isn't mentioned by Connection: that I've noticed.  However, RFC 7230 says "any recipient along the request/response chain MAY decode the received transfer coding(s) or apply additional transfer coding(s) to the message body, assuming that corresponding changes are made to the Transfer-Encoding field-value."

So it's not end-to-end, but I don't see it explicitly called hop-by-hop either.  (If I'm missing someplace, please point it out to me.)  The second paragraph does say that Transfer-Encoding SHOULD be removed.  However, SHOULD means someone might not, and there's no explicit text about decoding, just about removing the header.  (Decoding is a reasonable inference when removing the header, though, obviously.)

We chose not to follow the SHOULD, found a compatibility problem with someone else who treated it as a MUST, and that's the issue we're trying to resolve here.  If it's actually a MUST, we should fix the spec, lest someone else make the same mistake.  If it's actually a SHOULD, then we should fix Chrome.  It's just a matter of making the call.

-----Original Message-----
From: Martin Thomson [mailto:[email-masked]] 
Sent: Thursday, August 7, 2014 2:53 PM
To: Osama Mazahir
Cc: Mark Nottingham; Michael Sweet; Zhong Yu; Johnny Graettinger; HTTP Working Group
Subject: Re: legality of Transfer-Encoding: chunked bodies in HTTP/2

On 7 August 2014 14:48, Osama Mazahir <[email-masked]> wrote:
> If so, we need to add some inescapably obvious editorial text into the 
> spec stating that "Transfer-Encoding: chunked" is illegal

The first paragraph is unequivocal enough, in my opinion.  But I wrote that, so I'll defer to others on this point.

---

From: Martin Thomson <[email-masked]>
Date: Thu, 7 Aug 2014 15:35:11 -0700

Unlike Content-Encoding (Section 3.1.2.1 of [RFC7231]),
   Transfer-Encoding is a property of the message, not of the
   representation...

I think that the reason this hasn't come up before is that it seemed
obvious to everyone implementing HTTP/2 that it was the octets of the
representation that go in DATA.

Since it seems that it is not equally obvious to everyone, how about I
add a section on this subject.

---

From: Mark Nottingham <[email-masked]>
Date: Fri, 8 Aug 2014 16:50:47 +1000

Indeed. From <http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1>:

>   3.  zero or more DATA frames containing the message payload (see [RFC7230], Section 3.3)

"payload" is a very specific term, and it is *not* processed for chunks.

Cheers,

---

From: Amos Jeffries <[email-masked]>
Date: Fri, 08 Aug 2014 23:08:11 +1200

> "payload" is a very specific term, and it is *not* processed for chunks.

Excellent.

So the sender application just excludes Content-Length and
chunked-encodes the representation. The framing layer DATA frames the
chunks without inspecting to find where END_STREAM flag applies.

* Applications (whether DoS generators or innocent bunglers) can now
force recipients to hold onto HTTP/2 stream context indefinitely for up
to 2^31-1 streams. Just by emitting chunked encoded byte stream for DATA
encoding. It not being de-chunked to find the 0-chunk where END_STREAM
applies.

* request smugglers can now abuse h2->1.1 gateways. Just send without
content-length and having a "0\r\n" prefix on the 1.1 message smuggled
inside DATA.

Amos

---

From: Martin Thomson <[email-masked]>
Date: Fri, 8 Aug 2014 09:12:57 -0700

> content-length and having a "0\r\n" prefix on the 1.1 message smuggled

The complete opposite in fact.  If you pack chunked encoding in, then
that is (likely) garbage.  That's all.