cleanup and refactor error handling
[rainbows.git] / lib / rainbows / ev_core.rb
blobf61b33f8f13b175d55a570244666a531996599c7
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 = Error.response(e) and write(msg)
30       ensure
31         quit
32     end
34     # TeeInput doesn't map too well to this right now...
35     def on_read(data)
36       case @state
37       when :headers
38         @hp.headers(@env, @buf << data) or return
39         @state = :body
40         len = @hp.content_length
41         if len == 0
42           @input = HttpRequest::NULL_IO
43           app_call # common case
44         else # nil or len > 0
45           # since we don't do streaming input, we have no choice but
46           # to take over 100-continue handling from the Rack application
47           if @env[HTTP_EXPECT] =~ /\A100-continue\z/i
48             write(EXPECT_100_RESPONSE)
49             @env.delete(HTTP_EXPECT)
50           end
51           @input = len && len <= MAX_BODY ? StringIO.new("") : Util.tmpio
52           @hp.filter_body(@buf2 = @buf.dup, @buf)
53           @input << @buf2
54           on_read("")
55         end
56       when :body
57         if @hp.body_eof?
58           @state = :trailers
59           on_read(data)
60         elsif data.size > 0
61           @hp.filter_body(@buf2, @buf << data)
62           @input << @buf2
63           on_read("")
64         end
65       when :trailers
66         if @hp.trailers(@env, @buf << data)
67           @input.rewind
68           app_call
69           @input.close if File === @input
70         end
71       end
72       rescue => e
73         handle_error(e)
74     end
76   end
77 end