upgrade to Kgio 2.x and Unicorn 3.x
[rainbows.git] / lib / rainbows / max_body.rb
blob5e2cb6405d0a6011103c6ba50f25cd2ed271060d
1 # -*- encoding: binary -*-
3 # Middleware used to enforce client_max_body_size for TeeInput users.
5 # There is no need to configure this middleware manually, it will
6 # automatically be configured for you based on the client_max_body_size
7 # setting.
9 # For more fine-grained conrol, you may also define it per-endpoint in
10 # your Rack config.ru like this:
12 #        map "/limit_1M" do
13 #          use Rainbows::MaxBody, 1024*1024
14 #          run MyApp
15 #        end
16 #        map "/limit_10M" do
17 #          use Rainbows::MaxBody, 1024*1024*10
18 #          run MyApp
19 #        end
21 class Rainbows::MaxBody
24   # :call-seq:
25   #   # in config.ru:
26   #   use Rainbows::MaxBody, 4096
27   #   run YourApplication.new
28   def initialize(app, limit = Rainbows.max_bytes)
29     Integer === limit or raise ArgumentError, "limit not an Integer"
30     @app, @limit = app, limit
31   end
33   # :stopdoc:
34   RACK_INPUT = "rack.input".freeze
35   CONTENT_LENGTH = "CONTENT_LENGTH"
36   HTTP_TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING"
38   # our main Rack middleware endpoint
39   def call(env)
40     catch(:rainbows_EFBIG) do
41       len = env[CONTENT_LENGTH]
42       if len && len.to_i > @limit
43         return err
44       elsif /\Achunked\z/i =~ env[HTTP_TRANSFER_ENCODING]
45         limit_input!(env)
46       end
47       @app.call(env)
48     end || err
49   end
51   # this is called after forking, so it won't ever affect the master
52   # if it's reconfigured
53   def self.setup # :nodoc:
54     Rainbows.max_bytes or return
55     case Rainbows::G.server.use
56     when :Rev, :EventMachine, :NeverBlock
57       return
58     end
60     # force ourselves to the outermost middleware layer
61     Rainbows::G.server.app = self.new(Rainbows::G.server.app)
62   end
64   # Rack response returned when there's an error
65   def err # :nodoc:
66     [ 413, { 'Content-Length' => '0', 'Content-Type' => 'text/plain' }, [] ]
67   end
69   def limit_input!(env)
70     input = env[RACK_INPUT]
71     klass = input.respond_to?(:rewind) ? RewindableWrapper : Wrapper
72     env[RACK_INPUT] = klass.new(input, @limit)
73   end
75   # :startdoc:
76 end
77 require 'rainbows/max_body/wrapper'
78 require 'rainbows/max_body/rewindable_wrapper'