'Connection reset by peer' when replying before the end of POST data

Lunar lunar at anargeek.net
Tue Feb 28 22:15:46 UTC 2012


Short introduction: I am working on a simple "one-click" file
sharing web application specific focus on protecting users' privacy [1].
Thanks to Rainbows! it can work with big uploads without any request
buffering which is simply marvelous! :)

I am currently trying to implement a limit on the maximum uploaded file
size, not unlike what is already done by Rainbows::MaxBody.
Unfortunately, it looks like answering a request while the client is in
the middle of posting data is not supported that well by Rainbows!

Here is a minimal test case:

--- 8< --- config.ru ---------------------------------------------------

class InterruptTest
  def call(env)
    # HTTP 1.1 standard (and curl) needs this
    /\A100-continue\z/i =~ env['HTTP_EXPECT'] and return [ 100, {}, [] ]

    error = "Request entity too large!\n"
    env['rack.input'].read 1000
    Rainbows.sleep 1
    [ 403, { 'Content-Type' => 'text/plain' }, [ error ] ]

run InterruptTest.new

--- >8 -----------------------------------------------------------------

--- 8< --- rainbows.conf -----------------------------------------------

Rainbows! do
  use :ThreadSpawn
  rewindable_input false
  client_max_body_size nil

--- >8 -----------------------------------------------------------------

I am starting Rainbows! with the following command-line:

    $ rainbows -E none -p 8081 -c rainbows.conf rackup.ru

And then, when asking curl (the `test` file is 7636 bytes long):

    $ curl -v -F "file=@test;type=text/plain" http://localhost:8081
    * About to connect() to localhost port 8081 (#0)
    *   Trying connected
    * Connected to localhost ( port 8081 (#0)
    > POST / HTTP/1.1
    > User-Agent: curl/7.21.0 (i486-pc-linux-gnu) libcurl/7.21.0
    > OpenSSL/0.9.8o zlib/ libidn/1.15 libssh2/1.2.6
    > Host: localhost:8081
    > Accept: */*
    > Content-Length: 7829
    > Expect: 100-continue
    > Content-Type: multipart/form-data;
    > boundary=----------------------------cd790f73307f
    < HTTP/1.1 100 Continue
    < HTTP/1.1 403 Forbidden
    < Date: Tue, 28 Feb 2012 21:20:15 GMT
    < Status: 403 Forbidden
    < Connection: close
    < Content-Type: text/plain
    Request entity too large!
    * Recv failure: Connection reset by peer
    * Closing connection #0
    curl: (56) Recv failure: Connection reset by peer

This "connection reset by peer" is annoying as it will result in Apache
stating "Bad gateway", or Firefox displaying "The connection was reset".

I believe Rainbows::MaxBody having the same issue, but I am not sure.
Also looking at the code, it looks like Rainbows::MaxBody trust the
Content-Length header and do not mesure the actual amount of bytes
received if the header is present. I believe Content-Length can be faked
by malicious clients, so it might be better to use limit_input! for
every connections.

In any cases, I would very much like to solve this issue, but I feel a
little bit lost on where to start.

My assumption was that other webservers were doing it right, otherwise
no one would ever see a 413 Request Entity Too Large message in a
browser. I see that Nginx has options related to SO_LINGER and that
Apache also mention "Lingering Close" when discussing how to close
connections. But I don't really know where to poke on Rainbows! to play
with these kinds of options. Anyway, this issue looks closeloy like the
one described in section 8 of

Thanks again for any help solving this. :)

[1] You can have a look at
    <https://git.codecoop.org/projects/coquelicot>, but there is not
    that much to see yet. The code using Rainbows! is not yet in a
    releasable state, but progress is steady, so expect some news
    later. :)
[2] <https://httpd.apache.org/docs/2.2/misc/perf-tuning.html>


More information about the rainbows-talk mailing list