# -*- encoding: binary -*- # :enddoc: # base module for evented models like Rev and EventMachine module Rainbows::EvCore include Rainbows::Const include Rainbows::Response G = Rainbows::G NULL_IO = Unicorn::HttpRequest::NULL_IO HttpParser = Rainbows::HttpParser autoload :CapInput, 'rainbows/ev_core/cap_input' # Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ] ASYNC_CALLBACK = "async.callback".freeze ASYNC_CLOSE = "async.close".freeze def post_init @hp = HttpParser.new @env = @hp.env @buf = @hp.buf @state = :headers # [ :body [ :trailers ] ] :app_call :close end # graceful exit, like SIGQUIT def quit @state = :close end def want_more end def handle_error(e) msg = Rainbows::Error.response(e) and write(msg) ensure quit end # returns whether to enable response chunking for autochunk models def stream_response_headers(status, headers) if headers['Content-Length'] rv = false else rv = !!(headers['Transfer-Encoding'] =~ %r{\Achunked\z}i) rv = false if headers.delete('X-Rainbows-Autochunk') == 'no' end write(response_header(status, headers)) rv end def prepare_request_body # since we don't do streaming input, we have no choice but # to take over 100-continue handling from the Rack application if @env[HTTP_EXPECT] =~ /\A100-continue\z/i write(EXPECT_100_RESPONSE) @env.delete(HTTP_EXPECT) end @input = mkinput @hp.filter_body(@buf2 = "", @buf) @input << @buf2 on_read("") end # TeeInput doesn't map too well to this right now... def on_read(data) case @state when :headers @buf << data @hp.parse or return want_more @state = :body if 0 == @hp.content_length @input = NULL_IO app_call # common case else # nil or len > 0 prepare_request_body end when :body if @hp.body_eof? if @hp.content_length @input.rewind app_call else @state = :trailers on_read(data) end elsif data.size > 0 @hp.filter_body(@buf2, @buf << data) @input << @buf2 on_read("") else want_more end when :trailers if @hp.trailers(@env, @buf << data) @input.rewind app_call else want_more end end rescue => e handle_error(e) end def err_413(msg) write(Rainbows::Const::ERROR_413_RESPONSE) quit # zip back up the stack raise IOError, msg, [] end TmpIO = Unicorn::TmpIO def io_for(bytes) bytes <= CBB ? StringIO.new("") : TmpIO.new end def mkinput max = Rainbows.max_bytes len = @hp.content_length if len if max && (len > max) err_413("Content-Length too big: #{len} > #{max}") end io_for(len) else max ? CapInput.new(io_for(max), self, max) : TmpIO.new end end def self.setup const_set :CBB, Unicorn::TeeInput.client_body_buffer_size end end