rainbows 0.97.0 → 1.0.0pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.manifest +14 -2
  2. data/ChangeLog +87 -118
  3. data/GIT-VERSION-FILE +1 -1
  4. data/GIT-VERSION-GEN +1 -1
  5. data/GNUmakefile +1 -1
  6. data/README +1 -1
  7. data/bin/rainbows +15 -20
  8. data/lib/rainbows/actor_spawn.rb +20 -22
  9. data/lib/rainbows/app_pool.rb +89 -93
  10. data/lib/rainbows/base.rb +4 -61
  11. data/lib/rainbows/client.rb +9 -0
  12. data/lib/rainbows/configurator.rb +37 -39
  13. data/lib/rainbows/const.rb +18 -18
  14. data/lib/rainbows/dev_fd_response.rb +2 -1
  15. data/lib/rainbows/error.rb +39 -37
  16. data/lib/rainbows/ev_core.rb +103 -109
  17. data/lib/rainbows/event_machine.rb +188 -196
  18. data/lib/rainbows/fiber/base.rb +69 -88
  19. data/lib/rainbows/fiber/io/compat.rb +13 -0
  20. data/lib/rainbows/fiber/io/methods.rb +49 -0
  21. data/lib/rainbows/fiber/io/pipe.rb +7 -0
  22. data/lib/rainbows/fiber/io/socket.rb +7 -0
  23. data/lib/rainbows/fiber/io.rb +125 -84
  24. data/lib/rainbows/fiber/rev/heartbeat.rb +8 -0
  25. data/lib/rainbows/fiber/rev/kato.rb +22 -0
  26. data/lib/rainbows/fiber/rev/methods.rb +55 -0
  27. data/lib/rainbows/fiber/rev/server.rb +32 -0
  28. data/lib/rainbows/fiber/rev/sleeper.rb +15 -0
  29. data/lib/rainbows/fiber/rev.rb +6 -164
  30. data/lib/rainbows/fiber.rb +23 -5
  31. data/lib/rainbows/fiber_pool.rb +31 -37
  32. data/lib/rainbows/fiber_spawn.rb +21 -28
  33. data/lib/rainbows/http_server.rb +80 -80
  34. data/lib/rainbows/max_body.rb +26 -28
  35. data/lib/rainbows/process_client.rb +61 -0
  36. data/lib/rainbows/queue_pool.rb +19 -22
  37. data/lib/rainbows/read_timeout.rb +28 -0
  38. data/lib/rainbows/rev/client.rb +10 -10
  39. data/lib/rainbows/rev/core.rb +2 -3
  40. data/lib/rainbows/rev/thread.rb +1 -1
  41. data/lib/rainbows/rev_fiber_spawn.rb +21 -24
  42. data/lib/rainbows/revactor.rb +18 -15
  43. data/lib/rainbows/thread_pool.rb +2 -4
  44. data/lib/rainbows/thread_spawn.rb +1 -2
  45. data/lib/rainbows/writer_thread_pool.rb +14 -4
  46. data/lib/rainbows/writer_thread_spawn.rb +14 -4
  47. data/lib/rainbows.rb +7 -15
  48. data/local.mk.sample +3 -11
  49. data/rainbows.gemspec +2 -4
  50. data/t/kgio-pipe-response.ru +10 -0
  51. data/t/t0035-kgio-pipe-response.sh +70 -0
  52. data/t/test_isolate.rb +2 -1
  53. metadata +46 -30
  54. data/lib/rainbows/acceptor.rb +0 -26
  55. data/lib/rainbows/byte_slice.rb +0 -17
@@ -4,168 +4,10 @@ require 'rev'
4
4
  require 'rainbows/fiber'
5
5
  require 'rainbows/fiber/io'
6
6
 
