Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 364174

Summary: Jetty double-chunks when servlets attempt to send chunked responses.
Product: [RT] Jetty Reporter: Don Werve <don.werve>
Component: serverAssignee: Greg Wilkins <gregw>
Status: RESOLVED WONTFIX QA Contact:
Severity: major    
Priority: P3 CC: jetty-inbox
Version: 8.0.4   
Target Milestone: 7.5.x   
Hardware: PC   
OS: Linux   
Whiteboard:

Description Don Werve CLA 2011-11-18 11:22:11 EST
Build Identifier: 8.0.4.v20111024

Jetty appears to 'double-chunk' requests that are not supplied with a 'Content-Length' header.

In my case, I have an application that handles its own chunking, and sets 'Transfer-Encoding: chunked', as well as appropriately munging the body. These are then fed to Jetty, which then appears to 'chunk' the request again. A telnet to the server shows something like this:

   GET / HTTP/1.1
   Host: localhost

   HTTP/1.1 200 OK
   Last-Modified: Fri, 18 Nov 2011 15:15:56 GMT
   Content-Type: text/html; charset=utf-8
   Cache-Control: max-age=0, private, must-revalidate
   X-UA-Compatible: IE=Edge
   Set-Cookie: _session=BAh7ByIPc2Vzc2lvbl9pZCIlOTRiNDE5YjhlN2VhMWQ1MWQ1NzIzZTViNTZlOTlkMDUiEF9jc3JmX3Rva2VuIjE1MlNwNlUzWDIzdFN4YU9VU2lpWmlMR0tBVTFoN0EyVzVGSjQyaWI1bmVrPQ%3D%3D--c2e6c62fc98ef203ff50d5f72ffd7920dd8d6731; path=/; HttpOnly
   X-Runtime: 1.167000
   Transfer-Encoding: chunked
   Server: Jetty(8.0.4.v20111024)

   65A
   64e
   <!DOCTYPE html>
   <html>
   <!-- HTML Content Here -->
   </html>

   0


   0

A quick look at the Jetty source seems to confirm this behavior; if a Content-Length isn't set, it looks like Jetty will chunk the already-chunked response.

This violates expected behavior, as for HTTP 1.1 requests, the spec indicates that the response headers must contain either a 'Content-Length' or a 'Transfer-Encoding' (summarized in the Wikipedia entry for chunked transfer encoding):

    http://en.wikipedia.org/wiki/Chunked_transfer_encoding

The correct behavior should be to leave the body and headers alone if "Transfer-Encoding: chunked" or 'Content-Length' are set.

Unfortunately, I wasn't able to follow the header comparison code well enough to figure out how to write a patch to fix the problem.

Thanks!


Reproducible: Always

Steps to Reproduce:
1. Send a chunked response from a servlet.
2. Watch as Jetty re-chunks it.
Comment 1 Greg Wilkins CLA 2011-11-20 23:11:26 EST
Don,

I believe that jetty is correctly following the servlet specification by chunking the output stream that is written to a servlet.

Glancing at the complete header code, I can see that we do know if the Transfer Encoding heading has been set by the application or not, so I guess potentially we could assume that the output stream is already chunked if this is set.

However, the are problems with that, as Jetty still needs to know when the response is complete, so we would probably need to parse the output stream looking for the end chunk.   Given that, then it would be simpler for the application to do that parsing and give the non chunked data to the output stream.

Why is your servlet chunking the response anyway? this is the responsibility of the container and not the application.  Even if you are acting as a proxy, your HTTP client should be dechunking.

Do you know how other servlet containers handle this case?
Comment 2 Don Werve CLA 2011-11-23 06:44:37 EST
I've reviewed the Servlet 3.0 spec, and I can't find anything specifying how chunked responses are handled in the servlet, other than this:

    When using HTTP 1.1 chunked encoding (which means that the response has a 
    Transfer-Encoding header), do not set the Content-Length header.

