Does Rack-servers block until the whole request body has been read?

236 views
Skip to first unread message

Daniel Abrahamsson

unread,
Apr 13, 2011, 3:17:41 AM4/13/11
to rack-...@googlegroups.com
Hi,

I am working on a server that deals with large file uploads (currently built upon Rails, with a Metal taking care of the uploads). According to the rack-specification:
"... handler developers must buffer the input data into some rewindable object if the underlying input stream is not rewindable".

For Passenger and Mongrel, this means a temp file is created for the request (at least for requests as big as those I am dealing with).

Now, my question is, does Rack wait until all data from the client has been read to a tempfile before passing on control to the Rack app (in this case, Rails)? Or does it pass on control directly, and block if the application reads data faster than the client is sending it?

After some initial testing, it appears to me that the former is the case. Can anyone give me advice on how to test this? Perhaps it depends on the web server used? Perhaps on the framework?

Thank you in advance for any advance or clarification on this matter.

//Daniel

Joshua Ballanco

unread,
Apr 25, 2011, 10:26:40 AM4/25/11
to rack-...@googlegroups.com
Hi Daniel,

I faced this same issue when writing ControlTower. When a request gets handed off to the application does depend on the server in question. Mongrel waits for the entire body to be received first (even though it uses a tempfile). Passenger, on the other hand, does not wait (at least, it didn't the last time I tested it). Passenger also does not directly use a tempfile, but rather a custom request body class. If you wanted to test directly, have a look at Net::HTTP using body_stream (there seems to be some info you might use here: http://stackoverflow.com/questions/213613/buffered-multipart-form-posts-in-ruby).

Cheers,

Josh

Eric Wong

unread,
Apr 25, 2011, 9:16:28 PM4/25/11
to rack-...@googlegroups.com
Daniel Abrahamsson <ham...@gmail.com> wrote:
> I am working on a server that deals with large file uploads (currently built
> upon Rails, with a Metal taking care of the uploads). According to the
> rack-specification:
> "... handler developers must buffer the input data into some rewindable
> object if the underlying input stream is not rewindable".

The current Rack spec requires rewindability, but I think the
requirement will go away for Rack 2.0

> For Passenger and Mongrel, this means a temp file is created for the request
> (at least for requests as big as those I am dealing with).
>
> Now, my question is, does Rack wait until all data from the client has been
> read to a tempfile before passing on control to the Rack app (in this case,
> Rails)? Or does it pass on control directly, and block if the application
> reads data faster than the client is sending it?

It depends on the Rack webserver. I can confirm Mongrel waits
until everthing is sent.


Disclaimer: I'm the BFDL for both Unicorn and Rainbows!

Both Unicorn[1] and Rainbows![3] can pass control directly and
block inside the app if the app is faster than the client.

Unicorn reads off the socket as data comes in and "tee"'s off the
input to a temporary file for rewindability. However, you can disable
the temporary file backing store by setting the
"rewindable_input false" option in the Unicorn config file[2].

Setting "rewindable_input false" violates the Rack spec, but can save
you a lot of filesystem I/O if you handle large files.

Unless all your clients are fast (on the same LAN), Unicorn requires
nginx in front of it and nginx reads everything before sending it to
Unicorn), so "rewindable_input false" doesn't do much, however...

Rainbows! is based on Unicorn, but it is designed to handle slow
clients. It offers many concurrency options, and some of them offer
rack.input streaming like Unicorn does. I recommend
ThreadSpawn/ThreadPool for the greatest compatibility if you want input
streaming[4].

> After some initial testing, it appears to me that the former is the case.
> Can anyone give me advice on how to test this? Perhaps it depends on the web
> server used? Perhaps on the framework?

The framework also matters. You'll want to audit all your layers and
make sure they don't call methods like "rewind" or "size" on the
env["rack.input"] object.

I once wrote an app (which I haven't touched in a long time,
but probably still works) to do upload progress with Rack + Rainbows!
based on the streaming rack.input support I have:

http://upr.bogomips.org/


[1] http://unicorn.bogomips.org/
[2] http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-rewindable_input
[3] http://rainbows.rubyforge.org/
[4] http://rainbows.rubyforge.org/Summary.html

--
Eric Wong

Reply all
Reply to author
Forward
0 new messages