7
- module Rainbows::Fiber
8
- module Rev
9
- G = Rainbows::G
10
-
11
- # keep-alive timeout class
12
- class Kato < ::Rev::TimerWatcher
13
- def initialize
14
- @watch = []
15
- super(1, true)
16
- end
17
-
18
- def <<(fiber)
19
- @watch << fiber
20
- enable unless enabled?
21
- end
22
-
23
- def on_timer
24
- @watch.uniq!
25
- while f = @watch.shift
26
- f.resume if f.alive?
27
- end
28
- disable
29
- end
30
- end
31
-
32
- class Heartbeat < ::Rev::TimerWatcher
33
- def on_timer
34
- exit if (! G.tick && G.cur <= 0)
35
- end
36
- end
37
-
38
- class Sleeper < ::Rev::TimerWatcher
39
-
40
- def initialize(seconds)
41
- @f = ::Fiber.current
42
- super(seconds, false)
43
- attach(::Rev::Loop.default)
44
- ::Fiber.yield
45
- end
46
-
47
- def on_timer
48
- @f.resume
49
- end
50
- end
51
-
52
- class Server < ::Rev::IOWatcher
53
- include Unicorn
54
- include Rainbows
55
- include Rainbows::Const
56
- include Rainbows::Response
57
- include Rainbows::Acceptor
58
- FIO = Rainbows::Fiber::IO
59
-
60
- def to_io
61
- @io
62
- end
63
-
64
- def initialize(io)
65
- @io = io
66
- super(self, :r)
67
- end
68
-
69
- def close
70
- detach if attached?
71
- @io.close
72
- end
73
-
74
- def on_readable
75
- return if G.cur >= MAX
76
- c = accept(@io) and ::Fiber.new { process(c) }.resume
77
- end
78
-
79
- def process(io)
80
- G.cur += 1
81
- client = FIO.new(io, ::Fiber.current)
82
- buf = client.read_timeout or return
83
- hp = HttpParser.new
84
- env = {}
85
- remote_addr = Rainbows.addr(io)
86
-
87
- begin # loop
88
- buf << (client.read_timeout or return) until hp.headers(env, buf)
89
-
90
- env[CLIENT_IO] = client
91
- env[RACK_INPUT] = 0 == hp.content_length ?
92
- HttpRequest::NULL_IO : TeeInput.new(client, env, hp, buf)
93
- env[REMOTE_ADDR] = remote_addr
94
- status, headers, body = APP.call(env.update(RACK_DEFAULTS))
95
-
96
- if 100 == status.to_i
97
- client.write(EXPECT_100_RESPONSE)
98
- env.delete(HTTP_EXPECT)
99
- status, headers, body = APP.call(env)
100
- end
101
-
102
- if hp.headers?
103
- headers = HH.new(headers)
104
- range = make_range!(env, status, headers) and status = range.shift
105
- headers[CONNECTION] = if hp.keepalive? && G.alive
106
- KEEP_ALIVE
107
- else
108
- env = false
109
- CLOSE
110
- end
111
- client.write(response_header(status, headers))
112
- end
113
- write_body(client, body, range)
114
- end while env && env.clear && hp.reset.nil?
115
- rescue => e
116
- Error.write(io, e)
117
- ensure
118
- G.cur -= 1
119
- client.close
120
- end
121
- end
122
- end
123
-
124
- class IO # see rainbows/fiber/io for original definition
125
-
126
- class Watcher < ::Rev::IOWatcher
127
- def initialize(fio, flag)
128
- @fiber = fio.f
129
- super(fio, flag)
130
- attach(::Rev::Loop.default)
131
- end
132
-
133
- def on_readable
134
- @fiber.resume
135
- end
136
-
137
- alias on_writable on_readable
138
- end
139
-
140
- undef_method :wait_readable
141
- undef_method :wait_writable
142
- undef_method :close
143
-
144
- def initialize(*args)
145
- super
146
- @r = @w = false
147
- end
148
-
149
- def close
150
- @w.detach if @w
151
- @r.detach if @r
152
- @r = @w = false
153
- to_io.close unless to_io.closed?
154
- end
155
-
156
- def wait_writable
157
- @w ||= Watcher.new(self, :w)
158
- @w.enable unless @w.enabled?
159
- ::Fiber.yield
160
- @w.disable
161
- end
162
-
163
- def wait_readable
164
- @r ||= Watcher.new(self, :r)
165
- @r.enable unless @r.enabled?
166
- KATO << f
167
- ::Fiber.yield
168
- @r.disable
169
- end
170
- end
7
+ module Rainbows::Fiber::Rev
8
+ autoload :Heartbeat, 'rainbows/fiber/rev/heartbeat'
9
+ autoload :Kato, 'rainbows/fiber/rev/kato'
10
+ autoload :Server, 'rainbows/fiber/rev/server'
11
+ autoload :Sleeper, 'rainbows/fiber/rev/sleeper'
171
12
  end
