rack/rack — pull request #753 by vais (2014-11-03 12:21:29+00:00)
Title: Avoid WEBrick handler buffering the entire response in RAM

WEBrick rack handler builds up the entire response in memory as a string before handing it off to WEBrick to send to the client. This is undesirable, especially when serving static files or dynamic responses of any considerable size.

The original behavior is to iterate over rack body with `each`, stuffing strings into WEBrick's `res.body` (which is just a string), then let WEBrick take over and write this built-up response string out to a socket.

The changed behavior is to write strings inside the `each` loop directly to the client socket on each iteration. In order to enable this, I (unfortunately) had to monkey-patch one method in WEBrick's `HTTPResponse` class. Here is the original:
```ruby
module WEBrick
class HTTPResponse
def send_body(socket) # :nodoc:
case @body
when IO then send_body_io(socket)
else send_body_string(socket)
end
end
end
end
```
So, WEBrick expects `@body` to be either an `IO` or a `String`. I extended this method to allow for `@body` to be a `Proc`, and, if `@body` is a `Proc`, to call it and pass it the client socket:
```ruby
module WEBrick
class HTTPResponse
alias :overridden_send_body :send_body
def send_body(socket)
case @body
when Proc then @body.call(socket)
else overridden_send_body(socket)
end
end
end
end
```
I am expecting `content-length` header to be set by the time rack response body is handed off to WEBrick. If it is not set, the handler reverts to the original behavior of buffering the body into WEBrick's `res.body`, so that WEBrick can set `content-length` based on the length of the resulting string. In practice, this does not appear to be an issue (for example, `Rack::File` sets `content-length` automatically).

Please note that I did not write any new tests for this change because the existing tests cover the expected behavior already. Do let me know if you think otherwise though.