rainbows.git  about / heads / tags
Unicorn for sleepy apps and slow clients
blob a8abbf737168a00d07c8a6da3d6eb0d8216ffee7 2832 bytes (raw)
$ git show v4.0.0:lib/rainbows/max_body.rb	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 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
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
 
# -*- encoding: binary -*-

# Middleware used to enforce client_max_body_size for TeeInput users.
#
# There is no need to configure this middleware manually, it will
# automatically be configured for you based on the client_max_body_size
# setting.
#
# For more fine-grained control, you may also define it per-endpoint in
# your Rack config.ru like this:
#
#        map "/limit_1M" do
#          use Rainbows::MaxBody, 1024*1024
#          run MyApp
#        end
#        map "/limit_10M" do
#          use Rainbows::MaxBody, 1024*1024*10
#          run MyApp
#        end
#
# This is only compatible with concurrency models that expose a streaming
# "rack.input" to the Rack application.  Thus it is NOT compatible with
# any of the following as they fully buffer the request body before
# the application dispatch:
#
# * :Coolio
# * :CoolioThreadPool
# * :CoolioThreadSpawn
# * :Epoll
# * :EventMachine
# * :NeverBlock
# * :Rev
# * :RevThreadPool
# * :RevThreadSpawn
# * :XEpoll
#
# However, the global Rainbows::Configurator#client_max_body_size is compatible
# with all concurrency models \Rainbows! supports.
class Rainbows::MaxBody

  # This is automatically called when used with Rack::Builder#use
  def initialize(app, limit = nil)
    case limit
    when Integer, nil
    else
      raise ArgumentError, "limit not an Integer"
    end
    @app, @limit = app, limit
  end

  # :stopdoc:
  RACK_INPUT = "rack.input".freeze
  CONTENT_LENGTH = "CONTENT_LENGTH"
  HTTP_TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING"

  # our main Rack middleware endpoint
  def call(env)
    @limit = Rainbows.server.client_max_body_size if nil == @limit
    catch(:rainbows_EFBIG) do
      len = env[CONTENT_LENGTH]
      if len && len.to_i > @limit
        return err
      elsif /\Achunked\z/i =~ env[HTTP_TRANSFER_ENCODING]
        limit_input!(env)
      end
      @app.call(env)
    end || err
  end

  # this is called after forking, so it won't ever affect the master
  # if it's reconfigured
  def self.setup # :nodoc:
    Rainbows.server.client_max_body_size or return
    case Rainbows.server.use
    when :Rev, :Coolio, :EventMachine, :NeverBlock,
         :RevThreadSpawn, :RevThreadPool,
         :CoolioThreadSpawn, :CoolioThreadPool,
         :Epoll, :XEpoll
      return
    end

    # force ourselves to the outermost middleware layer
    Rainbows.server.app = self.new(Rainbows.server.app)
  end

  # Rack response returned when there's an error
  def err # :nodoc:
    [ 413, { 'Content-Length' => '0', 'Content-Type' => 'text/plain' }, [] ]
  end

  def limit_input!(env)
    input = env[RACK_INPUT]
    klass = input.respond_to?(:rewind) ? RewindableWrapper : Wrapper
    env[RACK_INPUT] = klass.new(input, @limit)
  end

  # :startdoc:
end
require 'rainbows/max_body/wrapper'
require 'rainbows/max_body/rewindable_wrapper'

git clone https://yhbt.net/rainbows.git