13
+ require 'rainbows/fiber/rev/methods'
@@ -6,11 +6,29 @@ rescue LoadError
6
6
  defined?(NeverBlock) or raise
7
7
  end
8
8
 
9
- module Rainbows
9
+ # core module for all things that use Fibers in Rainbows!
10
+ module Rainbows::Fiber
10
11
 
11
- # core module for all things that use Fibers in Rainbows!
12
- module Fiber
13
- autoload :Base, 'rainbows/fiber/base'
14
- autoload :Queue, 'rainbows/fiber/queue'
12
+ # blocked readers (key: fileno, value: Rainbows::Fiber::IO object)
13
+ RD = []
14
+
15
+ # blocked writers (key: fileno, value: Rainbows::Fiber::IO object)
16
+ WR = []
17
+
18
+ # sleeping fibers go here (key: Fiber object, value: wakeup time)
19
+ ZZ = {}
20
+
21
+ # puts the current Fiber into uninterruptible sleep for at least
22
+ # +seconds+. Unlike Kernel#sleep, this it is not possible to sleep
23
+ # indefinitely to be woken up (nobody wants that in a web server,
24
+ # right?). Calling this directly is deprecated, use
25
+ # Rainbows.sleep(seconds) instead.
26
+ def self.sleep(seconds)
27
+ ZZ[::Fiber.current] = Time.now + seconds
28
+ ::Fiber.yield
15
29
  end
30
+
31
+ autoload :Base, 'rainbows/fiber/base'
32
+ autoload :Queue, 'rainbows/fiber/queue'
33
+ autoload :IO, 'rainbows/fiber/io'
16
34
  end
@@ -1,45 +1,39 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'rainbows/fiber'
3
3
 
4
- module Rainbows
4
+ # A Fiber-based concurrency model for Ruby 1.9. This uses a pool of
5
+ # Fibers to handle client IO to run the application and the root Fiber
6
+ # for scheduling and connection acceptance. The pool size is equal to
7
+ # the number of +worker_connections+. Compared to the ThreadPool
8
+ # model, Fibers are very cheap in terms of memory usage so you can
9
+ # have more active connections. This model supports a streaming
10
+ # "rack.input" with lightweight concurrency. Applications are
11
+ # strongly advised to wrap all slow IO objects (sockets, pipes) using
12
+ # the Rainbows::Fiber::IO class whenever possible.
13
+ module Rainbows::FiberPool
14
+ include Rainbows::Fiber::Base
5
15
 
6
- # A Fiber-based concurrency model for Ruby 1.9. This uses a pool of
7
- # Fibers to handle client IO to run the application and the root Fiber
8
- # for scheduling and connection acceptance. The pool size is equal to
9
- # the number of +worker_connections+. Compared to the ThreadPool
10
- # model, Fibers are very cheap in terms of memory usage so you can
11
- # have more active connections. This model supports a streaming
12
- # "rack.input" with lightweight concurrency. Applications are
13
- # strongly advised to wrap all slow IO objects (sockets, pipes) using
14
- # the Rainbows::Fiber::IO class whenever possible.
16
+ def worker_loop(worker) # :nodoc:
17
+ init_worker_process(worker)
18
+ pool = []
19
+ worker_connections.times {
20
+ Fiber.new {
21
+ process(Fiber.yield) while pool << Fiber.current
22
+ }.resume # resume to hit ::Fiber.yield so it waits on a client
23
+ }
24
+ Rainbows::Fiber::Base.setup(self.class, app)
15
25
 
16
- module FiberPool
17
- include Fiber::Base
18
- include Rainbows::Acceptor
19
-
20
- def worker_loop(worker) # :nodoc:
21
- init_worker_process(worker)
22
- pool = []
23
- worker_connections.times {
24
- ::Fiber.new {
25
- process_client(::Fiber.yield) while pool << ::Fiber.current
26
- }.resume # resume to hit ::Fiber.yield so it waits on a client
27
- }
28
- Fiber::Base.setup(self.class, app)
29
-
30
- begin
31
- schedule do |l|
32
- fib = pool.shift or break # let another worker process take it
33
- if io = accept(l)
34
- fib.resume(Fiber::IO.new(io, fib))
35
- else
36
- pool << fib
37
- end
26
+ begin
27
+ schedule do |l|
28
+ fib = pool.shift or break # let another worker process take it
29
+ if io = l.kgio_tryaccept
30
+ fib.resume(io)
31
+ else
32
+ pool << fib
38
33
  end
