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'
12 Rainbows.config!(self, :client_header_buffer_size)
13 HTTP_VERSION = "HTTP_VERSION"
15 # Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
16 ASYNC_CALLBACK = "async.callback".freeze
17 ASYNC_CLOSE = "async.close".freeze
19 def write_async_response(response)
20 status, headers, body = response
22 # we can't do HTTP keepalive without Content-Length or
23 # "Transfer-Encoding: chunked", and the async.callback stuff
24 # isn't Rack::Lint-compatible, so we have to enforce it here.
25 headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
26 alive = headers.include?(Content_Length) ||
27 !!(%r{\Achunked\z}i =~ headers[Transfer_Encoding])
30 ev_write_response(status, headers, body, alive)
37 @state = :headers # [ :body [ :trailers ] ] :app_call :close
40 # graceful exit, like SIGQUIT
49 msg = Rainbows::Error.response(e) and write(msg)
54 # returns whether to enable response chunking for autochunk models
55 # returns nil if request was hijacked in response stage
56 def stream_response_headers(status, headers, alive, body)
57 headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
58 if headers.include?(Content_Length)
59 write_headers(status, headers, alive, body) or return
63 case @env[HTTP_VERSION]
64 when "HTTP/1.0" # disable HTTP/1.0 keepalive to stream
65 write_headers(status, headers, false, body) or return
71 rv = !!(headers[Transfer_Encoding] =~ %r{\Achunked\z}i)
72 rv = false unless @env["rainbows.autochunk"]
73 write_headers(status, headers, alive, body) or return
78 def prepare_request_body
79 # since we don't do streaming input, we have no choice but
80 # to take over 100-continue handling from the Rack application
81 if @env[HTTP_EXPECT] =~ /\A100-continue\z/i
82 write(EXPECT_100_RESPONSE)
83 @env.delete(HTTP_EXPECT)
86 @hp.filter_body(@buf2 = "", @buf)
91 # TeeInput doesn't map too well to this right now...
95 @hp.add_parse(data) or return want_more
97 if 0 == @hp.content_length
98 app_call NULL_IO # common case
104 if @hp.content_length
112 @hp.filter_body(@buf2, @buf << data)
119 if @hp.add_parse(data)
130 ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"
133 write(ERROR_413_RESPONSE)
135 # zip back up the stack
136 raise IOError, msg, []
139 TmpIO = Unicorn::TmpIO
140 CBB = Unicorn::TeeInput.client_body_buffer_size
143 bytes <= CBB ? StringIO.new("") : TmpIO.new
147 max = Rainbows.server.client_max_body_size
148 len = @hp.content_length
150 if max && (len > max)
151 err_413("Content-Length too big: #{len} > #{max}")
155 max ? CapInput.new(io_for(max), self, max) : TmpIO.new