remove unnecessary header munging for static file serving
[rainbows.git] / lib / rainbows / max_body.rb
blob23e4fa6a1caf87a92266c8f89fed889d6a4fc2c2
1 # -*- encoding: binary -*-
2 # :enddoc:
3 module Rainbows
5 # middleware used to enforce client_max_body_size for TeeInput users,
6 # there is no need to configure this middleware manually, it will
7 # automatically be configured for you based on the client_max_body_size
8 # setting
9 class MaxBody < Struct.new(:app)
11   # this is meant to be included in Rainbows::TeeInput (and derived
12   # classes) to limit body sizes
13   module Limit
14     Util = Unicorn::Util
16     def initialize(socket, req, parser, buf)
17       self.len = parser.content_length
19       max = Rainbows.max_bytes # never nil, see MaxBody.setup
20       if len && len > max
21         socket.write(Const::ERROR_413_RESPONSE)
22         socket.close
23         raise IOError, "Content-Length too big: #{len} > #{max}", []
24       end
26       self.req = req
27       self.parser = parser
28       self.buf = buf
29       self.socket = socket
30       self.buf2 = ""
31       if buf.size > 0
32         parser.filter_body(buf2, buf) and finalize_input
33         buf2.size > max and raise IOError, "chunked request body too big", []
34       end
35       self.tmp = len && len < Const::MAX_BODY ? StringIO.new("") : Util.tmpio
36       if buf2.size > 0
37         tmp.write(buf2)
38         tmp.seek(0)
39         max -= buf2.size
40       end
41       @max_body = max
42     end
44     def tee(length, dst)
45       rv = super
46       if rv && ((@max_body -= rv.size) < 0)
47         # make HttpParser#keepalive? => false to force an immediate disconnect
48         # after we write
49         parser.reset
50         throw :rainbows_EFBIG
51       end
52       rv
53     end
55   end
57   # this is called after forking, so it won't ever affect the master
58   # if it's reconfigured
59   def self.setup
60     Rainbows.max_bytes or return
61     case G.server.use
62     when :Rev, :EventMachine, :NeverBlock
63       return
64     end
66     TeeInput.class_eval { include Limit }
68     # force ourselves to the outermost middleware layer
69     G.server.app = MaxBody.new(G.server.app)
70   end
72   # Rack response returned when there's an error
73   def err(env)
74     [ 413, [ %w(Content-Length 0), %w(Content-Type text/plain) ], [] ]
75   end
77   # our main Rack middleware endpoint
78   def call(env)
79     catch(:rainbows_EFBIG) { app.call(env) } || err(env)
80   end
82 end # class
83 end # module