39
- rescue => e
40
- Error.listen_loop(e)
41
- end while G.alive || G.cur > 0
42
- end
43
-
34
+ end
35
+ rescue => e
36
+ Rainbows::Error.listen_loop(e)
37
+ end while G.alive || G.cur > 0
44
38
  end
45
39
  end
@@ -1,35 +1,28 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'rainbows/fiber'
3
3
 
4
- module Rainbows
4
+ # Simple Fiber-based concurrency model for 1.9. This spawns a new
5
+ # Fiber for every incoming client connection and the root Fiber for
6
+ # scheduling and connection acceptance. This exports a streaming
7
+ # "rack.input" with lightweight concurrency. Applications are
8
+ # strongly advised to wrap all slow IO objects (sockets, pipes) using
9
+ # the Rainbows::Fiber::IO class whenever possible.
10
+ module Rainbows::FiberSpawn
11
+ include Rainbows::Fiber::Base
5
12
 
6
- # Simple Fiber-based concurrency model for 1.9. This spawns a new
7
- # Fiber for every incoming client connection and the root Fiber for
8
- # scheduling and connection acceptance. This exports a streaming
9
- # "rack.input" with lightweight concurrency. Applications are
10
- # strongly advised to wrap all slow IO objects (sockets, pipes) using
11
- # the Rainbows::Fiber::IO class whenever possible.
12
-
13
- module FiberSpawn
14
- include Fiber::Base
15
- include Rainbows::Acceptor
16
-
17
- def worker_loop(worker) # :nodoc:
18
- init_worker_process(worker)
19
- Fiber::Base.setup(self.class, app)
20
- limit = worker_connections
21
- fio = Rainbows::Fiber::IO
22
-
23
- begin
24
- schedule do |l|
25
- break if G.cur >= limit
26
- io = accept(l) or next
27
- ::Fiber.new { process_client(fio.new(io, ::Fiber.current)) }.resume
28
- end
29
- rescue => e
30
- Error.listen_loop(e)
31
- end while G.alive || G.cur > 0
32
- end
13
+ def worker_loop(worker) # :nodoc:
14
+ init_worker_process(worker)
15
+ Rainbows::Fiber::Base.setup(self.class, app)
16
+ limit = worker_connections
33
17
 
18
+ begin
19
+ schedule do |l|
20
+ break if G.cur >= limit
21
+ io = l.kgio_tryaccept or next
22
+ Fiber.new { process(io) }.resume
23
+ end
24
+ rescue => e
25
+ Rainbows::Error.listen_loop(e)
26
+ end while G.alive || G.cur > 0
34
27
  end
35
28
  end
@@ -1,97 +1,97 @@
1
1
  # -*- encoding: binary -*-
2
2
  # :enddoc:
3
- module Rainbows
4
3
 
5
- class HttpServer < ::Unicorn::HttpServer
6
- include Rainbows
4
+ class Rainbows::HttpServer < Unicorn::HttpServer
5
+ G = Rainbows::G
7
6
 
8
- class << self
9
- def setup(block)
10
- G.server.instance_eval(&block)
11
- end
12
- end
13
-
14
- def initialize(app, options)
15
- G.server = self
16
- rv = super(app, options)
17
- defined?(@use) or use(:Base)
18
- @worker_connections ||= MODEL_WORKER_CONNECTIONS[@use]
19
- end
7
+ def self.setup(block)
8
+ G.server.instance_eval(&block)
9
+ end
20
10
 
