rainbows.git  about / heads / tags
Unicorn for sleepy apps and slow clients
blob 2679b5a20d8e518e0e59b482ce96149561c65c25 2194 bytes (raw)
$ git show v0.5.0:lib/rainbows/ev_core.rb	# shows this blob on the CLI

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
 
# -*- encoding: binary -*-

module Rainbows

  # base module for evented models like Rev and EventMachine
  module EvCore
    include Unicorn
    include Rainbows::Const
    G = Rainbows::G

    def post_init
      @remote_addr = ::TCPSocket === @_io ? @_io.peeraddr.last : LOCALHOST
      @env = {}
      @hp = HttpParser.new
      @state = :headers # [ :body [ :trailers ] ] :app_call :close
      @buf = ""
    end

    # graceful exit, like SIGQUIT
    def quit
      @state = :close
    end

    def handle_error(e)
      msg = case e
      when EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
        ERROR_500_RESPONSE
      when HttpParserError # try to tell the client they're bad
        ERROR_400_RESPONSE
      else
        G.logger.error "Read error: #{e.inspect}"
        G.logger.error e.backtrace.join("\n")
        ERROR_500_RESPONSE
      end
      write(msg)
      ensure
        quit
    end

    # TeeInput doesn't map too well to this right now...
    def on_read(data)
      case @state
      when :headers
        @hp.headers(@env, @buf << data) or return
        @state = :body
        len = @hp.content_length
        if len == 0
          @input = HttpRequest::NULL_IO
          app_call # common case
        else # nil or len > 0
          # 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 = len && len <= MAX_BODY ? StringIO.new("") : Util.tmpio
          @hp.filter_body(@buf2 = @buf.dup, @buf)
          @input << @buf2
          on_read("")
        end
      when :body
        if @hp.body_eof?
          @state = :trailers
          on_read(data)
        elsif data.size > 0
          @hp.filter_body(@buf2, @buf << data)
          @input << @buf2
          on_read("")
        end
      when :trailers
        if @hp.trailers(@env, @buf << data)
          app_call
          @input.close if File === @input
        end
      end
      rescue Object => e
        handle_error(e)
    end

  end
end

git clone https://yhbt.net/rainbows.git