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
| | # -*- encoding: binary -*-
# :enddoc:
module Rainbows
# 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
class MaxBody < Struct.new(:app)
# this is meant to be included in Rainbows::TeeInput (and derived
# classes) to limit body sizes
module Limit
Util = Unicorn::Util
def initialize(socket, req, parser, buf)
self.len = parser.content_length
max = Rainbows.max_bytes # never nil, see MaxBody.setup
if len && len > max
socket.write(Const::ERROR_413_RESPONSE)
socket.close
raise IOError, "Content-Length too big: #{len} > #{max}", []
end
self.req = req
self.parser = parser
self.buf = buf
self.socket = socket
self.buf2 = ""
if buf.size > 0
parser.filter_body(buf2, buf) and finalize_input
buf2.size > max and raise IOError, "chunked request body too big", []
end
self.tmp = len && len < Const::MAX_BODY ? StringIO.new("") : Util.tmpio
if buf2.size > 0
tmp.write(buf2)
tmp.seek(0)
max -= buf2.size
end
@max_body = max
end
def tee(length, dst)
rv = super
if rv && ((@max_body -= rv.size) < 0)
# make HttpParser#keepalive? => false to force an immediate disconnect
# after we write
parser.reset
throw :rainbows_EFBIG
end
rv
end
end
# this is called after forking, so it won't ever affect the master
# if it's reconfigured
def self.setup
Rainbows.max_bytes or return
case G.server.use
when :Rev, :EventMachine, :NeverBlock
return
end
TeeInput.class_eval { include Limit }
# force ourselves to the outermost middleware layer
G.server.app = MaxBody.new(G.server.app)
end
# Rack response returned when there's an error
def err(env)
[ 413, [ %w(Content-Length 0), %w(Content-Type text/plain) ], [] ]
end
# our main Rack middleware endpoint
def call(env)
catch(:rainbows_EFBIG) { app.call(env) } || err(env)
end
end # class
end # module
|