Project

long_body

0.0
No commit activity in last 3 years
No release in over 3 years
Direct-to-socket streaming of Rack response bodies
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 1.0
~> 2.0.1
>= 2.13.4, ~> 2
~> 3.12
< 3.3, ~> 3.2
~> 1.6

Runtime

~> 1
 Project Readme

long_body

Build Status

Universal Rack middleware for immediate (after-headers) streaming of long Rack response bodies. Normally most Rack handler webservers will buffer your entire response, or buffer your response without telling you for some time before sending it.

This module provides a universal wrapper that will use the features of a specific webserver to send your response with as little buffering as possible (direct to socket). For decent servers that allow rack.hijack it will use that, for Thin it will use deferrables.

Note that within Thin sleeping in a long body might block the EM loop.

Usage examples

Server-sent events combined with Transfer-Encoding: chunked (can be used for chat applications and so forth):

class EventSource
  def each
    20.times do | event_num |
      yield "event: ping\n"
      yield "data: ping_number_#{event_num}"
      yield "\n\n"
      sleep 3
    end
  end
end

# config.ru
use LongBody
use Rack::Chunked
run ->(env) {
  h = {'Content-Type' => 'text/event-stream'}
  [200, h, EventSource.new]
}

Streaming a large file, without buffering:

# config.ru
use LongBody
run ->(env) {
  s = File.size("/tmp/large_file.bin")
  h = {'Content-Length' => s}
  [200, h, File.open(s, 'rb')]
}

Selective bypass

Most requests in your application (assets, HTML pages and so on) probably do not need this and are better to be sent as-is. Also, such processing will likely bypass all HTTP caching you set up. long_body is "always on" by default. To bypass it, send X-Rack-Long-Body-Skip header with any truthy contents in your response headers (better use a string value so that Rack::Lint does not complain).

Compatibility

This gem is tested on Ruby 2.2, and should run acceptably well on Ruby 2.+. If you are using Thin it is recommended to use Ruby 2.+ because of the fiber stack size limitation. If you are using Puma, Rainbows or other threaded server running this gem on 1.9.3+ should be possible as well.

Webserver Version tested Compatibility
Puma 2.13.4 Yes (use versions >= 2.13.4 due to a bug)
Passenger 5.0.15 Yes (only on Apache where PassengerBufferResponse is available and set to "off")
Thin 1.6.3 Yes
Unicorn 4.9.0 Yes (but this is not a recommended use case)
Rainbows 4.6.2 Yes
WEBrick stdlib 2.2.1 No (uses IO.pipe for hijack)

Implicit buffering

Some servers do not buffer by default, but you may have differing results - and this behavior is not enforced by the Rack specification, so it can break at any time:

Webserver Version tested Applies buffering
Puma 2.13.4 No
Passenger 5.0.15 Yes
Thin 1.6.3 Yes
Unicorn 4.9.0 No
Rainbows 4.6.2 No
WEBrick stdlib 2.2.1 Yes

Contributing to long_body

  • Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
  • Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
  • Fork the project.
  • Start a feature/bugfix branch.
  • Commit and push until you are happy with your contribution.
  • Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.

Copyright

Copyright (c) 2015 Julik Tarkhanov. See LICENSE.txt for further details.