21
- def reopen_worker_logs(worker_nr)
22
- logger.info "worker=#{worker_nr} reopening logs..."
23
- Unicorn::Util.reopen_logs
24
- logger.info "worker=#{worker_nr} done reopening logs"
25
- rescue
26
- G.quit! # let the master reopen and refork us
27
- end
11
+ def initialize(app, options)
12
+ G.server = self
13
+ @logger = Unicorn::Configurator::DEFAULTS[:logger]
14
+ rv = super(app, options)
15
+ defined?(@use) or use(:Base)
16
+ @worker_connections ||= Rainbows::MODEL_WORKER_CONNECTIONS[@use]
17
+ end
28
18
 
29
- #:stopdoc:
30
- #
31
- # Add one second to the timeout since our fchmod heartbeat is less
32
- # precise (and must be more conservative) than Unicorn does. We
33
- # handle many clients per process and can't chmod on every
34
- # connection we accept without wasting cycles. That added to the
35
- # fact that we let clients keep idle connections open for long
36
- # periods of time means we have to chmod at a fixed interval.
37
- def timeout=(nr)
38
- super(nr + 1)
39
- end
40
- #:startdoc:
19
+ def reopen_worker_logs(worker_nr)
20
+ logger.info "worker=#{worker_nr} reopening logs..."
21
+ Unicorn::Util.reopen_logs
22
+ logger.info "worker=#{worker_nr} done reopening logs"
23
+ rescue
24
+ G.quit! # let the master reopen and refork us
25
+ end
41
26
 
42
- def use(*args)
43
- model = args.shift or return @use
44
- mod = begin
45
- Rainbows.const_get(model)
46
- rescue NameError
47
- raise ArgumentError, "concurrency model #{model.inspect} not supported"
48
- end
27
+ #:stopdoc:
28
+ #
29
+ # Add one second to the timeout since our fchmod heartbeat is less
30
+ # precise (and must be more conservative) than Unicorn does. We
31
+ # handle many clients per process and can't chmod on every
32
+ # connection we accept without wasting cycles. That added to the
33
+ # fact that we let clients keep idle connections open for long
34
+ # periods of time means we have to chmod at a fixed interval.
35
+ def timeout=(nr)
36
+ @timeout = nr + 1
37
+ end
38
+ #:startdoc:
49
39
 
50
- Module === mod or
51
- raise ArgumentError, "concurrency model #{model.inspect} not supported"
52
- extend(mod)
53
- args.each do |opt|
54
- case opt
55
- when Hash; O.update(opt)
56
- when Symbol; O[opt] = true
57
- else; raise ArgumentError, "can't handle option: #{opt.inspect}"
58
- end
59
- end
60
- mod.setup if mod.respond_to?(:setup)
61
- Const::RACK_DEFAULTS['rainbows.model'] = @use = model.to_sym
62
- Const::RACK_DEFAULTS['rack.multithread'] = !!(model.to_s =~ /Thread/)
40
+ def use(*args)
41
+ model = args.shift or return @use
42
+ mod = begin
43
+ Rainbows.const_get(model)
44
+ rescue NameError => e
45
+ logger.error "error loading #{model.inspect}: #{e}"
46
+ e.backtrace.each { |l| logger.error l }
47
+ raise ArgumentError, "concurrency model #{model.inspect} not supported"
48
+ end
63
49
 
64
- case @use
65
- when :Rev, :EventMachine, :NeverBlock
66
- Const::RACK_DEFAULTS['rainbows.autochunk'] = true
50
+ Module === mod or
51
+ raise ArgumentError, "concurrency model #{model.inspect} not supported"
52
+ extend(mod)
53
+ args.each do |opt|
54
+ case opt
55
+ when Hash; O.update(opt)
56
+ when Symbol; O[opt] = true
57
+ else; raise ArgumentError, "can't handle option: #{opt.inspect}"
67
58
  end
68
59
  end
69
-
70
- def worker_connections(*args)
71
- return @worker_connections if args.empty?
72
- nr = args[0]
73
- (Integer === nr && nr > 0) or
74
- raise ArgumentError, "worker_connections must be a positive Integer"
75
- @worker_connections = nr
60
+ mod.setup if mod.respond_to?(:setup)
61
+ new_defaults = {
62
+ 'rainbows.model' => (@use = model.to_sym),
63
+ 'rack.multithread' => !!(model.to_s =~ /Thread/),
64
+ }
65
+ case @use
66
+ when :Rev, :EventMachine, :NeverBlock
67
+ new_defaults['rainbows.autochunk'] = true
76
68
  end
