1 # -*- encoding: binary -*-
3 # base module for evented models like Rev and EventMachine
4 module Rainbows::EvCore
5 include Rainbows::Const
6 include Rainbows::Response
7 NULL_IO = Unicorn::HttpRequest::NULL_IO
8 HttpParser = Rainbows::HttpParser
9 autoload :CapInput, 'rainbows/ev_core/cap_input'
11 # Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
12 ASYNC_CALLBACK = "async.callback".freeze
14 def write_async_response(response)
15 status, headers, body = response
17 # we can't do HTTP keepalive without Content-Length or
18 # "Transfer-Encoding: chunked", and the async.callback stuff
19 # isn't Rack::Lint-compatible, so we have to enforce it here.
20 headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
21 alive = headers.include?(Content_Length) ||
22 !!(%r{\Achunked\z}i =~ headers[Transfer_Encoding])
24 write_response(status, headers, body, alive)
27 ASYNC_CLOSE = "async.close".freeze
33 @state = :headers # [ :body [ :trailers ] ] :app_call :close
36 # graceful exit, like SIGQUIT
45 msg = Rainbows::Error.response(e) and write(msg)
50 # returns whether to enable response chunking for autochunk models
51 def stream_response_headers(status, headers, alive)
52 headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
53 if headers.include?(Content_Length)
56 rv = !!(headers[Transfer_Encoding] =~ %r{\Achunked\z}i)
57 rv = false if headers.delete('X-Rainbows-Autochunk') == 'no'
59 write_headers(status, headers, alive)
63 def prepare_request_body
64 # since we don't do streaming input, we have no choice but
65 # to take over 100-continue handling from the Rack application
66 if @env[HTTP_EXPECT] =~ /\A100-continue\z/i
67 write(EXPECT_100_RESPONSE)
68 @env.delete(HTTP_EXPECT)
71 @hp.filter_body(@buf2 = "", @buf)
76 # TeeInput doesn't map too well to this right now...
81 @hp.parse or return want_more
83 if 0 == @hp.content_length
85 app_call # common case
99 @hp.filter_body(@buf2, @buf << data)
106 if @hp.trailers(@env, @buf << data)
118 write(Rainbows::Const::ERROR_413_RESPONSE)
120 # zip back up the stack
121 raise IOError, msg, []
124 TmpIO = Unicorn::TmpIO
127 bytes <= CBB ? StringIO.new("") : TmpIO.new
131 max = Rainbows.max_bytes
132 len = @hp.content_length
134 if max && (len > max)
135 err_413("Content-Length too big: #{len} > #{max}")
139 max ? CapInput.new(io_for(max), self, max) : TmpIO.new
144 const_set :CBB, Unicorn::TeeInput.client_body_buffer_size