... which seems to indicate that the servlet should send responses that conform to the HTTP 1.1 spec. There's nothing in there that I can see that indicates that the container should handle the chunking in all cases.

While I'm not sure how other containers handle it, it seems sensible to me to allow the developer to figure how how and where they want to chunk responses. In my specific case, I'm using Jetty to host up Rack apps via JRuby, and Rack does its own chunking for streaming content or asynchronous requests.

It's worth noting that Jetty's current behavior is entirely sensible when the application has specified neither Content-Length nor Transfer-Encoding.

Regarding the need to know when the request is complete, this is also covered by the HTTP 1.1 spec -- for chunked encoding: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6

Does this help at all?
Comment 3 Greg Wilkins CLA 2011-11-27 19:50:13 EST
Don,

The issue I have with closing is that I would have to put a lot of trust in the application to get chunking right.

For example consider a Servlet that set Transfer-Encoding:chunked and then sent a body that looked like
 
  B
  Hello World
  0
  HTTP/1.1 200 OK

  Some evil content

By trusting the servlet to do the chunking, it allows it to do a response injection like this, where the evil content would be treated as the response to the next request on the connection.   To prevent this, Jetty would have to parse the stream looking for the 0 chunk and then block further output.

So I'm still thinking that it is far better for your app to do the dechunking yourself.  Note that Jetty might be running larger buffers than JRuby, so the chunking make be more efficient to be done on different boundaries.


But I am no 100% on this... so I might still be convinced to try to allow the app to chunk... but I need a lot of convincing.
Comment 4 Don Werve CLA 2011-12-09 05:27:22 EST
Maybe I'm missing an important point, but I don't think that this is a
problem in practice.

Let's see if I understand things.

Jetty is used to deploy one or more servlets within a JVM. Usually,
these are all controlled by the same person; e.g., deploying
its application on Jetty.

In this case, preventing the injections of malicious payloads is a moot
point, as the company controls its infrastructure, and can do whatever
malicious things they want.

Very easy to hit your own stack with a man-in-the-middle attack. :)

Embedded Jettys work the same way.

The only other use case that I can think of is when Jetty is used by a
hosting provider (or similar) to serve up multiple applications.

This is the riskiest use-case, so let's assume the worst configuration
possible:

1. Multiple servlets with different authors share the same JVM.

2. All of these servlets share the same Connector, and requests are
divided between servlets based on their Host header.

If they don't share the same Connector, then the problem goes away, as
each servlet has a separate connection to its respective clients.

Let's say that Servlets A and B are set up like this, and client C has
connected to both servlets from the same browser. If they share the same
connection, then it should be completely possible for B to inject both
chunked and non-chunked responses into A, whether or not the responses
from A are well-formed.

I think it's fair to say that these use cases cover the spectrum of
Jetty deployments, and in both cases, there's no added security
in mandating that Jetty chunk all requests, regardless of whether or not
the application has handled chunking.

Regarding un-chunking my application's chunked requests before handing
the response off to Jetty, this is totally do-able, but it seems very
clunky to me. Having the application framework chunk data, and then
having to spend CPU cycles to un-chunk it, so that the webserver can
then re-chunk it, feels a lot like the programming equivalent of filling
out tax paperwork.

But maybe I've missed an important case here?
Comment 5 Greg Wilkins CLA 2011-12-22 01:58:02 EST
Don,

sorry but having pondered this, I'm not going to change this.   Your last paragraph convinced me that we don't want to do this.  Chunking is a hop by hop fragmentation of a message.    Jetty may have sufficient buffers to hold the entire response, in which case it wont be sent chunked, but will be sent with a content length instead.

The contract of the API is that the application provides the content and jetty decides how that content should be sent.

cheers
Comment 6 Don Werve CLA 2011-12-29 09:15:23 EST
Fair enough; thanks for taking the time to think about it. I'll go ahead an implement the 'dechunking rechunker'. :)