rev_thread_spawn: make 1.9 TeeInput performance tolerable
[rainbows.git] / lib / rainbows / ev_core.rb
blob017fe5c6f5b2190026397bae6d1cd2ce242d119d
1 # -*- encoding: binary -*-
3 module Rainbows
5   # base module for evented models like Rev and EventMachine
6   module EvCore
7     include Unicorn
8     include Rainbows::Const
9     G = Rainbows::G
11     def self.setup(klass)
12       klass.const_set(:APP, G.server.app)
13     end
15     def post_init
16       @remote_addr = ::TCPSocket === @_io ? @_io.peeraddr.last : LOCALHOST
17       @env = {}
18       @hp = HttpParser.new
19       @state = :headers # [ :body [ :trailers ] ] :app_call :close
20       @buf = ""
21     end
23     # graceful exit, like SIGQUIT
24     def quit
25       @state = :close
26     end
28     def handle_error(e)
29       msg = case e
30       when EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
31         ERROR_500_RESPONSE
32       when HttpParserError # try to tell the client they're bad
33         ERROR_400_RESPONSE
34       else
35         G.server.logger.error "Read error: #{e.inspect}"
36         G.server.logger.error e.backtrace.join("\n")
37         ERROR_500_RESPONSE
38       end
39       write(msg)
40       ensure
41         quit
42     end
44     # TeeInput doesn't map too well to this right now...
45     def on_read(data)
46       case @state
47       when :headers
48         @hp.headers(@env, @buf << data) or return
49         @state = :body
50         len = @hp.content_length
51         if len == 0
52           @input = HttpRequest::NULL_IO
53           app_call # common case
54         else # nil or len > 0
55           # since we don't do streaming input, we have no choice but
56           # to take over 100-continue handling from the Rack application
57           if @env[HTTP_EXPECT] =~ /\A100-continue\z/i
58             write(EXPECT_100_RESPONSE)
59             @env.delete(HTTP_EXPECT)
60           end
61           @input = len && len <= MAX_BODY ? StringIO.new("") : Util.tmpio
62           @hp.filter_body(@buf2 = @buf.dup, @buf)
63           @input << @buf2
64           on_read("")
65         end
66       when :body
67         if @hp.body_eof?
68           @state = :trailers
69           on_read(data)
70         elsif data.size > 0
71           @hp.filter_body(@buf2, @buf << data)
72           @input << @buf2
73           on_read("")
74         end
75       when :trailers
76         if @hp.trailers(@env, @buf << data)
77           app_call
78           @input.close if File === @input
79         end
80       end
81       rescue Object => e
82         handle_error(e)
83     end
85   end
86 end