69
+ Rainbows::Const::RACK_DEFAULTS.update(new_defaults)
70
+ end
77
71
 
78
- def keepalive_timeout(nr)
79
- (Integer === nr && nr >= 0) or
80
- raise ArgumentError, "keepalive must be a non-negative Integer"
81
- G.kato = nr
82
- end
72
+ def worker_connections(*args)
73
+ return @worker_connections if args.empty?
74
+ nr = args[0]
75
+ (Integer === nr && nr > 0) or
76
+ raise ArgumentError, "worker_connections must be a positive Integer"
77
+ @worker_connections = nr
78
+ end
83
79
 
84
- def client_max_body_size(nr)
85
- err = "client_max_body_size must be nil or a non-negative Integer"
86
- case nr
87
- when nil
88
- when Integer
89
- nr >= 0 or raise ArgumentError, err
90
- else
91
- raise ArgumentError, err
92
- end
93
- Rainbows.max_bytes = nr
94
- end
80
+ def keepalive_timeout(nr)
81
+ (Integer === nr && nr >= 0) or
82
+ raise ArgumentError, "keepalive must be a non-negative Integer"
83
+ G.kato = nr
95
84
  end
96
85
 
86
+ def client_max_body_size(nr)
87
+ err = "client_max_body_size must be nil or a non-negative Integer"
88
+ case nr
89
+ when nil
90
+ when Integer
91
+ nr >= 0 or raise ArgumentError, err
92
+ else
93
+ raise ArgumentError, err
94
+ end
95
+ Rainbows.max_bytes = nr
96
+ end
97
97
  end
@@ -1,42 +1,42 @@
1
1
  # -*- encoding: binary -*-
2
2
  # :enddoc:
3
- module Rainbows
4
3
 
5
4
  # middleware used to enforce client_max_body_size for TeeInput users,
6
5
  # there is no need to configure this middleware manually, it will
7
6
  # automatically be configured for you based on the client_max_body_size
8
7
  # setting
9
- class MaxBody < Struct.new(:app)
8
+ class Rainbows::MaxBody < Struct.new(:app)
10
9
 
11
10
  # this is meant to be included in Rainbows::TeeInput (and derived
12
11
  # classes) to limit body sizes
13
12
  module Limit
14
- Util = Unicorn::Util
13
+ TmpIO = Unicorn::TmpIO
14
+ MAX_BODY = Rainbows::Const::MAX_BODY
15
15
 
16
- def initialize(socket, req, parser, buf)
17
- self.len = parser.content_length
16
+ def initialize(socket, request)
17
+ @parser = request
18
+ @buf = request.buf
19
+ @env = request.env
20
+ @len = request.content_length
18
21
 
19
22
  max = Rainbows.max_bytes # never nil, see MaxBody.setup
20
- if len && len > max
21
- socket.write(Const::ERROR_413_RESPONSE)
23
+ if @len && @len > max
24
+ socket.write(Rainbows::Const::ERROR_413_RESPONSE)
22
25
  socket.close
23
- raise IOError, "Content-Length too big: #{len} > #{max}", []
26
+ raise IOError, "Content-Length too big: #@len > #{max}", []
24
27
  end
25
28
 
26
- self.req = req
27
- self.parser = parser
28
- self.buf = buf
29
- self.socket = socket
30
- self.buf2 = ""
31
- if buf.size > 0
32
- parser.filter_body(buf2, buf) and finalize_input
33
- buf2.size > max and raise IOError, "chunked request body too big", []
29
+ @socket = socket
30
+ @buf2 = ""
31
+ if @buf.size > 0
32
+ parser.filter_body(@buf2, @buf) and finalize_input
33
+ @buf2.size > max and raise IOError, "chunked request body too big", []
34
34
  end
35
- self.tmp = len && len < Const::MAX_BODY ? StringIO.new("") : Util.tmpio
36
- if buf2.size > 0
37
- tmp.write(buf2)
38
- tmp.seek(0)
39
- max -= buf2.size
35
+ @tmp = @len && @len < MAX_BODY ? StringIO.new("") : TmpIO.new
36
+ if @buf2.size > 0
37
+ @tmp.write(@buf2)
38
+ @tmp.rewind
39
+ max -= @buf2.size
40
40
  end
