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
| | # -*- 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
# 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'
|