Providing a web service for a bunch of browsers is a relatively straightforward affair. It’s really only once you jump out of the back-end and into the front-end side of things where issues like browser incompatibilities start to become a problem. Thankfully, I feel like I’m in the position where I think I’ve got my head wrapped around what’s involved in providing solutions to these problems.

And then mobile phones came along.

The service I’m working on at the moment is consumed by a bunch of clients, including but not limited to web browsers, WAP browsers, the Flash player, iPhones, J2ME devices. It’s the last one that’s causing headaches at the moment.

You see, despite the fact that HTTP/1.1 is about 9 years old, not all web servers support the features that were introduced. The particular one I’m talking about is chunked tranfer encoding, but I’m sure there are many others.

To give you a general idea, the HTTP implementations on many mobile handsets will decide to use a chunked transfer encoding if the payload of a PUT/POST request is over an arbitrary threshold. This causes issues with servers like Nginx, Lighttpd, Mongrel, and Thin, since most of those assume that an incoming HTTP request with a payload will also include a Content-Length header.

Well guess what? As of 9 years ago, that hasn’t been the case.

Take a look at this request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /search.json HTTP/1.1
User-Agent: curl/7.16.4 (i486-pc-linux-gnu) libcurl/7.16.4 OpenSSL/0.9.8e zlib/1.2.3.3 libidn/1.0
Host: ooboontoo
Accept: */*
Transfer-Encoding: chunked
Content-Type: multipart/form-data; boundary=----------------------------ab5090ac7869


92
------------------------------ab5090ac7869
Content-Disposition: form-data; name="query"

zoooom
------------------------------ab5090ac7869--


0

Notice anything? If not, try and find a Content-Length header. Pre-HTTP/1.1 you’d expect to get an HTTP 411 error (length required), but after HTTP/1.1 it’s pretty clear what the HTTP/1.1 applications are obliged to do:

All HTTP/1.1 applications that receive entities MUST accept the “chunked” transfer-coding (section 3.6), thus allowing this mechanism to be used for messages when the message length cannot be determined in advance.

That’s why I find it so surprising that hacks are involved in allowing mobile clients to POST/PUT to what I’d traditionally thought of as HTTP/1.1 compliant web servers.

But anyway, you want to see the solution right?

Well, our initial solution involved Gerald writing a little web server in Python which went by the name of “Dechunker”. You can imagine what it did, but we quickly found that while it was the simplest way to avoid the problem, it also meant that over time we would end up needing to implement the functionality that was already available in most other web servers. Servers like Apache2 and Lighttpd have become incredibly hardy over the years, and we’re not going to achieve that overnight.

So I then took another look at Apache2, knowing that some modules do support chunked transfer encoding while others don’t. What I discovered was that Apache’s mod_proxy module could be used in front of anything that doesn’t support chunked encoding, since it can be configured to “dechunk” requests before passing them to a backend.

It looks a little something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ProxyRequests Off

<Proxy http://localhost:81>
  Order deny,allow
  Allow from all
</Proxy>

<VirtualHost *:80>
  SetEnv proxy-sendcl 1
  ProxyPass / http://localhost:81/
  ProxyPassReverse / http://localhost:81/
  ProxyPreserveHost On
  ProxyVia Full

  <Directory proxy:*>
    Order deny,allow
    Allow from all
  </Directory>

</VirtualHost>

Listen 81

<VirtualHost *:81>
  ServerName ooboontoo
  DocumentRoot /path/to/my/rails/root/public
  RailsEnv development
</VirtualHost>

As you can see I’ve got Apache2 listening on port 80, which uses the “proxy-sendcl” environment variable available in mod_proxy to repack the HTTP body and add a Content-Length header to the request. This request is then passed back to a virtual host running on port 81, which is configured to use Phusion Passenger.

Turns out it’s pretty simple, and from what I’ve seen there haven’t been any negative performance impacts by proxying all requests. It’s not a permanent solution, and as soon as Phusion Passenger fixes the chunked encoding bug, I’ll drop mod_proxy from our configuration.

Hope that helps someone!

10 Responses to “"Transfer-Encoding: chunked", or, Chunky HTTP!”

  1. pandora Says:

    ya, i’ve dealt with this a few times before. and again now. my problem is that there’s a nice compliant apache2 server sitting there waiting to properly handle my chunks, but in front of it is a big fat stupid squid proxy. squid claims to support chunked encoding, but very promptly eats sht when I post through it. *sigh solution? none. I don’t have access to the server, I’m just the j2me developer.

    there’s a lot of talk about why this happens and some possible issues with the sun wtk emulator sending poorly formatted chunks, but it still happens on a real phone, so thats kind of moot.

    oh ya, real solution… use http 1.0. then it works like a charm because chunked isn’t supported! tada! except that you can’t specify the http version on an actual phone.

    /frustrated

  2. Nathan de Vries Says:

    @pandora: One solution (albeit dirty) would be to do the same thing I’m doing…use mod_proxy to repackage the requests as regular non-chunked POST requests. To do this, your setup would be as follows:

    Apache (mod_proxy) -> Squid -> Apache

    Makes me cringe, but at least it would work with minimal fuss.

  3. David Turnbull Says:

    Don’t forget:

    RequestHeader edit Transfer-Encoding Chunked chunked early

    For iPhone support :)

  4. Nathan de Vries Says:

    @Dave: Yep, I’m actually writing that up as a separate article right now :).

  5. atleta Says:

    Great piece of information. Too bad that it took quite a lot of googling to get here, because without figuring out that mod_proxy would do the dechunking, I wasn’t able to find neither your page nor any outher useful source. I could only find fellow j2me developers complaining about this very same problem on public forums without any usable answer.

    I hope my comment will help google to find this page when someone is looking for a solution how to dechunk POST requests directed to CGI scripts by j2me MIDlets ;)).

  6. Nathan de Vries Says:

    @Atleta: I’ll add a new article linking to this one, with a hopefully less cryptic title :).

  7. Kristoffer Lawson Says:

    Thanks for this idea, it certainly helped us. There’s a slight epilogue to our story, based on a problem with using the 1.0.3 MIDP environment (nothing later available on the Mac :I ). So if someone is having funny IOExceptions, this might be useful:

    http://www.screditcrunch.com/?p=86

  8. Cory Says:

    Thanks very much for this post! Was extremely helpful.

  9. Gardner Says:

    Hi there. Is possible that this problem cause php to receive an incomplete image? Thanks.

  10. Nathan de Vries Says:

    Hi Gardner,

    Usually if your web server (or application server running within your web server) does not support “chunked” transfers, it will respond with a “411 Length Required” header. I’ve not seen anything accept partial POST requests, but I wouldn’t be surprised. Your best bet would be to use something like Wireshark to follow the HTTP connections being made to and from your client and server.

Leave a Reply