rainbows 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -0
- data/.gitignore +1 -0
- data/.manifest +46 -18
- data/.wrongdoc.yml +8 -0
- data/ChangeLog +849 -374
- data/Documentation/comparison.haml +26 -21
- data/FAQ +6 -0
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +23 -65
- data/LATEST +27 -0
- data/NEWS +53 -26
- data/README +7 -7
- data/Rakefile +1 -98
- data/Summary +0 -7
- data/TODO +2 -2
- data/lib/rainbows/app_pool.rb +2 -1
- data/lib/rainbows/base.rb +1 -0
- data/lib/rainbows/configurator.rb +9 -0
- data/lib/rainbows/const.rb +1 -1
- data/lib/rainbows/coolio/client.rb +191 -0
- data/lib/rainbows/coolio/core.rb +25 -0
- data/lib/rainbows/{rev → coolio}/deferred_chunk_response.rb +3 -2
- data/lib/rainbows/{rev → coolio}/deferred_response.rb +3 -3
- data/lib/rainbows/coolio/heartbeat.rb +20 -0
- data/lib/rainbows/{rev → coolio}/master.rb +2 -3
- data/lib/rainbows/{rev → coolio}/sendfile.rb +1 -1
- data/lib/rainbows/coolio/server.rb +11 -0
- data/lib/rainbows/coolio/thread_client.rb +36 -0
- data/lib/rainbows/coolio.rb +45 -0
- data/lib/rainbows/coolio_fiber_spawn.rb +26 -0
- data/lib/rainbows/coolio_support.rb +9 -0
- data/lib/rainbows/coolio_thread_pool/client.rb +8 -0
- data/lib/rainbows/coolio_thread_pool/watcher.rb +14 -0
- data/lib/rainbows/coolio_thread_pool.rb +57 -0
- data/lib/rainbows/coolio_thread_spawn/client.rb +8 -0
- data/lib/rainbows/coolio_thread_spawn.rb +27 -0
- data/lib/rainbows/dev_fd_response.rb +6 -2
- data/lib/rainbows/ev_core/cap_input.rb +3 -2
- data/lib/rainbows/ev_core.rb +13 -3
- data/lib/rainbows/event_machine/client.rb +124 -0
- data/lib/rainbows/event_machine/response_pipe.rb +1 -2
- data/lib/rainbows/event_machine/server.rb +15 -0
- data/lib/rainbows/event_machine.rb +13 -137
- data/lib/rainbows/fiber/base.rb +6 -7
- data/lib/rainbows/fiber/body.rb +4 -2
- data/lib/rainbows/fiber/coolio/heartbeat.rb +15 -0
- data/lib/rainbows/fiber/{rev → coolio}/methods.rb +4 -5
- data/lib/rainbows/fiber/{rev → coolio}/server.rb +1 -1
- data/lib/rainbows/fiber/{rev → coolio}/sleeper.rb +2 -2
- data/lib/rainbows/fiber/coolio.rb +12 -0
- data/lib/rainbows/fiber/io/methods.rb +6 -0
- data/lib/rainbows/fiber/io.rb +8 -10
- data/lib/rainbows/fiber/queue.rb +24 -30
- data/lib/rainbows/fiber.rb +7 -4
- data/lib/rainbows/fiber_pool.rb +1 -1
- data/lib/rainbows/http_server.rb +9 -2
- data/lib/rainbows/max_body.rb +3 -1
- data/lib/rainbows/never_block/core.rb +15 -0
- data/lib/rainbows/never_block/event_machine.rb +8 -3
- data/lib/rainbows/never_block.rb +37 -70
- data/lib/rainbows/process_client.rb +3 -6
- data/lib/rainbows/rack_input.rb +17 -0
- data/lib/rainbows/response/body.rb +18 -19
- data/lib/rainbows/response.rb +1 -1
- data/lib/rainbows/rev.rb +21 -43
- data/lib/rainbows/rev_fiber_spawn.rb +4 -19
- data/lib/rainbows/rev_thread_pool.rb +21 -75
- data/lib/rainbows/rev_thread_spawn.rb +18 -36
- data/lib/rainbows/revactor/body.rb +4 -1
- data/lib/rainbows/revactor/tee_socket.rb +44 -0
- data/lib/rainbows/revactor.rb +13 -48
- data/lib/rainbows/socket_proxy.rb +24 -0
- data/lib/rainbows/sync_close.rb +37 -0
- data/lib/rainbows/thread_pool.rb +66 -70
- data/lib/rainbows/thread_spawn.rb +40 -50
- data/lib/rainbows/thread_timeout.rb +33 -27
- data/lib/rainbows/timed_read.rb +5 -1
- data/lib/rainbows/worker_yield.rb +16 -0
- data/lib/rainbows/writer_thread_pool/client.rb +19 -0
- data/lib/rainbows/writer_thread_pool.rb +60 -91
- data/lib/rainbows/writer_thread_spawn/client.rb +69 -0
- data/lib/rainbows/writer_thread_spawn.rb +37 -117
- data/lib/rainbows.rb +12 -4
- data/rainbows.gemspec +15 -19
- data/t/GNUmakefile +4 -4
- data/t/close-has-env.ru +65 -0
- data/t/simple-http_Coolio.ru +9 -0
- data/t/simple-http_CoolioFiberSpawn.ru +10 -0
- data/t/simple-http_CoolioThreadPool.ru +9 -0
- data/t/simple-http_CoolioThreadSpawn.ru +9 -0
- data/t/t0004-heartbeat-timeout.sh +2 -2
- data/t/t0007-worker-follows-master-to-death.sh +1 -1
- data/t/t0015-working_directory.sh +7 -1
- data/t/t0017-keepalive-timeout-zero.sh +1 -1
- data/t/t0019-keepalive-cpu-usage.sh +62 -0
- data/t/t0040-keepalive_requests-setting.sh +51 -0
- data/t/t0050-response-body-close-has-env.sh +109 -0
- data/t/t0102-rack-input-short.sh +6 -6
- data/t/t0106-rack-input-keepalive.sh +48 -2
- data/t/t0113-rewindable-input-false.sh +28 -0
- data/t/t0113.ru +12 -0
- data/t/t0114-rewindable-input-true.sh +28 -0
- data/t/t0114.ru +12 -0
- data/t/t9100-thread-timeout.sh +24 -2
- data/t/t9101-thread-timeout-threshold.sh +6 -13
- data/t/test-lib.sh +2 -1
- data/t/test_isolate.rb +9 -4
- data/t/times.ru +6 -0
- metadata +109 -42
- data/GIT-VERSION-FILE +0 -1
- data/lib/rainbows/fiber/rev/heartbeat.rb +0 -8
- data/lib/rainbows/fiber/rev/kato.rb +0 -22
- data/lib/rainbows/fiber/rev.rb +0 -13
- data/lib/rainbows/rev/client.rb +0 -194
- data/lib/rainbows/rev/core.rb +0 -41
- data/lib/rainbows/rev/heartbeat.rb +0 -23
- data/lib/rainbows/rev/thread.rb +0 -46
- data/man/man1/rainbows.1 +0 -193
data/Rakefile
CHANGED
@@ -3,105 +3,8 @@ autoload :Gem, 'rubygems'
|
|
3
3
|
autoload :Tempfile, 'tempfile'
|
4
4
|
|
5
5
|
# most tasks are in the GNUmakefile which offers better parallelism
|
6
|
-
|
7
|
-
def tags
|
8
|
-
timefmt = '%Y-%m-%dT%H:%M:%SZ'
|
9
|
-
@tags ||= `git tag -l`.split(/\n/).map do |tag|
|
10
|
-
if %r{\Av[\d\.]+} =~ tag
|
11
|
-
header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
|
12
|
-
header = header.split(/\n/)
|
13
|
-
tagger = header.grep(/\Atagger /).first
|
14
|
-
body ||= "initial"
|
15
|
-
{
|
16
|
-
:time => Time.at(tagger.split(/ /)[-2].to_i).utc.strftime(timefmt),
|
17
|
-
:tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1].strip,
|
18
|
-
:tagger_email => %r{<([^>]+)>}.match(tagger)[1].strip,
|
19
|
-
:id => `git rev-parse refs/tags/#{tag}`.chomp!,
|
20
|
-
:tag => tag,
|
21
|
-
:subject => subject,
|
22
|
-
:body => body,
|
23
|
-
}
|
24
|
-
end
|
25
|
-
end.compact.sort { |a,b| b[:time] <=> a[:time] }
|
26
|
-
end
|
27
|
-
|
28
6
|
cgit_url = "http://git.bogomips.org/cgit/rainbows.git"
|
29
|
-
git_url =
|
30
|
-
|
31
|
-
desc 'prints news as an Atom feed'
|
32
|
-
task :news_atom do
|
33
|
-
require 'nokogiri'
|
34
|
-
new_tags = tags[0,10]
|
35
|
-
puts(Nokogiri::XML::Builder.new do
|
36
|
-
feed :xmlns => "http://www.w3.org/2005/Atom" do
|
37
|
-
id! "http://rainbows.rubyforge.org/NEWS.atom.xml"
|
38
|
-
title "Rainbows! news"
|
39
|
-
subtitle "Unicorn for sleepy apps and slow clients"
|
40
|
-
link! :rel => 'alternate', :type => 'text/html',
|
41
|
-
:href => 'http://rainbows.rubyforge.org/NEWS.html'
|
42
|
-
updated(new_tags.empty? ? "1970-01-01T00:00:00Z" : new_tags.first[:time])
|
43
|
-
new_tags.each do |tag|
|
44
|
-
entry do
|
45
|
-
title tag[:subject]
|
46
|
-
updated tag[:time]
|
47
|
-
published tag[:time]
|
48
|
-
author {
|
49
|
-
name tag[:tagger_name]
|
50
|
-
email tag[:tagger_email]
|
51
|
-
}
|
52
|
-
url = "#{cgit_url}/tag/?id=#{tag[:tag]}"
|
53
|
-
link! :rel => "alternate", :type => "text/html", :href =>url
|
54
|
-
id! url
|
55
|
-
message_only = tag[:body].split(/\n.+\(\d+\):\n {6}/s).first.strip
|
56
|
-
content({:type =>:text}, message_only)
|
57
|
-
content(:type =>:xhtml) { pre tag[:body] }
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end.to_xml)
|
62
|
-
end
|
63
|
-
|
64
|
-
desc 'prints RDoc-formatted news'
|
65
|
-
task :news_rdoc do
|
66
|
-
tags.each do |tag|
|
67
|
-
time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
|
68
|
-
puts "=== #{tag[:tag].sub(/^v/, '')} / #{time}"
|
69
|
-
puts ""
|
70
|
-
|
71
|
-
body = tag[:body]
|
72
|
-
puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
|
73
|
-
puts ""
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
desc "print release changelog for Rubyforge"
|
78
|
-
task :release_changes do
|
79
|
-
version = ENV['VERSION'] or abort "VERSION= needed"
|
80
|
-
version = "v#{version}"
|
81
|
-
vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
|
82
|
-
prev = vtags[vtags.index(version) - 1]
|
83
|
-
if prev
|
84
|
-
system('git', 'diff', '--stat', prev, version) or abort $?
|
85
|
-
puts ""
|
86
|
-
system('git', 'log', "#{prev}..#{version}") or abort $?
|
87
|
-
else
|
88
|
-
system('git', 'log', version) or abort $?
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
desc "print release notes for Rubyforge"
|
93
|
-
task :release_notes do
|
94
|
-
spec = Gem::Specification.load('rainbows.gemspec')
|
95
|
-
puts spec.description.strip
|
96
|
-
puts ""
|
97
|
-
puts "* #{spec.homepage}"
|
98
|
-
puts "* #{spec.email}"
|
99
|
-
puts "* #{git_url}"
|
100
|
-
|
101
|
-
_, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
|
102
|
-
print "\nChanges:\n\n"
|
103
|
-
puts body
|
104
|
-
end
|
7
|
+
git_url = 'git://git.bogomips.org/rainbows.git'
|
105
8
|
|
106
9
|
desc "read news article from STDIN and post to rubyforge"
|
107
10
|
task :publish_news do
|
data/Summary
CHANGED
data/TODO
CHANGED
@@ -24,11 +24,11 @@ care about.
|
|
24
24
|
(those who do not require streaming input can use
|
25
25
|
{rack-fiber_pool}[http://github.com/mperham/rack-fiber_pool])
|
26
26
|
|
27
|
-
*
|
27
|
+
* CoolioFiberPool
|
28
28
|
|
29
29
|
* ThreadPoolRevFiber{Spawn,Pool}: just because
|
30
30
|
|
31
|
-
*
|
31
|
+
* Coolio + callcc - current Coolio model with callcc (should work with MBARI)
|
32
32
|
|
33
33
|
* Omnibus - haven't looked into it, probably like Revactor with 1.8?
|
34
34
|
|
data/lib/rainbows/app_pool.rb
CHANGED
@@ -88,7 +88,8 @@ class Rainbows::AppPool < Struct.new(:pool, :re)
|
|
88
88
|
# concurrency models
|
89
89
|
self.re ||= begin
|
90
90
|
case env["rainbows.model"]
|
91
|
-
when :FiberSpawn, :FiberPool, :Revactor, :NeverBlock,
|
91
|
+
when :FiberSpawn, :FiberPool, :Revactor, :NeverBlock,
|
92
|
+
:RevFiberSpawn, :CoolioFiberSpawn
|
92
93
|
self.pool = Rainbows::Fiber::Queue.new(pool)
|
93
94
|
end
|
94
95
|
true
|
data/lib/rainbows/base.rb
CHANGED
@@ -13,6 +13,7 @@ module Rainbows::Configurator
|
|
13
13
|
# worker_connections 400
|
14
14
|
# keepalive_timeout 0 # zero disables keepalives entirely
|
15
15
|
# client_max_body_size 5*1024*1024 # 5 megabytes
|
16
|
+
# keepalive_requests 666 # default:100
|
16
17
|
# end
|
17
18
|
#
|
18
19
|
# # the rest of the Unicorn configuration
|
@@ -33,6 +34,14 @@ module Rainbows::Configurator
|
|
33
34
|
# The default +client_max_body_size+ is 1 megabyte (1024 * 1024 bytes),
|
34
35
|
# setting this to +nil+ will disable body size checks and allow any
|
35
36
|
# size to be specified.
|
37
|
+
#
|
38
|
+
# The default +keepalive_requests+ is 100, meaning a client may
|
39
|
+
# complete 100 keepalive requests after the initial request before
|
40
|
+
# \Rainbows! forces a disconnect. Lowering this can improve
|
41
|
+
# load-balancing characteristics as it forces HTTP/1.1 clients to
|
42
|
+
# reconnect after the specified number of requests, hopefully to a
|
43
|
+
# less busy host or worker process. This may also be used to mitigate
|
44
|
+
# denial-of-service attacks that use HTTP pipelining.
|
36
45
|
def Rainbows!(&block)
|
37
46
|
block_given? or raise ArgumentError, "Rainbows! requires a block"
|
38
47
|
Rainbows::HttpServer.setup(block)
|
data/lib/rainbows/const.rb
CHANGED
@@ -0,0 +1,191 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
class Rainbows::Coolio::Client < Coolio::IO
|
4
|
+
include Rainbows::EvCore
|
5
|
+
G = Rainbows::G
|
6
|
+
SF = Rainbows::StreamFile
|
7
|
+
CONN = Rainbows::Coolio::CONN
|
8
|
+
KATO = Rainbows::Coolio::KATO
|
9
|
+
DeferredResponse = Rainbows::Coolio::DeferredResponse
|
10
|
+
DeferredChunkResponse = Rainbows::Coolio::DeferredChunkResponse
|
11
|
+
|
12
|
+
def initialize(io)
|
13
|
+
CONN[self] = false
|
14
|
+
super(io)
|
15
|
+
post_init
|
16
|
+
@deferred = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def want_more
|
20
|
+
enable unless enabled?
|
21
|
+
end
|
22
|
+
|
23
|
+
def quit
|
24
|
+
super
|
25
|
+
close if @deferred.nil? && @_write_buffer.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
# override the Coolio::IO#write method try to write directly to the
|
29
|
+
# kernel socket buffers to avoid an extra userspace copy if
|
30
|
+
# possible.
|
31
|
+
def write(buf)
|
32
|
+
if @_write_buffer.empty?
|
33
|
+
begin
|
34
|
+
case rv = @_io.kgio_trywrite(buf)
|
35
|
+
when nil
|
36
|
+
return enable_write_watcher
|
37
|
+
when :wait_writable
|
38
|
+
break # fall through to super(buf)
|
39
|
+
when String
|
40
|
+
buf = rv # retry, skb could grow or been drained
|
41
|
+
end
|
42
|
+
rescue => e
|
43
|
+
return handle_error(e)
|
44
|
+
end while true
|
45
|
+
end
|
46
|
+
super(buf)
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_readable
|
50
|
+
buf = @_io.kgio_tryread(16384)
|
51
|
+
case buf
|
52
|
+
when :wait_readable
|
53
|
+
when nil # eof
|
54
|
+
close
|
55
|
+
else
|
56
|
+
on_read buf
|
57
|
+
end
|
58
|
+
rescue Errno::ECONNRESET
|
59
|
+
close
|
60
|
+
end
|
61
|
+
|
62
|
+
# queued, optional response bodies, it should only be unpollable "fast"
|
63
|
+
# devices where read(2) is uninterruptable. Unfortunately, NFS and ilk
|
64
|
+
# are also part of this. We'll also stick DeferredResponse bodies in
|
65
|
+
# here to prevent connections from being closed on us.
|
66
|
+
def defer_body(io)
|
67
|
+
@deferred = io
|
68
|
+
enable_write_watcher
|
69
|
+
end
|
70
|
+
|
71
|
+
# allows enabling of write watcher even when read watcher is disabled
|
72
|
+
def evloop
|
73
|
+
LOOP # this constant is set in when a worker starts
|
74
|
+
end
|
75
|
+
|
76
|
+
def next!
|
77
|
+
attached? or return
|
78
|
+
@deferred = nil
|
79
|
+
enable_write_watcher
|
80
|
+
end
|
81
|
+
|
82
|
+
def timeout?
|
83
|
+
@deferred.nil? && @_write_buffer.empty? and close.nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
# used for streaming sockets and pipes
|
87
|
+
def stream_response(status, headers, io, body)
|
88
|
+
c = stream_response_headers(status, headers) if headers
|
89
|
+
# we only want to attach to the Coolio::Loop belonging to the
|
90
|
+
# main thread in Ruby 1.9
|
91
|
+
io = (c ? DeferredChunkResponse : DeferredResponse).new(io, self, body)
|
92
|
+
defer_body(io.attach(LOOP))
|
93
|
+
end
|
94
|
+
|
95
|
+
def coolio_write_response(response, alive)
|
96
|
+
status, headers, body = response
|
97
|
+
headers = @hp.headers? ? HH.new(headers) : nil
|
98
|
+
|
99
|
+
headers[CONNECTION] = alive ? KEEP_ALIVE : CLOSE if headers
|
100
|
+
if body.respond_to?(:to_path)
|
101
|
+
io = body_to_io(body)
|
102
|
+
st = io.stat
|
103
|
+
|
104
|
+
if st.file?
|
105
|
+
offset, count = 0, st.size
|
106
|
+
if headers
|
107
|
+
if range = make_range!(@env, status, headers)
|
108
|
+
status, offset, count = range
|
109
|
+
end
|
110
|
+
write(response_header(status, headers))
|
111
|
+
end
|
112
|
+
return defer_body(SF.new(offset, count, io, body))
|
113
|
+
elsif st.socket? || st.pipe?
|
114
|
+
return stream_response(status, headers, io, body)
|
115
|
+
end
|
116
|
+
# char or block device... WTF? fall through to body.each
|
117
|
+
end
|
118
|
+
write(response_header(status, headers)) if headers
|
119
|
+
write_body_each(self, body, nil)
|
120
|
+
end
|
121
|
+
|
122
|
+
def app_call
|
123
|
+
KATO.delete(self)
|
124
|
+
@env[RACK_INPUT] = @input
|
125
|
+
@env[REMOTE_ADDR] = @_io.kgio_addr
|
126
|
+
response = APP.call(@env.update(RACK_DEFAULTS))
|
127
|
+
|
128
|
+
coolio_write_response(response, alive = @hp.next? && G.alive)
|
129
|
+
return quit unless alive && :close != @state
|
130
|
+
@state = :headers
|
131
|
+
disable if enabled?
|
132
|
+
end
|
133
|
+
|
134
|
+
def on_write_complete
|
135
|
+
case @deferred
|
136
|
+
when DeferredResponse then return
|
137
|
+
when NilClass # fall through
|
138
|
+
else
|
139
|
+
begin
|
140
|
+
return rev_sendfile(@deferred)
|
141
|
+
rescue EOFError # expected at file EOF
|
142
|
+
close_deferred
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
case @state
|
147
|
+
when :close
|
148
|
+
close if @_write_buffer.empty?
|
149
|
+
when :headers
|
150
|
+
if @buf.empty?
|
151
|
+
unless enabled?
|
152
|
+
enable
|
153
|
+
KATO[self] = Time.now
|
154
|
+
end
|
155
|
+
else
|
156
|
+
on_read("")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
rescue => e
|
160
|
+
handle_error(e)
|
161
|
+
end
|
162
|
+
|
163
|
+
def handle_error(e)
|
164
|
+
close_deferred
|
165
|
+
if msg = Rainbows::Error.response(e)
|
166
|
+
@_io.kgio_trywrite(msg) rescue nil
|
167
|
+
end
|
168
|
+
@_write_buffer.clear
|
169
|
+
ensure
|
170
|
+
quit
|
171
|
+
end
|
172
|
+
|
173
|
+
def close_deferred
|
174
|
+
case @deferred
|
175
|
+
when DeferredResponse, NilClass
|
176
|
+
else
|
177
|
+
begin
|
178
|
+
@deferred.close
|
179
|
+
rescue => e
|
180
|
+
G.server.logger.error("closing #@deferred: #{e}")
|
181
|
+
end
|
182
|
+
@deferred = nil
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def on_close
|
187
|
+
close_deferred
|
188
|
+
CONN.delete(self)
|
189
|
+
KATO.delete(self)
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
module Rainbows::Coolio::Core
|
4
|
+
include Rainbows::Base
|
5
|
+
|
6
|
+
# runs inside each forked worker, this sits around and waits
|
7
|
+
# for connections and doesn't die until the parent dies (or is
|
8
|
+
# given a INT, QUIT, or TERM signal)
|
9
|
+
def worker_loop(worker)
|
10
|
+
Rainbows::Response.setup(Rainbows::Coolio::Client)
|
11
|
+
require 'rainbows/coolio/sendfile'
|
12
|
+
Rainbows::Coolio::Client.__send__(:include, Rainbows::Coolio::Sendfile)
|
13
|
+
init_worker_process(worker)
|
14
|
+
mod = Rainbows.const_get(@use)
|
15
|
+
rloop = Rainbows::Coolio::Server.const_set(:LOOP, Coolio::Loop.default)
|
16
|
+
Rainbows::Coolio::Client.const_set(:LOOP, rloop)
|
17
|
+
Rainbows::Coolio::Server.const_set(:MAX, @worker_connections)
|
18
|
+
Rainbows::Coolio::Server.const_set(:CL, mod.const_get(:Client))
|
19
|
+
Rainbows::EvCore.const_set(:APP, G.server.app)
|
20
|
+
Rainbows::EvCore.setup
|
21
|
+
Rainbows::Coolio::Heartbeat.new(1, true).attach(rloop)
|
22
|
+
LISTENERS.map! { |s| Rainbows::Coolio::Server.new(s).attach(rloop) }
|
23
|
+
rloop.run
|
24
|
+
end
|
25
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
# :enddoc:
|
3
3
|
#
|
4
|
-
# this is class is specific to
|
5
|
-
class Rainbows::
|
4
|
+
# this is class is specific to Coolio for proxying IO-derived objects
|
5
|
+
class Rainbows::Coolio::DeferredChunkResponse <
|
6
|
+
Rainbows::Coolio::DeferredResponse
|
6
7
|
def on_read(data)
|
7
8
|
@client.write("#{data.size.to_s(16)}\r\n")
|
8
9
|
@client.write(data)
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
# :enddoc:
|
3
3
|
#
|
4
|
-
# this is class is specific to
|
4
|
+
# this is class is specific to Coolio for writing large static files
|
5
5
|
# or proxying IO-derived objects
|
6
|
-
class Rainbows::
|
6
|
+
class Rainbows::Coolio::DeferredResponse < Coolio::IO
|
7
7
|
def initialize(io, client, body)
|
8
8
|
super(io)
|
9
9
|
@client, @body = client, body
|
@@ -14,7 +14,7 @@ class Rainbows::Rev::DeferredResponse < ::Rev::IO
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def on_close
|
17
|
-
@client.next! if @client.attached? # attached? is false if write fails
|
18
17
|
@body.respond_to?(:close) and @body.close
|
18
|
+
@client.next!
|
19
19
|
end
|
20
20
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
# This class handles the Unicorn fchmod heartbeat mechanism
|
4
|
+
# in Coolio-based concurrency models to prevent the master
|
5
|
+
# process from killing us unless we're blocked. This class
|
6
|
+
# will also detect and execute the graceful exit if triggered
|
7
|
+
# by SIGQUIT
|
8
|
+
class Rainbows::Coolio::Heartbeat < Coolio::TimerWatcher
|
9
|
+
KATO = Rainbows::Coolio::KATO
|
10
|
+
CONN = Rainbows::Coolio::CONN
|
11
|
+
G = Rainbows::G
|
12
|
+
|
13
|
+
def on_timer
|
14
|
+
if (ot = G.kato) >= 0
|
15
|
+
ot = Time.now - ot
|
16
|
+
KATO.delete_if { |client, time| time < ot and client.timeout? }
|
17
|
+
end
|
18
|
+
exit if (! G.tick && CONN.size <= 0)
|
19
|
+
end
|
20
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
# :enddoc:
|
3
|
-
module Rainbows::
|
3
|
+
module Rainbows::Coolio::Sendfile
|
4
4
|
if IO.method_defined?(:sendfile_nonblock)
|
5
5
|
def rev_sendfile(sf) # +sf+ is a Rainbows::StreamFile object
|
6
6
|
sf.offset += (n = @_io.sendfile_nonblock(sf, sf.offset, sf.count))
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
class Rainbows::Coolio::Server < Coolio::IO
|
4
|
+
CONN = Rainbows::Coolio::CONN
|
5
|
+
# CL and MAX will be defined in the corresponding worker loop
|
6
|
+
|
7
|
+
def on_readable
|
8
|
+
return if CONN.size >= MAX
|
9
|
+
io = @_io.kgio_tryaccept and CL.new(io).attach(LOOP)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
|
4
|
+
RUBY_VERSION =~ %r{\A1\.8} and
|
5
|
+
warn "Coolio and Threads do not mix well under Ruby 1.8"
|
6
|
+
|
7
|
+
class Rainbows::Coolio::ThreadClient < Rainbows::Coolio::Client
|
8
|
+
def app_call
|
9
|
+
KATO.delete(self)
|
10
|
+
disable if enabled?
|
11
|
+
@env[RACK_INPUT] = @input
|
12
|
+
app_dispatch # must be implemented by subclass
|
13
|
+
end
|
14
|
+
|
15
|
+
# this is only called in the master thread
|
16
|
+
def response_write(response)
|
17
|
+
alive = @hp.next? && G.alive
|
18
|
+
coolio_write_response(response, alive)
|
19
|
+
return quit unless alive && :close != @state
|
20
|
+
|
21
|
+
@state = :headers
|
22
|
+
end
|
23
|
+
|
24
|
+
# fails-safe application dispatch, we absolutely cannot
|
25
|
+
# afford to fail or raise an exception (killing the thread)
|
26
|
+
# here because that could cause a deadlock and we'd leak FDs
|
27
|
+
def app_response
|
28
|
+
begin
|
29
|
+
@env[REMOTE_ADDR] = @_io.kgio_addr
|
30
|
+
APP.call(@env.update(RACK_DEFAULTS))
|
31
|
+
rescue => e
|
32
|
+
Rainbows::Error.app(e) # we guarantee this does not raise
|
33
|
+
[ 500, {}, [] ]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'rainbows/coolio_support'
|
3
|
+
|
4
|
+
# Implements a basic single-threaded event model with
|
5
|
+
# {Cool.io}[http://coolio.github.com/]. It is capable of handling
|
6
|
+
# thousands of simultaneous client connections, but with only a
|
7
|
+
# single-threaded app dispatch. It is suited for slow clients and
|
8
|
+
# fast applications (applications that do not have slow network
|
9
|
+
# dependencies) or applications that use DevFdResponse for deferrable
|
10
|
+
# response bodies. It does not require your Rack application to be
|
11
|
+
# thread-safe, reentrancy is only required for the DevFdResponse body
|
12
|
+
# generator.
|
13
|
+
#
|
14
|
+
# Compatibility: Whatever Cool.io itself supports, currently Ruby
|
15
|
+
# 1.8/1.9.
|
16
|
+
#
|
17
|
+
# This model does not implement as streaming "rack.input" which
|
18
|
+
# allows the Rack application to process data as it arrives. This
|
19
|
+
# means "rack.input" will be fully buffered in memory or to a
|
20
|
+
# temporary file before the application is entered.
|
21
|
+
module Rainbows::Coolio
|
22
|
+
# :stopdoc:
|
23
|
+
# keep-alive timeout scoreboard
|
24
|
+
KATO = {}
|
25
|
+
|
26
|
+
# all connected clients
|
27
|
+
CONN = {}
|
28
|
+
|
29
|
+
if {}.respond_to?(:compare_by_identity)
|
30
|
+
CONN.compare_by_identity
|
31
|
+
KATO.compare_by_identity
|
32
|
+
end
|
33
|
+
|
34
|
+
autoload :Master, 'rainbows/coolio/master'
|
35
|
+
autoload :ThreadClient, 'rainbows/coolio/thread_client'
|
36
|
+
autoload :DeferredChunkResponse, 'rainbows/coolio/deferred_chunk_response'
|
37
|
+
# :startdoc:
|
38
|
+
end
|
39
|
+
# :enddoc:
|
40
|
+
require 'rainbows/coolio/heartbeat'
|
41
|
+
require 'rainbows/coolio/server'
|
42
|
+
require 'rainbows/coolio/core'
|
43
|
+
require 'rainbows/coolio/deferred_response'
|
44
|
+
require 'rainbows/coolio/client'
|
45
|
+
Rainbows::Coolio.__send__ :include, Rainbows::Coolio::Core
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'rainbows/fiber/coolio'
|
3
|
+
|
4
|
+
# A combination of the Coolio and FiberSpawn models. This allows Ruby
|
5
|
+
# 1.9 Fiber-based concurrency for application processing while
|
6
|
+
# exposing a synchronous execution model and using scalable network
|
7
|
+
# concurrency provided by Cool.io. A "rack.input" is exposed as well
|
8
|
+
# being Sunshowers-compatible. Applications are strongly advised to
|
9
|
+
# wrap all slow IO objects (sockets, pipes) using the
|
10
|
+
# Rainbows::Fiber::IO or a Cool.io-compatible class whenever possible.
|
11
|
+
module Rainbows::CoolioFiberSpawn
|
12
|
+
|
13
|
+
include Rainbows::Base
|
14
|
+
include Rainbows::Fiber::Coolio
|
15
|
+
|
16
|
+
def worker_loop(worker) # :nodoc:
|
17
|
+
Rainbows::Response.setup(Server)
|
18
|
+
init_worker_process(worker)
|
19
|
+
Server.const_set(:MAX, @worker_connections)
|
20
|
+
Rainbows::Fiber::Base.setup(Server, nil)
|
21
|
+
Server.const_set(:APP, G.server.app)
|
22
|
+
Heartbeat.new(1, true).attach(Coolio::Loop.default)
|
23
|
+
LISTENERS.map! { |s| Server.new(s).attach(Coolio::Loop.default) }
|
24
|
+
Coolio::Loop.default.run
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
class Rainbows::CoolioThreadPool::Watcher < Coolio::TimerWatcher
|
4
|
+
G = Rainbows::G
|
5
|
+
|
6
|
+
def initialize(threads)
|
7
|
+
@threads = threads
|
8
|
+
super(G.server.timeout, true)
|
9
|
+
end
|
10
|
+
|
11
|
+
def on_timer
|
12
|
+
@threads.each { |t| t.join(0) and G.quit! }
|
13
|
+
end
|
14
|
+
end
|