Class: LongBody

Inherits:
Object
  • Object
show all
Defined in:
lib/long_body.rb,
lib/long_body/version.rb

Overview

The base middleware class to use in your Rack application, Sinatra, Rails etc.

use LongBody

Note that if you want to use Rack::Chunked (and you most likely do) you have to insert it below LongBody:

use LongBody
use Rack::Chunked

Defined Under Namespace

Modules: HijackHandler, ThinHandler Classes: DeferrableBody, MisconfiguredBody

Constant Summary collapse

STREAMABLE_CODES =
[200, 206]
C_rack_logger =
'rack.logger'.freeze
HIJACK_SUPPORTED =
'rack.hijack?'.freeze
ASYNC_SUPPORT =
'async.callback'.freeze
VERSION =
'0.0.2'

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ LongBody

Returns a new instance of LongBody.



37
38
39
# File 'lib/long_body.rb', line 37

def initialize(app)
  @app = app
end

Instance Method Details

#call(env) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/long_body.rb', line 45

def call(env)
  # Call the upstream first
  s, h, b = @app.call(env)
  
  # Return as-is if the long body skip is requested
  return [s, h,b] if h.delete('X-Rack-Long-Body-Skip')
  
  # If the response has nothing to do with the streaming response, just
  # let it go through as it is not big enough to bother. Also if there is no hijack
  # support there is no sense to bother at all.
  return [s, h, b] unless status_might_have_streamable_body?(s)
  
  # If the body is nil or is not each-able, return the original response - there probably is some other
  # async trickery going on downstream from us, and we should not intercept this response.
  return [s, h, b] if (b.nil? || !b.respond_to?(:each) || (b.respond_to?(:empty?) && b.empty?))
  
  # Ensure either Content-Length or chunking is in place
  ensure_chunked_or_content_length!(h)
  
  # TODO: ensure not already hijacked
  
  if env[ASYNC_SUPPORT]
    env[C_rack_logger].info("Streaming via async.callback (Thin)") if env[C_rack_logger].respond_to?(:info)
    ThinHandler.perform(env, s, h, b)
  elsif env[HIJACK_SUPPORTED]
    env[C_rack_logger].info("Streaming via hijack and IO.select") if env[C_rack_logger].respond_to?(:info)
    HijackHandler.perform(env, s, h, b)
  else
    [s, h, b] # No recourse
  end
end

#ensure_chunked_or_content_length!(header_hash) ⇒ Object



31
32
33
34
35
# File 'lib/long_body.rb', line 31

def ensure_chunked_or_content_length!(header_hash)
  unless header_hash['Transfer-Encoding'] == 'chunked' || header_hash['Content-Length']
    raise MisconfiguredBody
  end
end

#status_might_have_streamable_body?(status_code) ⇒ Boolean

Returns:

  • (Boolean)


27
28
29
# File 'lib/long_body.rb', line 27

def status_might_have_streamable_body?(status_code)
  STREAMABLE_CODES.include?(status_code.to_i)
end