41
41
  @max_body = max
42
42
  end
@@ -46,7 +46,7 @@ class MaxBody < Struct.new(:app)
46
46
  if rv && ((@max_body -= rv.size) < 0)
47
47
  # make HttpParser#keepalive? => false to force an immediate disconnect
48
48
  # after we write
49
- parser.reset
49
+ @parser.reset
50
50
  throw :rainbows_EFBIG
51
51
  end
52
52
  rv
@@ -58,15 +58,15 @@ class MaxBody < Struct.new(:app)
58
58
  # if it's reconfigured
59
59
  def self.setup
60
60
  Rainbows.max_bytes or return
61
- case G.server.use
61
+ case Rainbows::G.server.use
62
62
  when :Rev, :EventMachine, :NeverBlock
63
63
  return
64
64
  end
65
65
 
66
- TeeInput.class_eval { include Limit }
66
+ Rainbows::TeeInput.__send__(:include, Limit)
67
67
 
68
68
  # force ourselves to the outermost middleware layer
69
- G.server.app = MaxBody.new(G.server.app)
69
+ Rainbows::G.server.app = self.new(Rainbows::G.server.app)
70
70
  end
71
71
 
72
72
  # Rack response returned when there's an error
@@ -78,6 +78,4 @@ class MaxBody < Struct.new(:app)
78
78
  def call(env)
79
79
  catch(:rainbows_EFBIG) { app.call(env) } || err(env)
80
80
  end
81
-
82
- end # class
83
- end # module
81
+ end
@@ -0,0 +1,61 @@
1
+ # -*- encoding: binary -*-
2
+ # :enddoc:
3
+ module Rainbows::ProcessClient
4
+ G = Rainbows::G
5
+ include Rainbows::Response
6
+ HttpParser = Unicorn::HttpParser
7
+ NULL_IO = Unicorn::HttpRequest::NULL_IO
8
+ RACK_INPUT = Unicorn::HttpRequest::RACK_INPUT
9
+ TeeInput = Rainbows::TeeInput
10
+ include Rainbows::Const
11
+
12
+ def wait_headers_readable(client)
13
+ IO.select([client], nil, nil, G.kato)
14
+ end
15
+
16
+ # once a client is accepted, it is processed in its entirety here
17
+ # in 3 easy steps: read request, call app, write app response
18
+ # this is used by synchronous concurrency models
19
+ # Base, ThreadSpawn, ThreadPool
20
+ def process_client(client) # :nodoc:
21
+ hp = HttpParser.new
22
+ client.kgio_read!(16384, buf = hp.buf)
23
+ remote_addr = client.kgio_addr
24
+
25
+ begin # loop
26
+ until env = hp.parse
27
+ wait_headers_readable(client) or return
28
+ buf << client.kgio_read!(16384)
29
+ end
30
+
31
+ env[CLIENT_IO] = client
32
+ env[RACK_INPUT] = 0 == hp.content_length ?
33
+ NULL_IO : TeeInput.new(client, hp)
34
+ env[REMOTE_ADDR] = remote_addr
35
+ status, headers, body = APP.call(env.update(RACK_DEFAULTS))
36
+
37
+ if 100 == status.to_i
38
+ client.write(EXPECT_100_RESPONSE)
39
+ env.delete(HTTP_EXPECT)
40
+ status, headers, body = APP.call(env)
41
+ end
42
+
43
+ if hp.headers?
44
+ headers = HH.new(headers)
45
+ range = make_range!(env, status, headers) and status = range.shift
46
+ env = hp.keepalive? && G.alive
47
+ headers[CONNECTION] = env ? KEEP_ALIVE : CLOSE
48
+ client.write(response_header(status, headers))
49
+ end
50
+ write_body(client, body, range)
51
+ end while env && hp.reset.nil?
52
+ # if we get any error, try to write something back to the client
53
+ # assuming we haven't closed the socket, but don't get hung up
54
+ # if the socket is already closed or broken. We'll always ensure
55
+ # the socket is closed at the end of this function
56
+ rescue => e
57
+ Rainbows::Error.write(client, e)
58
+ ensure
59
+ client.close unless client.closed?
60
+ end
61
+ end