rainbows 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|