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

Bug 363757

Summary: HttpGenerator produces 'bad chunk char' IOExceptions when unable to fully flush to socket buffer
Product: [RT] Jetty Reporter: James Mace <jmace.sfdc>
Component: clientAssignee: Greg Wilkins <gregw>
Status: RESOLVED FIXED QA Contact:
Severity: major    
Priority: P3 CC: gregw, jetty-inbox
Version: unspecified   
Target Milestone: 7.5.x   
Hardware: All   
OS: Linux   
Whiteboard:
Attachments:
Description Flags
test case exhibiting the problem
none
Patch file with proposed changes none

Description James Mace CLA 2011-11-14 16:48:14 EST
Build Identifier: 8.0.4.v20111024

When sending large amounts (several hundred KB) of data over a socket connection, it's relatively common for HttpGenerator to be unable to completely flush its buffers of pending data due to the socket buffers becoming saturated.  When chunked transfer encoding is used under these conditions, HttpGenerator can exhibit a number of bugs.  The most common is the omission of one or more chunk headers, though other abnormal behavior is possible.

The incorrectly encoded data stream typically results in a 'bad chunk char' exception on the server.

There appear to be three main problems with the code:

1. The 3-Buffer variant of ChannelEndPoint.flush() will move data between buffers if sufficient space exists.  Doing so may invalidate assumptions made by the chunk encoding logic and the bypass write optimization.  HttpGenerator must take care to not allow buffer coalescing when performing chunk encoding.

2. There are some code paths in HttpGenerator that fail to properly reset the _bypass and _bufferChunked flags when flushes cannot be fully absorbed by the ChannelEndPoint.

3. The _content Buffer will often retain a reference to the Buffer passed to  HttpGenerator.addContent().  Normally, _content is nulled out when it is emptied; however, there exist code paths for which this is not the case.  When this happens, it's possible to place incoming data in an inappropriate location for chunk encoding.

I've attached a reworked version of HttpGenerator which (I believe) addresses these problems.  Additional comments are present in the code.

Additionally, I have attached a test case (testcase.SluggishServer) demonstrating the problem.

Setting severity to 'major' as this interferes with use of chunked transfer encoding for large requests and responses.

Reproducible: Always

Steps to Reproduce:
1. Compile the attached test case (testcase.SluggishServer)
2. Run the test case with the arguments (0, 2000000, 5)
3. If the bug does not manifest, try increasing the request size (second argument to the test case) or altering the READ_DELAY constant in the test case.

The key here is to get the socket buffers into a state in which they are unable to accept all of the pending HttpGenerator data in a single flush.  I'm able to reproduce the problem easily on an Ubuntu 10.04 system; it's somewhat more difficult to trigger on Mac OS.  I've not tried on Windows.
Comment 1 James Mace CLA 2011-11-14 16:49:42 EST
Created attachment 206993 [details]
test case exhibiting the problem
Comment 2 James Mace CLA 2011-11-14 16:56:08 EST
Created attachment 206994 [details]
Patch file with proposed changes
Comment 3 James Mace CLA 2011-11-14 16:58:16 EST
Comment on attachment 206994 [details]
Patch file with proposed changes

Patch is for the version of HttpGenerator.java from Jetty 8.0.4.v20111024
Comment 4 Greg Wilkins CLA 2011-11-15 01:53:59 EST
James, thanks for this analysis and patch.  We are just looking at it in detail at the moment, but will need to change it as we are already in the process of refactoring the HttpGenerator somewhat for 7.6 (which will soon flow through to 8.0)
Comment 5 Greg Wilkins CLA 2011-11-15 16:43:10 EST
James,

Thanks for the thorough analysis, test harness and patch!   

I've created a new unit test based on your sluggish server test (checked in).

For now I have removed the optimisation in the triple buffer flush that copied from the buffer to the header - I think that optimisation violates the contract of that method.  Far better to strive to achieve gather writes.

I also have not yet looked at detail at the rest of you patch... mostly as I wanted to understand the issue myself (as it was my bad in the first place).  I think the 7.6 had partially fixed (or at least changed) the issue, but I did need to add another check for _bypass in prepare buffers.

This fix is now committed to master for jetty-7 and it passes your unit test.

However, I will now review your full patch and make sure that I understand all your changes and verify that we have them covered in master.

thanks again
Comment 6 Greg Wilkins CLA 2011-11-27 21:04:18 EST
Reviewed patch again and I think we have captured all the goodness with our changes.