rainbows-4.6.2/0000755000004100000410000000000012354770160013404 5ustar www-datawww-datarainbows-4.6.2/Rakefile0000644000004100000410000000377512354770160015065 0ustar www-datawww-data# -*- encoding: binary -*- autoload :Gem, 'rubygems' autoload :Tempfile, 'tempfile' require 'wrongdoc' cgit_url = Wrongdoc.config[:cgit_url] git_url = Wrongdoc.config[:git_url] desc "read news article from STDIN and post to rubyforge" task :publish_news do require 'rubyforge' spec = Gem::Specification.load('rainbows.gemspec') tmp = Tempfile.new('rf-news') _, subject, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3) tmp.puts subject tmp.puts tmp.puts spec.description.strip tmp.puts "" tmp.puts "* #{spec.homepage}" tmp.puts "* #{spec.email}" tmp.puts "* #{git_url}" tmp.print "\nChanges:\n\n" tmp.puts body tmp.flush system(ENV["VISUAL"], tmp.path) or abort "#{ENV["VISUAL"]} failed: #$?" msg = File.readlines(tmp.path) subject = msg.shift blank = msg.shift blank == "\n" or abort "no newline after subject!" subject.strip! body = msg.join("").strip! rf = RubyForge.new.configure rf.login rf.post_news('rainbows', subject, body) end desc "post to FM" task :fm_update do require 'net/http' require 'net/netrc' require 'json' version = ENV['VERSION'] or abort "VERSION= needed" uri = URI.parse('https://freecode.com/projects/rainbows/releases.json') rc = Net::Netrc.locate('rainbows-fm') or abort "~/.netrc not found" api_token = rc.password _, subject, body = `git cat-file tag v#{version}`.split(/\n\n/, 3) tmp = Tempfile.new('fm-changelog') tmp.puts subject tmp.puts tmp.puts body tmp.flush system(ENV["VISUAL"], tmp.path) or abort "#{ENV["VISUAL"]} failed: #$?" changelog = File.read(tmp.path).strip req = { "auth_code" => api_token, "release" => { "tag_list" => "Stable", "version" => version, "changelog" => changelog, }, }.to_json if ! changelog.strip.empty? && version =~ %r{\A[\d\.]+\d+\z} Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http| p http.post(uri.path, req, {'Content-Type'=>'application/json'}) end else warn "not updating freshmeat for v#{version}" end end rainbows-4.6.2/bin/0000755000004100000410000000000012354770160014154 5ustar www-datawww-datarainbows-4.6.2/bin/rainbows0000755000004100000410000000706612354770160015737 0ustar www-datawww-data#!/this/will/be/overwritten/or/wrapped/anyways/do/not/worry/ruby # -*- encoding: binary -*- require 'unicorn/launcher' require 'rainbows' require 'optparse' ENV["RACK_ENV"] ||= "development" rackup_opts = Unicorn::Configurator::RACKUP options = rackup_opts[:options] op = OptionParser.new("", 24, ' ') do |opts| cmd = File.basename($0) opts.banner = "Usage: #{cmd} " \ "[ruby options] [#{cmd} options] [rackup config file]" opts.separator "Ruby options:" lineno = 1 opts.on("-e", "--eval LINE", "evaluate a LINE of code") do |line| eval line, TOPLEVEL_BINDING, "-e", lineno lineno += 1 end opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") do $DEBUG = true end opts.on("-w", "--warn", "turn warnings on for your script") do $-w = true end opts.on("-I", "--include PATH", "specify $LOAD_PATH (may be used more than once)") do |path| $LOAD_PATH.unshift(*path.split(/:/)) end opts.on("-r", "--require LIBRARY", "require the library, before executing your script") do |library| require library end opts.separator "#{cmd} options:" # some of these switches exist for rackup command-line compatibility, opts.on("-o", "--host HOST", "listen on HOST (default: #{Unicorn::Const::DEFAULT_HOST})") do |h| rackup_opts[:host] = h rackup_opts[:set_listener] = true end opts.on("-p", "--port PORT", "use PORT (default: #{Unicorn::Const::DEFAULT_PORT})") do |p| rackup_opts[:port] = p.to_i rackup_opts[:set_listener] = true end opts.on("-E", "--env RACK_ENV", "use RACK_ENV for defaults (default: development)") do |e| ENV["RACK_ENV"] = e end opts.on("-N", "--no-default-middleware", "do not load middleware implied by RACK_ENV") do |e| rackup_opts[:no_default_middleware] = true end opts.on("-D", "--daemonize", "run daemonized in the background") do |d| rackup_opts[:daemonize] = !!d end opts.on("-P", "--pid FILE", "DEPRECATED") do |f| warn "Use of --pid/-P is strongly discouraged" warn "Use the 'pid' directive in the Rainbows!/Unicorn config file instead" options[:pid] = f end opts.on("-s", "--server SERVER", "this flag only exists for compatibility") do |s| warn "-s/--server only exists for compatibility with rackup" end # Rainbows!/Unicorn-specific stuff opts.on("-l", "--listen {HOST:PORT|PATH}", "listen on HOST:PORT or PATH", "this may be specified multiple times", "(default: #{Unicorn::Const::DEFAULT_LISTEN})") do |address| options[:listeners] << address end opts.on("-c", "--config-file FILE", "Rainbows!-specific config file") do |f| options[:config_file] = f end # I'm avoiding Unicorn-specific config options on the command-line. # IMNSHO, config options on the command-line are redundant given # config files and make things unnecessarily complicated with multiple # places to look for a config option. opts.separator "Common options:" opts.on_tail("-h", "--help", "Show this message") do puts opts.to_s.gsub(/^.*DEPRECATED.*$/s, '') exit end opts.on_tail("-v", "--version", "Show version") do puts "Rainbows! v#{Rainbows::Const::RAINBOWS_VERSION}" exit end opts.parse! ARGV end app = Unicorn.builder(ARGV[0] || 'config.ru', op) op = nil if $DEBUG require 'pp' pp({ :unicorn_options => options, :app => app, :daemonize => rackup_opts[:daemonize], }) end Unicorn::Launcher.daemonize!(options) if rackup_opts[:daemonize] Rainbows::HttpServer.new(app, options).start.join rainbows-4.6.2/examples/0000755000004100000410000000000012354770160015222 5ustar www-datawww-datarainbows-4.6.2/examples/reverse_proxy.ru0000644000004100000410000000031212354770160020502 0ustar www-datawww-data# see Rainbows::ReverseProxy RDoc cfg = { :upstreams => [ "/tmp/.r.sock", "http://bogomips.org/", [ "http://10.6.6.6:666/", { :weight => 666 } ], ] } run Rainbows::ReverseProxy.new(cfg) rainbows-4.6.2/t/0000755000004100000410000000000012354770160013647 5ustar www-datawww-datarainbows-4.6.2/t/t0050-response-body-close-has-env.sh0000644000004100000410000000376312354770160022215 0ustar www-datawww-data#!/bin/sh . ./test-lib.sh skip_models StreamResponseEpoll t_plan 29 "keepalive does not clear Rack env prematurely for $model" t_begin "setup and start" && { rainbows_setup rtmpfiles curl_out curl_err echo "preload_app true" >> $unicorn_config rainbows -D close-has-env.ru -c $unicorn_config rainbows_wait_start } req_pipelined () { pfx=$1 t_begin "make pipelined requests to trigger $pfx response body" && { > $r_out rm -f $ok ( cat $fifo > $tmp & printf 'GET /%s/1 HTTP/1.1\r\n' $pfx printf 'Host: example.com\r\n\r\n' printf 'GET /%s/2 HTTP/1.1\r\n' $pfx printf 'Host: example.com\r\n\r\n' printf 'GET /%s/3 HTTP/1.1\r\n' $pfx printf 'Host: example.com\r\n' printf 'Connection: close\r\n\r\n' wait echo ok > $ok ) | socat - TCP4:$listen > $fifo test xok = x$(cat $ok) } } reload () { t_begin 'reloading Rainbows! to ensure writeout' && { # ensure worker is loaded before HUP rm -f $curl_err $curl_out curl -vs http://$listen/ >$curl_out 2> $curl_err # reload to ensure everything is flushed kill -HUP $rainbows_pid test xSTART = x"$(cat $fifo)" } } check_log () { pfx="$1" t_begin "check body close messages" && { < $r_out awk ' /^path_info=\/'$pfx'\/[1-3]$/ { next } { exit(2) } END { exit(NR == 3 ? 0 : 1) } ' } } req_keepalive () { pfx="$1" t_begin "make keepalive requests to trigger $pfx response body" && { > $r_out rm -f $curl_err $curl_out curl -vsSf http://$listen/$pfx/[1-3] 2> $curl_err > $curl_out } } req_keepalive file reload check_log file req_pipelined file reload check_log file req_keepalive blob reload check_log blob req_pipelined blob reload check_log blob req_keepalive pipe reload check_log pipe req_pipelined pipe reload check_log pipe t_begin "enable sendfile gem" && { echo "require 'sendfile'" >> $unicorn_config } reload req_keepalive file reload check_log file req_pipelined file reload check_log file t_begin "killing succeeds" && { kill $rainbows_pid } t_begin "check stderr" && { check_stderr } t_done rainbows-4.6.2/t/bin/0000755000004100000410000000000012354770160014417 5ustar www-datawww-datarainbows-4.6.2/t/bin/sha1sum.rb0000755000004100000410000000133612354770160016333 0ustar www-datawww-data#!/usr/bin/env ruby # -*- encoding: binary -*- # Reads from stdin and outputs the SHA1 hex digest of the input this is # ONLY used as a last resort, our test code will try to use sha1sum(1), # openssl(1), or gsha1sum(1) before falling back to using this. We try # all options first because we have a strong and healthy distrust of our # Ruby abilities in general, and *especially* when it comes to # understanding (and trusting the implementation of) Ruby 1.9 encoding. require 'digest/sha1' $stdout.sync = $stderr.sync = true $stdout.binmode $stdin.binmode bs = 16384 digest = Digest::SHA1.new if buf = $stdin.read(bs) begin digest.update(buf) end while $stdin.read(bs, buf) end $stdout.syswrite("#{digest.hexdigest}\n") rainbows-4.6.2/t/bin/unused_listen0000755000004100000410000000233612354770160017232 0ustar www-datawww-data#!/usr/bin/env ruby # -*- encoding: binary -*- # this is to remain compatible with the unused_port function in the # Unicorn test/test_helper.rb file require 'socket' require 'tmpdir' default_port = 8080 addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1' retries = 100 base = 5000 port = sock = lock_path = nil begin begin port = base + rand(32768 - base) while port == default_port port = base + rand(32768 - base) end sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) sock.bind(Socket.pack_sockaddr_in(port, addr)) sock.listen(5) rescue Errno::EADDRINUSE, Errno::EACCES sock.close rescue nil retry if (retries -= 1) >= 0 end # since we'll end up closing the random port we just got, there's a race # condition could allow the random port we just chose to reselect itself # when running tests in parallel with gmake. Create a lock file while # we have the port here to ensure that does not happen. lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock" lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600) rescue Errno::EEXIST sock.close rescue nil retry end sock.close rescue nil puts %Q(listen=#{addr}:#{port} T_RM_LIST="$T_RM_LIST #{lock_path}") rainbows-4.6.2/t/bin/content-md5-put0000755000004100000410000000173612354770160017317 0ustar www-datawww-data#!/usr/bin/env ruby # -*- encoding: binary -*- # simple chunked HTTP PUT request generator (and just that), # it reads stdin and writes to stdout so socat can write to a # UNIX or TCP socket (or to another filter or file) along with # a Content-MD5 trailer. require 'digest/md5' $stdout.sync = $stderr.sync = true $stdout.binmode $stdin.binmode bs = ENV['bs'] ? ENV['bs'].to_i : 4096 if ARGV.grep("--no-headers").empty? $stdout.write( "PUT / HTTP/1.1\r\n" \ "Host: example.com\r\n" \ "Connection: #{ENV["Connection"] || 'close'}\r\n" \ "Transfer-Encoding: chunked\r\n" \ "Trailer: Content-MD5\r\n" \ "\r\n" ) end digest = Digest::MD5.new if buf = $stdin.readpartial(bs) begin digest.update(buf) $stdout.write("%x\r\n" % [ buf.size ]) $stdout.write(buf) $stdout.write("\r\n") end while $stdin.read(bs, buf) end digest = [ digest.digest ].pack('m').strip $stdout.write("0\r\n") $stdout.write("Content-MD5: #{digest}\r\n\r\n") rainbows-4.6.2/t/async-response.ru0000644000004100000410000000034312354770160017170 0ustar www-datawww-datause Rack::Chunked use Rainbows::DevFdResponse run lambda { |env| io = IO.popen('for i in 0 1 2 3 4 5 6 7 8 9; do date; sleep 1; done', 'rb') [ 200, { 'Content-Type' => 'text/plain', }, io ].freeze } rainbows-4.6.2/t/t0500-cramp-streaming.sh0000755000004100000410000000335712354770160020055 0ustar www-datawww-data#!/bin/sh . ./test-lib.sh case $model in disabled) ;; *) t_info "skipping $T since it's not compatible with $model" exit 0 ;; esac RUBYLIB=$($RUBY test_isolate_cramp.rb):$RUBYLIB export RUBYLIB require_check cramp Cramp::VERSION t_plan 7 "streaming test for Cramp" CONFIG_RU=cramp/streaming.ru t_begin "setup and start" && { rainbows_setup rtmpfiles a b c curl_err expect # requiring Rubygems for this test only since Cramp depends on # pre versions of several gems # Like the rest of the EM/async stuff, it's not Rack::Lint compatible rainbows -E deployment -D $CONFIG_RU -c $unicorn_config rainbows_wait_start } # this will spew any unexpected input to stdout and be silent on success check () { ( i=0 while read hello world do t1=$(unix_time) diff=$(($t1 - $t0)) t_info "i=$i diff=$diff hello=$hello world=$world" test $diff -ge 1 || echo "$i: diff: $diff < 1 second" t0=$t1 test xHello = x"$hello" || echo "$i: Hello != $hello" test xWorld = x"$world" || echo "$i: World != $world" i=$(($i + 1)) test $i -le 3 || echo "$i: $i > 3" done ) } t_begin "send async requests off in parallel" && { t0=$(unix_time) curl --no-buffer -sSf http://$listen/ 2>> $curl_err | check >$a 2>&1 & curl --no-buffer -sSf http://$listen/ 2>> $curl_err | check >$b 2>&1 & curl --no-buffer -sSf http://$listen/ 2>> $curl_err | check >$c 2>&1 & } t_begin "wait for curl terminations" && { wait t1=$(unix_time) elapsed=$(( $t1 - $t0 )) t_info "elapsed=$elapsed (should be 4-5s)" } t_begin "termination signal sent" && { kill $rainbows_pid } t_begin "no errors from curl" && { test ! -s $curl_err } t_begin "no errors in stderr" && check_stderr t_begin "silence is golden" && { test ! -s $a test ! -s $b test ! -s $c } t_done rainbows-4.6.2/t/t9100.ru0000644000004100000410000000034212354770160014773 0ustar www-datawww-datause Rack::ContentLength use Rack::ContentType, 'text/plain' use Rainbows::ThreadTimeout, :timeout => 1 run lambda { |env| if env["PATH_INFO"] =~ %r{/([\d\.]+)\z} Rainbows.sleep($1.to_f) end [ 200, [], [ "HI\n" ] ] } rainbows-4.6.2/t/t0113.ru0000644000004100000410000000033312354770160014766 0ustar www-datawww-data#\ -E none use Rack::ContentLength use Rack::ContentType, 'text/plain' app = lambda do |env| case env['rack.input'] when Unicorn::StreamInput [ 200, {}, %w(OK) ] else [ 500, {}, %w(NO) ] end end run app rainbows-4.6.2/t/t0001-unix-http.sh0000755000004100000410000000456712354770160016724 0ustar www-datawww-data#!/bin/sh . ./test-lib.sh skip_models StreamResponseEpoll t_plan 19 "simple HTTP connection keepalive/pipelining tests for $model" t_begin "checking for config.ru for $model" && { tbase=simple-http_$model.ru test -f "$tbase" } t_begin "setup and start" && { rtmpfiles unix_socket rainbows_setup echo "listen '$unix_socket'" >> $unicorn_config rainbows -D $tbase -c $unicorn_config rainbows_wait_start } t_begin "pid file exists" && { test -f $pid } t_begin "single TCP request" && { curl -sSfv http://$listen/ } t_begin "handles client EOF gracefully" && { printf 'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n' | \ socat - UNIX:$unix_socket > $tmp dbgcat tmp if grep 'HTTP.* 500' $tmp then die "500 error returned on client shutdown(SHUT_WR)" fi check_stderr } dbgcat r_err t_begin "pipelining partial requests" && { req='GET / HTTP/1.1\r\nHost: example.com\r\n' ( cat $fifo > $tmp & printf "$req"'\r\n'"$req" sleep 1 printf 'Connection: close\r\n\r\n' wait echo ok > $ok ) | socat - UNIX:$unix_socket > $fifo } dbgcat tmp t_begin "two HTTP/1.1 responses" && { test 2 -eq $(grep '^HTTP/1.1' $tmp | count_lines) } t_begin "two HTTP/1.1 200 OK responses" && { test 2 -eq $(grep '^HTTP/1.1 200 OK' $tmp | count_lines) } t_begin 'one "Connection: keep-alive" response' && { test 1 -eq $(grep '^Connection: keep-alive' $tmp | count_lines) } t_begin 'one "Connection: close" response' && { test 1 -eq $(grep '^Connection: close' $tmp | count_lines) } t_begin 'check subshell success' && { test x"$(cat $ok)" = xok } t_begin "check stderr" && { check_stderr } t_begin "burst pipelining requests" && { req='GET / HTTP/1.1\r\nHost: example.com\r\n' ( cat $fifo > $tmp & printf "$req"'\r\n'"$req"'Connection: close\r\n\r\n' wait echo ok > $ok ) | socat - UNIX:$unix_socket > $fifo } dbgcat tmp dbgcat r_err t_begin "two HTTP/1.1 responses" && { test 2 -eq $(grep '^HTTP/1.1' $tmp | count_lines) } t_begin "two HTTP/1.1 200 OK responses" && { test 2 -eq $(grep '^HTTP/1.1 200 OK' $tmp | count_lines) } t_begin 'one "Connection: keep-alive" response' && { test 1 -eq $(grep '^Connection: keep-alive' $tmp | count_lines) } t_begin 'one "Connection: close" response' && { test 1 -eq $(grep '^Connection: close' $tmp | count_lines) } t_begin 'check subshell success' && { test x"$(cat $ok)" = xok } t_begin "killing succeeds" && { kill $rainbows_pid } t_done rainbows-4.6.2/t/fork-sleep.ru0000644000004100000410000000045612354770160016273 0ustar www-datawww-data# we do not want Rack::Lint or anything to protect us use Rack::ContentLength use Rack::ContentType, "text/plain" trap(:CHLD) { $stderr.puts Process.waitpid2(-1).inspect } map "/" do time = ENV["nr"] || '15' pid = fork { exec('sleep', time) } run lambda { |env| [ 200, {}, [ "#{pid}\n" ] ] } end rainbows-4.6.2/t/simple-http_Revactor.ru0000644000004100000410000000036512354770160020336 0ustar www-datawww-datause Rack::ContentLength use Rack::ContentType run lambda { |env| if env['rack.multithread'] == false && env['rainbows.model'] == :Revactor [ 200, {}, [ Thread.current.inspect << "\n" ] ] else raise "rack.multithread is true" end } rainbows-4.6.2/t/simple-http_ThreadPool.ru0000644000004100000410000000036212354770160020607 0ustar www-datawww-datause Rack::ContentLength use Rack::ContentType run lambda { |env| if env['rack.multithread'] && env['rainbows.model'] == :ThreadPool [ 200, {}, [ Thread.current.inspect << "\n" ] ] else raise "rack.multithread is not true" end } rainbows-4.6.2/t/simple-http_WriterThreadSpawn.ru0000644000004100000410000000036612354770160022167 0ustar www-datawww-datause Rack::ContentLength use Rack::ContentType run lambda { |env| if env['rack.multithread'] && env['rainbows.model'] == :WriterThreadSpawn [ 200, {}, [ Thread.current.inspect << "\n" ] ] else raise "rack.multithread is false" end } rainbows-4.6.2/t/t0200-async-response.sh0000755000004100000410000000260512354770160017725 0ustar www-datawww-data#!/bin/sh CONFIG_RU=${CONFIG_RU-'async-response.ru'} . ./test-lib.sh skip_models Base WriterThreadPool WriterThreadSpawn skip_models StreamResponseEpoll case $CONFIG_RU in *no-autochunk.ru) t_plan 7 "async response w/o autochunk for $model" skip_autochunk=true ;; *) t_plan 6 "async response for $model" skip_autochunk=false ;; esac t_begin "setup and start" && { rainbows_setup rtmpfiles a b c curl_err # can't load Rack::Lint here since it'll cause Rev to slurp rainbows -E none -D $CONFIG_RU -c $unicorn_config rainbows_wait_start } t_begin "send async requests off in parallel" && { t0=$(unix_time) ( curl --no-buffer -sSf http://$listen/ 2>> $curl_err | tee $a) & ( curl --no-buffer -sSf http://$listen/ 2>> $curl_err | tee $b) & ( curl --no-buffer -sSf http://$listen/ 2>> $curl_err | tee $c) & wait t1=$(unix_time) } t_begin "ensure elapsed requests were processed in parallel" && { elapsed=$(( $t1 - $t0 )) echo "elapsed=$elapsed < 30" test $elapsed -lt 30 } t_begin "termination signal sent" && { kill $rainbows_pid } dbgcat a dbgcat b dbgcat c dbgcat r_err dbgcat curl_err t_begin "no errors from curl" && { test ! -s $curl_err } t_begin "no errors in stderr" && check_stderr dbgcat r_err if $skip_autochunk then t_begin "no responses are chunked" && { test x"$(cat $a)" = x0123456789 test x"$(cat $b)" = x0123456789 test x"$(cat $c)" = x0123456789 } fi t_done rainbows-4.6.2/t/t0016-onenine-encoding-is-tricky.sh0000755000004100000410000000104512354770160022111 0ustar www-datawww-data#!/bin/sh . ./test-lib.sh t_plan 4 "proper handling of onenine encoding for $model" t_begin "setup and startup" && { rainbows_setup $model rainbows -E none -D ./t0016.rb -c $unicorn_config rainbows_wait_start expect_sha1=8ff79d8115f9fe38d18be858c66aa08a1cc27a66 } t_begin "response matches expected" && { rm -f $ok ( curl -sSf http://$listen/ && echo ok > $ok ) | rsha1 > $tmp test x$expect_sha1 = x"$(cat $tmp)" } t_begin "shutdown server" && { kill -QUIT $rainbows_pid } dbgcat r_err t_begin "check stderr" && check_stderr t_done rainbows-4.6.2/t/simple-http_NeverBlock.ru0000644000004100000410000000040712354770160020600 0ustar www-datawww-datause Rack::ContentLength use Rack::ContentType run lambda { |env| if env['rack.multithread'] == false && EM.reactor_running? && env['rainbows.model'] == :NeverBlock [ 200, {}, [ Thread.current.inspect << "\n" ] ] else raise env.inspect end } rainbows-4.6.2/t/t0005-large-file-response.sh0000755000004100000410000000373412354770160020626 0ustar www-datawww-data#!/bin/sh . ./test-lib.sh skip_models StreamResponseEpoll test -r random_blob || die "random_blob required, run with 'make $0'" if ! grep -v ^VmRSS: /proc/self/status >/dev/null 2>&1 then t_info "skipping, can't read RSS from /proc/self/status" exit 0 fi t_plan 10 "large file response slurp avoidance for $model" t_begin "setup and startup" && { rtmpfiles curl_out rainbows_setup $model 1 # can't load Rack::Lint here since it'll cause Rev to slurp rainbows -E none -D large-file-response.ru -c $unicorn_config rainbows_wait_start } t_begin "read random blob sha1 and size" && { random_blob_sha1=$(rsha1 < random_blob) random_blob_size=$(count_bytes < random_blob) } t_begin "read current RSS" && { curl -v http://$listen/rss dbgcat r_err rss_before=$(curl -sSfv http://$listen/rss) t_info "rss_before=$rss_before" } t_begin "send a series HTTP/1.1 requests sequentially" && { for i in a b c do sha1=$( (curl -sSfv http://$listen/random_blob && echo ok >$ok) | rsha1) test $sha1 = $random_blob_sha1 test xok = x$(cat $ok) done } # this was a problem during development t_begin "HTTP/1.0 test" && { sha1=$( (curl -0 -sSfv http://$listen/random_blob && echo ok >$ok) | rsha1) test $sha1 = $random_blob_sha1 test xok = x$(cat $ok) } t_begin "HTTP/0.9 test" && { ( printf 'GET /random_blob\r\n' rsha1 < $fifo > $tmp & wait echo ok > $ok ) | socat - TCP:$listen > $fifo test $(cat $tmp) = $random_blob_sha1 test xok = x$(cat $ok) } dbgcat r_err t_begin "read RSS again" && { curl -v http://$listen/rss rss_after=$(curl -sSfv http://$listen/rss) t_info "rss_after=$rss_after" } t_begin "shutdown server" && { kill -QUIT $rainbows_pid } t_begin "compare RSS before and after" && { diff=$(( $rss_after - $rss_before )) # default GC malloc limit in MRI: fudge=$(( 8 * 1024 * 1024 )) t_info "test diff=$diff < orig=$random_blob_size" test $diff -le $(( $random_blob_size + $fudge )) } dbgcat r_err t_begin "check stderr" && check_stderr t_done rainbows-4.6.2/t/t0020-large-sendfile-response.sh0000755000004100000410000000616412354770160021475 0ustar www-datawww-data#!/bin/sh . ./test-lib.sh skip_models StreamResponseEpoll test -r random_blob || die "random_blob required, run with 'make $0'" case $RUBY_ENGINE in ruby) ;; *) t_info "skipping $T since it can't load the sendfile gem, yet" exit 0 ;; esac t_plan 12 "large sendfile response for $model" t_begin "setup and startup" && { rtmpfiles curl_out a b c slow_a slow_b rainbows_setup $model echo 'require "sendfile"' >> $unicorn_config echo 'def (::IO).copy_stream(*x); abort "NO"; end' >> $unicorn_config # can't load Rack::Lint here since it clobbers body#to_path rainbows -E none -D large-file-response.ru -c $unicorn_config rainbows_wait_start } t_begin "read random blob sha1" && { random_blob_sha1=$(rsha1 < random_blob) three_sha1=$(cat random_blob random_blob random_blob | rsha1) } t_begin "send keepalive HTTP/1.1 requests in parallel" && { for i in $a $b $c $slow_a $slow_b do curl -sSf http://$listen/random_blob \ http://$listen/random_blob \ http://$listen/random_blob | rsha1 > $i & done wait for i in $a $b $c $slow_a $slow_b do test x$(cat $i) = x$three_sha1 done } t_begin "send a batch of abortive HTTP/1.1 requests in parallel" && { for i in $a $b $c $slow_a $slow_b do rm -f $i ( curl -sSf --max-time 2 --limit-rate 1K \ http://$listen/random_blob >/dev/null || echo ok > $i ) & done wait } t_begin "all requests timed out" && { for i in $a $b $c $slow_a $slow_b do test x$(cat $i) = xok done } s='$NF ~ /worker_connections=[0-9]+/{gsub(/[^0-9]/,"",$3); print $3; exit}' t_begin "check proc to ensure file is closed properly (Linux only)" && { worker_pid=$(awk "$s" < $r_err) test -n "$worker_pid" if test -d /proc/$worker_pid/fd then if ls -l /proc/$worker_pid/fd | grep random_blob then t_info "random_blob file is open ($model)" fi else t_info "/proc/$worker_pid/fd not found" fi } t_begin "send a bunch of HTTP/1.1 requests in parallel" && { ( curl -sSf --limit-rate 5M http://$listen/random_blob | \ rsha1 > $slow_a ) & ( curl -sSf --limit-rate 6M http://$listen/random_blob | \ rsha1 > $slow_b ) & for i in $a $b $c do ( curl -sSf http://$listen/random_blob | rsha1 > $i ) & done wait for i in $a $b $c $slow_a $slow_b do test x$(cat $i) = x$random_blob_sha1 done } # this was a problem during development t_begin "HTTP/1.0 test" && { sha1=$( (curl -0 -sSf http://$listen/random_blob && echo ok >$ok) | rsha1) test $sha1 = $random_blob_sha1 test xok = x$(cat $ok) } t_begin "HTTP/0.9 test" && { ( printf 'GET /random_blob\r\n' rsha1 < $fifo > $tmp & wait echo ok > $ok ) | socat - TCP:$listen > $fifo test $(cat $tmp) = $random_blob_sha1 test xok = x$(cat $ok) } t_begin "check proc to ensure file is closed properly (Linux only)" && { worker_pid=$(awk "$s" < $r_err) test -n "$worker_pid" if test -d /proc/$worker_pid/fd then if ls -l /proc/$worker_pid/fd | grep random_blob then t_info "random_blob file is open ($model)" fi else t_info "/proc/$worker_pid/fd not found" fi } t_begin "shutdown server" && { kill -QUIT $rainbows_pid } dbgcat r_err t_begin "check stderr" && check_stderr t_done rainbows-4.6.2/t/simple-http_RevThreadSpawn.ru0000644000004100000410000000035012354770160021440 0ustar www-datawww-datause Rack::ContentLength use Rack::ContentType run lambda { |env| if env['rack.multithread'] && env['rainbows.model'] == :RevThreadSpawn [ 200, {}, [ env.inspect << "\n" ] ] else raise "rack.multithread is false" end } rainbows-4.6.2/t/test_isolate_cramp.rb0000644000004100000410000000110312354770160020050 0ustar www-datawww-datarequire 'rubygems' require 'isolate' engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' path = "tmp/isolate/#{engine}-#{RUBY_VERSION}/cramp" opts = { :system => false, # we want "ruby-1.8.7" and not "ruby-1.8", so disable multiruby :multiruby => false, :path => path, } old_out = $stdout.dup $stdout.reopen($stderr) lock = File.open(__FILE__, "rb") lock.flock(File::LOCK_EX) Isolate.now!(opts) do if engine == "ruby" gem 'cramp', '0.15' end end $stdout.reopen(old_out) dirs = Dir["#{path}/gems/*-*/lib"] puts dirs.map { |x| File.expand_path(x) }.join(':') rainbows-4.6.2/t/simple-http_RevThreadPool.ru0000644000004100000410000000034712354770160021267 0ustar www-datawww-datause Rack::ContentLength use Rack::ContentType run lambda { |env| if env['rack.multithread'] && env['rainbows.model'] == :RevThreadPool [ 200, {}, [ env.inspect << "\n" ] ] else raise "rack.multithread is false" end } rainbows-4.6.2/t/hijack.ru0000644000004100000410000000240312354770160015447 0ustar www-datawww-datause Rack::Lint use Rack::ContentLength use Rack::ContentType, "text/plain" class DieIfUsed def each abort "body.each called after response hijack\n" end def close abort "body.close called after response hijack\n" end end def lazy_close(io) thr = Thread.new do # wait and see if Rainbows! accidentally closes us sleep((ENV["DELAY"] || 10).to_i) begin io.close rescue => e warn "E: #{e.message} (#{e.class})" exit!(3) end end at_exit { thr.join } end run lambda { |env| case env["PATH_INFO"] when "/hijack_req" if env["rack.hijack?"] io = env["rack.hijack"].call if io.respond_to?(:read_nonblock) && env["rack.hijack_io"].respond_to?(:read_nonblock) # exercise both, since we Rack::Lint may use different objects env["rack.hijack_io"].write("HTTP/1.0 200 OK\r\n\r\n") io.write("request.hijacked") lazy_close(io) return [ 500, {}, DieIfUsed.new ] end end [ 500, {}, [ "hijack BAD\n" ] ] when "/hijack_res" r = "response.hijacked" [ 200, { "Content-Length" => r.bytesize.to_s, "rack.hijack" => proc do |io| io.write(r) lazy_close(io) end }, DieIfUsed.new ] end } rainbows-4.6.2/t/async_examples/0000755000004100000410000000000012354770160016662 5ustar www-datawww-datarainbows-4.6.2/t/async_examples/README0000644000004100000410000000025312354770160017542 0ustar www-datawww-dataThese examples in this directory are stolen from Thin 1.2.5 with only trivial changes. All examples in this directory retain their original license (MIT) and copyrights. rainbows-4.6.2/t/async_examples/async_app.ru0000644000004100000410000000731712354770160021217 0ustar www-datawww-data#!/usr/bin/env rackup -s thin # # async_app.ru # raggi/thin # # A second demo app for async rack + thin app processing! # Now using http status code 100 instead. # # Created by James Tucker on 2008-06-17. # Copyright 2008 James Tucker . # #-- # Benchmark Results: # # raggi@mbk:~$ ab -c 100 -n 500 http://127.0.0.1:3000/ # This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0 # Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ # Copyright 2006 The Apache Software Foundation, http://www.apache.org/ # # Benchmarking 127.0.0.1 (be patient) # Completed 100 requests # Completed 200 requests # Completed 300 requests # Completed 400 requests # Finished 500 requests # # # Server Software: thin # Server Hostname: 127.0.0.1 # Server Port: 3000 # # Document Path: / # Document Length: 12 bytes # # Concurrency Level: 100 # Time taken for tests: 5.263089 seconds # Complete requests: 500 # Failed requests: 0 # Write errors: 0 # Total transferred: 47000 bytes # HTML transferred: 6000 bytes # Requests per second: 95.00 [#/sec] (mean) # Time per request: 1052.618 [ms] (mean) # Time per request: 10.526 [ms] (mean, across all concurrent requests) # Transfer rate: 8.55 [Kbytes/sec] received # # Connection Times (ms) # min mean[+/-sd] median max # Connect: 0 3 2.2 3 8 # Processing: 1042 1046 3.1 1046 1053 # Waiting: 1037 1042 3.6 1041 1050 # Total: 1045 1049 3.1 1049 1057 # # Percentage of the requests served within a certain time (ms) # 50% 1049 # 66% 1051 # 75% 1053 # 80% 1053 # 90% 1054 # 95% 1054 # 98% 1056 # 99% 1057 # 100% 1057 (longest request) class DeferrableBody include EventMachine::Deferrable def call(body) body.each do |chunk| @body_callback.call(chunk) end end def each &blk @body_callback = blk end end class AsyncApp # This is a template async response. N.B. Can't use string for body on 1.9 AsyncResponse = [-1, {}, []].freeze def call(env) body = DeferrableBody.new # Get the headers out there asap, let the client know we're alive... EventMachine::next_tick { env['async.callback'].call [200, {'Content-Type' => 'text/plain'}, body] } # Semi-emulate a long db request, instead of a timer, in reality we'd be # waiting for the response data. Whilst this happens, other connections # can be serviced. # This could be any callback based thing though, a deferrable waiting on # IO data, a db request, an http request, an smtp send, whatever. EventMachine::add_timer(1) { body.call ["Woah, async!\n"] EventMachine::next_tick { # This could actually happen any time, you could spawn off to new # threads, pause as a good looking lady walks by, whatever. # Just shows off how we can defer chunks of data in the body, you can # even call this many times. body.call ["Cheers then!"] body.succeed } } # throw :async # Still works for supporting non-async frameworks... AsyncResponse # May end up in Rack :-) end end # The additions to env for async.connection and async.callback absolutely # destroy the speed of the request if Lint is doing it's checks on env. # It is also important to note that an async response will not pass through # any further middleware, as the async response notification has been passed # right up to the webserver, and the callback goes directly there too. # Middleware could possibly catch :async, and also provide a different # async.connection and async.callback. # use Rack::Lint run AsyncApp.new rainbows-4.6.2/t/async_examples/async_tailer.ru0000644000004100000410000000372212354770160021713 0ustar www-datawww-data#!/usr/bin/env rackup -s thin # # async_tailer.ru # raggi/thin # # Tested with 150 spawned tails on OS X # # Created by James Tucker on 2008-06-18. # Copyright 2008 James Tucker . # Uncomment if appropriate for you.. # EM.epoll # EM.kqueue tail_log_file = ENV["TAIL_LOG_FILE"] or abort "TAIL_LOG_FILE= env must be set" unless ::File.file?(tail_log_file) && ::File.readable?(tail_log_file) abort "#{tail_log_file} must be a readable regular file" end class DeferrableBody include EventMachine::Deferrable def initialize @queue = [] # make sure to flush out the queue before closing the connection callback{ until @queue.empty? @queue.shift.each{|chunk| @body_callback.call(chunk) } end } end def schedule_dequeue return unless @body_callback EventMachine::next_tick do next unless body = @queue.shift body.each do |chunk| @body_callback.call(chunk) end schedule_dequeue unless @queue.empty? end end def call(body) @queue << body schedule_dequeue end def each &blk @body_callback = blk schedule_dequeue end end module TailRenderer attr_accessor :callback def receive_data(data) @callback.call([data]) end def unbind @callback.succeed end end class AsyncTailer AsyncResponse = [-1, {}, []].freeze def call(env) body = DeferrableBody.new EventMachine::next_tick do env['async.callback'].call [200, {'Content-Type' => 'text/html'}, body] body.call ["

Async Tailer

"]

    end

    EventMachine::popen("tail -f #{ENV["TAIL_LOG_FILE"]}", TailRenderer) do |t|

      t.callback = body

      # If for some reason we 'complete' body, close the tail.
      body.callback do
        t.close_connection
      end

      # If for some reason the client disconnects, close the tail.
      body.errback do
        t.close_connection
      end

    end

    AsyncResponse
  end

end

run AsyncTailer.new
rainbows-4.6.2/t/simple-http_CoolioThreadPool.ru0000644000004100000410000000035212354770160021753 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] && env['rainbows.model'] == :CoolioThreadPool
    [ 200, {}, [ env.inspect << "\n" ] ]
  else
    raise "rack.multithread is false"
  end
}
rainbows-4.6.2/t/t0008-ensure-usable-after-limit.sh0000755000004100000410000000612612354770160021751 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
test -r random_blob || die "random_blob required, run with 'make $0'"

t_plan 14 "ensure we're accounting worker_connections properly"
nr=2

t_begin "setup" && {
	rtmpfiles a b c d
	rainbows_setup $model $nr
	rainbows -D sha1.ru -c $unicorn_config
	rainbows_wait_start
}

null_sha1=da39a3ee5e6b4b0d3255bfef95601890afd80709

t_begin "fire off concurrent processes" && {

	req='POST / HTTP/1.1\r\n'
	req="$req"'Host: example.com\r\n'
	req="$req"'Transfer-Encoding: chunked\r\n\r\n'

	for i in a b c d
	do
		rtmpfiles ${i}_fifo ${i}_tmp
		eval 'i_fifo=$'${i}_fifo
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		(
			(
				cat $i_fifo > $i_tmp &
				# need a full HTTP request to get around
				# httpready
				printf "$req"
				sleep 5
				printf '0\r\n\r\n'
				wait
				echo ok > $i
			) | socat - TCP:$listen > $i_fifo
		) &
	done
	wait
}

t_begin "check results" && {
	for i in a b c d
	do
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		test xok = x$(cat $i)
		test x$null_sha1 = x$(tail -1 $i_tmp)
	done
}

t_begin "repeat concurrent tests with faster clients" && {
	for i in a b c d
	do
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		curl -sSf -T-  $i 2> $i_tmp &
	done
	wait
}

t_begin "check results" && {
	for i in a b c d
	do
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		test ! -s $i_tmp
		test x$null_sha1 = x$(cat $i)
	done
}

t_begin "fire off truncated concurrent requests" && {

	req='POST / HTTP/1.1\r\n'
	req="$req"'Host: example.com\r\n'
	req="$req"'Transfer-Encoding: chunked\r\n'

	for i in a b c d
	do
		rtmpfiles ${i}_tmp
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		(
			(
				# need a full HTTP request to get around
				# httpready
				printf "$req"
				echo ok > $i
			) | socat - TCP:$listen > $i_tmp
		) &
	done
	wait
}

t_begin "check broken results" && {
	for i in a b c d
	do
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		test xok = x$(cat $i)
		dbgcat i_tmp
	done
}

t_begin "repeat concurrent tests with faster clients" && {
	for i in a b c d
	do
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		curl -sSf -T-  $i 2> $i_tmp &
	done
	wait
}

t_begin "check results" && {
	for i in a b c d
	do
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		test ! -s $i_tmp
		test x$null_sha1 = x$(cat $i)
	done
}

t_begin "fire off garbage" && {
	for i in a b c d
	do
		rtmpfiles ${i}_fifo ${i}_tmp
		eval 'i_fifo=$'${i}_fifo
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		(
			(
				cat $i_fifo > $i_tmp &
				dd if=random_blob bs=4096 count=1
				wait
				echo ok > $i
			) | socat - TCP:$listen > $i_fifo
		) &
	done
	wait
}

t_begin "check broken results" && {
	for i in a b c d
	do
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		test xok = x$(cat $i)
		grep -F 'HTTP/1.1 400 Bad Request' $i_tmp
	done
}

t_begin "repeat concurrent tests with faster clients" && {
	for i in a b c d
	do
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		curl -sSf -T-  $i 2> $i_tmp &
	done
	wait
}

t_begin "check results" && {
	for i in a b c d
	do
		eval 'i_tmp=$'${i}_tmp
		eval "i=$"$i
		test ! -s $i_tmp
		test x$null_sha1 = x$(cat $i)
	done
}

t_begin "teardown" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/t0009.ru0000644000004100000410000000042412354770160014773 0ustar  www-datawww-data# we do not want Rack::Lint or anything to protect us
use Rack::ContentLength
use Rack::ContentType, "text/plain"
map "/" do
  run lambda { |env| [ 200, {}, [ "OK\n" ] ] }
end
map "/raise" do
  run lambda { |env| raise "BAD" }
end
map "/nil" do
  run lambda { |env| nil }
end
rainbows-4.6.2/t/t9101.ru0000644000004100000410000000036412354770160015000 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType, 'text/plain'
use Rainbows::ThreadTimeout, :timeout => 1, :threshold => -1
run lambda { |env|
  if env["PATH_INFO"] =~ %r{/([\d\.]+)\z}
    Rainbows.sleep($1.to_f)
  end
  [ 200, [], [ "HI\n" ] ]
}
rainbows-4.6.2/t/t9000-rack-app-pool.sh0000755000004100000410000000200512354770160017422 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
case $model in
*CoolioThread*|*RevThread*|Thread*|*Fiber*|Revactor|NeverBlock) ;;
*)
	t_info "skipping $T since it's not compatible with $model"
	exit 0
	;;
esac
nr_client=30 APP_POOL_SIZE=4

t_plan 6 "AppPool Rack middleware test for $model"

t_begin "configure and start" && {
	rtmpfiles curl_out curl_err
	rainbows_setup $model 50
	APP_POOL_SIZE=$APP_POOL_SIZE rainbows -D t9000.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "launch $nr_client requests" && {
	start=$(unix_time)
	seq="$(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" > $curl_out 2>> $curl_err &
	done
	wait
	t_info elapsed=$(( $(unix_time) - $start ))
}

t_begin "kill server" && {
	kill $rainbows_pid
}

t_begin "$APP_POOL_SIZE instances of app were used" && {
	test $APP_POOL_SIZE -eq $(sort < $curl_out | uniq | count_lines)
}

t_begin "no errors in curl stderr" && {
	test ! -s $curl_err
}

t_begin "no errors in Rainbows! stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/large-file-response.ru0000644000004100000410000000071312354770160020063 0ustar  www-datawww-data# lib-large-file-response will stop running if we're not on Linux here
use Rack::ContentLength
use Rack::ContentType
map "/rss" do
  run lambda { |env|
    # on Linux, this is in kilobytes
    GC.start if GC.respond_to?(:start)
    ::File.read("/proc/self/status") =~ /^VmRSS:\s+(\d+)/
    [ 200, {}, [ ($1.to_i * 1024).to_s ] ]
  }
end
map "/pid" do
  run lambda { |env| [ 200, {}, [ "#{Process.pid}\n" ] ] }
end
map "/" do
  run Rack::File.new(Dir.pwd)
end
rainbows-4.6.2/t/close-pipe-to_path-response.ru0000644000004100000410000000146312354770160021553 0ustar  www-datawww-data# must be run without Rack::Lint since that clobbers to_path
class MyMiddleware < Struct.new(:app)
  class Body < Struct.new(:body, :to_path)
    def each(&block); body.each(&block); end
    def close
      c = body.respond_to?(:close)
      ::File.open(ENV['fifo'], 'wb') do |fp|
        fp.syswrite("CLOSING #{body.inspect} #{to_path} (#{c})\n")
      end
      body.close if c
    end
  end

  def call(env)
    status, headers, body = app.call(env)
    body.respond_to?(:to_path) and body = Body.new(body, body.to_path)
    [ status, headers, body ]
  end
end
use MyMiddleware
use Rainbows::DevFdResponse
run(lambda { |env|
  io = IO.popen('cat random_blob', 'rb')
  [ 200,
    {
      'Content-Length' => ::File.stat('random_blob').size.to_s,
      'Content-Type' => 'application/octet-stream',
    },
    io ]
})
rainbows-4.6.2/t/GNUmakefile0000644000004100000410000000664412354770160015733 0ustar  www-datawww-data# we can run tests in parallel with GNU make

all::

pid := $(shell echo $$PPID)

MRI = ruby
RUBY = ruby
rainbows_lib := $(shell cd ../lib && pwd)
-include ../local.mk
ifeq ($(RUBY_VERSION),)
  RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
endif

ifeq ($(RUBY_VERSION),)
  $(error unable to detect RUBY_VERSION)
endif

RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
export RUBY_VERSION RUBY_ENGINE

ifeq (Linux,$(shell uname -s))
  models += XEpoll
  models += XEpollThreadSpawn
  models += XEpollThreadPool
  models += Epoll
endif
models += WriterThreadPool
models += WriterThreadSpawn
models += ThreadPool
models += ThreadSpawn
models += Coolio

models += StreamResponseEpoll

ifeq ($(RUBY_ENGINE),ruby)
  rp := )
  ONENINE := $(shell case $(RUBY_VERSION) in 1.9.*|2.*$(rp) echo true;;esac)
  ifeq ($(ONENINE),true)
    ifeq ($(RUBY_VERSION),1.9.2)
      models += Revactor
    endif
    models += FiberSpawn
    models += FiberPool
    models += CoolioThreadPool
    models += CoolioThreadSpawn
    models += CoolioFiberSpawn

    models += EventMachine
    models += NeverBlock
  endif
endif

ifeq ($(RUBY_ENGINE),rbx)
  models += ActorSpawn
endif
all_models := $(models) Base

T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)

MODEL_T := $(foreach m,$(all_models),$(addprefix $(m).,$(T)))
$(T): MODELS = $(models)

# some tests can be run with all models
t0000-simple-http.sh: MODELS = $(all_models)
t0001-unix-http.sh: MODELS = $(all_models)
t0002-graceful.sh: MODELS = $(all_models)
t0002-parser-error.sh: MODELS = $(all_models)
t0003-reopen-logs.sh: MODELS = $(all_models)

# recursively run per-model tests
# haven't figured out a good way to make make non-recursive here, yet...
$(T):
	$(MAKE) $(foreach m,$(MODELS),$(addprefix $(m).,$@))

$(all_models):
	$(MAKE) $(filter $@.%,$(MODEL_T))

all:: $(T)

# can't rely on "set -o pipefail" since we don't require bash or ksh93 :<
t_pfx = trash/$@-$(RUBY_ENGINE)-$(RUBY_VERSION)
TEST_OPTS =
# TRACER = strace -f -o $(t_pfx).strace -s 100000
# TRACER = /usr/bin/time -o $(t_pfx).time

ifdef V
  ifeq ($(V),2)
    TEST_OPTS += --trace
  else
    TEST_OPTS += --verbose
  endif
endif

bindir := $(CURDIR)/bin-$(RUBY_ENGINE)-$(RUBY_VERSION)
bin_rainbows := $(bindir)/rainbows
$(bin_rainbows): ruby_bin = $(shell which $(RUBY))
$(bin_rainbows): ../bin/rainbows
	mkdir -p $(@D)
	install -m 755 $^ $@.$(pid)
	$(MRI) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@.$(pid)
	mv $@.$(pid) $@

random_blob:
	dd if=/dev/urandom bs=1M count=30 of=$@.$(pid)
	mv $@.$(pid) $@

dependencies := socat curl
deps := $(addprefix .dep+,$(dependencies))
$(deps): dep_bin = $(lastword $(subst +, ,$@))
$(deps):
	@which $(dep_bin) > $@.$(pid) 2>/dev/null || :
	@test -s $@.$(pid) || \
	  { echo >&2 "E '$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
	@mv $@.$(pid) $@

libs := tmp/isolate/$(RUBY_ENGINE)-$(RUBY_VERSION)/.libs
$(libs): test_isolate.rb
	mkdir -p $(@D)
	$(RUBY) $< > $@+
	mv $@+ $@
t_deps := random_blob $(libs) $(deps) $(bin_rainbows) trash/.gitignore
$(T): $(t_deps)

$(MODEL_T): export model = $(firstword $(subst ., ,$@))
$(MODEL_T): script = $(subst $(model).,,$@)
$(MODEL_T): export RUBY := $(RUBY)
$(MODEL_T): export PATH := $(bindir):$(PATH)
$(MODEL_T): $(t_deps)
	RUBYLIB=$(rainbows_lib):$$(cat $(libs)):$(RUBYLIB) \
	   $(TRACER) $(SHELL) $(SH_TEST_OPTS) $(script) $(TEST_OPTS)

trash/.gitignore:
	mkdir -p $(@D)
	echo '*' > $@

clean:
	$(RM) -r trash/*.log trash/*.code $(bindir)

.PHONY: $(T) clean
rainbows-4.6.2/t/times.ru0000644000004100000410000000023412354770160015337 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType, "text/plain"
run lambda { |env|
  t = Process.times
  [ 200, {}, [ "utime=#{t.utime} stime=#{t.stime}" ] ]
}
rainbows-4.6.2/t/t0400-em-async-app.sh0000755000004100000410000000220112354770160017240 0ustar  www-datawww-data#!/bin/sh
nr=${nr-5}
. ./test-lib.sh
case $model in
NeverBlock|EventMachine) ;;
*)
	t_info "skipping $T since it's not compatible with $model"
	exit 0
	;;
esac

t_plan 7 "async_app test for test for EM"

CONFIG_RU=async_examples/async_app.ru

t_begin "setup and start" && {
	rainbows_setup
	rtmpfiles a b c curl_err expect

	# this does not does not support Rack::Lint
	rainbows -E deployment -D $CONFIG_RU -c $unicorn_config
	rainbows_wait_start
}

t_begin "send async requests off in parallel" && {
	t0=$(unix_time)
	curl --no-buffer -sSf http://$listen/ > $a 2>> $curl_err &
	curl --no-buffer -sSf http://$listen/ > $b 2>> $curl_err &
	curl --no-buffer -sSf http://$listen/ > $c 2>> $curl_err &
}

t_begin "wait for curl terminations" && {
	wait
	t1=$(unix_time)
	elapsed=$(( $t1 - $t0 ))
	t_info "elapsed=$elapsed"
}

t_begin "termination signal sent" && {
	kill $rainbows_pid
}

t_begin "no errors from curl" && {
	test ! -s $curl_err
}

t_begin "no errors in stderr" && check_stderr

t_begin "responses match expected" && {
	echo 'Woah, async!' > $expect
	printf 'Cheers then!' >> $expect
	cmp $expect $a
	cmp $expect $b
	cmp $expect $c
}

t_done
rainbows-4.6.2/t/simple-http_Coolio.ru0000644000004100000410000000035012354770160017767 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == false && env['rainbows.model'] == :Coolio
    [ 200, {}, [ env.inspect << "\n" ] ]
  else
    raise "rack.multithread is true"
  end
}
rainbows-4.6.2/t/README0000644000004100000410000000412312354770160014527 0ustar  www-datawww-data= \Rainbows! test suite - YES OUR TEST SUITE IS CONCURRENT!

These are all integration tests that start the server on random, unused
TCP ports or Unix domain sockets.  They're all designed to run
concurrently with other tests to minimize test time, but tests may be
run independently as well.

We write our tests primarily in Bourne shell because that's what we're
comfortable writing integration tests with.  This test suite is also
easily portable to non-Ruby web servers.

== Requirements

* {Ruby 1.8 or 1.9}[http://www.ruby-lang.org/] (duh!)
* {isolate ~> 2.1.0}[http://github.com/jbarnette/isolate] - for dependencies
* {GNU make}[http://www.gnu.org/software/make/]
* {socat}[http://www.dest-unreach.org/socat/]
* {curl >= 7.18.0}[http://curl.haxx.se/]
* standard UNIX shell utilities (Bourne sh, awk, sed, grep, ...)

We do not use bashisms or any non-portable, non-POSIX constructs
in our shell code.  We use the "pipefail" option if available and
mainly test with {ksh}[http://kornshell.com/], but occasionally
with {dash}[http://gondor.apana.org.au/~herbert/dash/] and
{bash}[http://www.gnu.org/software/bash/], too.

== Running Tests

*BSD users: use "gmake" instead of "make"

To run the entire test suite with 8 tests running at once:

  make -j8

To run one individual test for all concurrency models:

  make t0000-simple-http.sh

To run one individual test for one concurrency model:

  make Revactor.t0000-simple-http.sh

To run all tests for one concurrency model:

  make EventMachine

You may also increase verbosity by setting the "V" variable for
GNU make.  To disable trapping of stdout/stderr:

  make V=1

To enable the "set -x" option in shell scripts to trace execution

  make V=2

== Performance

Some of the tests are rather I/O intensive due to the rewindability
requirement of "rack.input" in the Rack specification and the somewhat
complicated (but awesome!) nature of the TeeInput class leading us to
test it very heavily.  If you have lots of RAM and a large tmpfs
partition, it is advisable to set your TMPDIR and also make the t/trash/
directory a symlink to a directory inside in your TMPDIR.
rainbows-4.6.2/t/t0107-rack-input-limit-zero.sh0000755000004100000410000000242012354770160021123 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
req_curl_chunked_upload_err_check

t_plan 6 "rack.input client_max_body_size zero"

t_begin "setup and startup" && {
	rtmpfiles curl_out curl_err
	rainbows_setup $model
	ed -s $unicorn_config < $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test x"$(cat $curl_out)" = x$empty_sha1
}

t_begin "chunked request" && {
	curl -vsSf -T- < /dev/null -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test x"$(cat $curl_out)" = x$empty_sha1
}

t_begin "small input chunked" && {
	rm -f $ok
	echo | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	fgrep 413 $curl_err
	test -e $ok
}

t_begin "small input content-length" && {
	rm -f $ok
	echo > $tmp
	curl -vsSf -T $tmp -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err || > $ok
	fgrep 413 $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test -e $ok
}

t_begin "shutdown" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/t0016.rb0000644000004100000410000000055412354770160014752 0ustar  www-datawww-data# -*- encoding: utf-8 -*-
module T0016
  CHUNK = '©' * 1024 * 1024
  BODY = (1..50).map { CHUNK }
  HEADER = {
    # BODY.inject(0) { |m,c| m += c.bytesize }.to_s,
    'Content-Length' => '104857600',
    'Content-Type' => 'text/plain',
  }

  def self.call(env)
    [ 200, HEADER, BODY ]
  end
end
$0 == __FILE__ and T0016::BODY.each { |x| $stdout.syswrite(x) }
rainbows-4.6.2/t/sha1.ru0000644000004100000410000000070512354770160015055 0ustar  www-datawww-data# SHA1 checksum generator
bs = ENV['bs'] ? ENV['bs'].to_i : 16384
require 'digest/sha1'
use Rack::ContentLength
app = lambda do |env|
  /\A100-continue\z/i =~ env['HTTP_EXPECT'] and
    return [ 100, {}, [] ]
  digest = Digest::SHA1.new
  input = env['rack.input']
  if buf = input.read(bs)
    begin
      digest.update(buf)
    end while input.read(bs, buf)
  end

  [ 200, {'Content-Type' => 'text/plain'}, [ digest.hexdigest << "\n" ] ]
end
run app
rainbows-4.6.2/t/t0043-quit-keepalive-disconnect.sh0000644000004100000410000000215112354770160022026 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
case $model in
Coolio|CoolioThreadSpawn|CoolioThreadPool|EventMachine) ;;
Epoll|XEpoll|XEpollThreadPool|XEpollThreadSpawn) ;;
*)
	t_info "$0 not supported for $model"
	exit 0
	;;
esac

t_plan 7 "keepalive clients disconnected on SIGQUIT for $model"

t_begin "setup and start" && {
	rainbows_setup $model 50 30
	rainbows -E none -D env.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "start a keepalive request" && {
	(
		cat < $fifo > $tmp &
		printf 'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'
		wait
	) | socat - TCP4:$listen > $fifo &
}

t_begin "wait for response" && {
	while ! tail -1 < $tmp | grep '}$' >/dev/null
	do
		sleep 1
	done
}

t_begin "stop Rainbows! gracefully" && {
	t0=$(unix_time)
	kill -QUIT $rainbows_pid
}

t_begin "keepalive client disconnected quickly" && {
	wait
	diff=$(( $(unix_time) - $t0 ))
	test $diff -le 2 || die "client diff=$diff > 2"
}

t_begin "wait for termination" && {
	while kill -0 $rainbows_pid
	do
		sleep 1
	done
	diff=$(( $(unix_time) - $t0 ))
	test $diff -le 4 || die "server diff=$diff > 4"
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/t0100-rack-input-hammer-content-length.sh0000755000004100000410000000250312354770160023223 0ustar  www-datawww-datanr_client=${nr_client-4}
. ./test-lib.sh
test -r random_blob || die "random_blob required, run with 'make $0'"

# basically we don't trust our own implementation of content-md5-put
# nor our Ruby 1.9 knowledge nor proper use of encodings in Ruby.
# So we try to use things like curl and sha1sum that are implemented
# without the Ruby interpreter to validate our own Ruby internals.

t_plan 7 "concurrent rack.input hammer stress test (content-length)"

t_begin "setup and startup" && {
	rtmpfiles curl_out curl_err
	rainbows_setup $model
	rainbows -D sha1.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "send $nr_client concurrent requests" && {
	start=$(unix_time)
	for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" > $curl_out 2>> $curl_err
		) &
	done
	wait
	t_info elapsed=$(( $(unix_time) - $start ))
}

t_begin "kill server" && kill $rainbows_pid

t_begin "got $nr_client responses" && {
	test $nr_client -eq $(count_lines < $curl_out)
}

t_begin "all responses identical" && {
	test 1 -eq $(sort < $curl_out | uniq | count_lines)
}

t_begin "sha1 matches on-disk sha1" && {
	blob_sha1=$(rsha1 < random_blob)
	t_info blob_sha1=$blob_sha1
	test x"$blob_sha1" = x"$(sort < $curl_out | uniq)"
}

t_begin "no errors in stderr log" && check_stderr

t_done
rainbows-4.6.2/t/t0201-async-response-no-autochunk.sh0000755000004100000410000000011712354770160022333 0ustar  www-datawww-data#!/bin/sh
CONFIG_RU=async-response-no-autochunk.ru
. ./t0200-async-response.sh
rainbows-4.6.2/t/t0013-reload-bad-config.sh0000755000004100000410000000166212354770160020215 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
t_plan 7 "reload config.ru error with preload_app true"

t_begin "setup and start" && {
	rainbows_setup
	rtmpfiles ru

	cat > $ru <<\EOF
use Rack::ContentLength
use Rack::ContentType, "text/plain"
x = { "hello" => "world" }
run lambda { |env| [ 200, {}, [ x.inspect << "\n" ] ] }
EOF
	echo 'preload_app true' >> $unicorn_config
	rainbows -D -c $unicorn_config $ru
	rainbows_wait_start
}

t_begin "hit with curl" && {
	out=$(curl -sSf http://$listen/)
	test x"$out" = x'{"hello"=>"world"}'
}

t_begin "introduce syntax error in rackup file" && {
	echo '...' >> $ru
}

t_begin "reload signal succeeds" && {
	kill -HUP $rainbows_pid
	rainbows_wait_start
	wait_for_reload $r_err error
	wait_for_reap
	> $r_err
}

t_begin "hit with curl" && {
	out=$(curl -sSf http://$listen/)
	test x"$out" = x'{"hello"=>"world"}'
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/t0105-rack-input-limit-bigger.sh0000755000004100000410000000546312354770160021413 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
test -r random_blob || die "random_blob required, run with 'make $0'"
req_curl_chunked_upload_err_check

t_plan 10 "rack.input client_max_body_size bigger"

t_begin "setup and startup" && {
	rtmpfiles curl_out curl_err
	rainbows_setup $model
	ed -s $unicorn_config < $curl_out 2> $curl_err || > $ok
	rm -f $tmp
	dbgcat curl_err
	dbgcat curl_out
	grep 413 $curl_err
	test -e $ok
}

t_begin "stops a large chunked request" && {
	rm -f $ok
	dd if=/dev/zero bs=10485761 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	grep 413 $curl_err
	test -e $ok
}

t_begin "small size sha1 chunked ok" && {
	blob_sha1=b376885ac8452b6cbf9ced81b1080bfd570d9b91
	rm -f $ok
	dd if=/dev/zero bs=256 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "small size sha1 content-length ok" && {
	blob_sha1=b376885ac8452b6cbf9ced81b1080bfd570d9b91
	rm -f $ok
	dd if=/dev/zero bs=256 count=1 of=$tmp
	curl -vsSf -T $tmp -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "right size sha1 chunked ok" && {
	blob_sha1=8c206a1a87599f532ce68675536f0b1546900d7a
	rm -f $ok
	dd if=/dev/zero bs=10485760 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "right size sha1 content-length ok" && {
	blob_sha1=8c206a1a87599f532ce68675536f0b1546900d7a
	rm -f $ok
	dd if=/dev/zero bs=10485760 count=1 of=$tmp
	curl -vsSf -T $tmp -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	rm -f $tmp
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "default size sha1 chunked ok" && {
	blob_sha1=3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
	rm -f $ok
	dd if=/dev/zero bs=1048576 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "default size sha1 content-length ok" && {
	blob_sha1=3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
	rm -f $ok
	dd if=/dev/zero bs=1048576 count=1 of=$tmp
	curl -vsSf -T $tmp -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	rm -f $tmp
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "shutdown" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/my-tap-lib.sh0000644000004100000410000001063712354770160016165 0ustar  www-datawww-data#!/bin/sh
# Copyright (c) 2009 Eric Wong 
#
# TAP-producing shell library for POSIX-compliant Bourne shells We do
# not _rely_ on Bourne Again features, though we will use "set -o
# pipefail" from ksh93 or bash 3 if available
#
# Only generic, non-project/non-language-specific stuff goes here.  We
# only have POSIX dependencies for the core tests (without --verbose),
# though we'll enable useful non-POSIX things if they're available.
#
# This test library is intentionally unforgiving, it does not support
# skipping tests nor continuing after any failure.  Any failures
# immediately halt execution as do any references to undefined
# variables.
#
# When --verbose is specified, we always prefix stdout/stderr
# output with "#" to avoid confusing TAP consumers.  Otherwise
# the normal stdout/stderr streams are redirected to /dev/null

# dup normal stdout(fd=1) and stderr (fd=2) to fd=3 and fd=4 respectively
# normal TAP output goes to fd=3, nothing should go to fd=4
exec 3>&1 4>&2

# ensure a sane environment
TZ=UTC LC_ALL=C LANG=C
export LANG LC_ALL TZ
unset CDPATH

# pipefail is non-POSIX, but very useful in ksh93/bash
( set -o pipefail 2>/dev/null ) && set -o pipefail

SED=${SED-sed}

# Unlike other test frameworks, we are unforgiving and bail immediately
# on any failures.  We do this because we're lazy about error handling
# and also because we believe anything broken should not be allowed to
# propagate throughout the rest of the test
set -e
set -u

# name of our test
T=${0##*/}

t_expect_nr=-1
t_nr=0
t_current=
t_complete=false

# list of files to remove unconditionally on exit
T_RM_LIST=

# list of files to remove only on successful exit
T_OK_RM_LIST=

# emit output to stdout, it'll be parsed by the TAP consumer
# so it must be TAP-compliant output
t_echo () {
	echo >&3 "$@"
}

# emits non-parsed information to stdout, it will be prefixed with a '#'
# to not throw off TAP consumers
t_info () {
	t_echo '#' "$@"
}

# exit with an error and print a diagnostic
die () {
	echo >&2 "$@"
	exit 1
}

# our at_exit handler, it'll fire for all exits except SIGKILL (unavoidable)
t_at_exit () {
	code=$?
	set +e
	if test $code -eq 0
	then
		$t_complete || {
			t_info "t_done not called"
			code=1
		}
	elif test -n "$t_current"
	then
		t_echo "not ok $t_nr - $t_current"
	fi
	if test $t_expect_nr -ne -1
	then
		test $t_expect_nr -eq $t_nr || {
			t_info "planned $t_expect_nr tests but ran $t_nr"
			test $code -ne 0 || code=1
		}
	fi
	$t_complete || {
		t_info "unexpected test failure"
		test $code -ne 0 || code=1
	}
	rm -f $T_RM_LIST
	test $code -eq 0 && rm -f $T_OK_RM_LIST
	set +x
	exec >&3 2>&4
	t_close_fds
	exit $code
}

# close test-specific extra file descriptors
t_close_fds () {
	exec 3>&- 4>&-
}

# call this at the start of your test to specify the number of tests
# you plan to run
t_plan () {
	test "$1" -ge 1 || die "must plan at least one test"
	test $t_expect_nr -eq -1 || die "tried to plan twice in one test"
	t_expect_nr=$1
	shift
	t_echo 1..$t_expect_nr "#" "$@"
	trap t_at_exit EXIT
}

_t_checkup () {
	test $t_expect_nr -le 0 && die "no tests planned"
	test -n "$t_current" && t_echo "ok $t_nr - $t_current"
	true
}

# finalizes any previously test and starts a new one
t_begin () {
	_t_checkup
	t_nr=$(( $t_nr + 1 ))
	t_current="$1"

	# just in case somebody wanted to cheat us:
	set -e
}

# finalizes the current test without starting a new one
t_end () {
	_t_checkup
	t_current=
}

# run this to signify the end of your test
t_done () {
	_t_checkup
	t_current=
	t_complete=true
	test $t_expect_nr -eq $t_nr || exit 1
	exit 0
}

# create and assign named-pipes to variable _names_ passed to this function
t_fifos () {
	for _id in "$@"
	do
		_name=$_id
		_tmp=$(mktemp -t $T.$$.$_id.XXXXXXXX)
		eval "$_id=$_tmp"
		rm -f $_tmp
		mkfifo $_tmp
		T_RM_LIST="$T_RM_LIST $_tmp"
	done
}

t_verbose=false t_trace=false

while test "$#" -ne 0
do
	arg="$1"
	shift
	case $arg in
	-v|--verbose) t_verbose=true ;;
	--trace) t_trace=true t_verbose=true ;;
	*) die "Unknown option: $arg" ;;
	esac
done

# we always only setup stdout, nothing should end up in the "real" stderr
if $t_verbose
then
	if test x"$(which mktemp 2>/dev/null)" = x
	then
		die "mktemp(1) not available for --verbose"
	fi
	t_fifos t_stdout t_stderr

	(
		# use a subshell so seds are not waitable
		$SED -e 's/^/#: /' < $t_stdout &
		$SED -e 's/^/#! /' < $t_stderr &
	) &
	wait
	exec > $t_stdout 2> $t_stderr
else
	exec > /dev/null 2> /dev/null
fi

$t_trace && set -x
true
rainbows-4.6.2/t/t9002-server-token.sh0000755000004100000410000000120212354770160017401 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
case $model in
Base) ;;
*) exit 0 ;; # don't waste cycles on trivial stuff :P
esac

t_plan 6 "ServerToken Rack middleware test for $model"

t_begin "configure and start" && {
	rtmpfiles curl_out curl_err
	rainbows_setup
	rainbows -D t9002.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "hit with curl" && {
	curl -sSfiI http://$listen/ > $curl_out 2> $curl_err
}

t_begin "kill server" && {
	kill $rainbows_pid
}

t_begin "no errors in curl stderr" && {
	test ! -s $curl_err
}

t_begin "no errors in Rainbows! stderr" && {
	check_stderr
}

t_begin "Server: token added" && {
	grep Server: $curl_out
}

t_done
rainbows-4.6.2/t/sha1-random-size.ru0000644000004100000410000000162212354770160017302 0ustar  www-datawww-data# SHA1 checksum generator
require 'digest/sha1'
use Rack::ContentLength
cap = 16384
app = lambda do |env|
  /\A100-continue\z/i =~ env['HTTP_EXPECT'] and
    return [ 100, {}, [] ]
  digest = Digest::SHA1.new
  input = env['rack.input']
  case env["PATH_INFO"]
  when "/gets_read_mix"
    warn "GETS_READ_MIX #{env['HTTP_TRANSFER_ENCODING'].inspect}"
    if buf = input.gets
      warn "input.rbuf: #{input.instance_variable_get(:@rbuf).inspect}"
      begin
        digest.update(buf)
        warn "buf.size : #{buf.size}"
      end while input.read(rand(cap), buf)
    end
  when "/each"
    input.each { |buf| digest.update(buf) }
  else
    if buf = input.read(rand(cap))
      begin
        raise "#{buf.size} > #{cap}" if buf.size > cap
        digest.update(buf)
      end while input.read(rand(cap), buf)
    end
  end

  [ 200, {'Content-Type' => 'text/plain'}, [ digest.hexdigest << "\n" ] ]
end
run app
rainbows-4.6.2/t/t0045-client_max_header_size.sh0000755000004100000410000000405512354770160021451 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll

t_plan 11 "client_max_header_size tests for $model"

t_begin "setup Rainbows!" && {
	rainbows_setup $model
}

t_begin "fails with zero size" && {
	ed -s $unicorn_config < $tmp &
		printf 'GET /\r\n'
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
	wait
	test xok = x"$(cat $ok)"
	test 1 -eq $(count_lines < $tmp)
	grep HTTP_VERSION $tmp && die "unexpected HTTP_VERSION in HTTP/0.9 request"
}

t_begin "HTTP/1.1 request fails" && {
	curl -vsSf http://$listen/ > $tmp 2>&1 && die "unexpected curl success"
	grep '400\( Bad Request\)\?$' $tmp
}

t_begin "increase client_max_header_size on reload" && {
	ed -s $unicorn_config < $tmp
	test 1 -eq $(count_lines < $tmp)
	dbgcat tmp
}

t_begin "no errors in stderr" && {
	check_stderr
}

t_begin "shutdown" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/t0025-write-on-close.sh0000755000004100000410000000065412354770160017632 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
t_plan 4 "write-on-close tests for funky response-bodies"

t_begin "setup and start" && {
	rainbows_setup
	rainbows -D -c $unicorn_config write-on-close.ru
	rainbows_wait_start
}

t_begin "write-on-close response body succeeds" && {
	test xGoodbye = x"$(curl -sSf --http1.0 http://$listen/)"
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/test_isolate.rb0000644000004100000410000000235312354770160016676 0ustar  www-datawww-datarequire 'rubygems'
require 'isolate'
engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'

path = "tmp/isolate/#{engine}-#{RUBY_VERSION}"
opts = {
  :system => false,
  # we want "ruby-1.8.7" and not "ruby-1.8", so disable multiruby
  :multiruby => false,
  :path => path,
}

old_out = $stdout.dup
$stdout.reopen($stderr)

lock = File.open(__FILE__, "rb")
lock.flock(File::LOCK_EX)
Isolate.now!(opts) do
  gem 'kgio', '2.9.2'
  gem 'rack', '1.5.2'
  gem 'kcar', '0.4.0'
  gem 'raindrops', '0.13.0'
  gem 'unicorn', '4.8.3'

  if engine == "ruby"
    gem 'sendfile', '1.2.1'
    gem 'cool.io', '1.1.0'

    gem 'eventmachine', '1.0.3'
    gem 'async_sinatra', '1.0.0'

    gem 'neverblock', '0.1.6.2'
  end

  if defined?(::Fiber) && engine == "ruby"
    gem 'revactor', '0.1.5'
    gem 'rack-fiber_pool', '0.9.2'
  end

  if RUBY_PLATFORM =~ /linux/
    gem 'sleepy_penguin', '3.3.0'

    # is 2.6.32 new enough?
    gem 'io_splice', '4.2.0' if `uname -r`.strip > '2.6.32'
  end
end

$stdout.reopen(old_out)

# don't load the old Rev if it exists, Cool.io 1.0.0 is compatible with it,
# even for everything Revactor uses.
dirs = Dir["#{path}/gems/*-*/lib"]
dirs.delete_if { |x| x =~ %r{/rev-[\d\.]+/lib} }
puts dirs.map { |x| File.expand_path(x) }.join(':')
rainbows-4.6.2/t/client_header_buffer_size.ru0000644000004100000410000000022512354770160021367 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType, "text/plain"
run lambda { |env|
  [ 200, {}, [ "#{Rainbows.server.client_header_buffer_size}\n" ] ]
}
rainbows-4.6.2/t/fast-pipe-response.ru0000644000004100000410000000044112354770160017742 0ustar  www-datawww-data# must be run without Rack::Lint since that clobbers to_path
use Rainbows::DevFdResponse
run(lambda { |env|
  [ 200,
    {
      'Content-Length' => ::File.stat('random_blob').size.to_s,
      'Content-Type' => 'application/octet-stream',
    },
    IO.popen('cat random_blob', 'rb') ]
})
rainbows-4.6.2/t/env_rack_env.ru0000644000004100000410000000015612354770160016661 0ustar  www-datawww-datause Rack::ContentLength
run proc { |env|
  [ 200, { "Content-Type" => "text/plain" }, [ ENV["RACK_ENV"] ] ]
}
rainbows-4.6.2/t/t0040-keepalive_requests-setting.sh0000755000004100000410000000222212354770160022324 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
t_plan 6 "keepalive_requests limit tests for $model"

t_begin "setup and start" && {
	rainbows_setup $model 50 666
	rtmpfiles curl_out curl_err
	grep 'keepalive_timeout 666' $unicorn_config
	rainbows -E none -D env.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "curl requests hit default keepalive_requests limit" && {
	curl -sSfv http://$listen/[0-101] > $curl_out 2> $curl_err
	test 1 -eq $(grep 'Connection: close' $curl_err |count_lines)
	test 101 -eq $(grep 'Connection: keep-alive' $curl_err |count_lines)
}

t_begin "reload with smaller keepalive_requests limit" && {
	ed -s $unicorn_config < $curl_out 2> $curl_err
	test 2 -eq $(grep 'Connection: close' $curl_err |count_lines)
	test 11 -eq $(grep 'Connection: keep-alive' $curl_err |count_lines)
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/t0114-rewindable-input-true.sh0000755000004100000410000000112312354770160021200 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models EventMachine NeverBlock
skip_models Rev RevThreadSpawn RevThreadPool
skip_models Coolio CoolioThreadSpawn CoolioThreadPool
skip_models Epoll XEpoll

t_plan 4 "rewindable_input toggled to true"

t_begin "setup and start" && {
	rainbows_setup
	echo rewindable_input true >> $unicorn_config
	rainbows -D -c $unicorn_config t0114.ru
	rainbows_wait_start
}

t_begin "ensure worker is started" && {
	test xOK = x$(curl -T t0114.ru -sSf http://$listen/)
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/test-lib.sh0000644000004100000410000001273412354770160015735 0ustar  www-datawww-data#!/bin/sh
# Copyright (c) 2009 Rainbows! developers
. ./my-tap-lib.sh

set +u

# sometimes we rely on http_proxy to avoid wasting bandwidth with Isolate
# and multiple Ruby versions
NO_PROXY=${UNICORN_TEST_ADDR-127.0.0.1}
export NO_PROXY

if test -z "$model"
then
	# defaulting to Base would unfortunately fail some concurrency tests
	model=ThreadSpawn
	t_info "model undefined, defaulting to $model"
fi

set -e
RUBY="${RUBY-ruby}"
RUBY_VERSION=${RUBY_VERSION-$($RUBY -e 'puts RUBY_VERSION')}
t_pfx=$PWD/trash/$model.$T-$RUBY_ENGINE-$RUBY_VERSION
set -u

PATH=$PWD/bin:$PATH
export PATH

test -x $PWD/bin/unused_listen || die "must be run in 't' directory"

# requires $1 and prints out the value of $2
require_check () {
	lib=$1
	const=$2
	if ! $RUBY -r$lib -e "puts $const" >/dev/null 2>&1
	then
		t_info "skipping $T since we don't have $lib"
		exit 0
	fi
}

# "date +%s" is not in POSIX, but in GNU, and FreeBSD 9.0 (possibly earlier)
unix_time () {
	$RUBY -e 'puts Time.now.to_i'
}

# "wc -l" outputs leading whitespace on *BSDs, filter it out for portability
count_lines () {
	wc -l | tr -d '[:space:]'
}

# "wc -c" outputs leading whitespace on *BSDs, filter it out for portability
count_bytes () {
	wc -c | tr -d '[:space:]'
}

skip_models () {
	for i in "$@"
	do
		if test x"$model" != x"$i"
		then
			continue
		fi
		t_info "skipping $T since it is not compatible with $model"
		exit 0
	done
}


# given a list of variable names, create temporary files and assign
# the pathnames to those variables
rtmpfiles () {
	for id in "$@"
	do
		name=$id
		_tmp=$t_pfx.$id
		eval "$id=$_tmp"

		case $name in
		*fifo)
			rm -f $_tmp
			mkfifo $_tmp
			T_RM_LIST="$T_RM_LIST $_tmp"
			;;
		*socket)
			rm -f $_tmp
			T_RM_LIST="$T_RM_LIST $_tmp"
			;;
		*)
			> $_tmp
			T_OK_RM_LIST="$T_OK_RM_LIST $_tmp"
			;;
		esac
	done
}

dbgcat () {
	id=$1
	eval '_file=$'$id
	echo "==> $id <=="
	sed -e "s/^/$id:/" < $_file
}

check_stderr () {
	set +u
	_r_err=${1-${r_err}}
	set -u
	if grep -i Error $_r_err
	then
		die "Errors found in $_r_err"
	elif grep SIGKILL $_r_err
	then
		die "SIGKILL found in $_r_err"
	fi
}

# rainbows_setup [ MODEL [ WORKER_CONNECTIONS ] ]
rainbows_setup () {
	eval $(unused_listen)
	rtmpfiles unicorn_config pid r_err r_out fifo tmp ok
	cat > $unicorn_config <> $unicorn_config
}

rainbows_wait_start () {
	# "cat $fifo" will block until the before_fork hook is called in
	# the Unicorn config file
	test xSTART = x"$(cat $fifo)"
	rainbows_pid=$(cat $pid)
}

wait_for_reload () {
	case $# in
	0) err_log=$r_err status=done ;;
	1) err_log=$1 status=done ;;
	2) err_log=$1 status=$2 ;;
	esac
	while ! egrep '(done|error) reloading' < $err_log >/dev/null
	do
		sleep 1
	done
	grep "$status reloading" $err_log >/dev/null
}

wait_for_reap () {
	case $# in
	0) err_log=$r_err ;;
	1) err_log=$1 ;;
	esac

	while ! grep reaped < $err_log >/dev/null
	do
		sleep 1
	done
}

rsha1 () {
	_cmd="$(which sha1sum 2>/dev/null || :)"
	test -n "$_cmd" || _cmd="$(which openssl 2>/dev/null || :) sha1"
	test "$_cmd" != " sha1" || _cmd="$(which gsha1sum 2>/dev/null || :)"

	# last resort, see comments in sha1sum.rb for reasoning
	test -n "$_cmd" || _cmd=sha1sum.rb
	expr "$($_cmd)" : '\([a-f0-9]\{40\}\)'
}

req_curl_chunked_upload_err_check () {
	set +e
	curl --version 2>/dev/null | awk '$1 == "curl" {
		split($2, v, /\./)
		if ((v[1] < 7) || (v[1] == 7 && v[2] < 18))
			code = 1
	}
	END { exit(code) }'
	if test $? -ne 0
	then
		t_info "curl >= 7.18.0 required for $T"
		exit 0
	fi
}

check_splice () {
	case $(uname -s) in
	Linux) ;;
	*)
		t_info "skipping $T since it's not Linux"
		exit 0
		;;
	esac

	# we only allow splice on 2.6.32+
	min=32 uname_r=$(uname -r)
	case $uname_r in
	2.6.*)
		sub=$(expr "$uname_r" : '2\.6\.\(.*\)$')
		if test $sub -lt $min
		then
			t_info "skipping $T (Linux $(uname_r < 2.6.$min)"
			exit 0
		fi
		;;
	[3-9].*)
		# OK
		;;
	*)
		t_info "skipping $T (Linux $uname_r < 2.6.$min)"
		exit 0
		;;
	esac
}

check_threaded_app_dispatch () {
	case $model in
	ThreadSpawn|ThreadPool) ;;
	RevThreadSpawn|RevThreadPool) ;;
	CoolioThreadSpawn|CoolioThreadPool) ;;
	XEpollThreadSpawn|XEpollThreadPool) ;;
	*)
		t_info "$0 is only compatible with threaded app dispatch"
		exit 0 ;;
	esac
}

check_copy_stream () {
	case $RUBY_VERSION in
	1.9.*) ;;
	*)
		t_info "skipping $T since it can't IO.copy_stream"
		exit 0
		;;
	esac

	case $model in
	ThreadSpawn|WriterThreadSpawn|ThreadPool|WriterThreadPool|Base) ;;
	XEpollThreadSpawn|XEpollThreadPool) ;;
	*)
		t_info "skipping $T since it doesn't use copy_stream"
		exit 0
		;;
	esac
}

case $model in
Rev) require_check rev Rev::VERSION ;;
Coolio) require_check coolio Coolio::VERSION ;;
Revactor) require_check revactor Revactor::VERSION ;;
EventMachine) require_check eventmachine EventMachine::VERSION ;;
esac
rainbows-4.6.2/t/t0003-reopen-logs.sh0000755000004100000410000000425512354770160017212 0ustar  www-datawww-data#!/bin/sh
# don't set nr_client for Rev, only _one_ app running at once :x
nr_client=${nr_client-2}
. ./test-lib.sh

t_plan 19 "reopen rotated logs"

t_begin "setup and startup" && {
	rtmpfiles curl_out curl_err r_rot
	rainbows_setup $model
	rainbows -D sleep.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "ensure server is responsive" && {
	curl -sSf http://$listen/ >/dev/null
}

t_begin "start $nr_client concurrent requests" && {
	start=$(unix_time)
	for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" > $curl_out 2>> $curl_err ) &
	done
}

t_begin "ensure stderr log is clean" && check_stderr

t_begin "external log rotation" && {
	rm -f $r_rot
	mv $r_err $r_rot
}

t_begin "send reopen log signal (USR1)" && {
	kill -USR1 $rainbows_pid
}

t_begin "wait for rotated log to reappear" && {
	nr=60
	while ! test -f $r_err && test $nr -ge 0
	do
		sleep 1
		nr=$(( $nr - 1 ))
	done
}

t_begin "wait for worker to reopen logs" && {
	nr=60
	re="worker=.* done reopening logs"
	while ! grep "$re" < $r_err >/dev/null && test $nr -ge 0
	do
		sleep 1
		nr=$(( $nr - 1 ))
	done
}

dbgcat r_rot
dbgcat r_err

t_begin "wait curl requests to finish" && {
	wait
	t_info elapsed=$(( $(unix_time) - $start ))
}

t_begin "ensure no errors from curl" && {
	test ! -s $curl_err
}

t_begin "curl got $nr_client responses" && {
	test "$(count_lines < $curl_out)" -eq $nr_client
}

t_begin "all responses were identical" && {
	nr=$(sort < $curl_out | uniq | count_lines)
	test "$nr" -eq 1
}

t_begin 'response was "Hello"' && {
	test x$(sort < $curl_out | uniq) = xHello
}

t_begin "current server stderr is clean" && check_stderr

t_begin "rotated stderr is clean" && {
	check_stderr $r_rot
}

t_begin "server is now writing logs to new stderr" && {
	before_rot=$(count_bytes < $r_rot)
	before_err=$(count_bytes < $r_err)
	curl -sSfv http://$listen/
	after_rot=$(count_bytes < $r_rot)
	after_err=$(count_bytes < $r_err)
	test $after_rot -eq $before_rot
	test $after_err -gt $before_err
}

t_begin "stop server" && {
	kill $rainbows_pid
}

dbgcat r_err

t_begin "current server stderr is clean" && check_stderr
t_begin "rotated stderr is clean" && check_stderr $r_rot

t_done
rainbows-4.6.2/t/t0101-rack-input-trailer.sh0000755000004100000410000000352712354770160020475 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
test -r random_blob || die "random_blob required, run with 'make $0'"

t_plan 13 "input trailer test $model"

t_begin "setup and startup" && {
	rtmpfiles curl_out
	rainbows_setup $model
	rainbows -D content-md5.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "staggered trailer upload" && {
	zero_md5="1B2M2Y8AsgTpgAmY7PhCfg=="
	(
		cat $fifo > $tmp &
		printf 'PUT /s HTTP/1.1\r\n'
		printf 'Host: example.com\r\n'
		printf 'Transfer-Encoding: chunked\r\n'
		printf 'Trailer: Content-MD5\r\n\r\n'
		printf '0\r\nContent-MD5: '
		sleep 5
		printf '%s\r\n\r\n' $zero_md5
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
	test xok = x"$(cat $ok)"
}

t_begin "HTTP response is OK" && {
	grep 'HTTP/1\.[01] 200 OK' $tmp
}

t_begin "upload small blob" && {
	(
		cat $fifo > $tmp &
		echo hello world | content-md5-put
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
	test xok = x"$(cat $ok)"
}

t_begin "HTTP response is OK" && grep 'HTTP/1\.[01] 200 OK' $tmp
t_begin "no errors in stderr log" && check_stderr

t_begin "big blob request" && {
	(
		cat $fifo > $tmp &
		content-md5-put < random_blob
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
	test xok = x"$(cat $ok)"
}

t_begin "HTTP response is OK" && grep 'HTTP/1\.[01] 200 OK' $tmp
t_begin "no errors in stderr log" && check_stderr

t_begin "staggered blob upload" && {
	(
		cat $fifo > $tmp &
		(
			dd bs=164 count=1 < random_blob
			sleep 2
			dd bs=4545 count=1 < random_blob
			sleep 2
			dd bs=1234 count=1 < random_blob
			echo subok > $ok
		) 2>/dev/null | content-md5-put
		test xsubok = x"$(cat $ok)"
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
	test xok = x"$(cat $ok)"
}

t_begin "HTTP response is OK" && grep 'HTTP/1\.[01] 200 OK' $tmp

t_begin "no errors in stderr log" && check_stderr

t_begin "kill server" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/t0021-sendfile-wrap-to_path.sh0000755000004100000410000000500012354770160021141 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
test -r random_blob || die "random_blob required, run with 'make $0'"
case $RUBY_ENGINE in
ruby) ;;
*)
	t_info "skipping $T since it can't load the sendfile gem, yet"
	exit 0
	;;
esac

t_plan 16 "sendfile wrap body response for $model"

t_begin "setup and startup" && {
	rtmpfiles out err http_fifo sub_ok
	rainbows_setup $model
	echo 'require "sendfile"' >> $unicorn_config
	echo 'def (::IO).copy_stream(*x); abort "NO"; end' >> $unicorn_config

	# can't load Rack::Lint here since it clobbers body#to_path
	export fifo
	rainbows -E none -D file-wrap-to_path.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "read random blob sha1" && {
	random_blob_sha1=$(rsha1 < random_blob)
}

t_begin "start FIFO reader" && {
	cat $fifo > $out &
}

t_begin "single request matches" && {
	sha1=$(curl -sSfv 2> $err http://$listen/random_blob | rsha1)
	test -n "$sha1"
	test x"$sha1" = x"$random_blob_sha1"
}

t_begin "body.close called" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "start FIFO reader for abortive HTTP/1.1 request" && {
	cat $fifo > $out &
}

t_begin "send abortive HTTP/1.1 request" && {
	rm -f $ok
	(
		printf 'GET /random_blob HTTP/1.1\r\nHost: example.com\r\n\r\n'
		dd bs=4096 count=1 < $http_fifo >/dev/null
		echo ok > $ok
	) | socat - TCP:$listen > $http_fifo || :
	test xok = x$(cat $ok)
}

t_begin "body.close called for aborted HTTP/1.1 request" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "start FIFO reader for abortive HTTP/1.0 request" && {
	cat $fifo > $out &
}

t_begin "send abortive HTTP/1.0 request" && {
	rm -f $ok
	(
		printf 'GET /random_blob HTTP/1.0\r\n\r\n'
		dd bs=4096 count=1 < $http_fifo >/dev/null
		echo ok > $ok
	) | socat - TCP:$listen > $http_fifo || :
	test xok = x$(cat $ok)
}

t_begin "body.close called for aborted HTTP/1.0 request" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "start FIFO reader for abortive HTTP/0.9 request" && {
	cat $fifo > $out &
}

t_begin "send abortive HTTP/0.9 request" && {
	rm -f $ok
	(
		printf 'GET /random_blob\r\n'
		dd bs=4096 count=1 < $http_fifo >/dev/null
		echo ok > $ok
	) | socat - TCP:$listen > $http_fifo || :
	test xok = x$(cat $ok)
}

t_begin "body.close called for aborted HTTP/0.9 request" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "shutdown server" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/simple-http_XEpoll.ru0000644000004100000410000000034412354770160017751 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == false && env['rainbows.model'] == :XEpoll
    [ 200, {}, [ Thread.current.inspect << "\n" ] ]
  else
    raise env.inspect
  end
}
rainbows-4.6.2/t/content-md5.ru0000644000004100000410000000113212354770160016351 0ustar  www-datawww-data# SHA1 checksum generator
bs = ENV['bs'] ? ENV['bs'].to_i : 4096
require 'digest/md5'
use Rack::ContentLength
app = lambda do |env|
  /\A100-continue\z/i =~ env['HTTP_EXPECT'] and
    return [ 100, {}, [] ]
  digest = Digest::MD5.new
  input = env['rack.input']
  if buf = input.read(bs)
    begin
      digest.update(buf)
    end while input.read(bs, buf)
  end

  expect = env['HTTP_CONTENT_MD5']
  readed = [ digest.digest ].pack('m').strip
  body = "expect=#{expect}\nreaded=#{readed}\n"
  status = expect == readed ? 200 : 500

  [ status, {'Content-Type' => 'text/plain'}, [ body ] ]
end
run app
rainbows-4.6.2/t/t0004-heartbeat-timeout.sh0000755000004100000410000000311612354770160020377 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh

t_plan 12 "heartbeat/timeout test for $model"

t_begin "setup and startup" && {
	rainbows_setup $model
	echo timeout 3 >> $unicorn_config
	echo preload_app true >> $unicorn_config
	rainbows -D heartbeat-timeout.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "read worker PID" && {
	worker_pid=$(curl -sSf http://$listen/)
	t_info "worker_pid=$worker_pid"
}

t_begin "sleep for a bit, ensure worker PID does not change" && {
	sleep 4
	test $(curl -sSf http://$listen/) -eq $worker_pid
}

t_begin "block the worker process to force it to die" && {
	rm $ok
	t0=$(unix_time)
	err="$(curl -sSf http://$listen/block-forever 2>&1 || > $ok)"
	t1=$(unix_time)
	elapsed=$(($t1 - $t0))
	t_info "elapsed=$elapsed err=$err"
	test x"$err" != x"Should never get here"
	test x"$err" != x"$worker_pid"
}

t_begin "ensure worker was killed" && {
	test -e $ok
	test 1 -eq $(grep timeout $r_err | grep killing | count_lines)
}

t_begin "ensure timeout took at least 3 seconds" && {
	test $elapsed -ge 3
}

t_begin "wait for new worker to start up" && {
	test xSTART = x"$(cat $fifo)"
}

t_begin "we get a fresh new worker process" && {
	new_worker_pid=$(curl -sSf http://$listen/)
	test $new_worker_pid -ne $worker_pid
}

t_begin "truncate the server error log" && {
	> $r_err
}

t_begin "SIGSTOP and SIGCONT on rainbows master does not kill worker" && {
	kill -STOP $rainbows_pid
	sleep 4
	kill -CONT $rainbows_pid
	sleep 2
	test $new_worker_pid -eq $(curl -sSf http://$listen/)
}

t_begin "stop server" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && check_stderr

dbgcat r_err

t_done
rainbows-4.6.2/t/sleep.ru0000644000004100000410000000044212354770160015327 0ustar  www-datawww-datause Rack::ContentLength

run lambda { |env|
  /\A100-continue\z/i =~ env['HTTP_EXPECT'] and return [ 100, {}, [] ]

  env['rack.input'].read
  nr = 1
  env["PATH_INFO"] =~ %r{/([\d\.]+)\z} and nr = $1.to_f

  Rainbows.sleep(nr)

  [ 200, {'Content-Type' => 'text/plain'}, [ "Hello\n" ] ]
}
rainbows-4.6.2/t/t0009-broken-app.sh0000755000004100000410000000217712354770160017025 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll

t_plan 9 "graceful handling of broken apps for $model"

t_begin "setup and start" && {
	rainbows_setup $model 1
	rainbows -E none -D t0009.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "normal response is alright" && {
	test xOK = x"$(curl -sSf http://$listen/)"
}

t_begin "app raised exception" && {
	curl -sSf http://$listen/raise 2> $tmp || :
	grep -F 500 $tmp
	> $tmp
}

t_begin "app exception logged and backtrace not swallowed" && {
	grep -F 'app error' $r_err
	grep -A1 -F 'app error' $r_err | tail -1 | grep t0009.ru:
	dbgcat r_err
	> $r_err
}

t_begin "trigger bad response" && {
	curl -sSf http://$listen/nil 2> $tmp || :
	grep -F 500 $tmp
	> $tmp
}

t_begin "app exception logged" && {
	grep -F 'app error' $r_err
	> $r_err
}

t_begin "normal responses alright afterwards" && {
	> $tmp
	curl -sSf http://$listen/ >> $tmp &
	curl -sSf http://$listen/ >> $tmp &
	curl -sSf http://$listen/ >> $tmp &
	curl -sSf http://$listen/ >> $tmp &
	wait
	test xOK = x$(sort < $tmp | uniq)
}

t_begin "teardown" && {
	kill $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/t0030-fast-pipe-response.sh0000755000004100000410000000273012354770160020500 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
test -r random_blob || die "random_blob required, run with 'make $0'"

t_plan 10 "fast pipe response for $model"

t_begin "setup and startup" && {
	rtmpfiles err out
	rainbows_setup $model
	rainbows -E none -D fast-pipe-response.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "read random blob sha1" && {
	random_blob_sha1=$(rsha1 < random_blob)
	three_sha1=$(cat random_blob random_blob random_blob | rsha1)
}

t_begin "single request matches" && {
	sha1=$(curl -sSfv 2> $err http://$listen/ | rsha1)
	test -n "$sha1"
	test x"$sha1" = x"$random_blob_sha1"
}

t_begin "Content-Length header preserved in response" && {
	grep "^< Content-Length:" $err
}

t_begin "send three keep-alive requests" && {
	sha1=$(curl -vsSf 2> $err \
	       http://$listen/ http://$listen/ http://$listen/ | rsha1)
	test -n "$sha1"
	test x"$sha1" = x"$three_sha1"
}

t_begin "ensure responses were all keep-alive" && {
	test 3 -eq $(grep '< Connection: keep-alive' < $err | count_lines)
}

t_begin "HTTP/1.0 test" && {
	sha1=$(curl -0 -v 2> $err -sSf http://$listen/ | rsha1)
	test $sha1 = $random_blob_sha1
	grep '< Connection: close' < $err
}

t_begin "HTTP/0.9 test" && {
	(
		printf 'GET /\r\n'
		rsha1 < $fifo > $tmp &
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
	test $(cat $tmp) = $random_blob_sha1
	test xok = x$(cat $ok)
}

t_begin "shutdown server" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/app_deferred.ru0000644000004100000410000000121312354770160016634 0ustar  www-datawww-data#\-E none
# can't use non-compatible middleware that doesn't pass "deferered?" calls
#
# used for testing deferred actions for Merb and possibly other frameworks
# ref: http://brainspl.at/articles/2008/04/18/deferred-requests-with-merb-ebb-and-thin

class DeferredApp < Struct.new(:app)
  def deferred?(env)
    env["PATH_INFO"] == "/deferred"
  end

  def call(env)
    env["rack.multithread"] or raise RuntimeError, "rack.multithread not true"
    body = "#{Thread.current.inspect}\n"
    headers = {
      "Content-Type" => "text/plain",
      "Content-Length" => body.size.to_s,
    }
    [ 200, headers, [ body ] ]
  end
end

run DeferredApp.new
rainbows-4.6.2/t/rack-fiber_pool/0000755000004100000410000000000012354770160016705 5ustar  www-datawww-datarainbows-4.6.2/t/rack-fiber_pool/app.ru0000644000004100000410000000033412354770160020035 0ustar  www-datawww-datarequire 'rack/fiber_pool'
use Rack::FiberPool
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
run lambda { |env|
  f = Fiber.current
  EM.add_timer(3) { f.resume }
  Fiber.yield
  [ 200, {}, [ "#{f}\n" ] ]
}
rainbows-4.6.2/t/kgio-pipe-response.ru0000644000004100000410000000062412354770160017741 0ustar  www-datawww-data# must be run without Rack::Lint since that clobbers to_path
use Rainbows::DevFdResponse
run(lambda { |env|
  io = case env["rainbows.model"].to_s
  when /Fiber/
    Rainbows::Fiber::IO::Pipe
  else
    Kgio::Pipe
  end.popen('cat random_blob', 'rb')

  [ 200,
    {
      'Content-Length' => ::File.stat('random_blob').size.to_s,
      'Content-Type' => 'application/octet-stream',
    },
    io
  ]
})
rainbows-4.6.2/t/t0103-rack-input-limit.sh0000755000004100000410000000271612354770160020152 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
test -r random_blob || die "random_blob required, run with 'make $0'"
req_curl_chunked_upload_err_check

t_plan 6 "rack.input client_max_body_size default"

t_begin "setup and startup" && {
	rtmpfiles curl_out curl_err cmbs_config
	rainbows_setup $model
	grep -v client_max_body_size < $unicorn_config > $cmbs_config
	rainbows -D sha1-random-size.ru -c $cmbs_config
	rainbows_wait_start
}

t_begin "regular request" && {
	rm -f $ok
	curl -vsSf -T random_blob -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	test -e $ok
}

t_begin "chunked request" && {
	rm -f $ok
	curl -vsSf -T- < random_blob -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	test -e $ok
}

t_begin "default size sha1 chunked" && {
	blob_sha1=3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
	rm -f $ok
	> $r_err
	dd if=/dev/zero bs=1048576 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	test "$(cat $curl_out)" = $blob_sha1
	dbgcat curl_err
	dbgcat curl_out
}

t_begin "default size sha1 content-length" && {
	blob_sha1=3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
	rm -f $ok
	dd if=/dev/zero bs=1048576 count=1 of=$tmp
	curl -vsSf -T $tmp -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	test "$(cat $curl_out)" = $blob_sha1
	dbgcat curl_err
	dbgcat curl_out
}

t_begin "shutdown" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/t0501-cramp-rainsocket.sh0000755000004100000410000000156012354770160020221 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
case $model in
disabled) ;;
*)
	t_info "skipping $T since it's not compatible with $model"
	exit 0
	;;
esac
RUBYLIB=$($RUBY test_isolate_cramp.rb):$RUBYLIB
export RUBYLIB
require_check cramp Cramp::VERSION

t_plan 4 "WebSocket monkey patch validity test for Cramp"

CONFIG_RU=cramp/rainsocket.ru

t_begin "setup and start" && {
	rainbows_setup
	rtmpfiles curl_err

	# Like the rest of the EM/async stuff, it's not Rack::Lint compatible
	rainbows -E deployment -D $CONFIG_RU -c $unicorn_config
	rainbows_wait_start
}

t_begin "wait for server to say hello to us" && {
	ok=$( (curl --no-buffer -sS http://$listen/ || :) | \
	     (tr -d '\0\0377' || :) | \
	     awk '/Hello from the Server/ { print "ok"; exit 0 }')

	test x"$ok" = xok
}

t_begin "termination signal sent" && {
	kill $rainbows_pid
}

t_begin "no errors in stderr" && check_stderr

t_done
rainbows-4.6.2/t/t0106-rack-input-keepalive.sh0000755000004100000410000000560512354770160021004 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
t_plan 11 "rack.input pipelining test"

t_begin "setup and startup" && {
	rainbows_setup $model
	rtmpfiles req
	rainbows -D sha1.ru -c $unicorn_config
	body=hello
	body_size=$(printf $body | count_bytes)
	body_sha1=$(printf $body | rsha1)
	random_blob_size=$(count_bytes < random_blob)
	random_blob_sha1=$(rsha1 < random_blob)
	rainbows_wait_start
}

t_begin "send big pipelined chunked requests" && {
	(
		cat $fifo > $tmp &
		Connection=keep-alive
		export Connection
		content-md5-put < random_blob
		content-md5-put < random_blob
		content-md5-put < random_blob
		printf 'PUT / HTTP/1.0\r\n'
		printf 'Content-Length: %d\r\n\r\n' $random_blob_size
		cat random_blob
		wait
		echo ok > $ok
	) | socat - TCP4:$listen > $fifo
	test x"$(cat $ok)" = xok
}

t_begin "check responses" && {
	dbgcat tmp
	test 4 -eq $(grep $random_blob_sha1 $tmp | count_lines)
}

t_begin "send big pipelined identity requests" && {
	(
		cat $fifo > $tmp &
		printf 'PUT / HTTP/1.0\r\n'
		printf 'Connection: keep-alive\r\n'
		printf 'Content-Length: %d\r\n\r\n' $random_blob_size
		cat random_blob
		printf 'PUT / HTTP/1.1\r\n'
		printf 'Content-Length: %d\r\n\r\n' $random_blob_size
		cat random_blob
		printf 'PUT / HTTP/1.0\r\n'
		printf 'Content-Length: %d\r\n\r\n' $random_blob_size
		cat random_blob
		wait
		echo ok > $ok
	) | socat - TCP4:$listen > $fifo
	test x"$(cat $ok)" = xok
}

t_begin "check responses" && {
	dbgcat tmp
	test 3 -eq $(grep $random_blob_sha1 $tmp | count_lines)
}

t_begin "send pipelined identity requests" && {

	{
		printf 'PUT / HTTP/1.0\r\n'
		printf 'Connection: keep-alive\r\n'
		printf 'Content-Length: %d\r\n\r\n%s' $body_size $body
		printf 'PUT / HTTP/1.1\r\nHost: example.com\r\n'
		printf 'Content-Length: %d\r\n\r\n%s' $body_size $body
		printf 'PUT / HTTP/1.0\r\n'
		printf 'Content-Length: %d\r\n\r\n%s' $body_size $body
	} > $req
	(
		cat $fifo > $tmp &
		cat $req
		wait
		echo ok > $ok
	) | socat - TCP4:$listen > $fifo
	test x"$(cat $ok)" = xok
}

t_begin "check responses" && {
	dbgcat tmp
	test 3 -eq $(grep $body_sha1 $tmp | count_lines)
}

t_begin "send pipelined chunked requests" && {

	{
		printf 'PUT / HTTP/1.0\r\n'
		printf 'Connection: keep-alive\r\n'
		printf 'Transfer-Encoding: chunked\r\n\r\n'
		printf '%x\r\n%s\r\n0\r\n\r\n' $body_size $body
		printf 'PUT / HTTP/1.1\r\nHost: example.com\r\n'
		printf 'Transfer-Encoding: chunked\r\n\r\n'
		printf '%x\r\n%s\r\n0\r\n\r\n' $body_size $body
		printf 'PUT / HTTP/1.0\r\n'
		printf 'Transfer-Encoding: chunked\r\n\r\n'
		printf '%x\r\n%s\r\n0\r\n\r\n' $body_size $body
	} > $req
	(
		cat $fifo > $tmp &
		cat $req
		wait
		echo ok > $ok
	) | socat - TCP4:$listen > $fifo
	test x"$(cat $ok)" = xok
}

t_begin "check responses" && {
	dbgcat tmp
	test 3 -eq $(grep $body_sha1 $tmp | count_lines)
}

t_begin "kill server" && kill $rainbows_pid

t_begin "no errors in stderr log" && check_stderr

t_done
rainbows-4.6.2/t/t9101-thread-timeout-threshold.sh0000755000004100000410000000172612354770160021715 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
check_threaded_app_dispatch

t_plan 6 "ThreadTimeout Rack middleware test for $model"

t_begin "configure and start" && {
	rtmpfiles curl_err curl_out
	rainbows_setup $model 10
	rainbows -D t9101.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "normal request should not timeout" && {
	test x"HI" = x"$(curl -sSf http://$listen/ 2>> $curl_err)"
}

t_begin "8 sleepy requests do not time out" && {
	> $curl_err
	for i in 1 2 3 4 5 6 7 8
	do
		curl --no-buffer -sSf http://$listen/3 \
		  2>> $curl_err >> $curl_out &
	done
	wait
	test 8 -eq "$(count_lines < $curl_out)"
	test xHI = x"$(sort < $curl_out | uniq)"
}

t_begin "9 sleepy requests, some time out" && {
	> $curl_err
	> $curl_out
	for i in 1 2 3 4 5 6 7 8 9
	do
		curl -sSf --no-buffer \
		  http://$listen/3 2>> $curl_err >> $curl_out &
	done
	wait
	grep 408 $curl_err
}

t_begin "kill server" && {
	kill $rainbows_pid
}

t_begin "no errors in Rainbows! stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/t0114.ru0000644000004100000410000000033012354770160014764 0ustar  www-datawww-data#\ -E none
use Rack::ContentLength
use Rack::ContentType, 'text/plain'
app = lambda do |env|
  case env['rack.input']
  when Unicorn::TeeInput
    [ 200, {}, %w(OK) ]
  else
    [ 500, {}, %w(NO) ]
  end
end
run app
rainbows-4.6.2/t/t0017-keepalive-timeout-zero.sh0000755000004100000410000000163112354770160021366 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
t_plan 6 "keepalive_timeout 0 tests for $model"

t_begin "setup and start" && {
	rainbows_setup $model 2 0
	grep 'keepalive_timeout 0' $unicorn_config
	rainbows -D env.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin 'check server responds with Connection: close' && {
	curl -sSfi http://$listen/ | grep 'Connection: close'
}

t_begin "send keepalive response that does not expect close" && {
	req='GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'
	t0=$(unix_time)
	(
		cat $fifo > $tmp &
		printf "$req"
		wait
		unix_time > $ok
	) | socat - TCP:$listen > $fifo
	now="$(cat $ok)"
	elapsed=$(( $now - $t0 ))
	t_info "elapsed=$elapsed (expecting <=3)"
	test $elapsed -le 3
}

t_begin "'Connection: close' header set" && {
	grep 'Connection: close' $tmp
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/t0014-config-conflict.sh0000755000004100000410000000157012354770160020023 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
t_plan 6 "config variables conflict with preload_app"

t_begin "setup and start" && {
	rainbows_setup
	rtmpfiles ru

	cat > $ru <<\EOF
use Rack::ContentLength
use Rack::ContentType, "text/plain"
config = ru = { "hello" => "world" }
run lambda { |env| [ 200, {}, [ ru.inspect << "\n" ] ] }
EOF
	echo 'preload_app true' >> $unicorn_config
	rainbows -D -c $unicorn_config $ru
	rainbows_wait_start
}

t_begin "hit with curl" && {
	out=$(curl -sSf http://$listen/)
	test x"$out" = x'{"hello"=>"world"}'
}

t_begin "modify rackup file" && {
	ed -s $ru <"WORLD"}'
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/t0031-close-pipe-response.sh0000755000004100000410000000420412354770160020647 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll

t_plan 16 "close pipe response for $model"

t_begin "setup and startup" && {
	rtmpfiles err out http_fifo sub_ok
	rainbows_setup $model
	export fifo
	rainbows -E none -D close-pipe-response.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "read random blob sha1" && {
	random_blob_sha1=$(rsha1 < random_blob)
}

t_begin "start FIFO reader" && {
	cat $fifo > $out &
}

t_begin "single request matches" && {
	sha1=$(curl -sSfv 2> $err http://$listen/ | rsha1)
	test -n "$sha1"
	test x"$sha1" = x"$random_blob_sha1"
}

t_begin "body.close called" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "start FIFO reader for abortive HTTP/1.1 request" && {
	cat $fifo > $out &
}

t_begin "send abortive HTTP/1.1 request" && {
	rm -f $ok
	(
		printf 'GET /random_blob HTTP/1.1\r\nHost: example.com\r\n\r\n'
		dd bs=4096 count=1 < $http_fifo >/dev/null
		echo ok > $ok
	) | socat - TCP:$listen > $http_fifo || :
	test xok = x$(cat $ok)
}

t_begin "body.close called for aborted HTTP/1.1 request" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "start FIFO reader for abortive HTTP/1.0 request" && {
	cat $fifo > $out &
}

t_begin "send abortive HTTP/1.0 request" && {
	rm -f $ok
	(
		printf 'GET /random_blob HTTP/1.0\r\n\r\n'
		dd bs=4096 count=1 < $http_fifo >/dev/null
		echo ok > $ok
	) | socat - TCP:$listen > $http_fifo || :
	test xok = x$(cat $ok)
}

t_begin "body.close called for aborted HTTP/1.0 request" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "start FIFO reader for abortive HTTP/0.9 request" && {
	cat $fifo > $out &
}

t_begin "send abortive HTTP/0.9 request" && {
	rm -f $ok
	(
		printf 'GET /random_blob\r\n'
		dd bs=4096 count=1 < $http_fifo >/dev/null
		echo ok > $ok
	) | socat - TCP:$listen > $http_fifo || :
	test xok = x$(cat $ok)
}

t_begin "body.close called for aborted HTTP/0.9 request" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "shutdown server" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/t0002-parser-error.sh0000755000004100000410000000111212354770160017367 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
t_plan 5 "parser error test for $model"

t_begin "setup and startup" && {
	rainbows_setup $model
	rainbows -D env.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "send request" && {
	(
		printf 'GET / HTTP/1/1\r\nHost: example.com\r\n\r\n'
		cat $fifo > $tmp &
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
	test xok = x$(cat $ok)
}

dbgcat tmp

t_begin "response should be a 400" && {
	grep -F 'HTTP/1.1 400 Bad Request' $tmp
}

t_begin "server stderr should be clean" && check_stderr

t_begin "term signal sent" && kill $rainbows_pid

t_done
rainbows-4.6.2/t/async_sinatra.ru0000644000004100000410000000043012354770160017052 0ustar  www-datawww-data# See http://github.com/raggi/async_sinatra
# gem install async_sinatra -v0.1.5
require 'sinatra/async'

class AsyncTest < Sinatra::Base
  register Sinatra::Async

  aget '/:n' do |n|
    EM.add_timer(n.to_i) { body { "delayed for #{n} seconds\n" } }
  end
end

run AsyncTest.new
rainbows-4.6.2/t/t0027-nil-copy_stream.sh0000644000004100000410000000242312354770160020063 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
test -r random_blob || die "random_blob required, run with 'make $0'"
check_copy_stream

t_plan 7 "large file 'copy_stream nil' test for $model"

t_begin "setup and startup" && {
	rtmpfiles curl_out
	rainbows_setup $model
	cat >> $unicorn_config <$ok) | rsha1)
		test $sha1 = $random_blob_sha1
		test xok = x$(cat $ok)
	done
}

# this was a problem during development
t_begin "HTTP/1.0 test" && {
	sha1=$( (curl -0 -sSfv http://$listen/random_blob &&
	         echo ok >$ok) | rsha1)
	test $sha1 = $random_blob_sha1
	test xok = x$(cat $ok)
}

t_begin "HTTP/0.9 test" && {
	(
		printf 'GET /random_blob\r\n'
		rsha1 < $fifo > $tmp &
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
	test $(cat $tmp) = $random_blob_sha1
	test xok = x$(cat $ok)
}

t_begin "shutdown server" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/simple-http_Rev.ru0000644000004100000410000000034512354770160017303 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == false && env['rainbows.model'] == :Rev
    [ 200, {}, [ env.inspect << "\n" ] ]
  else
    raise "rack.multithread is true"
  end
}
rainbows-4.6.2/t/file-wrap-to_path.ru0000644000004100000410000000100012354770160017530 0ustar  www-datawww-data# must be run without Rack::Lint since that clobbers to_path
class Wrapper < Struct.new(:app)
  def call(env)
    status, headers, body = app.call(env)
    body = Body.new(body) if body.respond_to?(:to_path)
    [ status, headers, body ]
  end

  class Body < Struct.new(:body)
    def to_path
      body.to_path
    end

    def each(&block)
      body.each(&block)
    end

    def close
      ::File.open(ENV['fifo'], 'wb') { |fp| fp.puts "CLOSING" }
    end
  end
end
use Wrapper
run Rack::File.new(Dir.pwd)
rainbows-4.6.2/t/t0024-pipelined-sendfile-response.sh0000755000004100000410000000405112354770160022351 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll

t_plan 6 "pipelined sendfile response for $model"

t_begin "setup and startup" && {
	rtmpfiles err out dd_fifo
	rainbows_setup $model
	echo 'require "sendfile"' >> $unicorn_config
	echo 'def (::IO).copy_stream(*x); abort "NO"; end' >> $unicorn_config

	# can't load Rack::Lint here since it clobbers body#to_path
	rainbows -E none -D large-file-response.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "read random blob sha1" && {
	random_blob_sha1=$(rsha1 < random_blob)
}

script='
require "digest/sha1"
require "kcar"
$stdin.binmode
expect = ENV["random_blob_sha1"]
kcar = Kcar::Response.new($stdin, {})
3.times do
	nr = 0
	status, headers, body = kcar.rack
	dig = Digest::SHA1.new
	body.each { |buf| dig << buf ; nr += buf.size }
	sha1 = dig.hexdigest
	sha1 == expect or abort "mismatch: sha1=#{sha1} != expect=#{expect}"
	body.close
end
$stdout.syswrite("ok\n")
'

t_begin "staggered pipeline of 3 HTTP requests" && {
	req='GET /random_blob HTTP/1.1\r\nHost: example.com\r\n'
	rm -f $ok
	(
		export random_blob_sha1
		$RUBY -e "$script" < $fifo >> $ok &
		printf "$req"'X-Req:0\r\n\r\n'
		exec 6>&1
		(
			dd bs=16384 count=1
			printf "$req" >&6
			dd bs=16384 count=1
			printf 'X-Req:1\r\n\r\n' >&6
			dd bs=16384 count=1
			printf "$req" >&6
			dd bs=16384 count=1
			printf 'X-Req:2\r\n' >&6
			dd bs=16384 count=1
			printf 'Connection: close\r\n\r' >&6
			dd bs=16384 count=1
			printf '\n' >&6
			cat
		) < $dd_fifo > $fifo &
		wait
		echo ok >> $ok
	) | socat - TCP:$listen > $dd_fifo
	test 2 -eq $(grep '^ok$' $ok |count_lines)
}

t_begin "pipeline 3 HTTP requests" && {
	rm -f $ok
	req='GET /random_blob HTTP/1.1\r\nHost: example.com\r\n'
	req="$req"'\r\n'"$req"'\r\n'"$req"
	req="$req"'Connection: close\r\n\r\n'
	(
		export random_blob_sha1
		$RUBY -e "$script" < $fifo >> $ok &
		printf "$req"
		wait
		echo ok >> $ok
	) | socat - TCP:$listen > $fifo
	test 2 -eq $(grep '^ok$' $ok |count_lines)
}

t_begin "shutdown server" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/t0015-working_directory.sh0000755000004100000410000000264312354770160020526 0ustar  www-datawww-data#!/bin/sh
if test -n "$RBX_SKIP"
then
	echo "$0 is broken under Rubinius for now"
	exit 0
fi
. ./test-lib.sh

t_plan 6 "config.ru inside alt working_directory"

t_begin "setup and start" && {
	rainbows_setup
	rtmpfiles unicorn_config_tmp
	rm -rf $t_pfx.app
	mkdir $t_pfx.app

	cat > $t_pfx.app/config.ru < $unicorn_config_tmp

	# the whole point of this exercise
	echo "working_directory '$t_pfx.app'" >> $unicorn_config_tmp

	# allows ppid to be 1 in before_fork
	echo "preload_app true" >> $unicorn_config_tmp
	cat >> $unicorn_config_tmp <<\EOF
before_fork do |server,worker|
  $master_ppid = Process.ppid # should be zero to detect daemonization
end
EOF

	mv $unicorn_config_tmp $unicorn_config

	# rely on --daemonize switch, no & or -D
	rainbows -c $unicorn_config
	rainbows_wait_start
}

t_begin "reload to avoid race condition" && {
	curl -sSf http://$listen/ >/dev/null
	kill -HUP $rainbows_pid
	test xSTART = x"$(cat $fifo)"
}

t_begin "hit with curl" && {
	body=$(curl -sSf http://$listen/)
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "response body ppid == 1 (daemonized)" && {
	test "$body" -eq 1
}

t_begin "cleanup working directory" && {
	rm -r $t_pfx.app
}

t_done
rainbows-4.6.2/t/simple-http_XEpollThreadSpawn.ru0000644000004100000410000000036212354770160022112 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == true &&
    env['rainbows.model'] == :XEpollThreadSpawn
    [ 200, {}, [ Thread.current.inspect << "\n" ] ]
  else
    raise env.inspect
  end
}
rainbows-4.6.2/t/t0026-splice-copy_stream-byte-range.sh0000644000004100000410000000111012354770160022602 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
test -r random_blob || die "random_blob required, run with 'make $0'"
check_copy_stream
check_splice

t_plan 13 "IO::Splice.copy_stream byte range response for $model"

t_begin "setup and startup" && {
	rtmpfiles out err
	rainbows_setup $model
	cat >> $unicorn_config <> $ok &
		printf "$req"'X-Req:0\r\n\r\n'
		exec 6>&1
		(
			dd bs=16384 count=1
			printf "$req" >&6
			dd bs=16384 count=1
			printf 'X-Req:1\r\n\r\n' >&6
			dd bs=16384 count=1
			printf "$req" >&6
			dd bs=16384 count=1
			printf 'X-Req:2\r\n' >&6
			dd bs=16384 count=1
			printf 'Connection: close\r\n\r' >&6
			dd bs=16384 count=1
			printf '\n' >&6
			cat
		) < $dd_fifo > $fifo &
		wait
		echo ok >> $ok
	) | socat - TCP:$listen > $dd_fifo
	test 2 -eq $(grep '^ok$' $ok |count_lines)
}

t_begin "pipeline 3 HTTP requests" && {
	rm -f $ok
	req='GET /random_blob HTTP/1.1\r\nHost: example.com\r\n'
	req="$req"'\r\n'"$req"'\r\n'"$req"
	req="$req"'Connection: close\r\n\r\n'
	(
		export random_blob_sha1
		$RUBY -e "$script" < $fifo >> $ok &
		printf "$req"
		wait
		echo ok >> $ok
	) | socat - TCP:$listen > $fifo
	test 2 -eq $(grep '^ok$' $ok |count_lines)
}

t_begin "shutdown server" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/.gitignore0000644000004100000410000000007012354770160015634 0ustar  www-datawww-data/test-results-*
/bin-*
/random_blob
/.dep+*
/trash
/tmp
rainbows-4.6.2/t/t0011-close-on-exec-set.sh0000755000004100000410000000205212354770160020202 0ustar  www-datawww-data#!/bin/sh
nr=${nr-"5"}
. ./test-lib.sh
skip_models StreamResponseEpoll

t_plan 7 "ensure close-on-exec flag is set for $model"

t_begin "setup and start" && {
	rainbows_setup $model 1 1
	nr=$nr rainbows -E none -D fork-sleep.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "send keepalive req expect it to timeout in ~1s" && {
	req='GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'
	t0=$(unix_time)
	(
		cat $fifo > $tmp &
		printf "$req"
		wait
		unix_time > $ok
	) | socat - TCP:$listen > $fifo
	now="$(cat $ok)"
	elapsed=$(( $now - $t0 ))
	t_info "elapsed=$elapsed (expecting >=1s)"
	test $elapsed -ge 1
}

t_begin 'sleep process is still running' && {
	sleep_pid="$(tail -1 $tmp)"
	kill -0 $sleep_pid
}

t_begin 'keepalive not unreasonably long' && {
	test $elapsed -lt $nr
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	t_info "about to start waiting $nr seconds..."
	sleep $nr
	check_stderr
}

t_begin 'sleep process is NOT running' && {
	if kill -0 $sleep_pid
	then
		die "sleep process should've died"
	fi
}

t_done
rainbows-4.6.2/t/simple-http_FiberPool.ru0000644000004100000410000000037212354770160020430 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == false && env['rainbows.model'] == :FiberPool
    [ 200, {}, [ Thread.current.inspect << "\n" ] ]
  else
    raise "rack.multithread is not true"
  end
}
rainbows-4.6.2/t/cramp/0000755000004100000410000000000012354770160014751 5ustar  www-datawww-datarainbows-4.6.2/t/cramp/README0000644000004100000410000000024612354770160015633 0ustar  www-datawww-dataThese examples in this directory are stolen from Cramp with only trivial
changes.  All examples in this directory retain their original license
(MIT) and copyrights.
rainbows-4.6.2/t/cramp/streaming.ru0000644000004100000410000000100412354770160017305 0ustar  www-datawww-data# based on examples/streaming.rb in git://github.com/lifo/cramp
# commit ca54f8a944ae582a0c858209daf3c74efea7d27c

# Rack::Lint does not like async + EM stuff, so disable it:
#\ -E deployment

require 'cramp'

class StreamController < Cramp::Action
  periodic_timer :send_data, :every => 1
  periodic_timer :check_limit, :every => 2

  def start
    @limit = 0
  end

  def send_data
    render ["Hello World", "\n"]
  end

  def check_limit
    @limit += 1
    finish if @limit > 1
  end

end

run StreamController
rainbows-4.6.2/t/cramp/rainsocket.ru0000644000004100000410000000107212354770160017463 0ustar  www-datawww-data# based on examples/rainsocket.ru git://github.com/lifo/cramp
# Rack::Lint does not like async + EM stuff, so disable it:
#\ -E deployment
require 'cramp'

Cramp::Websocket.backend = :rainbows

class WelcomeController < Cramp::Websocket
  periodic_timer :send_hello_world, :every => 2
  on_data :received_data

  def received_data(data)
    if data =~ /fuck/
      render "You cant say fuck in here"
      finish
    else
      render "Got your #{data}"
    end
  end

  def send_hello_world
    render("Hello from the Server!\n" * 256)
  end
end

run WelcomeController
rainbows-4.6.2/t/simple-http_Base.ru0000644000004100000410000000015012354770160017413 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env| [ 200, {}, [ env.inspect << "\n" ] ] }
rainbows-4.6.2/t/t0000-simple-http.sh0000755000004100000410000000600412354770160017215 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
t_plan 25 "simple HTTP connection keepalive/pipelining tests for $model"

t_begin "checking for config.ru for $model" && {
	tbase=simple-http_$model.ru
	test -f "$tbase"
}

t_begin "setup and start" && {
	rainbows_setup
	rainbows -D $tbase -c $unicorn_config
	rainbows_wait_start
}

t_begin "pid file exists" && {
	test -f $pid
}

t_begin "single request" && {
	curl -sSfv http://$listen/
}

t_begin "handles client EOF gracefully" && {
	printf 'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n' | \
		socat - TCP4:$listen > $tmp
	dbgcat tmp
	if grep 'HTTP.* 500' $tmp
	then
		die "500 error returned on client shutdown(SHUT_WR)"
	fi
	check_stderr
}

dbgcat r_err

t_begin "two requests with keepalive" && {
	curl -sSfv http://$listen/a http://$listen/b > $tmp 2>&1
}

dbgcat r_err
dbgcat tmp

t_begin "reused existing connection" && {
	grep 'Re-using existing connection' < $tmp
}

t_begin "pipelining partial requests" && {
	req='GET / HTTP/1.1\r\nHost: example.com\r\n'
	(
		cat $fifo > $tmp &
		printf "$req"'\r\n'"$req"
		sleep 1
		printf 'Connection: close\r\n\r\n'
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
}
dbgcat tmp

t_begin "two HTTP/1.1 responses" && {
	test 2 -eq $(grep '^HTTP/1.1' $tmp | count_lines)
}

t_begin "two HTTP/1.1 200 OK responses" && {
	test 2 -eq $(grep '^HTTP/1.1 200 OK' $tmp | count_lines)
}

t_begin 'one "Connection: keep-alive" response' && {
	test 1 -eq $(grep '^Connection: keep-alive' $tmp | count_lines)
}

t_begin 'one "Connection: close" response' && {
	test 1 -eq $(grep '^Connection: close' $tmp | count_lines)
}

t_begin 'check subshell success' && {
	test x"$(cat $ok)" = xok
}


t_begin "check stderr" && {
	check_stderr
}

t_begin "burst pipelining requests" && {
	req='GET / HTTP/1.1\r\nHost: example.com\r\n'
	(
		cat $fifo > $tmp &
		printf "$req"'\r\n'"$req"'Connection: close\r\n\r\n'
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
}

dbgcat tmp
dbgcat r_err

t_begin "got 2 HTTP/1.1 responses from pipelining" && {
	test 2 -eq $(grep '^HTTP/1.1' $tmp | count_lines)
}

t_begin "got 2 HTTP/1.1 200 OK responses" && {
	test 2 -eq $(grep '^HTTP/1.1 200 OK' $tmp | count_lines)
}

t_begin "one keepalive connection" && {
	test 1 -eq $(grep '^Connection: keep-alive' $tmp | count_lines)
}

t_begin "second request closes connection" && {
	test 1 -eq $(grep '^Connection: close' $tmp | count_lines)
}

t_begin "subshell exited correctly" && {
	test x"$(cat $ok)" = xok
}

t_begin "stderr log has no errors" && {
	check_stderr
}

t_begin "HTTP/0.9 request should not return headers" && {
	(
		printf 'GET /\r\n'
		cat $fifo > $tmp &
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
}

dbgcat tmp
dbgcat r_err

t_begin "env.inspect should've put everything on one line" && {
	test 1 -eq $(count_lines < $tmp)
}

t_begin "no headers in output" && {
	if grep ^Connection: $tmp
	then
		die "Connection header found in $tmp"
	elif grep ^HTTP/ $tmp
	then
		die "HTTP/ found in $tmp"
	fi
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/t0018-reload-restore-settings.sh0000644000004100000410000000213712354770160021545 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
t_plan 8 "reload restore settings for $model"

t_begin "setup and start" && {
	rtmpfiles orig_config
	rainbows_setup
	cat $unicorn_config > $orig_config
	rainbows -D -c $unicorn_config -l $listen env.ru
	rainbows_wait_start
}

t_begin "HTTP request confirms we're running the correct model" && {
	curl -sSfv http://$listen/ | grep "\"rainbows.model\"=>:$model"
}

t_begin "clobber config and reload" && {
	cat > $unicorn_config <:Base" >/dev/null
}

t_begin "restore config and reload" && {
	cat $orig_config > $unicorn_config
	> $r_err
	kill -HUP $rainbows_pid
	rainbows_wait_start
	wait_for_reload
	wait_for_reap
}

t_begin "HTTP request confirms we're back on the correct model" && {
	curl -sSfv http://$listen/ | \
	  grep "\"rainbows.model\"=>:$model" >/dev/null
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/t0023-sendfile-byte-range.sh0000755000004100000410000000127312354770160020603 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
test -r random_blob || die "random_blob required, run with 'make $0'"
case $RUBY_ENGINE in
ruby) ;;
*)
	t_info "skipping $T since it can't load the sendfile gem, yet"
	exit 0
	;;
esac

skip_models EventMachine NeverBlock

t_plan 13 "sendfile byte range response for $model"

t_begin "setup and startup" && {
	rtmpfiles out err
	rainbows_setup $model
	echo 'require "sendfile"' >> $unicorn_config
	echo 'def (::IO).copy_stream(*x); abort "NO"; end' >> $unicorn_config

	# can't load Rack::Lint here since it clobbers body#to_path
	rainbows -E none -D large-file-response.ru -c $unicorn_config
	rainbows_wait_start
}

. ./byte-range-common.sh
rainbows-4.6.2/t/async-response-no-autochunk.ru0000644000004100000410000000106412354770160021602 0ustar  www-datawww-datause Rack::Chunked
use Rainbows::DevFdResponse
script_chunked = <<-EOF
for i in 0 1 2 3 4 5 6 7 8 9
do
	printf '1\r\n%s\r\n' $i
	sleep 1
done
printf '0\r\n\r\n'
EOF

script_identity = <<-EOF
for i in 0 1 2 3 4 5 6 7 8 9
do
	printf $i
	sleep 1
done
EOF

run lambda { |env|
  env['rainbows.autochunk'] = false
  headers = { 'Content-Type' => 'text/plain' }

  script = case env["HTTP_VERSION"]
  when nil, "HTTP/1.0"
    script_identity
  else
    headers['Transfer-Encoding'] = 'chunked'
    script_chunked
  end

  [ 200, headers, IO.popen(script, 'rb') ].freeze
}
rainbows-4.6.2/t/t0041-optional-pool-size.sh0000755000004100000410000000172012354770160020520 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh

case $model in
NeverBlock|CoolioThreadPool|XEpollThreadPool) ;;
*)
	t_info "skipping $model.$T since it doesn't support :pool_size"
	exit
	;;
esac

t_plan 6 "optional :pool_size argument for $model"

t_begin "setup and startup" && {
	rtmpfiles curl_out curl_err
	rainbows_setup $model
}

t_begin "fails with bad :pool_size" && {
	ed -s $unicorn_config < -666/
w
EOF
	grep "pool_size" $unicorn_config
	rainbows -D env.ru -c $unicorn_config || echo err=$? > $ok
	test x"$(cat $ok)" = "xerr=1"
}

t_begin "starts with correct :pool_size" && {
	ed -s $unicorn_config < 6/
w
EOF
	grep "pool_size" $unicorn_config
	rainbows -D env.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "regular TCP request works right" && {
	curl -sSfv http://$listen/
}

t_begin "no errors in stderr" && {
	check_stderr
}

t_begin "shutdown" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/t0202-async-response-one-oh.sh0000755000004100000410000000225012354770160021106 0ustar  www-datawww-data#!/bin/sh
CONFIG_RU=${CONFIG_RU-'async-response.ru'}
. ./test-lib.sh

skip_models Base WriterThreadPool WriterThreadSpawn
skip_models StreamResponseEpoll

t_plan 6 "async HTTP/1.0 response for $model"

t_begin "setup and start" && {
	rainbows_setup
	rtmpfiles a b c a_err b_err c_err
	# can't load Rack::Lint here since it'll cause Rev to slurp
	rainbows -E none -D $CONFIG_RU -c $unicorn_config
	rainbows_wait_start
}

t_begin "send async requests off in parallel" && {
	t0=$(unix_time)
	curl="curl -0 --no-buffer -vsSf http://$listen/"
	( $curl 2>> $a_err | tee $a) &
	( $curl 2>> $b_err | tee $b) &
	( $curl 2>> $c_err | tee $c) &
	wait
	t1=$(unix_time)
}

t_begin "ensure elapsed requests were processed in parallel" && {
	elapsed=$(( $t1 - $t0 ))
	echo "elapsed=$elapsed < 30"
	test $elapsed -lt 30
}

t_begin "termination signal sent" && {
	kill $rainbows_pid
}

dbgcat a
dbgcat b
dbgcat c
dbgcat a_err

t_begin "no errors from curl" && {
	if grep -i Transfer-Encoding $a_err $b_err $c_err
	then
		die "Unexpected Transfer-Encoding: header"
	fi
	for i in $a_err $b_err $c_err
	do
		grep 'Connection: close' $i
	done
}

dbgcat r_err
t_begin "no errors in stderr" && check_stderr

t_done
rainbows-4.6.2/t/simple-http_FiberSpawn.ru0000644000004100000410000000037312354770160020610 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == false && env['rainbows.model'] == :FiberSpawn
    [ 200, {}, [ Thread.current.inspect << "\n" ] ]
  else
    raise "rack.multithread is not true"
  end
}
rainbows-4.6.2/t/t0700-app-deferred.sh0000755000004100000410000000214412354770160017315 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
case $model in
EventMachine) ;;
*)
	t_info "skipping $T since it's not compatible with $model"
	exit 0
	;;
esac

t_plan 5 "basic test for app.deferred? usage"

CONFIG_RU=app_deferred.ru

t_begin "setup and start" && {
	rainbows_setup
	rtmpfiles deferred_err deferred_out sync_err sync_out
	rainbows -D -c $unicorn_config $CONFIG_RU
	rainbows_wait_start
}

t_begin "synchronous requests run in the same thread" && {
	curl --no-buffer -sSf http://$listen/ >> $sync_out 2>> $sync_err &
	curl --no-buffer -sSf http://$listen/ >> $sync_out 2>> $sync_err &
	curl --no-buffer -sSf http://$listen/ >> $sync_out 2>> $sync_err &
	wait
	test ! -s $sync_err
	test 3 -eq "$(count_lines < $sync_out)"
	test 1 -eq "$(uniq < $sync_out | count_lines)"
}

t_begin "deferred requests run in a different thread" && {
	curl -sSf http://$listen/deferred >> $deferred_out 2>> $deferred_err
	test ! -s $deferred_err
	sync_thread="$(uniq < $sync_out)"
	test x"$(uniq < $deferred_out)" != x"$sync_thread"
}

t_begin "termination signal sent" && {
	kill $rainbows_pid
}

t_begin "no errors in stderr" && check_stderr

t_done
rainbows-4.6.2/t/t0012-spurious-wakeups-quiet.sh0000755000004100000410000000143512354770160021450 0ustar  www-datawww-data#!/bin/sh
nr=${nr-4}
. ./test-lib.sh

# ApacheBench (ab) is commonly installed in the sbin paths in Debian-based
# systems...
AB="$(which ab 2>/dev/null || :)"
if test -z "$AB"
then
	AB=$(PATH=/usr/local/sbin:/usr/sbin:$PATH which ab 2>/dev/null || :)
fi

if test -z "$AB"
then
	t_info "skipping $T since 'ab' could not be found"
	exit 0
fi

t_plan 4 "quiet spurious wakeups for $model"

t_begin "setup and start" && {
	rainbows_setup $model
	echo "preload_app true" >> $unicorn_config
	echo "worker_processes $nr" >> $unicorn_config
	rainbows -D env.ru -c $unicorn_config -E none
	rainbows_wait_start
}

t_begin "spam the server with requests" && {
	$AB -c1 -n100 http://$listen/
}

t_begin "killing succeeds" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/byte-range-common.sh0000644000004100000410000001012412354770160017524 0ustar  www-datawww-datat_begin "byte-range setup vars" && {
	random_blob_size=$(count_bytes < random_blob)
	rb_1=$(( $random_blob_size - 1 ))
	range_head=-r-365
	range_tail=-r155-
	range_mid=-r200-300
	range_n1=-r0-$rb_1
	range_n2=-r0-$(($rb_1 - 1))
	range_1b_head=-r0-0
	range_1b_tail=-r$rb_1-$rb_1
	range_1b_mid=-r200-200
	range_all=-r0-$random_blob_size
	url=http://$listen/random_blob
}

check_content_range () {
	grep '^< HTTP/1\.1 206 Partial Content' $err
	grep 'Range:' $err
	# Content-Range: bytes #{offset}-#{offset+count-1}/#{clen}
	d='\([0-9]\+\)'
	start= end= size=
	eval $(< $err sed -n -e \
	  "s/^< Content-Range: bytes $d-$d\/$d"'.*$/start=\1 end=\2 size=\3/p')
	test -n "$start"
	test -n "$end"
	test -n "$size"

	# ensure we didn't screw up the sed invocation
	expect="< Content-Range: bytes $start-$end/$size"
	test x"$(grep -F "$expect" $err)" = x"$(grep '^< Content-Range:' $err)"

	test $start -le $end
	test $end -lt $size
}

t_begin "read random blob sha1s" && {
	sha1_head=$(curl -sSff $range_head file://random_blob | rsha1)
	sha1_tail=$(curl -sSff $range_tail file://random_blob | rsha1)
	sha1_mid=$(curl -sSff $range_mid file://random_blob | rsha1)
	sha1_n1=$(curl -sSff $range_n1 file://random_blob | rsha1)
	sha1_n2=$(curl -sSff $range_n2 file://random_blob | rsha1)
	sha1_1b_head=$(curl -sSff $range_1b_head file://random_blob | rsha1)
	sha1_1b_tail=$(curl -sSff $range_1b_tail file://random_blob | rsha1)
	sha1_1b_mid=$(curl -sSff $range_1b_mid file://random_blob | rsha1)
	sha1_all=$(rsha1 < random_blob)
	echo "$sha1_all=$sha1_n1"
}

t_begin "normal full request matches" && {
	sha1="$(curl -v 2>$err -sSf $url | rsha1)"
	test x"$sha1_all" = x"$sha1"
	grep 'Content-Range:' $err && die "Content-Range unexpected"
	grep 'HTTP/1.1 200 OK' $err || die "200 response expected"
}

t_begin "crazy offset goes over" && {
	range_insane=-r$(($random_blob_size * 2))-$(($random_blob_size * 4))
	curl -vsS 2>$err $range_insane $url >/dev/null
	grep '^< HTTP/1\.[01] 416 ' $err || die "expected 416 error"
	grep '^< Content-Range: bytes \*/'$random_blob_size $err || \
          die "expected Content-Range: bytes */SIZE"
}

t_begin "keepalive/pipelining is supported on 416 responses" && {
	rm -f $tmp
	(
		cat $fifo > $tmp &
		printf 'GET /byte-range-common.sh HTTP/1.1\r\n'
		printf 'Host: %s\r\n' $listen
		printf 'Range: bytes=9999999999-9999999999\r\n\r\n'
		printf 'GET /byte-range-common.sh HTTP/1.1\r\n'
		printf 'Host: %s\r\n' $listen
		printf 'Connection: close\r\n'
		printf 'Range: bytes=0-0\r\n\r\n'
		wait
	) | socat - TCP:$listen > $fifo

	< $tmp awk '
/^HTTP\/1\.1 / && NR == 1 && $2 == 416 { first = $2 }
/^HTTP\/1\.1 / && NR != 1 && $2 == 206 { second = $2 }
END { exit((first == 416 && second == 206) ? 0 : 1) }
	'
}

t_begin "full request matches with explicit ranges" && {
	sha1="$(curl -v 2>$err $range_all -sSf $url | rsha1)"
	check_content_range
	test x"$sha1_all" = x"$sha1"

	sha1="$(curl -v 2>$err $range_n1 -sSf $url | rsha1)"
	check_content_range
	test x"$sha1_all" = x"$sha1"

	range_over=-r0-$(($random_blob_size * 2))
	sha1="$(curl -v 2>$err $range_over -sSf $url | rsha1)"
	check_content_range
	test x"$sha1_all" = x"$sha1"
}

t_begin "no fence post errors" && {
	sha1="$(curl -v 2>$err $range_n2 -sSf $url | rsha1)"
	check_content_range
	test x"$sha1_n2" = x"$sha1"

	sha1="$(curl -v 2>$err $range_1b_head -sSf $url | rsha1)"
	check_content_range
	test x"$sha1_1b_head" = x"$sha1"

	sha1="$(curl -v 2>$err $range_1b_tail -sSf $url | rsha1)"
	check_content_range
	test x"$sha1_1b_tail" = x"$sha1"

	sha1="$(curl -v 2>$err $range_1b_mid -sSf $url | rsha1)"
	check_content_range
	test x"$sha1_1b_mid" = x"$sha1"
}

t_begin "head range matches" && {
	sha1="$(curl -sSfv 2>$err $range_head $url | rsha1)"
	check_content_range
	test x"$sha1_head" = x"$sha1"
}

t_begin "tail range matches" && {
	sha1="$(curl -sSfv 2>$err $range_tail $url | rsha1)"
	check_content_range
	test x"$sha1_tail" = x"$sha1"
}

t_begin "mid range matches" && {
	sha1="$(curl -sSfv 2>$err $range_mid $url | rsha1)"
	check_content_range
	test x"$sha1_mid" = x"$sha1"
}

t_begin "shutdown server" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/t0006-process-rack-env.sh0000755000004100000410000000160012354770160020134 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh

t_plan 4 'ensure ENV["RACK_ENV"] is set correctly for '$model

finish_checks () {
	kill $rainbows_pid
	test ! -s $curl_err
	check_stderr
}

t_begin "setup" && {
	rtmpfiles curl_out curl_err
}

t_begin "default RACK_ENV is 'development'" && {
	rainbows_setup
	rainbows -D -c $unicorn_config env_rack_env.ru
	rainbows_wait_start
	test x"$(curl -sSf http://$listen 2>$curl_err)" = x"development"
	finish_checks
}

t_begin "RACK_ENV from process ENV is inherited" && {
	rainbows_setup
	( RACK_ENV=production rainbows -D -c $unicorn_config env_rack_env.ru )
	rainbows_wait_start
	test x$(curl -sSf http://$listen 2>$curl_err) = x"production"
	finish_checks
}

t_begin "RACK_ENV from -E is set" && {
	rainbows_setup
	rainbows -D -c $unicorn_config -E none env_rack_env.ru
	rainbows_wait_start
	test x$(curl -sSf http://$listen 2>$curl_err) = x"none"
	finish_checks
}

t_done
rainbows-4.6.2/t/t9100-thread-timeout.sh0000755000004100000410000000215412354770160017716 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
check_threaded_app_dispatch

t_plan 6 "ThreadTimeout Rack middleware test for $model"

t_begin "configure and start" && {
	rtmpfiles curl_err
	rainbows_setup
	rainbows -D t9100.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "normal request should not timeout" && {
	test x"HI" = x"$(curl -sSf http://$listen/ 2>> $curl_err)"
}

t_begin "sleepy request times out with 408" && {
	rm -f $ok
	curl -sSf http://$listen/2 2>> $curl_err || > $ok
	test -e $ok
	grep 408 $curl_err
}

t_begin "short requests do not timeout while making a long one" && {
	rm -f $ok $curl_err
	> $ok
	curl -sSf http://$listen/2 2>$curl_err >/dev/null &
	(
		for i in $(awk =0;)print i}')
		do
			curl -sSf http://$listen/0.1 >> $ok 2>&1 &
			test x"HI" = x"$(curl -sSf http://$listen/0.05)"
		done
		wait
	)
	test x"HI" = x"$(curl -sSf http://$listen/)"
	wait
	test -f $ok
	test 20 -eq $(grep '^HI$' $ok | count_lines)
	test x = x"$(grep -v '^HI$' $ok)"
	grep 408 $curl_err
}

t_begin "kill server" && {
	kill $rainbows_pid
}

t_begin "no errors in Rainbows! stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/t0401-em-async-tailer.sh0000755000004100000410000000271012354770160017746 0ustar  www-datawww-data#!/bin/sh
nr=${nr-5}
. ./test-lib.sh
case $model in
NeverBlock|EventMachine) ;;
*)
	t_info "skipping $T since it's not compatible with $model"
	exit 0
	;;
esac

t_plan 8 "async_tailer test for test for EM"

CONFIG_RU=async_examples/async_tailer.ru

t_begin "setup and start" && {
	rainbows_setup
	rtmpfiles a b c curl_err TAIL_LOG_FILE expect

	printf '

Async Tailer

' >> $expect

	export TAIL_LOG_FILE

	# this does not does not support Rack::Lint
	rainbows -E deployment -D $CONFIG_RU -c $unicorn_config
	rainbows_wait_start
}

t_begin "send async requests off in parallel" && {
	t0=$(unix_time)
	curl --no-buffer -sSf http://$listen/ > $a 2>> $curl_err &
	curl_a=$!
	curl --no-buffer -sSf http://$listen/ > $b 2>> $curl_err &
	curl_b=$!
	curl --no-buffer -sSf http://$listen/ > $c 2>> $curl_err &
	curl_c=$!
}

t_begin "generate log output" && {
	for i in $(awk "BEGIN {for(i=0;i<$nr;i++) print i}" < /dev/null)
	do
		date >> $TAIL_LOG_FILE
		sleep 1
	done
	# sometimes tail(1) can be slow
	sleep 2
}

t_begin "kill curls and wait for termination" && {
	kill $curl_a $curl_b $curl_c
	wait
	t1=$(unix_time)
	elapsed=$(( $t1 - $t0 ))
	t_info "elapsed=$elapsed"
}

t_begin "termination signal sent" && {
	kill -QUIT $rainbows_pid
}

t_begin "no errors from curl" && {
	test ! -s $curl_err
}

t_begin "no errors in stderr" && check_stderr

t_begin "responses match expected" && {
	cat $TAIL_LOG_FILE >> $expect
	cmp $expect $a
	cmp $expect $b
	cmp $expect $c
}

t_done
rainbows-4.6.2/t/simple-http_XEpollThreadPool.ru0000644000004100000410000000036112354770160021732 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == true &&
    env['rainbows.model'] == :XEpollThreadPool
    [ 200, {}, [ Thread.current.inspect << "\n" ] ]
  else
    raise env.inspect
  end
}
rainbows-4.6.2/t/write-on-close.ru0000644000004100000410000000037312354770160017071 0ustar  www-datawww-dataclass WriteOnClose
  def each(&block)
    @callback = block
  end

  def close
    @callback.call "7\r\nGoodbye\r\n0\r\n\r\n"
  end
end
use Rack::ContentType, "text/plain"
run(lambda { |_| [ 200, [%w(Transfer-Encoding chunked)], WriteOnClose.new ] })
rainbows-4.6.2/t/t9001.ru0000644000004100000410000000027612354770160015001 0ustar  www-datawww-datause Rainbows::Sendfile
run lambda { |env|
  path = "#{Dir.pwd}/random_blob"
  [ 200,
    {
      'X-Sendfile' => path,
      'Content-Type' => 'application/octet-stream'
    },
    []
  ]
}
rainbows-4.6.2/t/async_chunk_app.ru0000644000004100000410000000315412354770160017367 0ustar  www-datawww-data# based on async_examples/async_app.ru by James Tucker
class DeferrableChunkBody
  include EventMachine::Deferrable

  def call(*body)
    body.each do |chunk|
      @body_callback.call("#{chunk.size.to_s(16)}\r\n")
      @body_callback.call(chunk)
      @body_callback.call("\r\n")
    end
  end

  def each(&block)
    @body_callback = block
  end

  def finish
    @body_callback.call("0\r\n\r\n")
  end
end if defined?(EventMachine)

class AsyncChunkApp
  def call(env)
    headers = {
      'Content-Type' => 'text/plain',
      'Transfer-Encoding' => 'chunked',
    }
    delay = env["HTTP_X_DELAY"].to_i

    case env["rainbows.model"]
    when :EventMachine, :NeverBlock
      body = DeferrableChunkBody.new
      body.callback { body.finish }
      task = lambda {
        env['async.callback'].call([ 200, headers, body ])
        EM.add_timer(1) {
          body.call "Hello "

          EM.add_timer(1) {
            body.call "World #{env['PATH_INFO']}\n"
            body.succeed
          }
        }
      }
      delay == 0 ? EM.next_tick(&task) : EM.add_timer(delay, &task)
    when :Coolio
      # Cool.io only does one-shot responses due to the lack of the
      # equivalent of EM::Deferrables
      body = [ "Hello ", "World #{env['PATH_INFO']}\n", '' ].map do |chunk|
        "#{chunk.size.to_s(16)}\r\n#{chunk}\r\n"
      end

      next_tick = Coolio::TimerWatcher.new(delay, false)
      next_tick.on_timer { env['async.callback'].call([ 200, headers, body ]) }
      next_tick.attach(Coolio::Loop.default)
    else
      raise "Not supported: #{env['rainbows.model']}"
    end
    nil
  end
end
run AsyncChunkApp.new
rainbows-4.6.2/t/t0102-rack-input-short.sh0000755000004100000410000000134412354770160020166 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
test -r random_blob || die "random_blob required, run with 'make $0'"

t_plan 4 "rack.input short read tests"

t_begin "setup and startup" && {
	rtmpfiles curl_out curl_err
	rainbows_setup $model
	rainbows -D sha1-random-size.ru -c $unicorn_config
	blob_sha1=$(rsha1 < random_blob)
	t_info "blob_sha1=$blob_sha1"
	rainbows_wait_start
}

t_begin "regular request" && {
	curl -sSf -T random_blob http://$listen/ > $curl_out 2> $curl_err
	test x$blob_sha1 = x$(cat $curl_out)
	test ! -s $curl_err
}

t_begin "chunked request" && {
	curl -sSf -T- < random_blob http://$listen/ > $curl_out 2> $curl_err
	test x$blob_sha1 = x$(cat $curl_out)
	test ! -s $curl_err
}

t_begin "shutdown" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/simple-http_ThreadSpawn.ru0000644000004100000410000000036312354770160020767 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] && env['rainbows.model'] == :ThreadSpawn
    [ 200, {}, [ Thread.current.inspect << "\n" ] ]
  else
    raise "rack.multithread is not true"
  end
}
rainbows-4.6.2/t/t0002-graceful.sh0000755000004100000410000000122412354770160016540 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh

t_plan 4 "graceful exit test for $model"

t_begin "setup and startup" && {
	rtmpfiles curl_out
	rainbows_setup $model
	rainbows -D sleep.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "send a request and SIGQUIT while request is processing" && {
	curl -sSfv -T-  $curl_out 2> $fifo &
	awk -v rainbows_pid=$rainbows_pid '
{ print $0 }
/100 Continue/ {
	print "awk: sending SIGQUIT to", rainbows_pid
	system("kill -QUIT "rainbows_pid)
}' $fifo
	wait
}

dbgcat r_err

t_begin 'response returned "Hello"' && {
	test x$(cat $curl_out) = xHello
}

t_begin 'stderr has no errors' && check_stderr

t_done
rainbows-4.6.2/t/t0100-rack-input-hammer-chunked.sh0000755000004100000410000000247712354770160021725 0ustar  www-datawww-datanr_client=${nr_client-4}
. ./test-lib.sh
test -r random_blob || die "random_blob required, run with 'make $0'"

# basically we don't trust our own implementation of content-md5-put
# nor our Ruby 1.9 knowledge nor proper use of encodings in Ruby.
# So we try to use things like curl and sha1sum that are implemented
# without the Ruby interpreter to validate our own Ruby internals.

t_plan 7 "concurrent rack.input hammer stress test (chunked)"

t_begin "setup and startup" && {
	rtmpfiles curl_out curl_err
	rainbows_setup $model
	rainbows -D sha1.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "send $nr_client concurrent requests" && {
	start=$(unix_time)
	for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" > $curl_out 2>> $curl_err
		) &
	done
	wait
	t_info elapsed=$(( $(unix_time) - $start ))
}

t_begin "kill server" && kill $rainbows_pid

t_begin "got $nr_client responses" && {
	test $nr_client -eq $(count_lines < $curl_out)
}

t_begin "all responses identical" && {
	test 1 -eq $(sort < $curl_out | uniq | count_lines)
}

t_begin "sha1 matches on-disk sha1" && {
	blob_sha1=$(rsha1 < random_blob)
	t_info blob_sha1=$blob_sha1
	test x"$blob_sha1" = x"$(sort < $curl_out | uniq)"
}

t_begin "no errors in stderr log" && check_stderr

t_done
rainbows-4.6.2/t/t0042-client_header_buffer_size.sh0000644000004100000410000000304112354770160022121 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh

t_plan 8 "client_header_buffer_size tests for $model"

t_begin "setup and startup" && {
	rainbows_setup $model
}

t_begin "fails with zero buffer size" && {
	ed -s $unicorn_config < $ok
	test x"$(cat $ok)" = "xerr=1"
}

t_begin "fails with negative value" && {
	ed -s $unicorn_config < $ok
	test x"$(cat $ok)" = "xerr=1"
}

t_begin "fails with negative value" && {
	ed -s $unicorn_config < $ok
	test x"$(cat $ok)" = "xerr=1"
}

t_begin "starts with correct value" && {
	ed -s $unicorn_config <> $expect
	echo 'Hello World /1' >> $expect
	echo 'Hello World /2' >> $expect
}

t_begin "async.callback supports pipelining" && {
	rm -f $tmp
	t0=$(unix_time)
	(
		cat $fifo > $tmp &
		printf 'GET /0 HTTP/1.1\r\nHost: example.com\r\n\r\n'
		printf 'GET /1 HTTP/1.1\r\nHost: example.com\r\n\r\n'
		printf 'GET /2 HTTP/1.0\r\nHost: example.com\r\n\r\n'
		wait
	) | socat - TCP:$listen > $fifo
	t1=$(unix_time)
	elapsed=$(( $t1 - $t0 ))
	t_info "elapsed=$elapsed $model.$0 ($t_current)"
	test 3 -eq "$(fgrep 'HTTP/1.1 200 OK' $tmp | count_lines)"
	test 3 -eq "$(grep '^Hello ' $tmp | count_lines)"
	test 3 -eq "$(grep 'World ' $tmp | count_lines)"
}

t_begin "async.callback supports delayed pipelining" && {
	rm -f $tmp
	t0=$(unix_time)
	(
		cat $fifo > $tmp &
		printf 'GET /0 HTTP/1.1\r\nHost: example.com\r\n\r\n'
		sleep 1
		printf 'GET /1 HTTP/1.1\r\nHost: example.com\r\n\r\n'
		sleep 1
		printf 'GET /2 HTTP/1.0\r\nHost: example.com\r\n\r\n'
		wait
	) | socat - TCP:$listen > $fifo
	t1=$(unix_time)
	elapsed=$(( $t1 - $t0 ))
	t_info "elapsed=$elapsed $model.$0 ($t_current)"
	test 3 -eq "$(fgrep 'HTTP/1.1 200 OK' $tmp | count_lines)"
	test 3 -eq "$(grep '^Hello ' $tmp | count_lines)"
	test 3 -eq "$(grep 'World ' $tmp | count_lines)"
}

t_begin "async.callback supports pipelining with delay $DELAY" && {
	rm -f $tmp
	t0=$(unix_time)
	(
		cat $fifo > $tmp &
		printf 'GET /0 HTTP/1.1\r\nX-Delay: %d\r\n' $DELAY
		printf 'Host: example.com\r\n\r\n'
		printf 'GET /1 HTTP/1.1\r\nX-Delay: %d\r\n' $DELAY
		printf 'Host: example.com\r\n\r\n'
		printf 'GET /2 HTTP/1.0\r\nX-Delay: %d\r\n' $DELAY
		printf 'Host: example.com\r\n\r\n'
		wait
	) | socat - TCP:$listen > $fifo
	t1=$(unix_time)
	elapsed=$(( $t1 - $t0 ))
	min=$(( $DELAY * 3 ))
	t_info "elapsed=$elapsed $model.$0 ($t_current) min=$min"
	test $elapsed -ge $min
	test 3 -eq "$(fgrep 'HTTP/1.1 200 OK' $tmp | count_lines)"
	test 3 -eq "$(grep '^Hello ' $tmp | count_lines)"
	test 3 -eq "$(grep 'World ' $tmp | count_lines)"
}

t_begin "async.callback supports keepalive" && {
	t0=$(unix_time)
	curl -v --no-buffer -sSf http://$listen/[0-2] > $tmp 2>> $curl_err
	t1=$(unix_time)
	elapsed=$(( $t1 - $t0 ))
	t_info "elapsed=$elapsed $model.$0 ($t_current)"
	cmp $expect $tmp
	test 2 -eq "$(fgrep 'Re-using existing connection!' $curl_err |count_lines)"
	rm -f $curl_err
}

t_begin "async.callback supports keepalive with delay $DELAY" && {
	t0=$(unix_time)
	curl -v --no-buffer -sSf -H "X-Delay: $DELAY" \
	  http://$listen/[0-2] > $tmp 2>> $curl_err
	t1=$(unix_time)
	elapsed=$(( $t1 - $t0 ))
	min=$(( $DELAY * 3 ))
	t_info "elapsed=$elapsed $model.$0 ($t_current) min=$min"
	test $elapsed -ge $min
	cmp $expect $tmp
	test 2 -eq "$(fgrep 'Re-using existing connection!' $curl_err |count_lines)"
	rm -f $curl_err
}

t_begin "send async requests off in parallel" && {
	t0=$(unix_time)
	curl --no-buffer -sSf http://$listen/[0-2] > $a 2>> $curl_err &
	curl --no-buffer -sSf http://$listen/[0-2] > $b 2>> $curl_err &
	curl --no-buffer -sSf http://$listen/[0-2] > $c 2>> $curl_err &
}

t_begin "wait for curl terminations" && {
	wait
	t1=$(unix_time)
	elapsed=$(( $t1 - $t0 ))
	t_info "elapsed=$elapsed"
}

t_begin "termination signal sent" && {
	kill $rainbows_pid
}

t_begin "no errors from curl" && {
	test ! -s $curl_err
}

t_begin "no errors in stderr" && check_stderr

t_begin "responses match expected" && {
	cmp $expect $a
	cmp $expect $b
	cmp $expect $c
}

t_done

rainbows-4.6.2/t/t0044-autopush.sh0000644000004100000410000000504612354770160016631 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
STRACE=$(which strace 2>/dev/null || :)
if ! test -x "$STRACE"
then
	t_info "strace not found, skipping $T"
	exit 0
fi
if test x"$(uname -s)" != xLinux
then
	t_info "Linux is the only supported OS for $T"
	exit 0
fi

# these buffer internally in external libraries, so we can't detect when
# to use TCP_CORK
skip_models EventMachine NeverBlock
skip_models StreamResponseEpoll
skip_models Coolio CoolioThreadPool CoolioThreadSpawn
skip_models Revactor Rev RevThreadPool RevThreadSpawn

# not sure why, but we don't have time to care about Ruby 1.8 too much
case $RUBY_VERSION in
1.8.*) skip_models WriterThreadSpawn WriterThreadPool ;;
esac

t_plan 13 "Kgio autopush tests"

start_strace () {
	# dbgcat strace_out
	> $strace_out
	sleep 1
	strace -p $worker_pid -e '!futex' -f -o $strace_out &
	strace_pid=$!
	while ! test -s $strace_out; do sleep 1; done
}

check_TCP_CORK () {
	nr=0
	while test 2 -gt $(grep TCP_CORK $strace_out | count_lines)
	do
		nr=$(( $nr + 1 ))
		if test $nr -gt 30
		then
			dbgcat strace_out
			die "waited too long ($nr seconds) for TCP_CORK"
		fi
		sleep 1
	done

	test 2 -eq $(grep TCP_CORK $strace_out | count_lines)
	fgrep 'SOL_TCP, TCP_CORK, [0],' $strace_out
	fgrep 'SOL_TCP, TCP_CORK, [1],' $strace_out
}

t_begin "setup and start" && {
	rainbows_setup $model 1 1
	rtmpfiles strace_out
	ed -s $unicorn_config < true, :tcp_nopush => true/
w
EOF
	rainbows -D large-file-response.ru -c $unicorn_config -E none
	rainbows_wait_start
}

t_begin "read worker pid" && {
	worker_pid=$(curl -sSf http://$listen/pid)
	kill -0 $worker_pid
}

t_begin "start strace on worker" && start_strace

t_begin "reading RSS uncorks" && {
	curl -sSf http://$listen/rss >/dev/null
}

t_begin "restart strace on worker" && {
	kill $strace_pid
	wait
	start_strace
}

t_begin "reading static file uncorks" && {
	curl -sSf http://$listen/random_blob >/dev/null
	check_TCP_CORK
}

t_begin "stop strace on worker" && {
	kill $strace_pid
	wait
}

t_begin "enable sendfile" && {
	echo >> $unicorn_config 'require "sendfile"'
	kill -HUP $rainbows_pid
	test xSTART = x"$(cat $fifo)"
}

t_begin "reread worker pid" && {
	worker_pid=$(curl -sSf http://$listen/pid)
	kill -0 $worker_pid
}

t_begin "restart strace on the worker" && start_strace

t_begin "HTTP/1.x GET on static file with sendfile uncorks" && {
	curl -sSf http://$listen/random_blob >/dev/null
	check_TCP_CORK
}

t_begin "killing succeeds" && {
	kill $strace_pid
	wait
	# dbgcat strace_out
	kill $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/simple-http_CoolioThreadSpawn.ru0000644000004100000410000000035312354770160022133 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] && env['rainbows.model'] == :CoolioThreadSpawn
    [ 200, {}, [ env.inspect << "\n" ] ]
  else
    raise "rack.multithread is false"
  end
}
rainbows-4.6.2/t/t9000.ru0000644000004100000410000000033412354770160014773 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
use Rainbows::AppPool, :size => ENV['APP_POOL_SIZE'].to_i
class Sleeper
  def call(env)
    Rainbows.sleep(1)
    [ 200, {}, [ "#{object_id}\n" ] ]
  end
end
run Sleeper.new
rainbows-4.6.2/t/t0800-rack-hijack.sh0000755000004100000410000000102712354770160017126 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
t_plan 5 "rack.hijack tests (Rack 1.5+ (Rack::VERSION >= [1,2]))"

t_begin "setup and start" && {
	rainbows_setup
	rainbows -D -c $unicorn_config hijack.ru
	rainbows_wait_start
}

t_begin "check request hijack" && {
	test "xrequest.hijacked" = x"$(curl -sSfv http://$listen/hijack_req)"
}

t_begin "check response hijack" && {
	test "xresponse.hijacked" = x"$(curl -sSfv http://$listen/hijack_res)"
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/simple-http_CoolioFiberSpawn.ru0000644000004100000410000000036212354770160021753 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == false &&
    env['rainbows.model'] == :CoolioFiberSpawn
    [ 200, {}, [ Thread.current.inspect << "\n" ] ]
  else
    raise env.inspect
  end
}
rainbows-4.6.2/t/t9002.ru0000644000004100000410000000016612354770160015000 0ustar  www-datawww-datarequire 'rainbows/server_token'
require 'rack/lobster'
use Rack::Head
use Rainbows::ServerToken
run Rack::Lobster.new
rainbows-4.6.2/t/simple-http_ActorSpawn.ru0000644000004100000410000000036112354770160020626 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] && env['rainbows.model'] == :ActorSpawn
    [ 200, {}, [ Actor.current.inspect << "\n" ] ]
  else
    raise "rack.multithread is not true"
  end
}
rainbows-4.6.2/t/t0022-copy_stream-byte-range.sh0000755000004100000410000000064612354770160021341 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
test -r random_blob || die "random_blob required, run with 'make $0'"
check_copy_stream

t_plan 13 "IO.copy_stream byte range response for $model"

t_begin "setup and startup" && {
	rtmpfiles out err
	rainbows_setup $model
	# can't load Rack::Lint here since it clobbers body#to_path
	rainbows -E none -D large-file-response.ru -c $unicorn_config
	rainbows_wait_start
}

. ./byte-range-common.sh
rainbows-4.6.2/t/heartbeat-timeout.ru0000644000004100000410000000050112354770160017636 0ustar  www-datawww-datause Rack::ContentLength
headers = { 'Content-Type' => 'text/plain' }
run lambda { |env|
  case env['PATH_INFO']
  when "/block-forever"
    Process.kill(:STOP, $$)
    sleep # in case STOP signal is not received in time
    [ 500, headers, [ "Should never get here\n" ] ]
  else
    [ 200, headers, [ "#$$\n" ] ]
  end
}
rainbows-4.6.2/t/t0007-worker-follows-master-to-death.sh0000755000004100000410000000226212354770160022750 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
t_plan 7 "ensure worker follows master to death"

t_begin "setup" && {
	rtmpfiles curl_err curl_out
	rainbows_setup
	echo timeout 3 >> $unicorn_config
	rainbows -D -c $unicorn_config worker-follows-master-to-death.ru
	rainbows_wait_start
}

t_begin "read worker PID" && {
	worker_pid=$(curl -sSf http://$listen/pid)
	t_info "worker_pid=$worker_pid"
}

t_begin "start a long sleeping request" && {
	curl -sSfv -T- $curl_out 2> $fifo &
	curl_pid=$!
	t_info "curl_pid=$curl_pid"
}

t_begin "nuke the master once we're connected" && {
	awk -v rainbows_pid=$rainbows_pid '
{ print $0 }
/100 Continue/ {
	print "awk: sending SIGKILL to", rainbows_pid
	system("kill -9 "rainbows_pid)
}' < $fifo > $curl_err
	wait
}

t_begin "worker is no longer running" && {
	nr=30
	while kill -0 $worker_pid 2>/dev/null && test $nr -gt 0
	do
		nr=$(( $nr - 1))
		sleep 1
	done
	kill -0 $worker_pid 2> $tmp && false
	test -s $tmp
}

t_begin "sleepy curl request is no longer running" && {
	kill -0 $curl_pid 2> $tmp && false
	test -s $tmp
}

t_begin "sleepy curl request completed gracefully" && {
	test x$(cat $curl_out) = x$worker_pid
	dbgcat curl_err
}

t_done
rainbows-4.6.2/t/t0035-kgio-pipe-response.sh0000755000004100000410000000273512354770160020506 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
test -r random_blob || die "random_blob required, run with 'make $0'"

t_plan 10 "fast Kgio pipe response for $model"

t_begin "setup and startup" && {
	rtmpfiles err out
	rainbows_setup $model
	rainbows -E none -D kgio-pipe-response.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "read random blob sha1" && {
	random_blob_sha1=$(rsha1 < random_blob)
	three_sha1=$(cat random_blob random_blob random_blob | rsha1)
}

t_begin "single request matches" && {
	sha1=$(curl -sSfv 2> $err http://$listen/ | rsha1)
	test -n "$sha1"
	test x"$sha1" = x"$random_blob_sha1"
}

t_begin "Content-Length header preserved in response" && {
	grep "^< Content-Length:" $err
}

t_begin "send three keep-alive requests" && {
	sha1=$(curl -vsSf 2> $err \
	       http://$listen/ http://$listen/ http://$listen/ | rsha1)
	test -n "$sha1"
	test x"$sha1" = x"$three_sha1"
}

t_begin "ensure responses were all keep-alive" && {
	test 3 -eq $(grep '< Connection: keep-alive' < $err | count_lines)
}

t_begin "HTTP/1.0 test" && {
	sha1=$(curl -0 -v 2> $err -sSf http://$listen/ | rsha1)
	test $sha1 = $random_blob_sha1
	grep '< Connection: close' < $err
}

t_begin "HTTP/0.9 test" && {
	(
		printf 'GET /\r\n'
		rsha1 < $fifo > $tmp &
		wait
		echo ok > $ok
	) | socat - TCP:$listen > $fifo
	test $(cat $tmp) = $random_blob_sha1
	test xok = x$(cat $ok)
}

t_begin "shutdown server" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/close-has-env.ru0000644000004100000410000000225512354770160016667 0ustar  www-datawww-data#\ -E none
use Rainbows::DevFdResponse
class ClosablePipe < ::IO
  attr_accessor :env

  def self.new(env)
    rv = popen "echo hello", "rb"
    rv.env = env
    rv
  end

  def close
    super
    $stdout.syswrite "path_info=#{@env['PATH_INFO']}\n"
  end
end

class ClosableFile < ::File
  attr_accessor :env
  alias to_path path
  def close
    super
    $stdout.syswrite "path_info=#{@env['PATH_INFO']}\n"
  end
end

class Blob
  def initialize(env)
    @env = env
  end

  def each(&block)
    yield "BLOB\n"
  end

  def close
    $stdout.syswrite "path_info=#{@env['PATH_INFO']}\n"
  end
end

run(lambda { |env|
  case env["PATH_INFO"]
  when %r{\A/pipe/}
    [ 200,
      [ %w(Content-Length 6), %w(Content-Type text/plain)],
      ClosablePipe.new(env)
    ]
  when %r{\A/file/}
    f = ClosableFile.open("env.ru", "rb")
    f.env = env
    [ 200, {
      'X-Req-Path' => env["PATH_INFO"],
      'Content-Length' => f.stat.size.to_s,
      'Content-Type' => 'text/plain' },
      f
    ]
  when %r{\A/blob/}
    [ 200,
      [%w(Content-Length 5), %w(Content-Type text/plain)],
      Blob.new(env)
    ]
  else
    [ 404, [%w(Content-Length 0), %w(Content-Type text/plain)], [] ]
  end
})
rainbows-4.6.2/t/t0000.ru0000644000004100000410000000015012354770160014756 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env| [ 200, {}, [ env.inspect << "\n" ] ] }
rainbows-4.6.2/t/t0010-keepalive-timeout-effective.sh0000755000004100000410000000144112354770160022337 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll

t_plan 6 "keepalive_timeout tests for $model"

t_begin "setup and start" && {
	rainbows_setup
	rainbows -D env.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin 'check server up' && {
	curl -sSf http://$listen/
}

t_begin "send keepalive response that does not expect close" && {
	req='GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'
	t0=$(unix_time)
	(
		cat $fifo > $tmp &
		printf "$req"
		wait
		unix_time > $ok
	) | socat - TCP:$listen > $fifo
	now="$(cat $ok)"
	elapsed=$(( $now - $t0 ))
	t_info "elapsed=$elapsed (expecting >=5s)"
	test $elapsed -ge 5
}

t_begin 'keepalive not unreasonably long' && {
	test $elapsed -lt 15
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/simple-http_RevFiberSpawn.ru0000644000004100000410000000035712354770160021267 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == false &&
    env['rainbows.model'] == :RevFiberSpawn
    [ 200, {}, [ Thread.current.inspect << "\n" ] ]
  else
    raise env.inspect
  end
}
rainbows-4.6.2/t/simple-http_EventMachine.ru0000644000004100000410000000035612354770160021117 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == false && env['rainbows.model'] == :EventMachine
    [ 200, {}, [ env.inspect << "\n" ] ]
  else
    raise "rack.multithread is true"
  end
}
rainbows-4.6.2/t/t0113-rewindable-input-false.sh0000755000004100000410000000114112354770160021312 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models EventMachine NeverBlock
skip_models Rev RevThreadSpawn RevThreadPool
skip_models Coolio CoolioThreadSpawn CoolioThreadPool
skip_models Epoll XEpoll

t_plan 4 "rewindable_input toggled to false"

t_begin "setup and start" && {
	rainbows_setup
	echo rewindable_input false >> $unicorn_config
	rainbows -D -c $unicorn_config t0113.ru
	rainbows_wait_start
}

t_begin "ensure worker is started" && {
	test xOK = x$(curl -T t0113.ru -H Expect: -vsSf http://$listen/)
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/t0300-async_sinatra.sh0000755000004100000410000000245612354770160017617 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh

# n - number of seconds to sleep
n=10
CONFIG_RU=async_sinatra.ru
case $model in
NeverBlock|EventMachine) ;;
*)
	t_info "skipping $T since it's not compatible with $model"
	exit 0
	;;
esac

t_plan 7 "async_sinatra test for EM"

t_begin "setup and start" && {
	rainbows_setup
	rtmpfiles a b c curl_err

	# Async Sinatra does not support Rack::Lint
	rainbows -E none -D $CONFIG_RU -c $unicorn_config
	rainbows_wait_start
}

t_begin "send async requests off in parallel" && {
	t0=$(unix_time)
	( curl --no-buffer -sSf http://$listen/$n 2>> $curl_err | tee $a) &
	( curl --no-buffer -sSf http://$listen/$n 2>> $curl_err | tee $b) &
	( curl --no-buffer -sSf http://$listen/$n 2>> $curl_err | tee $c) &
}

t_begin "ensure elapsed requests were processed in parallel" && {
	wait
	t1=$(unix_time)
	elapsed=$(( $t1 - $t0 ))
	echo "elapsed=$elapsed < 30"
	test $elapsed -lt 30
}

t_begin "termination signal sent" && {
	kill $rainbows_pid
}

dbgcat a
dbgcat b
dbgcat c
dbgcat r_err
dbgcat curl_err

t_begin "no errors from curl" && {
	test ! -s $curl_err
}

t_begin "no errors in stderr" && check_stderr

dbgcat r_err

t_begin "no responses are chunked" && {
	test x"$(cat $a)" = x"delayed for $n seconds"
	test x"$(cat $b)" = x"delayed for $n seconds"
	test x"$(cat $c)" = x"delayed for $n seconds"
}

t_done
rainbows-4.6.2/t/env.ru0000644000004100000410000000015012354770160015003 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env| [ 200, {}, [ env.inspect << "\n" ] ] }
rainbows-4.6.2/t/t0104-rack-input-limit-tiny.sh0000755000004100000410000001213412354770160021127 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
test -r random_blob || die "random_blob required, run with 'make $0'"
req_curl_chunked_upload_err_check

t_plan 18 "rack.input client_max_body_size tiny"

t_begin "setup and startup" && {
	rtmpfiles curl_out curl_err
	rainbows_setup $model
	ed -s $unicorn_config < $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	grep 413 $curl_err
	test -e $ok
}

t_begin "stops a large chunked request" && {
	rm -f $ok
	dd if=/dev/zero bs=257 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	grep 413 $curl_err
	test -e $ok
}

t_begin "small size sha1 chunked ok" && {
	blob_sha1=b376885ac8452b6cbf9ced81b1080bfd570d9b91
	rm -f $ok
	dd if=/dev/zero bs=256 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "small size sha1 content-length ok" && {
	blob_sha1=b376885ac8452b6cbf9ced81b1080bfd570d9b91
	rm -f $ok
	dd if=/dev/zero bs=256 count=1 of=$tmp
	curl -vsSf -T $tmp -H Expect: \
	  http://$listen/ > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "stops a regular request (gets_read_mix)" && {
	rm -f $ok
	dd if=/dev/zero bs=257 count=1 of=$tmp
	curl -vsSf -T $tmp -H Expect: \
	  http://$listen/gets_read_mix > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	grep 413 $curl_err
	test -e $ok
}

t_begin "stops a large chunked request (gets_read_mix)" && {
	rm -f $ok
	dd if=/dev/zero bs=257 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/gets_read_mix > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	grep 413 $curl_err
	test -e $ok
}

t_begin "stops a large line-based chunked request (gets_read_mix)" && {
	rm -f $ok
	=0;) print "hello world"}' | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/gets_read_mix > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	grep 413 $curl_err
	test -e $ok
}

t_begin "OK with line-based chunked request (gets_read_mix)" && {
	rm -f $ok
	=0;) print "hello world"}' | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/gets_read_mix > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test x"$(cat $curl_out)" = x23eab3cebcbe22a0456c8462e3d3bb01ae761702
}

t_begin "small size sha1 chunked ok (gets_read_mix)" && {
	blob_sha1=b376885ac8452b6cbf9ced81b1080bfd570d9b91
	rm -f $ok
	dd if=/dev/zero bs=256 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/gets_read_mix > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "small size sha1 content-length ok (gets_read_mix)" && {
	blob_sha1=b376885ac8452b6cbf9ced81b1080bfd570d9b91
	rm -f $ok
	dd if=/dev/zero bs=256 count=1 of=$tmp
	curl -vsSf -T $tmp -H Expect: \
	  http://$listen/gets_read_mix > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "stops a regular request (each)" && {
	rm -f $ok
	dd if=/dev/zero bs=257 count=1 of=$tmp
	curl -vsSf -T $tmp -H Expect: \
	  http://$listen/each > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	grep 413 $curl_err
	test -e $ok
}

t_begin "stops a large chunked request (each)" && {
	rm -f $ok
	dd if=/dev/zero bs=257 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/each > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	grep 413 $curl_err
	test -e $ok
}

t_begin "small size sha1 chunked ok (each)" && {
	blob_sha1=b376885ac8452b6cbf9ced81b1080bfd570d9b91
	rm -f $ok
	dd if=/dev/zero bs=256 count=1 | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/each > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "small size sha1 content-length ok (each)" && {
	blob_sha1=b376885ac8452b6cbf9ced81b1080bfd570d9b91
	rm -f $ok
	dd if=/dev/zero bs=256 count=1 of=$tmp
	curl -vsSf -T $tmp -H Expect: \
	  http://$listen/each > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test "$(cat $curl_out)" = $blob_sha1
}

t_begin "stops a large line-based chunked request (each)" && {
	rm -f $ok
	=0;) print "hello world"}' | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/each > $curl_out 2> $curl_err || > $ok
	dbgcat curl_err
	dbgcat curl_out
	grep 413 $curl_err
	test -e $ok
}

t_begin "OK with line-based chunked request (each)" && {
	rm -f $ok
	=0;) print "hello world"}' | \
	  curl -vsSf -T- -H Expect: \
	  http://$listen/each > $curl_out 2> $curl_err
	dbgcat curl_err
	dbgcat curl_out
	test x"$(cat $curl_out)" = x23eab3cebcbe22a0456c8462e3d3bb01ae761702
}

t_begin "shutdown" && {
	kill $rainbows_pid
}

t_done
rainbows-4.6.2/t/close-pipe-response.ru0000644000004100000410000000112712354770160020114 0ustar  www-datawww-data# must be run without Rack::Lint since that clobbers to_path
class CloseWrapper < Struct.new(:to_io)
  def each(&block)
    to_io.each(&block)
  end

  def close
    ::File.open(ENV['fifo'], 'wb') do |fp|
      fp.syswrite("CLOSING #{to_io}\n")
      if to_io.respond_to?(:close) && ! to_io.closed?
        to_io.close
      end
    end
  end
end
use Rainbows::DevFdResponse
run(lambda { |env|
  io = IO.popen('cat random_blob', 'rb')
  [ 200,
    {
      'Content-Length' => ::File.stat('random_blob').size.to_s,
      'Content-Type' => 'application/octet-stream',
    },
    CloseWrapper[io] ]
})
rainbows-4.6.2/t/t0600-rack-fiber_pool.sh0000755000004100000410000000174312354770160020020 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
case $model in
EventMachine) ;;
*)
	t_info "skipping $T since it's not compatible with $model"
	exit 0
	;;
esac

require_check rack/fiber_pool Rack::FiberPool

t_plan 7 "basic test with rack-fiber_pool gem"

CONFIG_RU=rack-fiber_pool/app.ru

t_begin "setup and start" && {
	rainbows_setup
	rtmpfiles curl_err curl_out

	rainbows -D -c $unicorn_config $CONFIG_RU
	rainbows_wait_start
}

t_begin "send requests off in parallel" && {
	curl --no-buffer -sSf http://$listen/ >> $curl_out 2>> $curl_err &
	curl --no-buffer -sSf http://$listen/ >> $curl_out 2>> $curl_err &
	curl --no-buffer -sSf http://$listen/ >> $curl_out 2>> $curl_err &
}

t_begin "wait for curl terminations" && {
	wait
}

t_begin "termination signal sent" && {
	kill $rainbows_pid
}

t_begin "no errors from curl" && {
	test ! -s $curl_err
}

t_begin "no errors in stderr" && check_stderr

t_begin "ensure we hit 3 separate fibers" && {
	test x3 = x"$(sort < $curl_out | uniq | count_lines)"
}

t_done
rainbows-4.6.2/t/worker-follows-master-to-death.ru0000644000004100000410000000045512354770160022213 0ustar  www-datawww-datause Rack::ContentLength
headers = { 'Content-Type' => 'text/plain' }
run lambda { |env|
  /\A100-continue\z/i =~ env['HTTP_EXPECT'] and return [ 100, {}, [] ]
  env['rack.input'].read

  case env["PATH_INFO"]
  when %r{/sleep/(\d+)}
    Rainbows.sleep($1.to_i)
  end
  [ 200, headers, [ "#$$\n" ] ]
}
rainbows-4.6.2/t/t0032-close-pipe-to_path-response.sh0000755000004100000410000000435112354770160022307 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll
if ! test -d /dev/fd
then
	t_info "skipping $T since /dev/fd is required"
	exit 0
fi

t_plan 16 "close pipe to_path response for $model"

t_begin "setup and startup" && {
	rtmpfiles err out http_fifo sub_ok
	rainbows_setup $model
	export fifo
	rainbows -E none -D close-pipe-to_path-response.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin "read random blob sha1" && {
	random_blob_sha1=$(rsha1 < random_blob)
}

t_begin "start FIFO reader" && {
	cat $fifo > $out &
}

t_begin "single request matches" && {
	sha1=$(curl -sSfv 2> $err http://$listen/ | rsha1)
	test -n "$sha1"
	test x"$sha1" = x"$random_blob_sha1"
}

t_begin "body.close called" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "start FIFO reader for abortive HTTP/1.1 request" && {
	cat $fifo > $out &
}

t_begin "send abortive HTTP/1.1 request" && {
	rm -f $ok
	(
		printf 'GET /random_blob HTTP/1.1\r\nHost: example.com\r\n\r\n'
		dd bs=4096 count=1 < $http_fifo >/dev/null
		echo ok > $ok
	) | socat - TCP:$listen > $http_fifo || :
	test xok = x$(cat $ok)
}

t_begin "body.close called for aborted HTTP/1.1 request" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "start FIFO reader for abortive HTTP/1.0 request" && {
	cat $fifo > $out &
}

t_begin "send abortive HTTP/1.0 request" && {
	rm -f $ok
	(
		printf 'GET /random_blob HTTP/1.0\r\n\r\n'
		dd bs=4096 count=1 < $http_fifo >/dev/null
		echo ok > $ok
	) | socat - TCP:$listen > $http_fifo || :
	test xok = x$(cat $ok)
}

t_begin "body.close called for aborted HTTP/1.0 request" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "start FIFO reader for abortive HTTP/0.9 request" && {
	cat $fifo > $out &
}

t_begin "send abortive HTTP/0.9 request" && {
	rm -f $ok
	(
		printf 'GET /random_blob\r\n'
		dd bs=4096 count=1 < $http_fifo >/dev/null
		echo ok > $ok
	) | socat - TCP:$listen > $http_fifo || :
	test xok = x$(cat $ok)
}

t_begin "body.close called for aborted HTTP/0.9 request" && {
	wait # for cat $fifo
	grep CLOSING $out || die "body.close not logged"
}

t_begin "shutdown server" && {
	kill -QUIT $rainbows_pid
}

t_begin "check stderr" && check_stderr

t_done
rainbows-4.6.2/t/t9001-sendfile-to-path.sh0000755000004100000410000000166312354770160020132 0ustar  www-datawww-data#!/bin/sh
. ./test-lib.sh
skip_models StreamResponseEpoll

t_plan 7 "Sendfile middleware test for $model"

t_begin "configure and start" && {
	rtmpfiles curl_err
	rainbows_setup

	# do not allow default middleware to be loaded since it may
	# kill body#to_path
	rainbows -E none -D t9001.ru -c $unicorn_config
	rainbows_wait_start
	random_blob_sha1=$(rsha1 < random_blob)
}

t_begin "hit with curl" && {
	sha1=$(curl -sSfv http://$listen/ 2> $curl_err | rsha1)
}

t_begin "kill server" && {
	kill $rainbows_pid
}

t_begin "SHA1 matches source" && {
	test x$random_blob_sha1 = x$sha1
}

t_begin "no errors in Rainbows! stderr" && {
	check_stderr
}

t_begin "X-Sendfile does not show up in headers" && {
	dbgcat curl_err
	if grep -i x-sendfile $curl_err
	then
		die "X-Sendfile did show up!"
	fi
}

t_begin "Content-Length is set correctly in headers" && {
	expect=$(count_bytes < random_blob)
	grep "^< Content-Length: $expect" $curl_err
}

t_done
rainbows-4.6.2/t/simple-http_WriterThreadPool.ru0000644000004100000410000000036512354770160022007 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] && env['rainbows.model'] == :WriterThreadPool
    [ 200, {}, [ Thread.current.inspect << "\n" ] ]
  else
    raise "rack.multithread is false"
  end
}
rainbows-4.6.2/t/t0019-keepalive-cpu-usage.sh0000644000004100000410000000261412354770160020615 0ustar  www-datawww-data#!/bin/sh
if test -z "$V" || test 0 -eq "$V"
then
	exit 0
fi
. ./test-lib.sh
skip_models WriterThreadSpawn WriterThreadPool Base
skip_models StreamResponseEpoll

t_plan 6 "keepalive_timeout CPU usage tests for $model"

t_begin "setup and start" && {
	rainbows_setup $model 50 666
	grep 'worker_connections 50' $unicorn_config
	grep 'keepalive_timeout 666' $unicorn_config
	rainbows -E deployment -D times.ru -c $unicorn_config
	rainbows_wait_start
}

t_begin 'read current times' && {
	eval "$(curl -sSf http://$listen/)"
	before_utime=$utime
	before_stime=$stime
	echo "utime=$utime stime=$stime"
}

t_begin 'keepalive connections' && {
	listen=$listen $RUBY -rsocket -e '
host, port = ENV["listen"].split(/:/)
port = port.to_i
socks = (1..49).map do |i|
  s = TCPSocket.new(host, port)
  # need to write something to get around deferred accepts
  s.write "GET /#{i} HTTP/1.1\r\nHost: example.com\r\n\r\n"
  s.readpartial 16384
  s
end
sleep
	' &
	ruby_pid=$!
	for i in $(awk 'BEGIN { for(i=0;i<60;++i) print i }'  $utime" \
		     "stime[$i] $before_stime => $stime"
	done
	kill $ruby_pid
}

t_begin "times not unreasonable" && {
	echo "utime: $before_utime => $utime" \
	     "stime: $before_stime => $stime"
}

t_begin "killing succeeds" && {
	kill $rainbows_pid
}

t_begin "check stderr" && {
	check_stderr
}

t_done
rainbows-4.6.2/t/simple-http_Epoll.ru0000644000004100000410000000034312354770160017620 0ustar  www-datawww-datause Rack::ContentLength
use Rack::ContentType
run lambda { |env|
  if env['rack.multithread'] == false && env['rainbows.model'] == :Epoll
    [ 200, {}, [ Thread.current.inspect << "\n" ] ]
  else
    raise env.inspect
  end
}
rainbows-4.6.2/local.mk.sample0000644000004100000410000000216712354770160016315 0ustar  www-datawww-data# this is the local.mk file used by Eric Wong on his dev boxes.
# GNUmakefile will source local.mk in the top-level source tree
# if it is present.
#
# This is depends on a bunch of GNU-isms from bash, sed, touch.

DLEXT := so

# if you have a decent amount of RAM, setting TMPDIR to be on tmpfs
# can significantly improve performance because uploads take a lot
# of disk I/O due to the rewindability requirement in Rack.
# TMPDIR := /dev/shm
# export TMPDIR

# Avoid loading rubygems to speed up tests because gmake is
# fork+exec heavy with Ruby.
prefix = $(HOME)

ifeq ($(r192),)
  RUBY := $(prefix)/bin/ruby
else
  prefix := $(prefix)/ruby-1.9.2
  export PATH := $(prefix)/bin:$(PATH)
  RUBY := $(prefix)/bin/ruby --disable-gems
endif

# pipefail is THE reason to use bash (v3+) or never revisions of ksh93
# SHELL := /bin/bash -e -o pipefail
SHELL := /bin/ksh93 -e -o pipefail

# trace execution of tests
# TRACER = strace -f -o $(t_pfx).strace -s 100000
# TRACER = /usr/bin/time -v -o $(t_pfx).time

full-test: test-18 test-192
test-18:
	$(MAKE) test 2>&1 | sed -e 's!^!1.8 !'
test-192:
	$(MAKE) test r192=T 2>&1 | sed -e 's!^!1.9.2 !'
rainbows-4.6.2/LATEST0000644000004100000410000000273112354770160014326 0ustar  www-datawww-data=== Rainbows! 4.6.2 - see you on the other side / 2014-05-12 07:32 UTC

  This release updates documentation to reflect the migration of the
  mailing list to a new public-inbox[1] instance.  This is necessary
  due to the impending RubyForge shutdown on May 15, 2014.

  The public-inbox address is: rainbows-public@bogomips.org
  (no subscription required, plain text only)
  ssoma[2] git archives: git://bogomips.org/rainbows-public
  browser-friendly archives: http://bogomips.org/rainbows-public/

  As evidenced by our git history, Rainbows! development has stagnated
  over the years.  Rainbows! was designed to be an unopinionated
  exploration into various concurrency options offered in the Ruby
  ecosystem.

  In recent years, I have come to favor the one-shot-based,
  worst-of-all-worlds design of yahns: http://yahns.yhbt.net/README
  Without the exploration from Rainbows!, yahns may not exist today.

  Disclaimer: Rainbows! has always been intolerant of buggy/broken code in
  libraries and apps.  yahns is even less tolerant of buggy/broken code,
  as the SIGKILL-based timeout mechanism inherited unicorn is completely
  gone. On the other hand, yahns has reasonable defaults so you do not
  have to read documentation to configure it.

  [1] policy: http://public-inbox.org/ - git://80x24.org/public-inbox
      an "archives first" approach to mailing lists
  [2] mechanism: http://ssoma.public-inbox.org/ - git://80x24.org/ssoma
      some sort of mail archiver (using git)

rainbows-4.6.2/Test_Suite0000644000004100000410000000412312354770160015417 0ustar  www-datawww-data= \Rainbows! test suite - YES OUR TEST SUITE IS CONCURRENT!

These are all integration tests that start the server on random, unused
TCP ports or Unix domain sockets.  They're all designed to run
concurrently with other tests to minimize test time, but tests may be
run independently as well.

We write our tests primarily in Bourne shell because that's what we're
comfortable writing integration tests with.  This test suite is also
easily portable to non-Ruby web servers.

== Requirements

* {Ruby 1.8 or 1.9}[http://www.ruby-lang.org/] (duh!)
* {isolate ~> 2.1.0}[http://github.com/jbarnette/isolate] - for dependencies
* {GNU make}[http://www.gnu.org/software/make/]
* {socat}[http://www.dest-unreach.org/socat/]
* {curl >= 7.18.0}[http://curl.haxx.se/]
* standard UNIX shell utilities (Bourne sh, awk, sed, grep, ...)

We do not use bashisms or any non-portable, non-POSIX constructs
in our shell code.  We use the "pipefail" option if available and
mainly test with {ksh}[http://kornshell.com/], but occasionally
with {dash}[http://gondor.apana.org.au/~herbert/dash/] and
{bash}[http://www.gnu.org/software/bash/], too.

== Running Tests

*BSD users: use "gmake" instead of "make"

To run the entire test suite with 8 tests running at once:

  make -j8

To run one individual test for all concurrency models:

  make t0000-simple-http.sh

To run one individual test for one concurrency model:

  make Revactor.t0000-simple-http.sh

To run all tests for one concurrency model:

  make EventMachine

You may also increase verbosity by setting the "V" variable for
GNU make.  To disable trapping of stdout/stderr:

  make V=1

To enable the "set -x" option in shell scripts to trace execution

  make V=2

== Performance

Some of the tests are rather I/O intensive due to the rewindability
requirement of "rack.input" in the Rack specification and the somewhat
complicated (but awesome!) nature of the TeeInput class leading us to
test it very heavily.  If you have lots of RAM and a large tmpfs
partition, it is advisable to set your TMPDIR and also make the t/trash/
directory a symlink to a directory inside in your TMPDIR.
rainbows-4.6.2/FAQ0000644000004100000410000000764312354770160013750 0ustar  www-datawww-data= Frequently Asked Questions about \Rainbows!

=== Why is \Rainbows! a separate project from Unicorn?

\Rainbows! is for the odd, corner-case requests that Unicorn is poorly
suited for.  More scalable concurrency models introduce additional
complexity that Unicorn users and developers are uncomfortable with for
the common cases.


=== What complexity?  Threads/events/actors are easy to work with!

Good for you.  Some of us depend on libraries incompatible with those
models, or are just too lazy to deal with them for the majority of
requests we service.


=== Isn't "rainbows" a branch of Unicorn?

That functionality is now in the Revactor model of \Rainbows!
However, \Revactor is not recommended since it is dormant
upstream and requires your application (and all its libraries)
to cooperate with \Revactor for concurrency.


=== What happened to the "gossamer" branch of Unicorn?

It became the ThreadPool model of \Rainbows!


=== Which concurrency model should I use?

It depends on your application, libraries, Ruby stack and use cases.
That's why we support as many concurrency model as we can.  Each model
has their own strengths and weaknesses in terms of maturity,
ease-of-debugging, compatibility, performance, and memory usage.


=== Should I put \Rainbows! behind nginx to serve slow clients?

It is optional.  You can still use nginx to route certain requests to
Unicorn and others to \Rainbows!  nginx will always outperform
\Rainbows! in both pure reverse proxy applications and for serving
static files,  but \Rainbows! is for hosting applications that are more
easily-implemented in Ruby than C.


=== Should I use \Rainbows! to serve static files?

It depends on the size and amount of static files you're serving.  If
you're serving a lot of static files (especially large ones), then by
all means use nginx.  If not, then \Rainbows! is likely a "good enough"
solution even if nginx will always outperform it in raw throughput.


=== How do I support SSL?

If you need streaming "rack.input" to do on-the-fly upload processing
within your Rack application, then using an SSL proxy such as
{Pound}[http://www.apsis.ch/pound/] or {Stunnel}[http://stunnel.org/] is
required.  Pound has built-in X-Forwarded-For support while Stunnel
requires a extra {patch}[http://haproxy.1wt.eu/download/patches/].

If you don't need streaming "rack.input", then nginx is a great HTTPS
reverse proxy.

Refer to the {Unicorn FAQ}[http://unicorn.bogomips.org/FAQ.html] on how
to ensure redirects go to "https://" URLs.


=== Is there a "rainbows_rails" command like there is "unicorn_rails"?

No.

"unicorn_rails" was written primarily to support older versions of
Rails.  Since \Rainbows! is designed for newer applications based on
Rack, it can just use a "config.ru" file like other Rack frameworks and
applications.

For Rails 3.x, you should already have a config.ru file and
"rainbows(1)" will work out-of-the-box like "rackup(1)".  Rails 3
will support RACK_ENV as set by "rainbows(1)", so you won't need
to set RAILS_ENV.

For Rails 2.3.x, the following config.ru will work for you:

  ENV["RAILS_ENV"] ||= ENV["RACK_ENV"]
  require "#{::File.expand_path('config/environment')}"
  use Rails::Rack::Static
  run ActionController::Dispatcher.new

For older versions of Rails, the following config.ru will work:

  ENV["RAILS_ENV"] ||= ENV["RACK_ENV"]
  require "#{::File.expand_path('config/boot')}"
  require "#{::File.expand_path('config/environment')}"
  require 'unicorn/app/old_rails'
  require 'unicorn/app/old_rails/static' # not needed with Unicorn 0.95+
  use Unicorn::App::OldRails::Static
  run Unicorn::App::OldRails.new

One thing to watch out for is that RAILS_ENV will not be set in the
environment for you, thus we set it to match RACK_ENV.

=== I'm using threads and Rails is misbehaving!

If you use any of the threaded concurrency models, you will need to use
{config.threadsafe!}[http://m.onkey.org/thread-safety-for-your-rails]
in your config/environments/$RAILS_ENV.rb
rainbows-4.6.2/GNUmakefile0000644000004100000410000000200112354770160015447 0ustar  www-datawww-data# use GNU Make to run tests in parallel, and without depending on RubyGems
all::
# RSYNC_DEST := rubyforge.org:/var/www/gforge-projects/rainbows
RSYNC_DEST := rainbows.bogomips.org:/srv/rainbows
rfproject := rainbows
rfpackage := rainbows

man-rdoc: man html
	$(MAKE) -C Documentation comparison.html
	for i in $(man1_rdoc); do echo > $$i; done
doc:: man-rdoc
include pkg.mk
ifneq ($(VERSION),)
release::
	$(RAKE) publish_news VERSION=$(VERSION)
	$(RAKE) fm_update VERSION=$(VERSION)
endif

base_bins := rainbows
bins := $(addprefix bin/, $(base_bins))
man1_rdoc := $(addsuffix _1, $(base_bins))
man1_bins := $(addsuffix .1, $(base_bins))
man1_paths := $(addprefix man/man1/, $(man1_bins))

clean:
	-$(MAKE) -C Documentation clean

man html:
	$(MAKE) -C Documentation install-$@

pkg_extra += $(man1_paths) lib/rainbows/version.rb

doc::
	cat Documentation/comparison.css >> doc/rdoc.css
	$(RM) $(man1_rdoc)

lib/rainbows/version.rb: GIT-VERSION-FILE

all:: test
test: lib/rainbows/version.rb
	$(MAKE) -C t

.PHONY: man html
rainbows-4.6.2/README0000644000004100000410000001366712354770160014301 0ustar  www-datawww-data= Rainbows! - Unicorn for sleepy apps and slow clients

\Rainbows! is an HTTP server for sleepy Rack applications.  It is based on
Unicorn, but designed to handle applications that expect long
request/response times and/or slow clients.

For Rack applications not heavily bound by slow external network
dependencies, consider Unicorn instead as it simpler and easier to
debug.

If you're on a small system, or write extremely tight and reliable code
and don't want multiple worker processes, check out
{Zbatery}[http://zbatery.bogomip.org/], too. Zbatery can use all the
crazy network concurrency options of \Rainbows! in a single worker
process.

== \Rainbows! is about Diversity

We aim to support as many concurrency models as we can because they all
suck; differently.

For network concurrency, models we currently support are:

* {Coolio}[link:Rainbows/Coolio.html]
* {CoolioFiberSpawn}[link:Rainbows/CoolioFiberSpawn.html]
* {CoolioThreadPool}[link:Rainbows/CoolioThreadPool.html]
* {CoolioThreadSpawn}[link:Rainbows/CoolioThreadSpawn.html]
* {Epoll}[link:Rainbows/Epoll.html]
* {EventMachine}[link:Rainbows/EventMachine.html]
* {FiberPool}[link:Rainbows/FiberPool.html]
* {FiberSpawn}[link:Rainbows/FiberSpawn.html]
* {NeverBlock}[link:Rainbows/NeverBlock.html]
* {Revactor}[link:Rainbows/Revactor.html]
* {ThreadPool}[link:Rainbows/ThreadPool.html]
* {ThreadSpawn}[link:Rainbows/ThreadSpawn.html]
* {WriterThreadPool}[link:Rainbows/WriterThreadPool.html]
* {WriterThreadSpawn}[link:Rainbows/WriterThreadSpawn.html]
* {XEpoll}[link:Rainbows/XEpoll.html]
* {XEpollThreadPool}[link:Rainbows/XEpollThreadPool.html]
* {XEpollThreadSpawn}[link:Rainbows/XEpollThreadSpawn.html]

We have {many more on the way}[link:TODO.html] for handling network
concurrency.  Additionally, we also use multiple processes (managed by
Unicorn) for robustness and CPU/memory/disk concurrency.

We also provide Rainbows::AppPool Rack middleware for some network
concurrency models for limiting application concurrency independently of
network concurrency.

== Features

* Designed for {Rack}[http://rack.github.io/], the standard for
  modern Ruby HTTP applications.

* Built on {Unicorn}[http://unicorn.bogomips.org/], inheriting its
  process/socket management features such as transparent upgrades and
  Ruby configuration DSL.

* As with Unicorn, it is able to stream large request bodies off the
  socket to the application while the client is still uploading.  Since
  \Rainbows! can handle slow clients, this feature is more useful than
  it is with Unicorn.

* Combines heavyweight concurrency (worker processes) with lightweight
  concurrency (Events/Fibers/Actors/Threads), allowing CPU/memory/disk to
  be scaled independently of client connections.  More concurrency models
  (listed in the TODO) will be supported as we find time for them.

* We give you {lots of options}[link:Summary.html] with more
  {on the way}[link:TODO.html].

== Applications

\Rainbows! is mainly designed for the odd things Unicorn sucks at:

* 3rd-party APIs (to services outside your control/LAN)
* OpenID consumers (to providers outside your control/LAN)
* Reverse proxy implementations with editing/censoring
  (to upstreams outside your control/LAN)
* Comet
* BOSH (with slow clients)
* HTTP server push
* Long polling
* Reverse AJAX
* real-time upload processing (via {upr}[http://upr.bogomips.org/])

\Rainbows! can also be used to service slow clients directly even with
fast applications.

== License

\Rainbows! is copyright 2009,2010 by all contributors (see logs in git).
\Rainbows! is licensed under the Ruby (1.8) license or the GPLv2 or later.
See the included {LICENSE}[link:LICENSE.html] file for more details.

\Rainbows! is 100% Free Software.

== Install

You may install it via RubyGems on RubyGems.org:

  gem install rainbows

== Usage

=== for Rack applications

In APP_ROOT (where config.ru is located), run:

  rainbows

\Rainbows! will bind to all interfaces on TCP port 8080 by default.

=== Configuration File(s)

\Rainbows! will look for the config.ru file used by rackup in APP_ROOT.

For deployments, it can use a config file for Unicorn and
\Rainbows!-specific options specified by the +--config-file/-c+
command-line switch.  \Rainbows! accepts all options found in
{Unicorn::Configurator}[http://unicorn.bogomips.org/Unicorn/Configurator.html]
as well as the "\Rainbows!" block, so you can have the following in your
config file:

    worker_processes 4 # assuming four CPU cores
    Rainbows! do
      use :FiberSpawn
      worker_connections 100
    end

See the {Rainbows! configuration}[link:Rainbows/Configurator.html]
{documentation}[link:Rainbows/Configurator.html]
for more details.

== Development

You can get the latest source via git from the following locations
(these versions may not be stable):

  git://bogomips.org/rainbows.git
  git://repo.or.cz/rainbows.git (mirror)

You may browse the code from the web and download the latest snapshot
tarballs here:

* http://bogomips.org/rainbows.git (cgit)
* http://repo.or.cz/w/rainbows.git (gitweb)

Inline patches (from "git format-patch") to the mailing list are
preferred because they allow code review and comments in the reply to
the patch.

We will adhere to mostly the same conventions for patch submissions as
git itself.  See the Documentation/SubmittingPatches document
distributed with git on on patch submission guidelines to follow.  Just
don't email the git mailing list or maintainer with \Rainbows! patches.

== Disclaimer

There is NO WARRANTY whatsoever if anything goes wrong, but let us know
and we'll try our best to fix it.

== Contact

All feedback (bug reports, user/development discussion, patches, pull
requests) go to the mailing list.  Patches must be sent inline
(git format-patch -M + git send-email).  No subscription is necessary
to post on the mailing list.  No top posting.  Address replies +To:+
the mailing list.

* email: mailto:rainbows-public@bogomips.org
* subscribe: mailto:rainbows-public+subscribe@bogomips.org
* archives: http://bogomips.org/rainbows-public/
rainbows-4.6.2/NEWS0000644000004100000410000011556112354770160014114 0ustar  www-datawww-data=== Rainbows! 4.6.2 - see you on the other side / 2014-05-12 07:32 UTC

  This release updates documentation to reflect the migration of the
  mailing list to a new public-inbox[1] instance.  This is necessary
  due to the impending RubyForge shutdown on May 15, 2014.

  The public-inbox address is: rainbows-public@bogomips.org
  (no subscription required, plain text only)
  ssoma[2] git archives: git://bogomips.org/rainbows-public
  browser-friendly archives: http://bogomips.org/rainbows-public/

  As evidenced by our git history, Rainbows! development has stagnated
  over the years.  Rainbows! was designed to be an unopinionated
  exploration into various concurrency options offered in the Ruby
  ecosystem.

  In recent years, I have come to favor the one-shot-based,
  worst-of-all-worlds design of yahns: http://yahns.yhbt.net/README
  Without the exploration from Rainbows!, yahns may not exist today.

  Disclaimer: Rainbows! has always been intolerant of buggy/broken code in
  libraries and apps.  yahns is even less tolerant of buggy/broken code,
  as the SIGKILL-based timeout mechanism inherited unicorn is completely
  gone. On the other hand, yahns has reasonable defaults so you do not
  have to read documentation to configure it.

  [1] policy: http://public-inbox.org/ - git://80x24.org/public-inbox
      an "archives first" approach to mailing lists
  [2] mechanism: http://ssoma.public-inbox.org/ - git://80x24.org/ssoma
      some sort of mail archiver (using git)

=== rainbows 4.6.1 - EventMachine fixes / 2014-02-02 00:30 UTC

  * event_machine: update for unicorn 4.8.x
  * disable cramp tests for now
  * update EventMachine tests
  * set executable bit rainbows executable

  Nothing relevant for non-EM users.

=== Rainbows! 4.6.0 - fix unicorn 4.8.0 compatibility / 2014-01-17 20:25 UTC

  The unicorn 4.8.0 internal changes unfortunately broke some
  unoffically supported behavior we depended on.  This release fixes
  that, but as a result, we lose compatibility of older unicorn
  versions.  (Oops!, oh well... :x)

  There's also minor bugfixes and documentation updates.
  The website is now at http://rainbows.bogomips.org/ since
  RubyForge is shutting down.  The mailing list will be migrated
  soon.

  In order to ease transitions to future versions of the GPL, we are
  now "GPLv2 or later" instead of explicitly GPLv2 + GPLv3(-only).
  The old Ruby 1.8 license remains an option.  If the FSF turns out
  a horrible GPLv4, users are free to continue using GPLv2 or GPLv3.

=== Rainbows! 4.5.0 - hijacking support / 2013-02-27 10:28 UTC

  This release adds hijacking support for Rack 1.5.x users.
  See Rack documentation for more information about hijacking.
  Lin Jen-Shin also provided the -N/--no-default-middleware option.
  Minor packaging cleanups and new HACKING document.

  There are also some corner-case bugfixes for *Epoll* users
  (sleepy_penguin, these bugs do not affect EM or Cool.io users)
  and test suite portability improvements.

=== Rainbows! 4.4.3 - bugfixes for EventMachine users / 2013-01-18 11:23 UTC

  This release fixes two EventMachine bugfixes from Lin Jen-Shin
  and Mark J. Titorenko.  There are also some minor cleanups.

  Lin Jen-Shin (1):
        event_machine: avoid close on deferred response

  Mark J. Titorenko (1):
        event_machine: join reactor_thread if it is already running

  Eric Wong (2):
        event_machine: cleanup confusing assignment
        t/GNUmakefile: cleanup test dependencies

=== Rainbows! 4.4.2 - EventMachine async.callback fix / 2012-12-06 11:41 UTC

  One bugfix allows stream(:keep_open) in Sinatra to work
  properly.

  Thanks to W. Andrew Loe III for the informative bug report
  and reproducible test case.

  ref: http://mid.gmane.org/CA+-9oNd1EFqsniPkkPTwu5opTCinbM7-2KHoXov7+y3LE4s4Tg@mail.gmail.com

=== Rainbows! 4.4.1 - a minor bugfix for Fiber users / 2012-08-31 01:54 UTC

  Fiber-based concurrency options avoids negative sleep
  intervals.  Thanks to Lin Jen-Shin for pointing this out.

=== Rainbows! 4.4.0 - minor improvements / 2012-08-18 07:32 UTC

  For epoll/Cool.io-based concurrency models, shutdown() is now
  used to timeout keepalive clients to avoid race conditions.
  Minor documentation improvements.

=== Rainbows! 4.3.1 - small bugfix / 2011-09-02 02:18 UTC

  This release fixes a potential reentrancy deadlock when
  using the default logger from the Ruby standard library.

=== Rainbows! 4.3.0 - pull in changes from unicorn 4.1.0 / 2011-08-20 01:20 UTC

  The deprecated Rainbows::HttpResponse class is finally gone
  thanks to Pratik Naik.  Logging of errors is more consistent
  with the changes in unicorn 4.1.0.  There are also minor
  documentation updates.  See the unicorn 4.1.0 release notes
  for more details:
    http://bogomips.org/unicorn.git/tag/?id=v4.1.0

=== Rainbows! 4.2.0 - Cramp WebSocket updates! / 2011-08-05 23:35 UTC

  This release includes updates to support WebSockets
  under Cramp 0.14 and later.  This will be the last
  release which supports Cramp 0.13.

  There are no changes in this release for non-Cramp
  users.

=== Rainbows! 4.1.0 - minor internal cleanups / 2011-07-30 22:43 UTC

  There are only some minor cleanups in this release and a bump to
  kgio 2.5 to remove the dependency on io/wait.  kgio 2.5 or later
  is now required (kgio 2.6+ will be required in the next
  release).

=== Rainbows! 4.0.0 - MOAR concurrency for MOAR COARS / 2011-06-27 09:33 UTC

  Rainbows! now scales to more than 1024 worker processes without
  special privileges.  To enable this, Rainbows! now depends on
  Unicorn 4.x and thus raindrops[1].

  client_max_header_size directive is added to limit per-client
  memory usage in headers.

  An experimental StreamResponseEpoll concurrency option now
  exists to buffer outgoing responses without any thread-safe
  dependencies.  Unlike the rest of Rainbows! which works fine
  without nginx, this concurrency option is /only/ supported
  behind nginx, even more strongly so than Unicorn itself.
  non-nginx LAN clients are NOT supported for this.  This relies
  on the sleepy_penguin[2] RubyGem (and Linux).

  There are some minor bug fixes and cleanups all around.  See
  "git log v3.4.0.." for details.

  [1] http://raindrops.bogomips.org/
  [2] http://bogomips.org/sleepy_penguin/

=== Rainbows 3.4.0 - minor updates and fixes / 2011-05-21 03:19 UTC

  SIGQUIT (graceful shutdown) now drops idle keepalive clients for
  the concurrency models where maintaining an idle client is
  relatively inexpensive: Coolio, CoolioThreadPool,
  CoolioThreadSpawn, Epoll, EventMachine, XEpoll,
  XEpollThreadPool, XEpollThreadSpawn.

  Kgio.autopush now works properly for all multi-threaded
  concurrency models (if you're using :tcp_nopush).

=== Rainbows! 3.3.0 - doc improvements and more / 2011-05-16 21:15 UTC

  * improved documentation all around, suggestions/comments to further
    improve documentation is greatly welcome at: rainbows-talk@rubyforge.org

  * added GPLv3 option to the license (now (Ruby|GPLv2|GPLv3), though
    Unicorn is still (Ruby|GPLv2) for now)

  * added client_header_buffer_size config directive (default 1K)

  * small default header buffer size (16K => 1K) to reduce memory usage,
    Rails apps with cookie sessions may want to increase this (~2K)

  * all concurrency models default to 50 connections per process

  * all concurrency models with a secondary :pool_size parameter also
    default to 50 (threads/fibers/whatever)

  * RLIMIT_NOFILE and RLIMIT_NPROC are automatically increased if needed

  * Rainbows::ThreadTimeout middleware rewritten, still not recommended,
    lazy people should be using Unicorn anyways :)

  * Several experimental Linux-only edge-triggered epoll options:
    XEpollThreadSpawn, XEpollThreadPool, XEpoll, and Epoll.
    The latter two were in previous releases but never announced.
    These require the "sleepy_penguin", "raindrops", and "sendfile" RubyGems

  === Deprecations

  * Rainbows::Fiber::IO* APIs all deprecated, Rainbows! will avoid
    having any concurrency model-specific APIs in the future and
    also avoid introducing new APIs for applications.

  * Fiber-based concurrency models are no longer recommended, they're
    too fragile for most apps, use at your own risk (they'll continue to
    be supported, however).  Linux NPTL + Ruby 1.9 is pretty lightweight
    and will be even lighter in Ruby 1.9.3 if you're careful with stack
    usage in your C extensions.

=== Rainbows! 3.2.0 - trying to send files to slow clients / 2011-03-15 12:45 UTC

  We now use IO#trysendfile in the sendfile 1.1.0 to reduce the
  cost of generating backtraces for slow clients (from EAGAIN).
  Nothing new for people not serving static files (but more
  on the way).

  Existing "sendfile" gem users must upgrade to 1.1.0
  or risk being left without sendfile support at all:
   http://bogomips.org/rainbows.git/patch?id=cd8a874d

=== Rainbows! 3.1.0 - minor updates / 2011-02-11 11:13 UTC

  Small bug fixes that have been sitting around, not much but
  it's already been one month since our last release.

  * Unicorn dependency updated to 3.4.0, so we get IPv6 support
    and Kgio.autopush support for ":tcp_nopush => true" users.

  * Optional :pool_size argument is fixed for NeverBlock and
    CoolioThreadPool users.

  * Mostly minor internal code cleanups

  * Sunshowers support removed, it was out-of-date and
    unmaintained.  Cramp remains supported for now.

  * X-Rainbows-* response headers support removed, nobody used it.

  There are severalnew features in this release not documented
  here.  Consider any new features not mentioned in these release
  notes to be subject to removal/renaming in future releases.

=== Rainbows! 3.0.0 - serving the fastest apps to slow clients faster! / 2011-01-12 01:12 UTC

  There is one incompatible change: We no longer assume application
  authors are crazy and use strangely-cased headers for "Content-Length",
  "Transfer-Encoding", and "Range".  This allows us to avoid the
  case-insensitivity of Rack::Utils::HeaderHash for a speed boost on the
  few apps that already serve thousands of requests/second per-worker.

  :Coolio got "async.callback" support like :EventMachine, but it
  currently lacks EM::Deferrables which would allow us to call
  "succeed"/"fail" callbacks.  This means only one-shot response writes
  are supported.

  There are numerous internal code cleanups and several bugfixes for
  handling partial static file responses.

=== Rainbows! 2.1.0 - Cool.io, bugfixes and more! / 2010-12-29 02:18 UTC

  Cool.io (new version of Rev) support is explicitly added
  (it always worked before).  ":Coolio" may be used in place
  of ":Rev" anywhere in your Rainbows! config file.

  There is a new "keepalive_requests" config directive to limit
  the number of requests a single connection may make (default:
  100, same as nginx).  This may be useful for better
  load-balancing characteristics.

  The old "Rev" prefixes remain supported as long as Cool.io
  remains compatible with Rev (likely forever).

  Bug fixes:

  * Rainbows::ThreadTimeout middleware with multiple clients
  * large, pipelined upload errors with Revactor+Coolio(Rev)
  * high CPU usage for maintaining idle keepalive on *Fiber*
  * needless ThreadPool wakeups
  * request env prematurely cleared keepalive requests,
    breaking some middlewares such as Clogger.
  * "close" not called on body if wrapper and sendfile used together

  Various code cleanups, and our RDoc website is JavaScript-free.
  See the ChangeLog or git for all changes.

=== Rainbows! 2.0.1 - upload pipelining fixes / 2010-12-03 01:26 UTC

  For HTTP clients living on the edge and pipelining uploads, we
  now fully support pipelined requests (as long as the application
  consumes each request in its entirety).

=== Rainbows! 2.0.0 - minority rules! / 2010-11-20 03:10 UTC

  This release is targeted at the minority of web applications
  that deal heavily with uploads.

  Thanks to Unicorn 3.x, we now support HTTP keepalive for
  requests with bodies as long as the application consumes them.
  Unicorn 3.x also allows disabling the rewindability requirement
  of "rack.input" (in violation of the Rack 1.x spec).

  The global client_body_max_size may also be applied per-endpoint
  using the Rainbows::MaxBody middleware described in:

    http://rainbows.rubyforge.org/Rainbows/MaxBody.html

=== Rainbows! 1.0.0 - internal cleanups / 2010-10-28 09:01 UTC

  This release is merely a milestone in our evolving internal API.
  Use of kgio may result in performance improvements under Ruby
  1.9.2 with non-blocking I/O-intensive workloads.

  The only bugfix is that SIGHUP reloads restores defaults on
  unset settings.  A similar fix is included in Unicorn 2.0.0
  as well.

=== Rainbows! 1.0.0pre1 - kinder, gentler I/O / 2010-10-26 21:33 UTC

  Mostly internal changes for kgio (and Unicorn) integration.
  There should be no (supported) user-visible changes from
  Rainbows! 0.97.0.  kgio should improve performance for
  concurrency models that use non-blocking I/O internally,
  especially under Ruby 1.9.2

=== Rainbows! 0.97.0 / 2010-08-28 19:46 UTC

  We now depend on Unicorn 1.1.3 to avoid race conditions during
  log cycling.  This bug mainly affected folks using Rainbows! as
  a multithreaded static file server.

  "keepalive_timeout 0" now works as documented for all backends
  to completely disable keepalive.  This was previously broken
  under EventMachine, Rev, and Revactor.

  There is a new Rainbows::ThreadTimeout Rack middleware which
  gives soft timeouts to apps running on multithreaded backends.

  There are several bugfixes for proxying IO objects and the usual
  round of small code cleanups and documentation updates.

  See the commits in git for all the details.

=== Rainbows! 0.96.0 - range support / 2010-08-03 09:04 UTC

  For concurrency models that use sendfile or IO.copy_stream, HTTP
  Range requests are honored when serving static files.  Due to
  the lack of known use cases, multipart range responses are not
  supported.

  When serving static files with sendfile and proxying
  pipe/socket bodies, responses bodies are always properly closed
  and we have more test cases for dealing with prematurely
  disconnecting clients.

  Concurrency model specific changes:

  EventMachine, NeverBlock -
  * keepalive is now supported when proxying pipes/sockets
  * pipelining works properly when using EM::FileStreamer
  * these remain the only concurrency models _without_
    Range support (EM::FileStreamer doesn't support ranges)

  Rev, RevThreadSpawn, RevThreadPool -
  * keepalive is now supported when proxying pipes/sockets
  * pipelining works properly when using sendfile

  RevThreadPool -
  * no longer supported under 1.8, it pegs the CPU at 100%.
    Use RevThreadSpawn (or any other concurrency model) if
    you're on 1.8, or better yet, switch to 1.9.

  Revactor -
  * proxying pipes/sockets with DevFdResponse is much faster
    thanks to a new Actor-aware IO wrapper (used transparently
    with DevFdResponse)
  * sendfile support added, along with Range responses

  FiberSpawn, FiberPool, RevFiberSpawn -
  * Range responses supported when using sendfile

  ThreadPool, ThreadSpawn, WriterThreadPool, WriterThreadSpawn -
  * Range responses supported when using sendfile or
    IO.copy_stream.

  See the full git logs for a list of all changes.

=== Rainbows! v0.95.1 - depend on newer Unicorn / 2010-07-11 02:53 UTC

  Eric Wong (3):
    test_isolate: document why we test with Rack 1.1.0
    doc: make RDoc skip private methods
    bump Unicorn dependency to 1.1.1

=== Rainbows! 0.95.0 - sendfile() support! / 2010-07-10 08:45 UTC

  In addition to the 1.9-only IO.copy_stream, the new sendfile
  1.0.0 gem may optionally be used with most concurrency models
  (even under 1.8).

  See http://rainbows.rubyforge.org/Static_Files.html for more info

  Other changes:

  * 1.9 encoding bugfix for (Rev)FiberSpawn and FiberPool
  * fixed potential rack.input corruption with Revactor
  * ThreadPool graceful shutdown no longer blocks until timeout
  * optional ServerToken middleware for to display Server: header
  * Dependencies bumped to Rack 1.1+ and Unicorn 1.1.0+
  * numerous internal cleanups, small bugfixes and speedups
  * more concise website oriented at users

=== Rainbows! 0.94.0 - one eight ate my homework! / 2010-06-04 08:42 UTC

  This release fixes corrupted large response bodies for Ruby 1.8
  users with the WriterThreadSpawn and WriterThreadPool models
  introduced in 0.93.0.  This bug did not affect Ruby 1.9 users
  nor the users of any older concurrency models.

  There is also a strange new Rainbows::Sendfile middleware.  It
  is used to negate the effect of Rack::Contrib::Sendfile, if that
  makes sense.  See the RDoc or
  http://rainbows.rubyforge.org/Rainbows/Sendfile.html for all the
  gory details.

  Finally, the RDoc for our test suite is on the website:

    http://rainbows.rubyforge.org/Test_Suite.html

  I wrote this document back when the project started but
  completely forgot to tell RDoc about it.  Personally, this
  test suite is one of my favorite parts of the project.

=== Rainbows! 0.93.0 - MOAR!!!1 / 2010-05-29 06:20 UTC

  In our race to have more concurrency options than real sites
  using this server, we've added two new and fully supported
  concurrency models: WriterThreadSpawn and WriterThreadPool

  They're both designed to for serving large static files and work
  best with IO.copy_stream (sendfile!) under Ruby 1.9.  They may
  also be used to dynamically generate long running, streaming
  responses after headers are sent (use "proxy_buffering off" with
  nginx).

  Unlike most concurrency options in Rainbows!, these are designed
  to run behind nginx (or haproxy if you don't support POST/PUT
  requests) and are vulnerable to slow client denial of service
  attacks.

  I floated the idea of doing something along these lines back in
  the early days of Unicorn, but deemed it too dangerous for some
  applications.  But nothing is too dangerous for Rainbows!  So
  here they are now for your experimentation.

=== Rainbows! 0.92.0 - inching towards the pot of gold / 2010-05-04 21:58 UTC

  Mostly internal cleanups and small improvements.

  The only backwards incompatible change was the addition of the
  "client_max_body_size" parameter to limit upload sizes to
  prevent DoS.  This defaults to one megabyte (same as nginx), so
  any apps relying on the limit-less behavior of previous will
  have to configure this in the Unicorn/Rainbows! config file:

        Rainbows! do
  	# nil for unlimited, or any number in bytes
  	client_max_body_size nil
        end

  The ThreadSpawn and ThreadPool models are now optimized for serving
  large static files under Ruby 1.9 using IO.copy_stream[1].

  The EventMachine model has always had optimized static file
  serving (using EM::Connection#stream_file_data[2]).

  The EventMachine model (finally) gets conditionally deferred app
  dispatch in a separate thread, as described by Ezra Zygmuntowicz
  for Merb, Ebb and Thin[3].

  [1] - http://euruko2008.csrug.cz/system/assets/documents/0000/0007/tanaka-IOcopy_stream-euruko2008.pdf
  [2] - http://eventmachine.rubyforge.org/EventMachine/Connection.html#M000312
  [3] - http://brainspl.at/articles/2008/04/18/deferred-requests-with-merb-ebb-and-thin

=== Rainbows! 0.91.1 - use a less-broken parser from Unicorn / 2010-04-19 21:13 UTC

  This release fixes a denial-of-service vector for deployments
  exposed directly to untrusted clients.

  The HTTP parser in Unicorn <= 0.97.0 would trip an assertion
  (killing the associated worker process) on invalid
  Content-Length headers instead of raising an exception.  Since
  Rainbows! and Zbatery supports multiple clients per worker
  process, all clients connected to the worker process that hit
  the assertion would be aborted.

  Deployments behind nginx are _not_ affected by this bug, as
  nginx will reject clients that send invalid Content-Length
  headers.

  The status of deployments behind other HTTP-aware proxies is
  unknown.  Deployments behind a non-HTTP-aware proxy (or no proxy
  at all) are certainly affected by this DoS.

  Users are strongly encouraged to upgrade as soon as possible,
  there are no other changes besides this bug fix from Rainbows!
  0.91.0 nor Unicorn 0.97.0

  This bug affects all previously released versions of Rainbows!
  and Zbatery.

=== Rainbows! 0.91.0 - Unicorn resync / 2010-03-01 10:03 UTC

  Unicorn 0.97.0 has a bunch of internal cleanups and small fixes
  and this is mainly to resync with those changes.

  keepalive_timeout now defaults to 5 seconds (from 2 seconds
  previous).  This should help out clients on slower connections.

  Some small fixes and cleanups:

  * Rainbows::Fiber::IO objects may leak if a rare app uses them
    explicitly with FiberSpawn/FiberPool-only (not RevFiberSpawn)

  * quiet down ENOTCONN handling, there's nothing we can do about
    this error so we won't fill our logs with it.

=== Rainbows! 0.90.2 / 2010-02-13 09:11 UTC

  This release depends on Unicorn 0.96.1 for an updated
  Unicorn::HttpParser to avoid leaking memory.

  The HttpParser in Unicorn <= 0.96.0 did not setup the parser
  object properly to be freed by the garbage collector.

  While this bug did not affect Unicorn itself, Rainbows!
  allocates a new Unicorn::HttpParser object for every new client
  connection and Unicorn did not properly setup the parser object
  to be freed by the Ruby garbage collector.

  There are also minor cosmetic cleanups and fixes:

  Eric Wong (10):
        http_response: disallow blank, multi-value headers
        Fix "rainbows -h" and "rainbows -v"
        Update docs + tests to reflect Rev 0.3.2 release
        local.mk.sample: bump Rack dependency
        Merge branch 'rack-1.1'
        add Cramp integration tests
        Rakefile: autoload Gem
        t/bin/*: encoding should be the first line after shebang
        gemspec: bump dependency on Unicorn to avoid leak
        Rainbows! 0.90.2

=== Rainbows! 0.90.1 / 2009-12-30 10:24 UTC

  This release contains minor bugfixes/compatibility improvements
  for ThreadSpawn, ThreadPool and EventMachine users.

  Excessive error messages from spurious wakeups using
  ThreadSpawn/ThreadPool under most platforms are silenced.  Only
  Ruby 1.9 users under Linux were unaffected by this bug.

  EventMachine users may now use EM::Deferrable objects in
  responses, vastly improving compatibility with existing
  async_sinatra apps.

=== Rainbows! 0.90.0 / 2009-12-22 21:54 UTC

  This release should fix ThreadSpawn green thread blocking issues
  under MRI 1.8.  Excessive socket closing is avoided when using
  Thread* models with Sunshowers (or clients disconnecting
  during uploads).

  There is a new RevFiberSpawn concurrency model which combines
  Rev with the traditional FiberSpawn model.

=== Rainbows! 0.9.0 / 2009-12-13 22:51 UTC

  This release introduces compatibility with Sunshowers, a library
  for Web Sockets, see http://rainbows.rubyforge.org/sunshowers
  for more information.  Several small cleanups and fixes.

  Eric Wong (20):
        add RevThreadPool to README
        rev: do not initialize a Rev::Loop in master process
        rainbows.1: update headers
        do not log IOError raised during app processing
        move "async.callback" constant to EvCore
        larger thread  pool default sizes ({Rev,}ThreadPool)
        ev_core: no need to explicitly close TmpIOs
        EventMachine: allow usage as a base class
        NeverBlock: resync with recent our EM-related expansion
        RevThread*: move warning message to a saner place
        EventMachineDefer: preliminary (and) broken version
        TODO: add EM Deferrables
        RevThread*: remove needless nil assignment
        README: HTML5 Web Sockets may not be supported, yet...
        env["hack.io"] for Fiber*, Revactor, Thread* models
        EventMachineDefer is experimental
        README: add Sunshowers reference
        Rakefile: resync with Unicorn
        doc/comparison: add Web Sockets to comparison
        README updates

=== Rainbows! 0.8.0 / 2009-12-02 08:55 UTC

  This release fixes a memory leak in our existing Revactor
  concurrency model.  A new RevThreadPool concurrency model has
  been added as well as small cleaups to exit handling in workers.

=== Rainbows! 0.7.0 / 2009-11-30 04:21 UTC

  keepalive_timeout (default: 2 seconds) is now supported to
  disconnect idle connections.  Several new concurrency models
  added include: NeverBlock, FiberSpawn and FiberPool; all of
  which have only been lightly tested.  RevThreadSpawn loses
  streaming input support to become simpler and faster for the
  general cases.  AppPool middleware is now compatible with all
  Fiber-based models including Revactor and NeverBlock.

  A new document gives a summary of all the options we give you:

    http://rainbows.rubyforge.org/Summary.html

  If you're using any of the Rev-based concurrency models, the
  latest iobuffer (0.1.3) gem will improve performance.  Also,
  RevThreadSpawn should become usable under MRI 1.8 with the next
  release of Rev (0.3.2).

=== Rainbows! 0.6.0 - bugfixes galore / 2009-11-15 23:29 UTC

  Client shutdowns/errors when streaming "rack.input" into the
  Rack application are quieter now.  Rev and EventMachine workers
  now shutdown correctly when the master dies.  Worker processes
  now fail gracefully if log reopening fails.  ThreadSpawn and
  ThreadPool models now load Unicorn classes in a thread-safe way.

  There's also an experimental RevThreadSpawn concurrency
  model which may be heavily reworked in the future...

  Eric Wong (30):
        Threaded models have trouble with late loading under 1.9
        cleanup worker heartbeat and master deathwatch
        tests: allow use of alternative sha1 implementations
        rev/event_machine: simplify keepalive checking a bit
        tests: sha1.ru now handles empty bodies
        rev: split out further into separate files for reuse
        rev: DeferredResponse is independent of parser state
        remove unnecessary class variable
        ev_core: cleanup handling of APP constant
        rev: DeferredResponse: always attach to main loop
        initial cut of the RevThreadSpawn model
        rev_thread_spawn/revactor: fix TeeInput for short reads
        rev_thread_spawn: make 1.9 TeeInput performance tolerable
        tests: add executable permissions to t0102
        tests: extra check to avoid race in reopen logs test
        rev_thread_spawn: 16K chunked reads work better
        tests: ensure proper accounting of worker_connections
        tests: heartbeat-timeout: simplify and avoid possible race
        tests: ensure we process "START" from FIFO when starting
        http_response: don't "rescue nil" for body.close
        cleanup error handling pieces
        tests: more stringent tests for error handling
        revactor/tee_input: unnecessary error handling
        gracefully exit workers if reopening logs fails
        revactor/tee_input: raise ClientDisconnect on EOFError
        bump versions since we depend on Unicorn::ClientShutdown
        revactor/tee_input: share error handling with superclass
        RevThreadSpawn is still experimental
        Revert "Threaded models have trouble with late loading under 1.9"
        Rakefile: add raa_update task

=== Rainbows! 0.5.0 / 2009-11-05 10:27 UTC

  We depend on the just-released Unicorn 0.94.0 for the fixed
  trailer handling.  As with `unicorn', the `rainbows' executable
  now sets and respects ENV["RACK_ENV"].  Also small fixes and
  cleanups including better FreeBSD 7.2 compatibility and
  less likely to over-aggressively kill slow/idle workers
  when a very low timeout is set.

  Eric Wong (20):
        rev: split out heartbeat class
        bump Unicorn dependency to (consistently) pass tests
        tests: avoid single backquote in echo
        event_machine: avoid slurping when proxying
        tests: make timeout tests reliable under 1.9
        thread_pool: comment for potential SMP issue under 1.9
        Allow 'use "model"' as a string as well as symbol
        Rev model is the only user of deferred_bodies
        ev_core: use Tempfile instead of Unicorn::Util::tmpio
        ev_core: ensure quit is triggered on all errors
        rainbows: set and use process-wide ENV["RACK_ENV"]
        http_server: add one second to any requested timeout
        thread_pool: update fchmod heartbeat every second
        t0004: tighten up timeout test
        ev_core: remove Tempfile usage once again
        cleanup: remove unused t????.ru test files
        tests: staggered trailer upload test
        ensure RACK_ENV is inherited from the parent env
        t0100: more precise `expr` usage

=== Rainbows! 0.4.0 / 2009-10-27 08:44 UTC

  Basic single-threaded EventMachine support is now included.  It
  supports async_synatra[1] via the "async.callback" Rack
  environment[2].  For EventMachine, we rely on the updated
  attach/watch API in EventMachine 0.12.10.

  As Revactor 0.1.5 is now available, our Revactor support now
  depends on it as it adds the ability to listen on UNIX domain
  sockets.

  Of course, all dependencies (besides Unicorn and Rack) are
  soft and only loaded if your configured concurrency model
  requires it.

  For developers/QA folks, the integration tests are completely
  revamped for easier maintenance when new concurrency models are
  introduced and should also produce TAP-compliant output.  The
  test suite remains highly parallelizable using GNU make.

  There are immediate plans to expand support for both Rev and
  EventMachine to support use with threaded application dispatch.

  Eric Wong (41):
        rev: remove Revactor-specific workaround
        README: change ordering of concurrency model listing
        tests: more correct HTTP/0.9 test
        test-lib: avoid stalling due to bad FIFO handling
        rev: fix static file responses under HTTP/0.9
        add news bodies to site NEWS.atom.xml
        tests: avoid needlessly remaking "rainbows"
        initial EventMachine support
        tests: hopefully fix stalls in input trailer tests
        tests: avoid race condition in reopen logs test
        tests: prefer "RUBY" to lowercased "ruby"
        tests: common setup and wait_start functions
        tests: add a TAP producer shell library
        tests: port all existing tests to TAP library
        tests: remove symlinks and small files, use Make
        t9000: bail if run with an unsupported/pointless model
        tests: allow "make $model" to run tests for that model
        rev: spell ECONNABORTED correctly
        rev/evma: move common code for event models into ev_core
        ev_core: do not drop deferred bodies on graceful quits
        eventmachine: get basic tests working
        rev: do not File.expand_path on result of body.to_path
        eventmachine 0.12.8 passes all tests
        tests: make large file memory tests more reliable
        eventmachine: require EM 0.12.10
        update gem dependencies in comments/local.mk.sample
        rev: enforce Rev::VERSION >= 0.3.0
        eventmachine: add async_sinatra support
        tests: only load Revactor tests under 1.9.1
        tests: gracefully exit if EventMachine is not available
        tests: error out if socat + curl aren't reachable
        thread*: fix MRI 1.8.6 compatibility
        local.mk.sample: cleanups and minor reorg
        eventmachine: remove unnecessary ivar assignment
        eventmachine: document our support of "async_synatra"
        doc: Update TODO and README
        tests: generate all dependencies atomically
        app_pool: update RDoc
        test-lib: DWIM handling of temp UNIX sockets
        revactor: require 0.1.5, remove 0.1.4 workarounds
        gemspec: bump up Unicorn dep version to 0.93.4

  [1] http://github.com/raggi/async_sinatra
  [2] this is not 100% Rack::Lint compatible, but we'll let it
      slide since there are already folks depending on
      the async_sinatra gem

=== Rainbows! 0.3.0 / 2009-10-19 18:21 UTC

  The major feature of this release is the new DeferredResponse
  middleware for the Rev-based concurrency model.  It should be
  transparently compatible with non-Rev models, as well.  As a
  pleasant side effect, this change also allows large files to be
  streamed to the client with Rev as the socket becomes writable
  instead of slurping the entire file into an IO::Buffer first.

  Bugfixes to graceful shutdowns support for all concurrency
  models.  The Rev-based model also gets a working heartbeat
  mechanism (oops!) and fixed HTTP/1.1 pipelining support.

  Eric Wong (38):
        app_pool: note it being currently broken with Revactor
        Revactor tests can sleep more easily
        tests: sleep.ru handles "Expect: 100-continue"
        Fix graceful shutdown handling of Thread* models harder
        DRY setting of rack.multithread
        test-lib: dbgcat adds headers with key name
        use timeout correctly to join threads on SIGQUIT
        Rev: simplification to error handling
        tests: sleep.ru slurps rack.input stream
        refactor graceful shutdowns again, harder
        tests: introduce require_for_model function
        tests: add unbuffered tee(1)-like helper
        tests: rack.input trailer tests for all models
        tests: fix issues with non-portable shell constructs
        tests: fix random_blob dependency
        tests: factor out a common parser error "library"
        tests: DRY setting of the "model" environment var
        tests: DRY Ruby requires based on model
        test-lib: quiet down pipefail error message
        tests: DRY require tests for Rev/Revactor
        rev: handle fully-buffered, pipelined requests
        rev: avoid stack overflow through pipelining
        tests: common basic HTTP tests for all models
        tests: rack.input hammer concurrency testing
        tests: for log reopening for all concurrency models
        http_response: filter out X-Rainbows-* headers
        rev: fix heartbeat timeouts
        revactor: switch to a 1 second heartbeat
        rev: async response bodies with DevFdResponse middleware
        tests: more reliable error checking
        tests: DWIM FIFO creation
        tests: predictable and simpler tempfile management
        rev: AsyncResponse => DeferredResponse API cleanup
        rev: update documentation for this model
        TUNING: update documentation notes
        TODO: update with new items
        local.mk.sample: sync with BDFL's version
        Rainbows! 0.3.0

=== Rainbows! 0.2.0 / 2009-10-15 08:01 UTC

  This release adds preliminary Rev support for network
  concurrency under Ruby 1.8 and Ruby 1.9.  There are caveats to
  this model and reading the RDoc for Rainbows::Rev is
  recommended.

  Rainbows::AppPool Rack middleware is now available to limit
  application concurrency on a per-process basis independently of
  network concurrency.  See the RDoc for this class for further
  details.

  Per-client timeouts have been removed, see
  http://mid.gmane.org/20091013062602.GA13128@dcvr.yhbt.net
  for the reasoning.

  Rack environment changes:

  * "rack.multithread" is now only true for models with "Thread"
    in their name.  Enabling thread-safe (but not reentrant) code
    may actually be harmful for Revactor.

  * "rainbows.model" is now exposed so the application can easily
    figure out which network concurrency model is in use.

  Bugfixes include better shutdown and error handling for all
  existing models, OpenBSD compatibility for the per-process
  heartbeat (same as found in unicorn v0.93.3).

  Eric Wong (54):
        add SIGNALS doc to RDoc
        SIGNALS: add Rainbows!-specific notes
        doc: better "Rainbows!" RDoc examples and linkage
        tests: generate random_blob once for all tests
        tests: move trash files to their own trash/ directory
        t0000: basic test includes keepalive + pipelining
        tests: simplify temporary file management
        tests: add dbgcat() utility method
        fchmod heartbeat flips between 0/1
        tests: add revactor pipelining/keepalive test
        thread_spawn: trap EAGAIN on accept_nonblock
        thread_spawn: more robust loop
        thread_spawn: non-blocking accept() shouldn't EINTR
        tests: enable pipefail shell option if possible
        README for test suite
        tests: TEST_OPTS => SH_TEST_OPTS
        tests: update TRACER examples in makefile
        tests: create a bad exit code by default
        thread_spawn: clean up nuking of timed-out threads
        factor out common listen loop error handling
        graceful exit on trap TypeError from IO.select
        expand and share init_worker_process
        revactor: break on EBADF in the accepting actors
        revactor: cleanups and remove redundancy
        No need to be halving timeout, already done for us
        revactor: graceful death of keepalive clients
        revactor: continue fchmod beat in graceful exit
        cleanup thread models, threads no longer time out
        revactor: fix graceful shutdown timeouts
        Fix graceful shutdowns for threaded models
        SIGINT/SIGTERM shuts down instantly in workers
        tests: check for common exceptions with "Error"
        DEPLOY: update with notes on DoS potential
        tests: add reopen logs test for revactor
        vs Unicorn: use diagrams for concurrency models
        vs Unicorn: fix wording to be consistent with diagrams
        vs Unicorn: fix copy+paste errors and grammar fail
        README: alter reply conventions for the mailing list
        preliminary Rev support
        local.mk.sample: use ksh93 as default $(SHELL)
        rack.multithread is only true for Thread* models
        Rev: general module documentation + caveats
        Rev: fix error handling for parser errors
        t3003: set executable bit
        documentation updates (mostly on network models)
        rack: expose "rainbows.model" in Rack environment
        tests: enforce rack.multithread and rainbows.model
        README: update URLs
        README: update with Rev model caveats
        Add Rainbows::AppPool Rack middleware
        t4003: chmod +x
        local.mk.sample: use rev 0.3.1 instead
        README: link to AppPool and extra note about Rev model
        Rainbows! 0.2.0

=== Rainbows! v0.1.1 / 2009-10-06 03:51 UTC

  Fixed Ruby 1.8 support (and all 1.9 systems without Revactor).
  Process-wide timeout handling for the ThreadSpawn concurrency
  model should now work properly.  Small cleanups everywhere.

  Eric Wong (16):
        Rakefile: add publish_news target
        Fix NEWS generation on single-paragraph tag messages
        README: move RDoc links down to fix gem description
        README: add install instructions
        summary: s/slow apps/sleepy apps/g
        Avoid naming names in LICENSE/README files
        rainbows/base: cleanup constant include
        tests: quiet down bin installation
        Add top-level "test" target for make
        local.mk.sample: sync to my current version
        tests: allow "make V=2" to set TEST_OPTS += -x
        cleanup temporary file usage in tests
        local.mk.sample: fix revactor dependency
        Thread* models: cleanup timeout management
        thread_spawn: fix timeout leading to worker death
        less error-prone timeouts for Thread models

=== Rainbows! 0.1.0 / 2009-10-05 10:44 UTC

  Initial release

  This release is currently highly experimental and is still
  missing a lot of test coverage.

rainbows-4.6.2/Static_Files0000644000004100000410000000505512354770160015705 0ustar  www-datawww-data= Static file serving with \Rainbows!

While Ruby application servers aren't traditionally used to serve static
files, it'll be fun for us to see how far we can go with \Rainbows!

We aren't delusional enough (yet :) to compete with C-based servers like
nginx or lighttpd in terms of raw performance, but wouldn't it be nice
to simplify your deployments and only deploy one server?

== {sendfile}[http://rubygems.org/gems/sendfile] RubyGem

To enable the "sendfile" gem, just make sure you have 1.1.0 or later and
"require" it in your \Rainbows!/Unicorn config file (not your Rack
config.ru):

    require 'sendfile' # that's it! nothing else to do

    # the rest of you Rainbows! config goes below:
    worker_processes 4
    stderr_path "/var/log/app/rainbows.err.log"
    Rainbows! do
      use :RevFiberSpawn
      worker_connections 100
    end

The sendfile gem is works for all of our concurrency models except
NeverBlock and EventMachine (see below).

The sendfile gem is less buggy than current (Ruby 1.9.2)
IO.copy_stream and supports FreeBSD and Solaris in addition to Linux.
This RubyGem also works under Ruby 1.8 (even with threads) and should
work with rubinius.git, too.

\Rainbows! supports the sendfile gem since v0.95.0

== IO.copy_stream (Ruby 1.9 only)

Users of pure-Ruby Thread-based models ThreadPool, ThreadSpawn, and
their Writer* variants use the core IO.copy_stream method under Ruby
1.9.  IO.copy_stream uses sendfile() under Linux, and a pread()/write()
loop (implemented in C) on other systems.

IO.copy_stream under Linux with Ruby 1.9.2 (and before) is also
subject to hanging indefinitely when a client disconnected prematurely.
This issue is fixed in Ruby trunk (r28557, July 2010).

\Rainbows! supports IO.copy_stream since v0.93.0

== EventMachine FileStreamer

EventMachine and NeverBlock users automatically take advantage of the
mmap()-based FileStreamer class distributed with EventMachine.
Unfortunately, as of EventMachine 0.12.10, FileStreamer cannot easily
support HTTP Range responses.

\Rainbows! supports EventMachine FileStreamer since v0.4.0

== Performance

With large files and high-throughput clients, there should be little
performance difference compared to optimal C implementation such as
nginx and lighttpd.  Ruby runtime overhead matters more when serving
slower clients and smaller files.

== The Future...

We'll also support an open file cache (similar to nginx) which
allows us to reuse open file descriptors.

Under Linux, we'll support the splice(2) system call for zero-copy
proxying {io_splice}[http://bogomips.org/ruby_io_splice/], too.
rainbows-4.6.2/lib/0000755000004100000410000000000012354770160014152 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/0000755000004100000410000000000012354770160015776 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/actor_spawn.rb0000644000004100000410000000205412354770160020644 0ustar  www-datawww-data# -*- encoding: binary -*-

require 'actor'

# Actor concurrency model for Rubinius.  We can't seem to get message
# passing working right, so we're throwing a Mutex into the mix for
# now.  Hopefully somebody can fix things for us.  Currently, this is
# exactly the same as the ThreadSpawn model since we don't use the
# message passing capabilities of the Actor model (and even then
# it wouldn't really make sense since Actors in Rubinius are just
# Threads underneath and our ThreadSpawn model is one layer of
# complexity less.
#
# This is different from the Revactor one which is not prone to race
# conditions within the same process at all (since it uses Fibers).
module Rainbows::ActorSpawn
  include Rainbows::ThreadSpawn

  # runs inside each forked worker, this sits around and waits
  # for connections and doesn't die until the parent dies (or is
  # given a INT, QUIT, or TERM signal)
  def worker_loop(worker) # :nodoc:
    Rainbows::Const::RACK_DEFAULTS["rack.multithread"] = true # :(
    init_worker_process(worker)
    accept_loop(Actor)
  end
end
rainbows-4.6.2/lib/rainbows/xepoll_thread_spawn/0000755000004100000410000000000012354770160022040 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/xepoll_thread_spawn/client.rb0000644000004100000410000000530712354770160023650 0ustar  www-datawww-data# -*- encoding: binary -*-
# :stopdoc:
module Rainbows::XEpollThreadSpawn::Client
  Rainbows.config!(self, :keepalive_timeout, :client_header_buffer_size)
  N = Raindrops.new(1)
  ACCEPTORS = Rainbows::HttpServer::LISTENERS.dup
  extend Rainbows::WorkerYield

  def self.included(klass) # included in Rainbows::Client
    max = Rainbows.server.worker_connections
    ACCEPTORS.map! do |sock|
      Thread.new do
        buf = ""
        begin
          if io = sock.kgio_accept(klass)
            N.incr(0, 1)
            io.epoll_once(buf)
          end
          worker_yield while N[0] >= max
        rescue => e
          Rainbows::Error.listen_loop(e)
        end while Rainbows.alive
      end
    end
  end

  ep = SleepyPenguin::Epoll
  EP = ep.new
  IN = ep::IN | ep::ONESHOT
  KATO = {}
  KATO.compare_by_identity if KATO.respond_to?(:compare_by_identity)
  LOCK = Mutex.new
  Rainbows.at_quit do
    clients = nil
    LOCK.synchronize { clients = KATO.keys; KATO.clear }
    clients.each { |io| io.closed? or io.shutdown }
  end
  @@last_expire = Time.now

  def kato_set
    LOCK.synchronize { KATO[self] = @@last_expire }
    EP.set(self, IN)
  end

  def kato_delete
    LOCK.synchronize { KATO.delete self }
  end

  def self.loop
    buf = ""
    begin
      EP.wait(nil, 1000) { |_, obj| obj.epoll_run(buf) }
      expire
    rescue Errno::EINTR
    rescue => e
      Rainbows::Error.listen_loop(e)
    end while Rainbows.tick || N[0] > 0
    Rainbows::JoinThreads.acceptors(ACCEPTORS)
  end

  def self.expire
    return if ((now = Time.now) - @@last_expire) < 1.0
    if (ot = KEEPALIVE_TIMEOUT) >= 0
      ot = now - ot
      defer = []
      LOCK.synchronize do
        KATO.delete_if { |client, time| time < ot and defer << client }
      end
      defer.each { |io| io.closed? or io.close }
    end
    @@last_expire = now
  end

  def epoll_once(buf)
    @hp = Rainbows::HttpParser.new
    epoll_run(buf)
  end

  def close
    super
    kato_delete
    N.decr(0, 1)
    nil
  end

  def handle_error(e)
    super
    ensure
      closed? or close
  end

  def epoll_run(buf)
    case kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, buf)
    when :wait_readable
      return kato_set
    when String
      kato_delete
      env = @hp.add_parse(buf) and return spawn(env, @hp)
    else
      return close
    end while true
    rescue => e
      handle_error(e)
  end

  def spawn(env, hp)
    Thread.new { process_pipeline(env, hp) }
  end

  def pipeline_ready(hp)
    hp.parse and return true
    case buf = kgio_tryread(CLIENT_HEADER_BUFFER_SIZE)
    when :wait_readable
      kato_set
      return false
    when String
      hp.add_parse(buf) and return true
      # continue loop
    else
      return close
    end while true
  end
end
rainbows-4.6.2/lib/rainbows/never_block/0000755000004100000410000000000012354770160020267 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/never_block/event_machine.rb0000644000004100000410000000036712354770160023427 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::NeverBlock::Client < Rainbows::EventMachine::Client
  def app_call input
    POOL.spawn do
      begin
        super input
      rescue => e
        handle_error(e)
      end
    end
  end
end
rainbows-4.6.2/lib/rainbows/never_block/core.rb0000644000004100000410000000102612354770160021543 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::NeverBlock::Core
  def init_worker_process(worker)
    super
    o = Rainbows::O
    pool = NB::Pool::FiberPool.new(o[:pool_size])
    base = o[:backend].to_s.gsub!(/([a-z])([A-Z])/, '\1_\2').downcase!
    require "rainbows/never_block/#{base}"
    client_class = Rainbows::NeverBlock::Client
    client_class.superclass.const_set(:APP, Rainbows.server.app)
    client_class.const_set(:POOL, pool)
    logger.info "NeverBlock/#{o[:backend]} pool_size=#{o[:pool_size]}"
  end
end
rainbows-4.6.2/lib/rainbows/max_body/0000755000004100000410000000000012354770160017600 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/max_body/wrapper.rb0000644000004100000410000000305712354770160021612 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
# This is only used for chunked request bodies, which are rare
class Rainbows::MaxBody::Wrapper
  def initialize(rack_input, limit)
    @input, @limit, @rbuf = rack_input, limit, ''
  end

  def each
    while line = gets
      yield line
    end
  end

  # chunked encoding means this method behaves more like readpartial,
  # since Rack does not support a method named "readpartial"
  def read(length = nil, rv = '')
    if length
      if length <= @rbuf.size
        length < 0 and raise ArgumentError, "negative length #{length} given"
        rv.replace(@rbuf.slice!(0, length))
      elsif @rbuf.empty?
        checked_read(length, rv) or return
      else
        rv.replace(@rbuf.slice!(0, @rbuf.size))
      end
      rv.empty? && length != 0 ? nil : rv
    else
      rv.replace(read_all)
    end
  end

  def gets
    sep = $/
    if sep.nil?
      rv = read_all
      return rv.empty? ? nil : rv
    end
    re = /\A(.*?#{Regexp.escape(sep)})/

    begin
      @rbuf.sub!(re, '') and return $1

      if tmp = checked_read(16384)
        @rbuf << tmp
      elsif @rbuf.empty? # EOF
        return nil
      else # EOF, return whatever is left
        return @rbuf.slice!(0, @rbuf.size)
      end
    end while true
  end

  def checked_read(length = 16384, buf = '')
    if @input.read(length, buf)
      throw :rainbows_EFBIG if ((@limit -= buf.size) < 0)
      return buf
    end
  end

  def read_all
    rv = @rbuf.slice!(0, @rbuf.size)
    tmp = ''
    while checked_read(16384, tmp)
      rv << tmp
    end
    rv
  end
end
rainbows-4.6.2/lib/rainbows/max_body/rewindable_wrapper.rb0000644000004100000410000000045612354770160024006 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::MaxBody::RewindableWrapper < Rainbows::MaxBody::Wrapper
  def initialize(rack_input, limit)
    @orig_limit = limit
    super
  end

  def rewind
    @limit = @orig_limit
    @rbuf = ''
    @input.rewind
  end

  def size
    @input.size
  end
end
rainbows-4.6.2/lib/rainbows/rev_fiber_spawn.rb0000644000004100000410000000115612354770160021501 0ustar  www-datawww-data# -*- encoding: binary -*-
Rainbows.const_set(:RevFiberSpawn, Rainbows::CoolioFiberSpawn)

# CoolioFiberSpawn is the new version of this, use that instead.
#
# A combination of the Rev and FiberSpawn models.  This allows Ruby
# 1.9 Fiber-based concurrency for application processing while
# exposing a synchronous execution model and using scalable network
# concurrency provided by Rev.  A streaming "rack.input" is exposed.
# Applications are strongly advised to wrap all slow IO objects
# (sockets, pipes) using the Rainbows::Fiber::IO or a Rev-compatible
# class whenever possible.
module Rainbows::RevFiberSpawn; end
rainbows-4.6.2/lib/rainbows/writer_thread_spawn.rb0000644000004100000410000000251212354770160022376 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'thread'
# This concurrency model implements a single-threaded app dispatch and
# spawns a new thread for writing responses.  This concurrency model
# should be ideal for apps that serve large responses or stream
# responses slowly.
#
# Unlike most \Rainbows! concurrency models, WriterThreadSpawn is
# designed to run behind nginx just like Unicorn is.  This concurrency
# model may be useful for existing Unicorn users looking for more
# output concurrency than socket buffers can provide while still
# maintaining a single-threaded application dispatch (though if the
# response body is generated on-the-fly, it must be thread safe).
#
# For serving large or streaming responses, setting
# "proxy_buffering off" in nginx is recommended.  If your application
# does not handle uploads, then using any HTTP-aware proxy like
# haproxy is fine.  Using a non-HTTP-aware proxy will leave you
# vulnerable to slow client denial-of-service attacks.

module Rainbows::WriterThreadSpawn
  include Rainbows::Base
  autoload :Client, 'rainbows/writer_thread_spawn/client'

  def process_client(client) # :nodoc:
    Client.new(client).process_loop
  end

  def worker_loop(worker)  # :nodoc:
    Client.const_set(:MAX, worker_connections)
    super # accept loop from Unicorn
    Client.quit
  end
  # :startdoc:
end
# :enddoc:
rainbows-4.6.2/lib/rainbows/event_machine.rb0000644000004100000410000001034412354770160021132 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'eventmachine'
EM::VERSION >= '0.12.10' or abort 'eventmachine 0.12.10 is required'

# Implements a basic single-threaded event model with
# {EventMachine}[http://rubyeventmachine.com/].  It is capable of
# handling thousands of simultaneous client connections, but with only
# a single-threaded app dispatch.  It is suited for slow clients,
# and can work with slow applications via asynchronous libraries such as
# {async_sinatra}[http://github.com/raggi/async_sinatra],
# {Cramp}[http://cramp.in/],
# and {rack-fiber_pool}[http://github.com/mperham/rack-fiber_pool].
#
# It does not require your Rack application to be thread-safe,
# reentrancy is only required for the DevFdResponse body
# generator.
#
# Compatibility: Whatever \EventMachine ~> 0.12.10 and Unicorn both
# support, currently Ruby 1.8/1.9.
#
# This model is compatible with users of "async.callback" in the Rack
# environment such as
# {async_sinatra}[http://github.com/raggi/async_sinatra].
#
# For a complete asynchronous framework,
# {Cramp}[http://cramp.in/] is fully
# supported when using this concurrency model.
#
# This model is fully-compatible with
# {rack-fiber_pool}[http://github.com/mperham/rack-fiber_pool]
# which allows each request to run inside its own \Fiber after
# all request processing is complete.
#
# Merb (and other frameworks/apps) supporting +deferred?+ execution as
# documented at Rainbows::EventMachine::TryDefer
#
# This model does not implement as streaming "rack.input" which allows
# the Rack application to process data as it arrives.  This means
# "rack.input" will be fully buffered in memory or to a temporary file
# before the application is entered.
#
# === RubyGem Requirements
#
# * event_machine 0.12.10
module Rainbows::EventMachine
  autoload :ResponsePipe, 'rainbows/event_machine/response_pipe'
  autoload :ResponseChunkPipe, 'rainbows/event_machine/response_chunk_pipe'
  autoload :TryDefer, 'rainbows/event_machine/try_defer'
  autoload :Client, 'rainbows/event_machine/client'

  include Rainbows::Base

  # Cramp (and possibly others) can subclass Rainbows::EventMachine::Client
  # and provide the :em_client_class option.  We /don't/ want to load
  # Rainbows::EventMachine::Client in the master process since we need
  # reloadability.
  def em_client_class
    case klass = Rainbows::O[:em_client_class]
    when Proc
      klass.call # e.g.: proc { Cramp::WebSocket::Rainbows }
    when Symbol, String
      eval(klass.to_s) # Object.const_get won't resolve multi-level paths
    else # @use should be either :EventMachine or :NeverBlock
      Rainbows.const_get(@use).const_get(:Client)
    end
  end

  # runs inside each forked worker, this sits around and waits
  # for connections and doesn't die until the parent dies (or is
  # given a INT, QUIT, or TERM signal)
  def worker_loop(worker) # :nodoc:
    init_worker_process(worker)
    server = Rainbows.server
    server.app.respond_to?(:deferred?) and
      server.app = TryDefer.new(server.app)

    # enable them both, should be non-fatal if not supported
    EM.epoll
    EM.kqueue
    logger.info "#@use: epoll=#{EM.epoll?} kqueue=#{EM.kqueue?}"
    client_class = em_client_class
    max = worker_connections + LISTENERS.size
    Rainbows::EventMachine::Server.const_set(:MAX, max)
    Rainbows::EventMachine::Server.const_set(:CL, client_class)
    Rainbows::EventMachine::Client.const_set(:APP, Rainbows.server.app)
    EM.run {
      conns = EM.instance_variable_get(:@conns) or
        raise RuntimeError, "EM @conns instance variable not accessible!"
      Rainbows::EventMachine::Server.const_set(:CUR, conns)
      Rainbows.at_quit do
        EM.next_tick do
          LISTENERS.clear
          conns.each_value do |c|
            case c
            when client_class
              c.quit
            when Rainbows::EventMachine::Server
              c.detach
            end
          end
        end
      end
      EM.add_periodic_timer(1) do
        EM.stop if ! Rainbows.tick && conns.empty? && EM.reactor_running?
      end
      LISTENERS.map! do |s|
        EM.watch(s, Rainbows::EventMachine::Server) do |c|
          c.notify_readable = true
        end
      end
    }
    EM.reactor_thread.join if EM.reactor_running?
  end
end
# :enddoc:
require 'rainbows/event_machine/server'
rainbows-4.6.2/lib/rainbows/const.rb0000644000004100000410000000102712354770160017451 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::Const
end
require 'rainbows/version'
module Rainbows::Const
  include Unicorn::Const

  RACK_DEFAULTS = Unicorn::HttpRequest::DEFAULTS.update({
    "SERVER_SOFTWARE" => "Rainbows! #{RAINBOWS_VERSION}",

    # using the Rev model, we'll automatically chunk pipe and socket objects
    # if they're the response body.  Unset by default.
    # "rainbows.autochunk" => false,
  })

  RACK_INPUT = Unicorn::HttpRequest::RACK_INPUT
  REMOTE_ADDR = Unicorn::HttpRequest::REMOTE_ADDR
end
rainbows-4.6.2/lib/rainbows/never_block.rb0000644000004100000410000000227212354770160020617 0ustar  www-datawww-data# -*- encoding: binary -*-

# {NeverBlock}[www.espace.com.eg/neverblock/] library that combines
# the EventMachine library with Ruby Fibers.  This includes use of
# Thread-based Fibers under Ruby 1.8.  It currently does NOT support
# a streaming "rack.input" but is compatible with everything else
# EventMachine supports.
#
# === :pool_size vs worker_connections
#
# In your Rainbows! config block, you may specify a Fiber pool size
# to limit your application concurrency (without using Rainbows::AppPool)
# independently of worker_connections.
#
#   Rainbows! do
#     use :NeverBlock, :pool_size => 50
#     worker_connections 100
#   end
#
module Rainbows::NeverBlock
  # :stopdoc:
  extend Rainbows::PoolSize

  # same pool size NB core itself uses
  def self.setup # :nodoc:
    super
    Rainbows::O[:backend] ||= :EventMachine # no Cool.io support, yet
    Rainbows.const_get(Rainbows::O[:backend])
    require "never_block" # require EM first since we need a higher version
  end

  def self.extended(klass)
    klass.extend(Rainbows.const_get(Rainbows::O[:backend])) # EventMachine
    klass.extend(Rainbows::NeverBlock::Core)
  end
  # :startdoc:
end
# :enddoc:
require 'rainbows/never_block/core'
rainbows-4.6.2/lib/rainbows/process_client.rb0000644000004100000410000000504112354770160021337 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::ProcessClient
  include Rainbows::Response
  include Rainbows::Const

  NULL_IO = Unicorn::HttpRequest::NULL_IO
  RACK_INPUT = Unicorn::HttpRequest::RACK_INPUT
  IC = Unicorn::HttpRequest.input_class
  Rainbows.config!(self, :client_header_buffer_size, :keepalive_timeout)

  def read_expire
    Time.now + KEEPALIVE_TIMEOUT
  end

  # used for reading headers (respecting keepalive_timeout)
  def timed_read(buf)
    expire = nil
    begin
      case rv = kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, buf)
      when :wait_readable
        return if expire && expire < Time.now
        expire ||= read_expire
        kgio_wait_readable(KEEPALIVE_TIMEOUT)
      else
        return rv
      end
    end while true
  end

  def process_loop
    @hp = hp = Rainbows::HttpParser.new
    kgio_read!(CLIENT_HEADER_BUFFER_SIZE, buf = hp.buf) or return

    begin # loop
      until env = hp.parse
        timed_read(buf2 ||= "") or return
        buf << buf2
      end

      set_input(env, hp)
      env[REMOTE_ADDR] = kgio_addr
      hp.hijack_setup(env, to_io)
      status, headers, body = APP.call(env.merge!(RACK_DEFAULTS))

      if 100 == status.to_i
        write(EXPECT_100_RESPONSE)
        env.delete(HTTP_EXPECT)
        status, headers, body = APP.call(env)
      end
      return if hp.hijacked?
      write_response(status, headers, body, alive = hp.next?) or return
    end while alive
  # if we get any error, try to write something back to the client
  # assuming we haven't closed the socket, but don't get hung up
  # if the socket is already closed or broken.  We'll always ensure
  # the socket is closed at the end of this function
  rescue => e
    handle_error(e)
  ensure
    close unless closed? || hp.hijacked?
  end

  def handle_error(e)
    Rainbows::Error.write(self, e)
  end

  def set_input(env, hp)
    env[RACK_INPUT] = 0 == hp.content_length ? NULL_IO : IC.new(self, hp)
  end

  def process_pipeline(env, hp)
    begin
      set_input(env, hp)
      env[REMOTE_ADDR] = kgio_addr
      hp.hijack_setup(env, to_io)
      status, headers, body = APP.call(env.merge!(RACK_DEFAULTS))
      if 100 == status.to_i
        write(EXPECT_100_RESPONSE)
        env.delete(HTTP_EXPECT)
        status, headers, body = APP.call(env)
      end
      return if hp.hijacked?
      write_response(status, headers, body, alive = hp.next?) or return
    end while alive && pipeline_ready(hp)
    alive or close
    rescue => e
      handle_error(e)
  end

  # override this in subclass/module
  def pipeline_ready(hp)
  end
end
rainbows-4.6.2/lib/rainbows/coolio/0000755000004100000410000000000012354770160017262 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/coolio/response_chunk_pipe.rb0000644000004100000410000000056712354770160023662 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
# this is class is specific to Coolio for proxying IO-derived objects
class Rainbows::Coolio::ResponseChunkPipe < Rainbows::Coolio::ResponsePipe
  def on_read(data)
    @client.write("#{data.size.to_s(16)}\r\n")
    @client.write(data)
    @client.write("\r\n")
  end

  def on_close
    @client.write("0\r\n\r\n")
    super
  end
end
rainbows-4.6.2/lib/rainbows/coolio/master.rb0000644000004100000410000000074112354770160021104 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
require 'thread'
class Rainbows::Coolio::Master < Coolio::IOWatcher

  def initialize(queue)
    @reader, @writer = Kgio::Pipe.new
    super(@reader)
    @queue = queue
    @wbuf, @rbuf = "\0", "\0"
  end

  def <<(output)
    @queue << output
    @writer.kgio_trywrite(@wbuf)
  end

  def on_readable
    if String === @reader.kgio_tryread(1, @rbuf)
      client, response = @queue.pop
      client.response_write(response)
    end
  end
end
rainbows-4.6.2/lib/rainbows/coolio/client.rb0000644000004100000410000001321712354770160021071 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::Coolio::Client < Coolio::IO
  include Rainbows::EvCore
  APP = Rainbows.server.app
  CONN = Rainbows::Coolio::CONN
  KATO = Rainbows::Coolio::KATO
  LOOP = Coolio::Loop.default

  def initialize(io)
    CONN[self] = false
    super(io)
    post_init
    @deferred = nil
  end

  def want_more
    enable unless enabled?
  end

  def quit
    super
    close if nil == @deferred && @_write_buffer.empty?
  end

  # override the Coolio::IO#write method try to write directly to the
  # kernel socket buffers to avoid an extra userspace copy if
  # possible.
  def write(buf)
    if @_write_buffer.empty?
      begin
        case rv = @_io.kgio_trywrite(buf)
        when nil
          return enable_write_watcher
        when :wait_writable
          break # fall through to super(buf)
        when String
          buf = rv # retry, skb could grow or been drained
        end
      rescue => e
        return handle_error(e)
      end while true
    end
    super(buf)
  end

  def on_readable
    buf = @_io.kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, RBUF)
    case buf
    when :wait_readable
    when nil # eof
      close
    else
      on_read buf
    end
  rescue Errno::ECONNRESET
    close
  end

  # allows enabling of write watcher even when read watcher is disabled
  def evloop
    LOOP
  end

  def next!
    attached? or return
    @deferred = nil
    enable_write_watcher # trigger on_write_complete
  end

  def timeout?
    if nil == @deferred && @_write_buffer.empty?
      @_io.shutdown
      true
    else
      false
    end
  end

  # used for streaming sockets and pipes
  def stream_response_body(body, io, chunk)
    # we only want to attach to the Coolio::Loop belonging to the
    # main thread in Ruby 1.9
    (chunk ? Rainbows::Coolio::ResponseChunkPipe :
             Rainbows::Coolio::ResponsePipe).new(io, self, body).attach(LOOP)
    @deferred = true
  end

  def hijacked
    CONN.delete(self)
    detach
    nil
  end

  def write_response_path(status, headers, body, alive)
    io = body_to_io(body)
    st = io.stat

    if st.file?
      defer_file(status, headers, body, alive, io, st)
    elsif st.socket? || st.pipe?
      chunk = stream_response_headers(status, headers, alive, body)
      return hijacked if nil == chunk
      stream_response_body(body, io, chunk)
    else
      # char or block device... WTF?
      write_response(status, headers, body, alive)
    end
  end

  def ev_write_response(status, headers, body, alive)
    if body.respond_to?(:to_path)
      body = write_response_path(status, headers, body, alive)
    else
      body = write_response(status, headers, body, alive)
    end
    return hijacked unless body
    return quit unless alive && :close != @state
    @state = :headers
  end

  def app_call input
    KATO.delete(self)
    disable if enabled?
    @env[RACK_INPUT] = input
    @env[REMOTE_ADDR] = @_io.kgio_addr
    @env[ASYNC_CALLBACK] = method(:write_async_response)
    @hp.hijack_setup(@env, @_io)
    status, headers, body = catch(:async) {
      APP.call(@env.merge!(RACK_DEFAULTS))
    }
    return hijacked if @hp.hijacked?

    (nil == status || -1 == status) ? @deferred = true :
        ev_write_response(status, headers, body, @hp.next?)
  end

  def on_write_complete
    case @deferred
    when true then return # #next! will clear this bit
    when nil # fall through
    else
      return if stream_file_chunk(@deferred)
      close_deferred # EOF, fall through
    end

    case @state
    when :close
      close if @_write_buffer.empty?
    when :headers
      if @buf.empty?
        buf = @_io.kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, RBUF) or return close
        String === buf and return on_read(buf)
        # buf == :wait_readable
        unless enabled?
          enable
          KATO[self] = Time.now
        end
      else
        on_read(Z)
      end
    end
    rescue => e
      handle_error(e)
  end

  def handle_error(e)
    close_deferred
    if msg = Rainbows::Error.response(e)
      @_io.kgio_trywrite(msg) rescue nil
    end
    @_write_buffer.clear
    ensure
      quit
  end

  def close_deferred
    if @deferred
      begin
        @deferred.close if @deferred.respond_to?(:close)
      rescue => e
        Unicorn.log_error(Rainbows.server.logger,
                          "closing deferred=#{@deferred.inspect}", e)
      end
      @deferred = nil
    end
  end

  def on_close
    close_deferred
    CONN.delete(self)
    KATO.delete(self)
  end

  if IO.method_defined?(:trysendfile)
    def defer_file(status, headers, body, alive, io, st)
      if r = sendfile_range(status, headers)
        status, headers, range = r
        body = write_headers(status, headers, alive, body) or return hijacked
        range and defer_file_stream(range[0], range[1], io, body)
      else
        write_headers(status, headers, alive, body) or return hijacked
        defer_file_stream(0, st.size, io, body)
      end
      body
    end

    def stream_file_chunk(sf) # +sf+ is a Rainbows::StreamFile object
      case n = @_io.trysendfile(sf, sf.offset, sf.count)
      when Integer
        sf.offset += n
        return if 0 == (sf.count -= n)
      when :wait_writable
        return enable_write_watcher
      else
        return
      end while true
    end
  else
    def defer_file(status, headers, body, alive, io, st)
      write_headers(status, headers, alive, body) or return hijacked
      defer_file_stream(0, st.size, io, body)
      body
    end

    def stream_file_chunk(body)
      buf = body.to_io.read(0x4000) and write(buf)
    end
  end

  def defer_file_stream(offset, count, io, body)
    @deferred = Rainbows::StreamFile.new(offset, count, io, body)
    enable_write_watcher
  end
end
rainbows-4.6.2/lib/rainbows/coolio/core.rb0000644000004100000410000000132012354770160020533 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::Coolio::Core
  include Rainbows::Base

  # runs inside each forked worker, this sits around and waits
  # for connections and doesn't die until the parent dies (or is
  # given a INT, QUIT, or TERM signal)
  def worker_loop(worker)
    init_worker_process(worker)
    mod = Rainbows.const_get(@use)
    rloop = Rainbows::Coolio::Server.const_set(:LOOP, Coolio::Loop.default)
    Rainbows::Coolio::Server.const_set(:MAX, @worker_connections)
    Rainbows::Coolio::Server.const_set(:CL, mod.const_get(:Client))
    Rainbows::Coolio::Heartbeat.new(1, true).attach(rloop)
    LISTENERS.map! { |s| Rainbows::Coolio::Server.new(s).attach(rloop) }
    rloop.run
  end
end
rainbows-4.6.2/lib/rainbows/coolio/heartbeat.rb0000644000004100000410000000135012354770160021545 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# This class handles the Unicorn fchmod heartbeat mechanism
# in Coolio-based concurrency models to prevent the master
# process from killing us unless we're blocked.  This class
# will also detect and execute the graceful exit if triggered
# by SIGQUIT
class Rainbows::Coolio::Heartbeat < Coolio::TimerWatcher
  KATO = Rainbows::Coolio::KATO
  CONN = Rainbows::Coolio::CONN
  Rainbows.config!(self, :keepalive_timeout)
  Rainbows.at_quit { KATO.each_key { |client| client.timeout? }.clear }

  def on_timer
    if (ot = KEEPALIVE_TIMEOUT) >= 0
      ot = Time.now - ot
      KATO.delete_if { |client, time| time < ot and client.timeout? }
    end
    exit if (! Rainbows.tick && CONN.size <= 0)
  end
end
rainbows-4.6.2/lib/rainbows/coolio/response_pipe.rb0000644000004100000410000000064612354770160022470 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
# this is class is specific to Coolio for writing large static files
# or proxying IO-derived objects
class Rainbows::Coolio::ResponsePipe < Coolio::IO
  def initialize(io, client, body)
    super(io)
    @client, @body = client, body
  end

  def on_read(data)
    @client.write(data)
  end

  def on_close
    @body.respond_to?(:close) and @body.close
    @client.next!
  end
end
rainbows-4.6.2/lib/rainbows/coolio/thread_client.rb0000644000004100000410000000175112354770160022420 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:

RUBY_VERSION =~ %r{\A1\.8} and
  warn "Coolio and Threads do not mix well under Ruby 1.8"

class Rainbows::Coolio::ThreadClient < Rainbows::Coolio::Client
  def app_call input
    KATO.delete(self)
    disable if enabled?
    @env[RACK_INPUT] = input
    app_dispatch # must be implemented by subclass
  end

  # this is only called in the master thread
  def response_write(response)
    return hijacked if @hp.hijacked?
    ev_write_response(*response, @hp.next?)
    rescue => e
      handle_error(e)
  end

  # fails-safe application dispatch, we absolutely cannot
  # afford to fail or raise an exception (killing the thread)
  # here because that could cause a deadlock and we'd leak FDs
  def app_response
    begin
      @env[REMOTE_ADDR] = @_io.kgio_addr
      @hp.hijack_setup(@env, @_io)
      APP.call(@env.merge!(RACK_DEFAULTS))
    rescue => e
      Rainbows::Error.app(e) # we guarantee this does not raise
      [ 500, {}, [] ]
    end
  end
end
rainbows-4.6.2/lib/rainbows/coolio/server.rb0000644000004100000410000000044712354770160021122 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::Coolio::Server < Coolio::IO
  CONN = Rainbows::Coolio::CONN
  # CL and MAX will be defined in the corresponding worker loop

  def on_readable
    return if CONN.size >= MAX
    io = @_io.kgio_tryaccept and CL.new(io).attach(LOOP)
  end
end
rainbows-4.6.2/lib/rainbows/dev_fd_response.rb0000644000004100000410000000600212354770160021466 0ustar  www-datawww-data# -*- encoding: binary -*-

# Rack response middleware wrapping any IO-like object with an
# OS-level file descriptor associated with it.  May also be used to
# create responses from integer file descriptors or existing +IO+
# objects.  This may be used in conjunction with the #to_path method
# on servers that support it to pass arbitrary file descriptors into
# the HTTP response without additional open(2) syscalls

class Rainbows::DevFdResponse < Struct.new(:app)

  # :stopdoc:
  FD_MAP = Rainbows::FD_MAP
  Content_Length = "Content-Length".freeze
  Transfer_Encoding = "Transfer-Encoding".freeze
  Rainbows_autochunk = "rainbows.autochunk".freeze
  Rainbows_model = "rainbows.model"
  HTTP_VERSION = "HTTP_VERSION"
  Chunked = "chunked"
  include Rack::Utils

  # Rack middleware entry point, we'll just pass through responses
  # unless they respond to +to_io+ or +to_path+
  def call(env)
    status, headers, body = response = app.call(env)

    # totally uninteresting to us if there's no body
    if STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) ||
       File === body ||
       (body.respond_to?(:to_path) && File.file?(body.to_path))
      return response
    end

    io = body.to_io if body.respond_to?(:to_io)
    io ||= File.open(body.to_path) if body.respond_to?(:to_path)
    return response if io.nil?

    headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
    st = io.stat
    fileno = io.fileno
    FD_MAP[fileno] = io
    if st.file?
      headers[Content_Length] ||= st.size.to_s
      headers.delete(Transfer_Encoding)
    elsif st.pipe? || st.socket? # epoll-able things
      unless headers.include?(Content_Length)
        if env[Rainbows_autochunk]
          case env[HTTP_VERSION]
          when "HTTP/1.0", nil
          else
            headers[Transfer_Encoding] = Chunked
          end
        else
          env[Rainbows_autochunk] = false
        end
      end

      # we need to make sure our pipe output is Fiber-compatible
      case env[Rainbows_model]
      when :FiberSpawn, :FiberPool, :RevFiberSpawn, :CoolioFiberSpawn
        io.respond_to?(:kgio_wait_readable) or
          io = Rainbows::Fiber::IO.new(io)
      when :Revactor
        io = Rainbows::Revactor::Proxy.new(io)
      end
    else # unlikely, char/block device file, directory, ...
      return response
    end
    [ status, headers, Body.new(io, "/dev/fd/#{fileno}", body) ]
  end

  class Body < Struct.new(:to_io, :to_path, :orig_body) # :nodoc:
    # called by the webserver or other middlewares if they can't
    # handle #to_path
    def each
      to_io.each { |x| yield x }
    end

    # remain Rack::Lint-compatible for people with wonky systems :P
    unless File.directory?("/dev/fd")
      alias to_path_orig to_path
      undef_method :to_path
    end

    # called by the web server after #each
    def close
      to_io.close unless to_io.closed?
      orig_body.close if orig_body.respond_to?(:close) # may not be an IO
    rescue IOError # could've been IO::new()'ed and closed
    end
  end
  #:startdoc:
end # class
rainbows-4.6.2/lib/rainbows/xepoll_thread_pool/0000755000004100000410000000000012354770160021661 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/xepoll_thread_pool/client.rb0000644000004100000410000000544312354770160023472 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# FIXME: lots of duplication from xepolll_thread_spawn/client

module Rainbows::XEpollThreadPool::Client
  Rainbows.config!(self, :keepalive_timeout, :client_header_buffer_size)
  N = Raindrops.new(1)
  ACCEPTORS = Rainbows::HttpServer::LISTENERS.dup
  extend Rainbows::WorkerYield

  def self.included(klass) # included in Rainbows::Client
    max = Rainbows.server.worker_connections
    ACCEPTORS.map! do |sock|
      Thread.new do
        buf = ""
        begin
          if io = sock.kgio_accept(klass)
            N.incr(0, 1)
            io.epoll_once(buf)
          end
          worker_yield while N[0] >= max
        rescue => e
          Rainbows::Error.listen_loop(e)
        end while Rainbows.alive
      end
    end
  end

  def self.app_run(queue)
    while client = queue.pop
      client.run
    end
  end

  QUEUE = Queue.new
  Rainbows::O[:pool_size].times { Thread.new { app_run(QUEUE) } }

  ep = SleepyPenguin::Epoll
  EP = ep.new
  IN = ep::IN | ep::ONESHOT
  KATO = {}
  KATO.compare_by_identity if KATO.respond_to?(:compare_by_identity)
  LOCK = Mutex.new
  Rainbows.at_quit do
    clients = nil
    LOCK.synchronize { clients = KATO.keys; KATO.clear }
    clients.each { |io| io.closed? or io.close }
  end
  @@last_expire = Time.now

  def kato_set
    LOCK.synchronize { KATO[self] = @@last_expire }
    EP.set(self, IN)
  end

  def kato_delete
    LOCK.synchronize { KATO.delete self }
  end

  def self.loop
    buf = ""
    begin
      EP.wait(nil, 1000) { |_, obj| obj.epoll_run(buf) }
      expire
    rescue Errno::EINTR
    rescue => e
      Rainbows::Error.listen_loop(e)
    end while Rainbows.tick || N[0] > 0
    Rainbows::JoinThreads.acceptors(ACCEPTORS)
  end

  def self.expire
    return if ((now = Time.now) - @@last_expire) < 1.0
    if (ot = KEEPALIVE_TIMEOUT) >= 0
      ot = now - ot
      defer = []
      LOCK.synchronize do
        KATO.delete_if { |client, time| time < ot and defer << client }
      end
      defer.each { |io| io.closed? or io.shutdown }
    end
    @@last_expire = now
  end

  def epoll_once(buf)
    @hp = Rainbows::HttpParser.new
    epoll_run(buf)
  end

  def close
    super
    kato_delete
    N.decr(0, 1)
    nil
  end

  def handle_error(e)
    super
    ensure
      closed? or close
  end

  def queue!
    QUEUE << self
    false
  end

  def epoll_run(buf)
    case kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, buf)
    when :wait_readable
      return kato_set
    when String
      kato_delete
      @hp.add_parse(buf) and return queue!
    else
      return close
    end while true
    rescue => e
      handle_error(e)
  end

  def run
    process_pipeline(@hp.env, @hp)
  end

  def pipeline_ready(hp)
    # be fair to other clients, let others run first
    hp.parse and return queue!
    epoll_run("")
    false
  end
end
rainbows-4.6.2/lib/rainbows/configurator.rb0000644000004100000410000001630412354770160021031 0ustar  www-datawww-data# -*- encoding: binary -*-

# This module adds \Rainbows! to the
# {Unicorn::Configurator}[http://unicorn.bogomips.org/Unicorn/Configurator.html]
# \Rainbows!-specific configuration options must be inside a the Rainbows!
# block, otherwise Unicorn::Configurator directives may be used anywhere
# in the file.
#
#   Rainbows! do
#     use :ThreadSpawn # concurrency model to use
#     worker_connections 400
#     keepalive_timeout 0 # zero disables keepalives entirely
#     client_max_body_size 5*1024*1024 # 5 megabytes
#     keepalive_requests 666 # default:100
#     client_header_buffer_size 2 * 1024 # 2 kilobytes
#   end
#
#   # the rest of the Unicorn configuration...
#   worker_processes 8
#   stderr_path "/path/to/error.log"
#   stdout_path "/path/to/output.log"
module Rainbows::Configurator
  Unicorn::Configurator::DEFAULTS.merge!({
    :use => Rainbows::Base,
    :worker_connections => 50,
    :keepalive_timeout => 5,
    :keepalive_requests => 100,
    :client_max_body_size => 1024 * 1024,
    :client_header_buffer_size => 1024,
    :client_max_header_size => 112 * 1024,
    :copy_stream => IO.respond_to?(:copy_stream) ? IO : false,
  })

  # Configures \Rainbows! with a given concurrency model to +use+ and
  # a +worker_connections+ upper-bound.  This method should be called
  # inside a Unicorn/\Rainbows! configuration file.
  #
  # All other methods in Rainbows::Configurator must be called
  # inside this block.
  def Rainbows!(&block)
    block_given? or raise ArgumentError, "Rainbows! requires a block"
    @block = true
    instance_eval(&block)
    ensure
      @block = false
  end

  def check! # :nodoc:
    @block or abort "must be inside a Rainbows! block"
  end

  # This limits the number of connected clients per-process.  The total
  # number of clients on a server is +worker_processes+ * +worker_connections+.
  #
  # This option has no effect with the Base concurrency model, which is
  # limited to +1+.
  #
  # Default: 50
  def worker_connections(clients)
    check!
    set_int(:worker_connections, clients, 1)
  end

  # Select a concurrency model for use with \Rainbows!.  You must select
  # this with a Symbol (prefixed with ":").  Thus if you wish to select
  # the Rainbows::ThreadSpawn concurrency model, you would use:
  #
  #   Rainbows! do
  #     use :ThreadSpawn
  #   end
  #
  # See the {Summary}[link:Summary.html] document for a summary of
  # supported concurrency models.  +options+ may be specified for some
  # concurrency models, but the majority do not support them.
  #
  # Default: :Base (no concurrency)
  def use(model, *options)
    check!
    mod = begin
      Rainbows.const_get(model)
    rescue NameError => e
      warn "error loading #{model.inspect}: #{e}"
      e.backtrace.each { |l| warn l }
      abort "concurrency model #{model.inspect} not supported"
    end
    Module === mod or abort "concurrency model #{model.inspect} not supported"
    options.each do |opt|
      case opt
      when Hash
        Rainbows::O.merge!(opt)
      when Symbol
        Rainbows::O[opt] = true
      else
        abort "cannot handle option: #{opt.inspect} in #{options.inspect}"
      end
    end
    mod.setup if mod.respond_to?(:setup)
    set[:use] = mod
  end

  # Sets the value (in seconds) the server will wait for a client in
  # between requests.  The default value should be enough under most
  # conditions for browsers to render the page and start retrieving
  # extra elements.
  #
  # Setting this value to +0+ disables keepalive entirely
  #
  # Default: 5 seconds
  def keepalive_timeout(seconds)
    check!
    set_int(:keepalive_timeout, seconds, 0)
  end

  # This limits the number of requests which can be made over a keep-alive
  # connection.  This is used to prevent single client from monopolizing
  # the server and to improve fairness when load-balancing across multiple
  # machines by forcing a client to reconnect.  This may be helpful
  # in mitigating some denial-of-service attacks.
  #
  # Default: 100 requests
  def keepalive_requests(count)
    check!
    case count
    when nil, Integer
      set[:keepalive_requests] = count
    else
      abort "not an integer or nil: keepalive_requests=#{count.inspect}"
    end
  end

  # Limits the maximum size of a request body for all requests.
  # Setting this to +nil+ disables the maximum size check.
  #
  # Default: 1 megabyte (1048576 bytes)
  #
  # If you want endpoint-specific upload limits and use a
  # "rack.input"-streaming concurrency model, see the Rainbows::MaxBody
  def client_max_body_size(bytes)
    check!
    err = "client_max_body_size must be nil or a non-negative Integer"
    case bytes
    when nil
    when Integer
      bytes >= 0 or abort err
    else
      abort err
    end
    set[:client_max_body_size] = bytes
  end

  # Limits the maximum size of a request header for all requests.
  #
  # Default: 112 kilobytes (114688 bytes)
  #
  # Lowering this will lower worst-case memory usage and mitigate some
  # denial-of-service attacks.  This should be larger than
  # client_header_buffer_size.
  def client_max_header_size(bytes)
    check!
    set_int(:client_max_header_size, bytes, 8)
  end

  # This governs the amount of memory allocated for an individual read(2) or
  # recv(2) system call when reading headers.  Applications that make minimal
  # use of cookies should not increase this from the default.
  #
  # Rails applications using session cookies may want to increase this to
  # 2048 bytes or more depending on expected request sizes.
  #
  # Increasing this will increase overall memory usage to your application,
  # as you will need at least this amount of memory for every connected client.
  #
  # Default: 1024 bytes
  def client_header_buffer_size(bytes)
    check!
    set_int(:client_header_buffer_size, bytes, 1)
  end

  # Allows overriding the +klass+ where the +copy_stream+ method is
  # used to do efficient copying of regular files, pipes, and sockets.
  #
  # This is only used with multi-threaded concurrency models:
  #
  # * ThreadSpawn
  # * ThreadPool
  # * WriterThreadSpawn
  # * WriterThreadPool
  # * XEpollThreadSpawn
  # * XEpollThreadPool
  #
  # Due to existing {bugs}[http://redmine.ruby-lang.org/search?q=copy_stream]
  # in the Ruby IO.copy_stream implementation, \Rainbows! uses the
  # "sendfile" RubyGem that instead of copy_stream to transfer regular files
  # to clients.  The "sendfile" RubyGem also supports more operating systems,
  # and works with more concurrency models.
  #
  # Recent Linux 2.6 users may override this with "IO::Splice" from the
  # "io_splice" RubyGem:
  #
  #   require "io/splice"
  #   Rainbows! do
  #     copy_stream IO::Splice
  #   end
  #
  # Keep in mind that splice(2) itself is a relatively new system call
  # and has been buggy in many older Linux kernels.  If you're proxying
  # the output of sockets to the client, be sure to use "io_splice"
  # 4.1.1 or later to avoid stalling responses.
  #
  # Default: IO on Ruby 1.9+, false otherwise
  def copy_stream(klass)
    check!
    if klass && ! klass.respond_to?(:copy_stream)
      abort "#{klass} must respond to `copy_stream' or be `false'"
    end
    set[:copy_stream] = klass
  end
end

# :enddoc:
# inject the Rainbows! method into Unicorn::Configurator
Unicorn::Configurator.__send__(:include, Rainbows::Configurator)
rainbows-4.6.2/lib/rainbows/response.rb0000644000004100000410000001520412354770160020163 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::Response
  include Unicorn::HttpResponse
  Close = "close"
  KeepAlive = "keep-alive"
  Content_Length = "Content-Length".freeze
  Transfer_Encoding = "Transfer-Encoding".freeze
  Rainbows.config!(self, :copy_stream)

  # private file class for IO objects opened by Rainbows! itself (and not
  # the app or middleware)
  class F < File; end

  # called after forking
  def self.setup
    Kgio.accept_class = Rainbows::Client
    0 == Rainbows.server.keepalive_timeout and
      Rainbows::HttpParser.keepalive_requests = 0
  end

  # Rack 1.5.0 (protocol version 1.2) adds response hijacking support
  if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
    RACK_HIJACK = "rack.hijack"

    def hijack_prepare(value)
      value
    end

    def hijack_socket
      @hp.env[RACK_HIJACK].call
    end
  else
    def hijack_prepare(_)
    end
  end

  # returns the original body on success
  # returns nil if the headers hijacked the response body
  def write_headers(status, headers, alive, body)
    @hp.headers? or return body
    hijack = nil
    status = CODES[status.to_i] || status
    buf = "HTTP/1.1 #{status}\r\n" \
          "Date: #{httpdate}\r\n" \
          "Status: #{status}\r\n"
    headers.each do |key, value|
      case key
      when %r{\A(?:Date\z|Connection\z)}i
        next
      when "rack.hijack"
        # this was an illegal key in Rack < 1.5, so it should be
        # OK to silently discard it for those older versions
        hijack = hijack_prepare(value)
        alive = false # No persistent connections for hijacking
      else
        if /\n/ =~ value
          # avoiding blank, key-only cookies with /\n+/
          buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
        else
          buf << "#{key}: #{value}\r\n"
        end
      end
    end
    write(buf << "Connection: #{alive ? KeepAlive : Close}\r\n\r\n")

    if hijack
      body = nil # ensure caller does not close body
      hijack.call(hijack_socket)
    end
    body
  end

  def close_if_private(io)
    io.close if F === io
  end

  def io_for_fd(fd)
    Rainbows::FD_MAP.delete(fd) || F.for_fd(fd)
  end

  # to_io is not part of the Rack spec, but make an exception here
  # since we can conserve path lookups and file descriptors.
  # \Rainbows! will never get here without checking for the existence
  # of body.to_path first.
  def body_to_io(body)
    if body.respond_to?(:to_io)
      body.to_io
    else
      # try to take advantage of Rainbows::DevFdResponse, calling F.open
      # is a last resort
      path = body.to_path
      %r{\A/dev/fd/(\d+)\z} =~ path ? io_for_fd($1.to_i) : F.open(path)
    end
  end

  module Each
    # generic body writer, used for most dynamically-generated responses
    def write_body_each(body)
      body.each { |chunk| write(chunk) }
    end

    # generic response writer, used for most dynamically-generated responses
    # and also when copy_stream and/or IO#trysendfile is unavailable
    def write_response(status, headers, body, alive)
      body = write_headers(status, headers, alive, body)
      write_body_each(body) if body
      body
      ensure
        body.close if body.respond_to?(:close)
    end
  end
  include Each

  if IO.method_defined?(:trysendfile)
    module Sendfile
      def write_body_file(body, range)
        io = body_to_io(body)
        range ? sendfile(io, range[0], range[1]) : sendfile(io, 0)
        ensure
          close_if_private(io)
      end
    end
    include Sendfile
  end

  if COPY_STREAM
    unless IO.method_defined?(:trysendfile)
      module CopyStream
        def write_body_file(body, range)
          range ? COPY_STREAM.copy_stream(body, self, range[1], range[0]) :
                  COPY_STREAM.copy_stream(body, self, nil, 0)
        end
      end
      include CopyStream
    end

    # write_body_stream is an alias for write_body_each if copy_stream
    # isn't used or available.
    def write_body_stream(body)
      COPY_STREAM.copy_stream(io = body_to_io(body), self)
      ensure
        close_if_private(io)
    end
  else # ! COPY_STREAM
    alias write_body_stream write_body_each
  end  # ! COPY_STREAM

  if IO.method_defined?(:trysendfile) || COPY_STREAM
    HTTP_RANGE = 'HTTP_RANGE'
    Content_Range = 'Content-Range'.freeze

    # This does not support multipart responses (does anybody actually
    # use those?)
    def sendfile_range(status, headers)
      status = status.to_i
      if 206 == status
        if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ headers[Content_Range]
          a, b = $1.to_i, $2.to_i
          return 206, headers, [ a,  b - a + 1 ]
        end
        return # wtf...
      end
      200 == status &&
      /\Abytes=(\d+-\d*|\d*-\d+)\z/ =~ @hp.env[HTTP_RANGE] or
        return
      a, b = $1.split(/-/)

      # HeaderHash is quite expensive, and Rack::File currently
      # uses a regular Ruby Hash with properly-cased headers the
      # same way they're presented in rfc2616.
      headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
      clen = headers[Content_Length] or return
      size = clen.to_i

      if b.nil? # bytes=M-
        offset = a.to_i
        count = size - offset
      elsif a.empty? # bytes=-N
        offset = size - b.to_i
        count = size - offset
      else  # bytes=M-N
        offset = a.to_i
        count = b.to_i + 1 - offset
      end

      if 0 > count || offset >= size
        headers[Content_Length] = "0"
        headers[Content_Range] = "bytes */#{clen}"
        return 416, headers, nil
      else
        count = size if count > size
        headers[Content_Length] = count.to_s
        headers[Content_Range] = "bytes #{offset}-#{offset+count-1}/#{clen}"
        return 206, headers, [ offset, count ]
      end
    end

    def write_response_path(status, headers, body, alive)
      if File.file?(body.to_path)
        if r = sendfile_range(status, headers)
          status, headers, range = r
          body = write_headers(status, headers, alive, body)
          write_body_file(body, range) if body && range
        else
          body = write_headers(status, headers, alive, body)
          write_body_file(body, nil) if body
        end
      else
        body = write_headers(status, headers, alive, body)
        write_body_stream(body) if body
      end
      body
      ensure
        body.close if body.respond_to?(:close)
    end

    module ToPath
      # returns nil if hijacked
      def write_response(status, headers, body, alive)
        if body.respond_to?(:to_path)
          write_response_path(status, headers, body, alive)
        else
          super
        end
      end
    end
    include ToPath
  end # COPY_STREAM || IO.method_defined?(:trysendfile)
end
rainbows-4.6.2/lib/rainbows/reverse_proxy/0000755000004100000410000000000012354770160020712 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/reverse_proxy/event_machine.rb0000644000004100000410000000237512354770160024053 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# TODO: handle large responses without having it all in memory
module Rainbows::ReverseProxy::EventMachine
  class Backend < EM::Connection
    include Rainbows::ReverseProxy::EvClient # provides receive_data

    # +addr+ is a packed sockaddr, so it can be either a UNIX or TCP socket
    def initialize(env)
      @env = env
      @rbuf = ""
      @parser = Kcar::Parser.new
      @response = @body = nil
      @headers = Rack::Utils::HeaderHash.new
    end

    # prevents us from sending too much at once and OOM-ing on large uploads
    def stream_input(input)
      if buf = input.read(16384)
        send_data buf
        EM.next_tick { stream_input(input) }
      end
    end

    def on_write_complete
      if @input
        buf = @input.read(16384, @junk) and return write(buf)
        @input = nil
      end
    end

    def unbind
      @env[AsyncCallback].call(@response || Rainbows::ReverseProxy::E502)
    end
  end

  UpstreamSocket = Rainbows::ReverseProxy::UpstreamSocket
  def call(env)
    input = prepare_input!(env)
    io = UpstreamSocket.start(pick_upstream(env))
    sock = EM.attach(io, Backend, env)
    sock.send_data(build_headers(env, input))
    sock.stream_input(input) if input
    throw :async
  end
end
rainbows-4.6.2/lib/rainbows/reverse_proxy/ev_client.rb0000644000004100000410000000163612354770160023215 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
require 'tempfile'
module Rainbows::ReverseProxy::EvClient
  include Rainbows::ReverseProxy::Synchronous
  AsyncCallback = "async.callback"
  CBB = Unicorn::TeeInput.client_body_buffer_size
  Content_Length = "Content-Length"
  Transfer_Encoding = "Transfer-Encoding"

  def receive_data(buf)
    if @body
      @body << buf
    else
      response = @parser.headers(@headers, @rbuf << buf) or return
      if (cl = @headers[Content_Length] && cl.to_i > CBB) ||
         (%r{\bchunked\b} =~ @headers[Transfer_Encoding])
        @body = LargeBody.new("")
        @body << @rbuf
        @response = response << @body
      else
        @body = @rbuf.dup
        @response = response << [ @body ]
      end
    end
  end

  class LargeBody < Tempfile
    def each
      buf = ""
      rewind
      while read(16384, buf)
        yield buf
      end
    end

    alias close close!
  end
end
rainbows-4.6.2/lib/rainbows/reverse_proxy/coolio.rb0000644000004100000410000000303312354770160022522 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# TODO: handle large responses without having it all in memory
module Rainbows::ReverseProxy::Coolio
  LOOP = Cool.io::Loop.default

  class Backend < Cool.io::IO
    include Rainbows::ReverseProxy::EvClient

    def initialize(env, addr, input)
      @env = env
      @input = input
      @junk, @rbuf = "", ""
      @parser = Kcar::Parser.new
      @response = @body = nil
      @headers = Rack::Utils::HeaderHash.new
      super(UpstreamSocket.start(addr)) # kgio-enabled socket
    end

    def on_write_complete
      if @input
        buf = @input.read(16384, @junk) and return write(buf)
        @input = nil
      end
    end

    def on_readable
      # avoiding IO#read_nonblock since that's expensive in 1.9.2
      case buf = @_io.kgio_tryread(16384, @junk)
      when String
        receive_data(buf)
      when :wait_readable
        return
      when nil
        @env[AsyncCallback].call(@response)
        return close
      end while true # we always read until EAGAIN or EOF

      rescue => e
        case e
        when Errno::ECONNRESET
          @env[AsyncCallback].call(@response)
          return close
        when SystemCallError
        else
          Unicorn.log_error(@env["rack.logger"], "on_readable", e)
        end
        @env[AsyncCallback].call(Rainbows::ReverseProxy::E502)
        close
    end
  end

  def call(env)
    input = prepare_input!(env)
    sock = Backend.new(env, pick_upstream(env), input).attach(LOOP)
    sock.write(build_headers(env, input))
    throw :async
  end
end
rainbows-4.6.2/lib/rainbows/reverse_proxy/synchronous.rb0000644000004100000410000000076512354770160023641 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::ReverseProxy::Synchronous
  UpstreamSocket = Rainbows::ReverseProxy::UpstreamSocket

  def each_block(input)
    buf = ""
    while input.read(16384, buf)
      yield buf
    end
  end

  def call(env)
    input = prepare_input!(env)
    req = build_headers(env, input)
    sock = UpstreamSocket.new(pick_upstream(env))
    sock.write(req)
    each_block(input) { |buf| sock.kgio_write(buf) } if input
    Kcar::Response.new(sock).rack
  end
end
rainbows-4.6.2/lib/rainbows/reverse_proxy/multi_thread.rb0000644000004100000410000000022112354770160023713 0ustar  www-datawww-data# -*- encoding -*-
# :enddoc:
module Rainbows::ReverseProxy::MultiThread
  def pick_upstream(env)
    @lock.synchronize { super(env) }
  end
end
rainbows-4.6.2/lib/rainbows/coolio_thread_pool/0000755000004100000410000000000012354770160021642 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/coolio_thread_pool/watcher.rb0000644000004100000410000000043012354770160023621 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::CoolioThreadPool::Watcher < Coolio::TimerWatcher
  def initialize(threads)
    @threads = threads
    super(Rainbows.server.timeout, true)
  end

  def on_timer
    @threads.each { |t| t.join(0) and Rainbows.quit! }
  end
end
rainbows-4.6.2/lib/rainbows/coolio_thread_pool/client.rb0000644000004100000410000000031512354770160023444 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::CoolioThreadPool::Client < Rainbows::Coolio::ThreadClient
  # QUEUE constant will be set in worker_loop
  def app_dispatch
    QUEUE << self
  end
end
rainbows-4.6.2/lib/rainbows/revactor.rb0000644000004100000410000000632612354770160020157 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'revactor'
require 'fcntl'
Revactor::VERSION >= '0.1.5' or abort 'revactor 0.1.5 is required'

# Enables use of the Actor model through {Revactor}[http://revactor.org]
# under Ruby 1.9.
#
# \Revactor dormant upstream, so the use of this is NOT recommended for
# new applications.
#
# It spawns one long-lived Actor for every listen socket in the process
# and spawns a new Actor for every client connection accept()-ed.
# +worker_connections+ will limit the number of client Actors we have
# running at any one time.
#
# Applications using this model are required to be reentrant, but do
# not have to worry about race conditions unless they use threads
# internally.  \Rainbows! does not spawn threads under this model.
# Multiple instances of the same app may run in the same address space
# sequentially (but at interleaved points).  Any network dependencies
# in the application using this model should be implemented using the
# \Revactor library as well, to take advantage of the networking
# concurrency features this model provides.
#
# === RubyGem Requirements
# * revactor 0.1.5 or later
module Rainbows::Revactor
  autoload :Client, 'rainbows/revactor/client'
  autoload :Proxy, 'rainbows/revactor/proxy'

  include Rainbows::Base

  # runs inside each forked worker, this sits around and waits
  # for connections and doesn't die until the parent dies (or is
  # given a INT, QUIT, or TERM signal)
  def worker_loop(worker) #:nodoc:
    Client.setup
    init_worker_process(worker)
    nr = 0
    limit = worker_connections
    actor_exit = Case[:exit, Actor, Object]

    revactorize_listeners.each do |l,close,accept|
      Actor.spawn do
        Actor.current.trap_exit = true
        l.controller = l.instance_variable_set(:@receiver, Actor.current)
        begin
          while nr >= limit
            l.disable if l.enabled?
            logger.info "busy: clients=#{nr} >= limit=#{limit}"
            Actor.receive do |f|
              f.when(close) {}
              f.when(actor_exit) { nr -= 1 }
              f.after(0.01) {} # another listener could've gotten an exit
            end
          end

          l.enable unless l.enabled?
          Actor.receive do |f|
            f.when(close) {}
            f.when(actor_exit) { nr -= 1 }
            f.when(accept) do |_, _, s|
              nr += 1
              Actor.spawn_link(s) { |c| Client.new(c).process_loop }
            end
          end
        rescue => e
          Rainbows::Error.listen_loop(e)
        end while Rainbows.alive
        Actor.receive do |f|
          f.when(close) {}
          f.when(actor_exit) { nr -= 1 }
        end while nr > 0
      end
    end

    Actor.sleep 1 while Rainbows.tick || nr > 0
    rescue Errno::EMFILE
      # ignore, let another worker process take it
  end

  def revactorize_listeners #:nodoc:
    LISTENERS.map do |s|
      case s
      when TCPServer
        l = Revactor::TCP.listen(s, nil)
        [ l, T[:tcp_closed, Revactor::TCP::Socket],
          T[:tcp_connection, l, Revactor::TCP::Socket] ]
      when UNIXServer
        l = Revactor::UNIX.listen(s)
        [ l, T[:unix_closed, Revactor::UNIX::Socket ],
          T[:unix_connection, l, Revactor::UNIX::Socket] ]
      end
    end
  end
  # :startdoc:
end
rainbows-4.6.2/lib/rainbows/coolio_thread_spawn.rb0000644000004100000410000000220712354770160022347 0ustar  www-datawww-data# -*- encoding: binary -*-
# A combination of the Coolio and ThreadSpawn models.  This allows Ruby
# Thread-based concurrency for application processing.  It DOES NOT
# expose a streamable "rack.input" for upload processing within the
# app.  DevFdResponse should be used with this class to proxy
# asynchronous responses.  All network I/O between the client and
# server are handled by the main thread and outside of the core
# application dispatch.
#
# Unlike ThreadSpawn, Cool.io makes this model highly suitable for
# slow clients and applications with medium-to-slow response times
# (I/O bound), but less suitable for sleepy applications.
#
# This concurrency model is designed for Ruby 1.9, and Ruby 1.8
# users are NOT advised to use this due to high CPU usage.
#
# === RubyGem Requirements
# * cool.io 1.0.0 or later
module Rainbows::CoolioThreadSpawn
  include Rainbows::Coolio::Core
  autoload :Client, 'rainbows/coolio_thread_spawn/client'

  def init_worker_process(worker) # :nodoc:
    super
    master = Rainbows::Coolio::Master.new(Queue.new)
    master.attach(Coolio::Loop.default)
    Client.const_set(:MASTER, master)
  end
end
# :enddoc:
rainbows-4.6.2/lib/rainbows/rev_thread_pool.rb0000644000004100000410000000167112354770160021504 0ustar  www-datawww-data# -*- encoding: binary -*-
# :stopdoc:
Rainbows.const_set(:RevThreadPool, Rainbows::CoolioThreadPool)
# :startdoc:

# CoolioThreadPool is the new version of this, use that instead.
#
# A combination of the Rev and ThreadPool models.  This allows Ruby
# Thread-based concurrency for application processing.  It DOES NOT
# expose a streamable "rack.input" for upload processing within the
# app.  DevFdResponse should be used with this class to proxy
# asynchronous responses.  All network I/O between the client and
# server are handled by the main thread and outside of the core
# application dispatch.
#
# Unlike ThreadPool, Rev makes this model highly suitable for
# slow clients and applications with medium-to-slow response times
# (I/O bound), but less suitable for sleepy applications.
#
# This concurrency model is designed for Ruby 1.9, and Ruby 1.8
# users are NOT advised to use this due to high CPU usage.
module Rainbows::RevThreadPool; end
rainbows-4.6.2/lib/rainbows/xepoll_thread_spawn.rb0000644000004100000410000000344412354770160022372 0ustar  www-datawww-data# -*- encoding: binary -*-
require "thread"
require "sleepy_penguin"
require "raindrops"

# This is an edge-triggered epoll concurrency model with blocking
# accept() in a (hopefully) native thread.  This is comparable to
# ThreadSpawn and CoolioThreadSpawn, but is Linux-only and able to exploit
# "wake one" accept() behavior of a blocking accept() call when used
# with native threads.
#
# This supports streaming "rack.input" and allows +:pool_size+ tuning
# independently of +worker_connections+
#
# === Disadvantages
#
# This is only supported under Linux 2.6 and later kernels.
#
# === Compared to CoolioThreadSpawn
#
# This does not buffer outgoing responses in userspace at all, meaning
# it can lower response latency to fast clients and also prevent
# starvation of other clients when reading slow disks for responses
# (when combined with native threads).
#
# CoolioThreadSpawn is likely better for trickling large static files or
# proxying responses to slow clients, but this is likely better for fast
# clients.
#
# Unlikely CoolioThreadSpawn, this supports streaming "rack.input" which
# is useful for reading large uploads from fast clients.
#
# === Compared to ThreadSpawn
#
# This can maintain idle connections without the memory overhead of an
# idle Thread.  The cost of handling/dispatching active connections is
# exactly the same for an equivalent number of active connections.
#
# === RubyGem Requirements
#
# * raindrops 0.6.0 or later
# * sleepy_penguin 3.0.1 or later
module Rainbows::XEpollThreadSpawn
  # :stopdoc:
  include Rainbows::Base

  def init_worker_process(worker)
    super
    require "rainbows/xepoll_thread_spawn/client"
    Rainbows::Client.__send__ :include, Client
  end

  def worker_loop(worker) # :nodoc:
    init_worker_process(worker)
    Client.loop
  end
  # :startdoc:
end
rainbows-4.6.2/lib/rainbows/app_pool.rb0000644000004100000410000000732012354770160020136 0ustar  www-datawww-data# -*- encoding: binary -*-

require 'thread'

# Rack middleware to limit application-level concurrency independently
# of network conncurrency in \Rainbows!   Since the +worker_connections+
# option in \Rainbows! is only intended to limit the number of
# simultaneous clients, this middleware may be used to limit the
# number of concurrent application dispatches independently of
# concurrent clients.
#
# Instead of using M:N concurrency in \Rainbows!, this middleware
# allows M:N:P concurrency where +P+ is the AppPool +:size+ while
# +M+ remains the number of +worker_processes+ and +N+ remains the
# number of +worker_connections+.
#
#   rainbows master
#    \_ rainbows worker[0]
#    |  \_ client[0,0]------\      ___app[0]
#    |  \_ client[0,1]-------\    /___app[1]
#    |  \_ client[0,2]-------->--<       ...
#    |  ...                __/    `---app[P]
#    |  \_ client[0,N]----/
#    \_ rainbows worker[1]
#    |  \_ client[1,0]------\      ___app[0]
#    |  \_ client[1,1]-------\    /___app[1]
#    |  \_ client[1,2]-------->--<       ...
#    |  ...                __/    `---app[P]
#    |  \_ client[1,N]----/
#    \_ rainbows worker[M]
#       \_ client[M,0]------\      ___app[0]
#       \_ client[M,1]-------\    /___app[1]
#       \_ client[M,2]-------->--<       ...
#       ...                __/    `---app[P]
#       \_ client[M,N]----/
#
# AppPool should be used if you want to enforce a lower value of +P+
# than +N+.
#
# AppPool has no effect on the Rev or EventMachine concurrency models
# as those are single-threaded/single-instance as far as application
# concurrency goes.  In other words, +P+ is always +one+ when using
# Rev or EventMachine.  As of \Rainbows! 0.7.0, it is safe to use with
# Revactor and the new FiberSpawn and FiberPool concurrency models.
#
# Since this is Rack middleware, you may load this in your Rack
# config.ru file and even use it in threaded servers other than
# \Rainbows!
#
#   use Rainbows::AppPool, :size => 30
#   map "/lobster" do
#     run Rack::Lobster.new
#   end
#
# You may to load this earlier or later in your middleware chain
# depending on the concurrency/copy-friendliness of your middleware(s).
class Rainbows::AppPool < Struct.new(:pool, :re)

  # +opt+ is a hash, +:size+ is the size of the pool (default: 6)
  # meaning you can have up to 6 concurrent instances of +app+
  # within one \Rainbows! worker process.  We support various
  # methods of the +:copy+ option: +dup+, +clone+, +deep+ and +none+.
  # Depending on your +app+, one of these options should be set.
  # The default +:copy+ is +:dup+ as is commonly seen in existing
  # Rack middleware.
  def initialize(app, opt = {})
    self.pool = Queue.new
    (1...(opt[:size] || 6)).each do
      pool << case (opt[:copy] || :dup)
      when :none then app
      when :dup then app.dup
      when :clone then app.clone
      when :deep then Marshal.load(Marshal.dump(app)) # unlikely...
      else
        raise ArgumentError, "unsupported copy method: #{opt[:copy].inspect}"
      end
    end
    pool << app # the original
  end

  # Rack application endpoint, +env+ is the Rack environment
  def call(env) # :nodoc:

    # we have to do this check at call time (and not initialize)
    # because of preload_app=true and models being changeable with SIGHUP
    # fortunately this is safe for all the reentrant (but not multithreaded)
    # classes that depend on it and a safe no-op for multithreaded
    # concurrency models
    self.re ||= begin
      case env["rainbows.model"]
      when :FiberSpawn, :FiberPool, :Revactor, :NeverBlock,
           :RevFiberSpawn, :CoolioFiberSpawn
        self.pool = Rainbows::Fiber::Queue.new(pool)
      end
      true
    end

    app = pool.shift
    app.call(env)
    ensure
      pool << app
  end
end
rainbows-4.6.2/lib/rainbows/socket_proxy.rb0000644000004100000410000000102612354770160021053 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
module Rainbows::SocketProxy
  def kgio_addr
    to_io.kgio_addr
  end

  def kgio_read(size, buf = "")
    to_io.kgio_read(size, buf)
  end

  def kgio_read!(size, buf = "")
    to_io.kgio_read!(size, buf)
  end

  def kgio_trywrite(buf)
    to_io.kgio_trywrite(buf)
  end

  def kgio_tryread(size, buf = "")
    to_io.kgio_tryread(size, buf)
  end

  def kgio_wait_readable(timeout = nil)
    to_io.kgio_wait_readable(timeout)
  end

  def timed_read(buf)
    to_io.timed_read(buf)
  end
end
rainbows-4.6.2/lib/rainbows/reverse_proxy.rb0000644000004100000410000001402612354770160021242 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
require 'socket'
require 'thread'
require 'uri'
require 'kcar' # http://bogomips.org/kcar/ -- gem install kcar

# This is lightly tested and has an unstable configuration interface.
# ***** Do not rely on anything under the ReverseProxy namespace! *****
#
# A reverse proxy implementation for \Rainbows!  It is a Rack application
# compatible and optimized for most \Rainbows! concurrency models.
#
# It makes HTTP/1.0 connections without keepalive to backends, so
# it is only recommended for proxying to upstreams on the same LAN
# or machine.  It can proxy to TCP hosts as well as UNIX domain sockets.
#
# Currently it only does simple round-robin balancing and does not
# know to retry connections from failed backends.
#
# Buffering-behavior is currently dependent on the concurrency model selected:
#
# Fully-buffered (uploads and response bodies):
#    Coolio, EventMachine, NeverBlock, CoolioThreadSpawn, CoolioThreadPool
# If you're proxying to Unicorn, fully-buffered is the way to go.
#
# Buffered input only (uploads, but not response bodies):
#    ThreadSpawn, ThreadPool, FiberSpawn, FiberPool, CoolioFiberSpawn
#
# It is not recommended to use Base, WriterThreadSpawn or WriterThreadPool
# to host this application.  However, you may proxy to a backend running
# one of these concurrency models with a fully-buffering concurrency model.
#
# See the {example config}[link:examples/reverse_proxy.ru] for a sample
# configuration
#
# TODO: Revactor support
# TODO: Support HTTP trailers
# TODO: optional streaming input for synchronous
# TODO: error handling
#
# WARNING! this is only lightly tested and has no automated tests, yet!
class Rainbows::ReverseProxy
  autoload :MultiThread, 'rainbows/reverse_proxy/multi_thread'
  autoload :Synchronous, 'rainbows/reverse_proxy/synchronous'
  autoload :Coolio, 'rainbows/reverse_proxy/coolio'
  autoload :EventMachine, 'rainbows/reverse_proxy/event_machine'
  autoload :EvClient, 'rainbows/reverse_proxy/ev_client'

  HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR"
  REMOTE_ADDR = "REMOTE_ADDR"
  REQUEST_METHOD = "REQUEST_METHOD"
  REQUEST_URI = "REQUEST_URI"
  CRLF = "\r\n"
  TR = %w(_ -)
  CONTENT_LENGTH = "CONTENT_LENGTH"
  HTTP_TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING"
  RackInput = "rack.input"
  E502 = [ 502, [ %w(Content-Length 0), %w(Content-Type text/plain) ], [] ]

  def initialize(opts)
    @lock = Mutex.new
    upstreams = opts[:upstreams]
    @upstreams = []
    upstreams.each do |url|
      url, cfg = *url if Array === url
      if url =~ %r{\Ahttp://}
        uri = URI.parse(url)
        host = uri.host =~ %r{\A\[([a-fA-F0-9:]+)\]\z} ? $1 : uri.host
        sockaddr = Socket.sockaddr_in(uri.port, host)
      else
        path = url.gsub(%r{\Aunix:}, "") # nginx compat
        %r{\A~} =~ path and path = File.expand_path(path)
        sockaddr = Socket.sockaddr_un(path)
      end
      ((cfg && cfg[:weight]) || 1).times { @upstreams << sockaddr }
    end
    @nr = 0
  end

  # detects the concurrency model at first run and replaces itself
  def call(env)
    if @lock.try_lock
      case model = env["rainbows.model"]
      when :EventMachine, :NeverBlock
        extend(EventMachine)
      when :Coolio, :CoolioThreadPool, :CoolioThreadSpawn
        extend(Coolio)
      when :RevFiberSpawn, :Rev, :RevThreadPool, :RevThreadSpawn
        warn "#{model} is not *well* supported with #{self.class}"
        warn "Switch to #{model.to_s.gsub(/Rev/, 'Coolio')}!"
        extend(Synchronous)
      when :Revactor
        warn "Revactor is not *well* supported with #{self.class} yet"
        extend(Synchronous)
      when :FiberSpawn, :FiberPool, :CoolioFiberSpawn
        extend(Synchronous)
        Synchronous::UpstreamSocket.
          __send__(:include, Rainbows::Fiber::IO::Methods)
      when :WriterThreadSpawn, :WriterThreadPool
        warn "#{model} is not recommended for use with #{self.class}"
        extend(Synchronous)
      else
        extend(Synchronous)
      end
      extend(MultiThread) if env["rack.multithread"]
      @lock.unlock
    else
      @lock.synchronize {} # wait for the first locker to finish
    end
    call(env)
  end

  # returns request headers for sending to the upstream as a string
  def build_headers(env, input)
    remote_addr = env[REMOTE_ADDR]
    xff = env[HTTP_X_FORWARDED_FOR]
    xff = xff ? "#{xff},#{remote_addr}" : remote_addr
    req = "#{env[REQUEST_METHOD]} #{env[REQUEST_URI]} HTTP/1.0\r\n" \
          "Connection: close\r\n" \
          "X-Forwarded-For: #{xff}\r\n"
    uscore, dash = *TR
    env.each do |key, value|
      %r{\AHTTP_(\w+)\z} =~ key or next
      key = $1
      next if %r{\A(?:VERSION|CONNECTION|KEEP_ALIVE|X_FORWARDED_FOR)\z}x =~ key
      key.tr!(uscore, dash)
      req << "#{key}: #{value}\r\n"
    end
    input and req << (input.respond_to?(:size) ?
                     "Content-Length: #{input.size}\r\n" :
                     "Transfer-Encoding: chunked\r\n")
    req << CRLF
  end

  def pick_upstream(env) # +env+ is reserved for future expansion
    @nr += 1
    @upstreams[@nr %= @upstreams.size]
  end

  def prepare_input!(env)
    if cl = env[CONTENT_LENGTH]
      size = cl.to_i
      size > 0 or return
    elsif %r{\Achunked\z}i =~ env.delete(HTTP_TRANSFER_ENCODING)
      # do people use multiple transfer-encodings?
    else
      return
    end

    input = env[RackInput]
    if input.respond_to?(:rewind)
      if input.respond_to?(:size)
        input.size # TeeInput-specific behavior
        return input
      else
        return SizedInput.new(input, size)
      end
    end
    tmp = size && size < 0x4000 ? StringIO.new("") : Unicorn::TmpIO.new
    each_block(input) { |x| tmp.syswrite(x) }
    tmp.rewind
    tmp
  end

  class SizedInput
    attr_reader :size

    def initialize(input, n)
      buf = ""
      if n == nil
        n = 0
        while input.read(16384, buf)
          n += buf.size
        end
        input.rewind
      end
      @input, @size = input, n
    end

    def read(*args)
      @input.read(*args)
    end
  end

  class UpstreamSocket < Kgio::Socket
    alias readpartial kgio_read!
  end
end
rainbows-4.6.2/lib/rainbows/epoll/0000755000004100000410000000000012354770160017111 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/epoll/response_chunk_pipe.rb0000644000004100000410000000045612354770160023506 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
class Rainbows::Epoll::ResponseChunkPipe < Rainbows::Epoll::ResponsePipe
  def tryread
    @io or return

    case rv = super
    when String
      "#{rv.size.to_s(16)}\r\n#{rv}\r\n"
    when nil
      close
      "0\r\n\r\n"
    else
      rv
    end
  end
end
rainbows-4.6.2/lib/rainbows/epoll/client.rb0000644000004100000410000001546612354770160020730 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:

module Rainbows::Epoll::Client

  include Rainbows::EvCore
  APP = Rainbows.server.app
  Server = Rainbows::Epoll::Server
  IN = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ONESHOT
  OUT = SleepyPenguin::Epoll::OUT | SleepyPenguin::Epoll::ONESHOT
  EPINOUT = IN | OUT
  KATO = {}
  KATO.compare_by_identity if KATO.respond_to?(:compare_by_identity)
  Rainbows.at_quit { KATO.each_key { |k| k.timeout! }.clear }
  Rainbows.config!(self, :keepalive_timeout)
  EP = Rainbows::EP
  @@last_expire = Time.now

  def self.expire
    return if ((now = Time.now) - @@last_expire) < 1.0
    if (ot = KEEPALIVE_TIMEOUT) >= 0
      ot = now - ot
      KATO.delete_if { |client, time| time < ot and client.timeout! }
    end
    @@last_expire = now
  end

  def self.loop
    begin
      EP.wait(nil, 1000) { |_, obj| obj.epoll_run }
      expire
    rescue Errno::EINTR
    rescue => e
      Rainbows::Error.listen_loop(e)
    end while Rainbows.tick || Server.nr > 0
  end

  # only call this once
  def epoll_once
    @wr_queue = [] # may contain String, ResponsePipe, and StreamFile objects
    post_init
    on_readable
    rescue => e
      handle_error(e)
  end

  def on_readable
    case rv = kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, RBUF)
    when String
      on_read(rv)
      return if @wr_queue[0] || closed?
      return hijacked if @hp.hijacked?
    when :wait_readable
      KATO[self] = @@last_expire if :headers == @state
      return EP.set(self, IN)
    else
      break
    end until :close == @state
    close unless closed?
    rescue Errno::ECONNRESET
      close
    rescue IOError
  end

  def app_call input # called by on_read()
    @env[RACK_INPUT] = input
    @env[REMOTE_ADDR] = kgio_addr
    @hp.hijack_setup(@env, self)
    status, headers, body = APP.call(@env.merge!(RACK_DEFAULTS))
    return hijacked if @hp.hijacked?
    ev_write_response(status, headers, body, @hp.next?)
  end

  def write_response_path(status, headers, body, alive)
    io = body_to_io(body)
    st = io.stat

    if st.file?
      defer_file(status, headers, body, alive, io, st)
    elsif st.socket? || st.pipe?
      chunk = stream_response_headers(status, headers, alive, body)
      return hijacked if nil == chunk
      stream_response_body(body, io, chunk)
    else
      # char or block device... WTF?
      write_response(status, headers, body, alive)
    end
  end

  # used for streaming sockets and pipes
  def stream_response_body(body, io, chunk)
    pipe = (chunk ? Rainbows::Epoll::ResponseChunkPipe :
                    Rainbows::Epoll::ResponsePipe).new(io, self, body)
    return @wr_queue << pipe if @wr_queue[0]
    stream_pipe(pipe) or return
    @wr_queue[0] or @wr_queue << Z
  end

  def ev_write_response(status, headers, body, alive)
    @state = alive ? :headers : :close
    if body.respond_to?(:to_path)
      write_response_path(status, headers, body, alive)
    else
      write_response(status, headers, body, alive)
    end
    return hijacked if @hp.hijacked?
    # try to read more if we didn't have to buffer writes
    next_request if alive && 0 == @wr_queue.size
  end

  def hijacked
    KATO.delete(self)
    Server.decr # no other place to do this
    EP.delete(self)
    nil
  end

  def next_request
    if 0 == @buf.size
      want_more
    else
      # pipelined request (already in buffer)
      on_read(Z)
      return if @wr_queue[0] || closed?
      return hijacked if @hp.hijacked?
      close if :close == @state
    end
  end

  def epoll_run
    if @wr_queue[0]
      on_writable
    else
      KATO.delete self
      on_readable
    end
  end

  def want_more
    EP.set(self, EPINOUT)
  end

  def on_deferred_write_complete
    :close == @state and return close
    next_request
  end

  def handle_error(e)
    msg = Rainbows::Error.response(e) and kgio_trywrite(msg) rescue nil
    ensure
      close
  end

  def write_deferred(obj)
    Rainbows::StreamFile === obj ? stream_file(obj) : stream_pipe(obj)
  end

  # writes until our write buffer is empty or we block
  # returns true if we're done writing everything
  def on_writable
    obj = @wr_queue.shift

    case rv = String === obj ? kgio_trywrite(obj) : write_deferred(obj)
    when nil
      obj = @wr_queue.shift or return on_deferred_write_complete
    when String
      obj = rv # retry
    when :wait_writable # Strings and StreamFiles only
      @wr_queue.unshift(obj)
      EP.set(self, OUT)
      return
    when :deferred
      return
    end while true
    rescue => e
      handle_error(e)
  end

  def write(buf)
    unless @wr_queue[0]
      case rv = kgio_trywrite(buf)
      when nil
        return # all written
      when String
        buf = rv # retry
      when :wait_writable
        @wr_queue << buf.dup # >3-word 1.9 strings are copy-on-write
        return EP.set(self, OUT)
      end while true
    end
    @wr_queue << buf.dup # >3-word 1.9 strings are copy-on-write
  end

  def close
    @wr_queue.each { |x| x.respond_to?(:close) and x.close rescue nil }
    super
    on_close
  end

  def on_close
    KATO.delete(self)
    Server.decr
  end

  def timeout!
    shutdown
    true
  end

  # Rack apps should not hijack here, but they may...
  def defer_file(status, headers, body, alive, io, st)
    if r = sendfile_range(status, headers)
      status, headers, range = r
      write_headers(status, headers, alive, body) or return hijacked
      range and defer_file_stream(range[0], range[1], io, body)
    else
      write_headers(status, headers, alive, body) or return hijacked
      defer_file_stream(0, st.size, io, body)
    end
  end

  # returns +nil+ on EOF, :wait_writable if the client blocks
  def stream_file(sf) # +sf+ is a Rainbows::StreamFile object
    case n = trysendfile(sf, sf.offset, sf.count)
    when Integer
      sf.offset += n
      0 == (sf.count -= n) and return sf.close
    else
      return n # :wait_writable or nil
    end while true
    rescue
      sf.close
      raise
  end

  def defer_file_stream(offset, count, io, body)
    sf = Rainbows::StreamFile.new(offset, count, io, body)
    unless @wr_queue[0]
      stream_file(sf) or return
    end
    @wr_queue << sf
    EP.set(self, OUT)
  end

  # this alternates between a push and pull model from the pipe -> client
  # to avoid having too much data in userspace on either end.
  def stream_pipe(pipe)
    case buf = pipe.tryread
    when String
      write(buf)
      if @wr_queue[0]
        # client is blocked on write, client will pull from pipe later
        EP.delete pipe
        @wr_queue << pipe
        EP.set(self, OUT)
        return :deferred
      end
      # continue looping...
    when :wait_readable
      # pipe blocked on read, let the pipe push to the client in the future
      EP.delete self
      EP.set(pipe, IN)
      return :deferred
    else # nil => EOF
      return pipe.close # nil
    end while true
    rescue
      pipe.close
      raise
  end
end
rainbows-4.6.2/lib/rainbows/epoll/response_pipe.rb0000644000004100000410000000115112354770160022307 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
class Rainbows::Epoll::ResponsePipe
  attr_reader :io
  alias to_io io
  RBUF = Rainbows::EvCore::RBUF
  EP = Rainbows::EP

  def initialize(io, client, body)
    @io, @client, @body = io, client, body
  end

  def epoll_run
    return close if @client.closed?
    @client.stream_pipe(self) or @client.on_deferred_write_complete
    rescue => e
      close
      @client.handle_error(e)
  end

  def close
    @io or return
    EP.delete self
    @body.respond_to?(:close) and @body.close
    @io = @body = nil
  end

  def tryread
    Kgio.tryread(@io, 16384, RBUF)
  end
end
rainbows-4.6.2/lib/rainbows/epoll/server.rb0000644000004100000410000000136612354770160020752 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::Epoll::Server
  @@nr = 0
  IN = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ET
  MAX = Rainbows.server.worker_connections
  THRESH = MAX - 1
  LISTENERS = Rainbows::HttpServer::LISTENERS
  EP = Rainbows::EP

  def self.nr
    @@nr
  end

  # rearms all listeners when there's a free slot
  def self.decr
    THRESH == (@@nr -= 1) and LISTENERS.each { |sock| EP.set(sock, IN) }
  end

  def self.extended(sock)
    EP.set(sock, IN)
  end

  def epoll_run
    return EP.delete(self) if @@nr >= MAX
    while io = kgio_tryaccept
      @@nr += 1
      # there's a chance the client never even sees epoll for simple apps
      io.epoll_once
      return EP.delete(self) if @@nr >= MAX
    end
  end
end
rainbows-4.6.2/lib/rainbows/fiber_pool.rb0000644000004100000410000000266712354770160020456 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'rainbows/fiber'

# A Fiber-based concurrency model for Ruby 1.9.  This uses a pool of
# Fibers to handle client IO to run the application and the root Fiber
# for scheduling and connection acceptance.
#
# This concurrency model is difficult to use with existing applications,
# lacks third-party support, and is thus NOT recommended.
#
# The pool size is equal to the number of +worker_connections+.
# Compared to the ThreadPool model, Fibers are very cheap in terms of
# memory usage so you can have more active connections.  This model
# supports a streaming "rack.input" with lightweight concurrency.
# Applications are strongly advised to wrap all slow IO objects
# (sockets, pipes) using the Rainbows::Fiber::IO class whenever
# possible.
module Rainbows::FiberPool
  include Rainbows::Fiber::Base

  def worker_loop(worker) # :nodoc:
    init_worker_process(worker)
    pool = []
    worker_connections.times {
      Fiber.new {
        process(Fiber.yield) while pool << Fiber.current
      }.resume # resume to hit Fiber.yield so it waits on a client
    }
    Rainbows::Fiber::Base.setup(self.class, app)

    begin
      schedule do |l|
        fib = pool.pop or break # let another worker process take it
        if io = l.kgio_tryaccept
          fib.resume(io)
        else
          pool << fib
        end
      end
    rescue => e
      Rainbows::Error.listen_loop(e)
    end while Rainbows.cur_alive
  end
end
rainbows-4.6.2/lib/rainbows/rev_thread_spawn.rb0000644000004100000410000000164512354770160021664 0ustar  www-datawww-data# -*- encoding: binary -*-
Rainbows.const_set(:RevThreadSpawn, Rainbows::CoolioThreadSpawn)

# CoolioThreadPool is the new version of this, use that instead.
#
# A combination of the Rev and ThreadSpawn models.  This allows Ruby
# Thread-based concurrency for application processing.  It DOES NOT
# expose a streamable "rack.input" for upload processing within the
# app.  DevFdResponse should be used with this class to proxy
# asynchronous responses.  All network I/O between the client and
# server are handled by the main thread and outside of the core
# application dispatch.
#
# Unlike ThreadSpawn, Rev makes this model highly suitable for
# slow clients and applications with medium-to-slow response times
# (I/O bound), but less suitable for sleepy applications.
#
# This concurrency model is designed for Ruby 1.9, and Ruby 1.8
# users are NOT advised to use this due to high CPU usage.
module Rainbows::RevThreadSpawn; end
rainbows-4.6.2/lib/rainbows/ev_core.rb0000644000004100000410000001012112354770160017740 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# base module for evented models like Rev and EventMachine
module Rainbows::EvCore
  include Rainbows::Const
  include Rainbows::Response
  NULL_IO = Unicorn::HttpRequest::NULL_IO
  HttpParser = Rainbows::HttpParser
  autoload :CapInput, 'rainbows/ev_core/cap_input'
  RBUF = ""
  Z = "".freeze
  Rainbows.config!(self, :client_header_buffer_size)
  HTTP_VERSION = "HTTP_VERSION"

  # Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
  ASYNC_CALLBACK = "async.callback".freeze
  ASYNC_CLOSE = "async.close".freeze

  def write_async_response(response)
    status, headers, body = response
    if alive = @hp.next?
      # we can't do HTTP keepalive without Content-Length or
      # "Transfer-Encoding: chunked", and the async.callback stuff
      # isn't Rack::Lint-compatible, so we have to enforce it here.
      headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
      alive = headers.include?(Content_Length) ||
              !!(%r{\Achunked\z}i =~ headers[Transfer_Encoding])
    end
    @deferred = nil
    ev_write_response(status, headers, body, alive)
  end

  def post_init
    @hp = HttpParser.new
    @env = @hp.env
    @buf = @hp.buf
    @state = :headers # [ :body [ :trailers ] ] :app_call :close
  end

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

  def want_more
  end

  def handle_error(e)
    msg = Rainbows::Error.response(e) and write(msg)
    ensure
      quit
  end

  # returns whether to enable response chunking for autochunk models
  # returns nil if request was hijacked in response stage
  def stream_response_headers(status, headers, alive, body)
    headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
    if headers.include?(Content_Length)
      write_headers(status, headers, alive, body) or return
      return false
    end

    case @env[HTTP_VERSION]
    when "HTTP/1.0" # disable HTTP/1.0 keepalive to stream
      write_headers(status, headers, false, body) or return
      @hp.clear
      false
    when nil # "HTTP/0.9"
      false
    else
      rv = !!(headers[Transfer_Encoding] =~ %r{\Achunked\z}i)
      rv = false unless @env["rainbows.autochunk"]
      write_headers(status, headers, alive, body) or return
      rv
    end
  end

  def prepare_request_body
    # 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 = mkinput
    @hp.filter_body(@buf2 = "", @buf)
    @input << @buf2
    on_read(Z)
  end

  # TeeInput doesn't map too well to this right now...
  def on_read(data)
    case @state
    when :headers
      @hp.add_parse(data) or return want_more
      @state = :body
      if 0 == @hp.content_length
        app_call NULL_IO # common case
      else # nil or len > 0
        prepare_request_body
      end
    when :body
      if @hp.body_eof?
        if @hp.content_length
          @input.rewind
          app_call @input
        else
          @state = :trailers
          on_read(data)
        end
      elsif data.size > 0
        @hp.filter_body(@buf2, @buf << data)
        @input << @buf2
        on_read(Z)
      else
        want_more
      end
    when :trailers
      if @hp.add_parse(data)
        @input.rewind
        app_call @input
      else
        want_more
      end
    end
    rescue => e
      handle_error(e)
  end

  ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"

  def err_413(msg)
    write(ERROR_413_RESPONSE)
    quit
    # zip back up the stack
    raise IOError, msg, []
  end

  TmpIO = Unicorn::TmpIO
  CBB = Unicorn::TeeInput.client_body_buffer_size

  def io_for(bytes)
    bytes <= CBB ? StringIO.new("") : TmpIO.new
  end

  def mkinput
    max = Rainbows.server.client_max_body_size
    len = @hp.content_length
    if len
      if max && (len > max)
        err_413("Content-Length too big: #{len} > #{max}")
      end
      io_for(len)
    else
      max ? CapInput.new(io_for(max), self, max) : TmpIO.new
    end
  end
end
rainbows-4.6.2/lib/rainbows/client.rb0000644000004100000410000000031212354770160017575 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:

# this class is used for most synchronous concurrency models
class Rainbows::Client < Kgio::Socket
  include Rainbows::ProcessClient

  alias write kgio_write
end
rainbows-4.6.2/lib/rainbows/coolio.rb0000644000004100000410000000344712354770160017617 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'rainbows/coolio_support'

# Implements a basic single-threaded event model with
# {Cool.io}[http://coolio.github.com/].  It is capable of handling
# thousands of simultaneous client connections, but with only a
# single-threaded app dispatch.  It is suited for slow clients and
# fast applications (applications that do not have slow network
# dependencies) or applications that use DevFdResponse for deferrable
# response bodies.  It does not require your Rack application to be
# thread-safe, reentrancy is only required for the DevFdResponse body
# generator.
#
# Compatibility: Whatever Cool.io itself supports, currently Ruby
# 1.8/1.9.
#
# This model does not implement as streaming "rack.input" which
# allows the Rack application to process data as it arrives.  This
# means "rack.input" will be fully buffered in memory or to a
# temporary file before the application is entered.
#
# This model is mostly compatible with users of "async.callback" in
# the Rack environment as long as they do not depend on EventMachine.
#
# === RubyGem Requirements
# * cool.io 1.0.0 or later
module Rainbows::Coolio
  # :stopdoc:
  # keep-alive timeout scoreboard
  KATO = {}

  # all connected clients
  CONN = {}

  if {}.respond_to?(:compare_by_identity)
    CONN.compare_by_identity
    KATO.compare_by_identity
  end

  autoload :Client, 'rainbows/coolio/client'
  autoload :Master, 'rainbows/coolio/master'
  autoload :ThreadClient, 'rainbows/coolio/thread_client'
  autoload :ResponsePipe, 'rainbows/coolio/response_pipe'
  autoload :ResponseChunkPipe, 'rainbows/coolio/response_chunk_pipe'
  autoload :Heartbeat, 'rainbows/coolio/heartbeat'
  # :startdoc:
end
# :enddoc:
require 'rainbows/coolio/server'
require 'rainbows/coolio/core'
Rainbows::Coolio.__send__ :include, Rainbows::Coolio::Core
rainbows-4.6.2/lib/rainbows/fiber/0000755000004100000410000000000012354770160017065 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/fiber/coolio/0000755000004100000410000000000012354770160020351 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/fiber/coolio/methods.rb0000644000004100000410000000171612354770160022346 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::Fiber::Coolio::Methods
  class Watcher < Coolio::IOWatcher
    def initialize(fio, flag)
      @f = Fiber.current
      super(fio, flag)
      attach(Coolio::Loop.default)
    end

    def on_readable
      @f.resume
    end

    alias on_writable on_readable
  end

  def close
    @w.detach if defined?(@w) && @w.attached?
    @r.detach if defined?(@r) && @r.attached?
    super
  end

  def kgio_wait_writable
    @w = Watcher.new(self, :w) unless defined?(@w)
    @w.enable unless @w.enabled?
    Fiber.yield
    @w.disable
  end

  def kgio_wait_readable(timeout = nil)
    @r = Watcher.new(self, :r) unless defined?(@r)
    @r.enable unless @r.enabled?
    Fiber.yield
    @r.disable
  end
end

[
  Rainbows::Fiber::IO,
  # the next two trigger autoload, ugh, oh well...
  Rainbows::Fiber::IO::Socket,
  Rainbows::Fiber::IO::Pipe
].each do |klass|
  klass.__send__(:include, Rainbows::Fiber::Coolio::Methods)
end
rainbows-4.6.2/lib/rainbows/fiber/coolio/heartbeat.rb0000644000004100000410000000065312354770160022641 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::Fiber::Coolio::Heartbeat < Coolio::TimerWatcher
  # ZZ gets populated by read_expire in rainbows/fiber/io/methods
  ZZ = Rainbows::Fiber::ZZ
  def on_timer
    exit if (! Rainbows.tick && Rainbows.cur <= 0)
    now = Time.now
    fibs = []
    ZZ.delete_if { |fib, time| now >= time ? fibs << fib : ! fib.alive? }
    fibs.each { |fib| fib.resume if fib.alive? }
  end
end
rainbows-4.6.2/lib/rainbows/fiber/coolio/sleeper.rb0000644000004100000410000000041712354770160022337 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::Fiber::Coolio::Sleeper < Coolio::TimerWatcher

  def initialize(seconds)
    @f = Fiber.current
    super(seconds, false)
    attach(Coolio::Loop.default)
    Fiber.yield
  end

  def on_timer
    @f.resume
  end
end
rainbows-4.6.2/lib/rainbows/fiber/coolio/server.rb0000644000004100000410000000072012354770160022203 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::Fiber::Coolio::Server < Coolio::IOWatcher
  def to_io
    @io
  end

  def initialize(io)
    @io = io
    super(self, :r)
  end

  def close
    detach if attached?
    @io.close
  end

  def on_readable
    return if Rainbows.cur >= MAX
    c = @io.kgio_tryaccept and Fiber.new { process(c) }.resume
  end

  def process(io)
    Rainbows.cur += 1
    io.process_loop
  ensure
    Rainbows.cur -= 1
  end
end
rainbows-4.6.2/lib/rainbows/fiber/io/0000755000004100000410000000000012354770160017474 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/fiber/io/compat.rb0000644000004100000410000000040212354770160021300 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
# Used to make Rainbows::Fiber::IO behave like 0.97.0 and earlier
module Rainbows::Fiber::IO::Compat
  def initialize(io, fiber = Fiber.current)
    @to_io, @f = io, fiber
  end

  def close
    @to_io.close
  end
end
rainbows-4.6.2/lib/rainbows/fiber/io/methods.rb0000644000004100000410000000207212354770160021465 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:

# this is used to augment Kgio::Socket and Kgio::Pipe-enhanced classes
# for use with Rainbows!  Do no use this directly, see
# Rainbows::Fiber::IO::Pipe and Rainbows::Fiber::IO::Socket instead.
module Rainbows::Fiber::IO::Methods
  RD = Rainbows::Fiber::RD
  WR = Rainbows::Fiber::WR
  ZZ = Rainbows::Fiber::ZZ
  attr_accessor :f

  def read_expire
    ZZ[Fiber.current] = super
  end

  # for wrapping output response bodies
  def each
    if buf = kgio_read(16384)
      yield buf
      yield buf while kgio_read(16384, buf)
    end
    self
  end

  def close
    fd = fileno
    RD[fd] = WR[fd] = nil
    super
  end

  def kgio_wait_readable(timeout = nil)
    fd = fileno
    @f = Fiber.current
    RD[fd] = self
    Fiber.yield
    ZZ.delete @f
    RD[fd] = nil
  end

  def kgio_wait_writable
    fd = fileno
    @f = Fiber.current
    WR[fd] = self
    Fiber.yield
    WR[fd] = nil
  end

  def self.included(klass)
    if klass.method_defined?(:kgio_write)
      klass.__send__(:alias_method, :write, :kgio_write)
    end
  end
end
rainbows-4.6.2/lib/rainbows/fiber/io/socket.rb0000644000004100000410000000105512354770160021312 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
# A Fiber-aware Socket class, gives users the illusion of a synchronous
# interface that yields away from the current Fiber whenever
# the underlying descriptor is blocked on reads or write.
#
# It's not recommended to use any of this in your applications
# unless you're willing to accept breakage.  Most of this is very
# difficult-to-use, fragile and we don't have much time to devote to
# supporting these in the future.
class Rainbows::Fiber::IO::Socket < Kgio::Socket
  include Rainbows::Fiber::IO::Methods
end
rainbows-4.6.2/lib/rainbows/fiber/io/pipe.rb0000644000004100000410000000104712354770160020760 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
# A Fiber-aware Pipe class, gives users the illusion of a synchronous
# interface that yields away from the current Fiber whenever
# the underlying descriptor is blocked on reads or write.
#
# It's not recommended to use any of this in your applications
# unless you're willing to accept breakage.  Most of this is very
# difficult-to-use, fragile and we don't have much time to devote to
# supporting these in the future.
class Rainbows::Fiber::IO::Pipe < Kgio::Pipe
  include Rainbows::Fiber::IO::Methods
end
rainbows-4.6.2/lib/rainbows/fiber/coolio.rb0000644000004100000410000000055212354770160020700 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
require 'rainbows/coolio_support'
require 'rainbows/fiber'
require 'rainbows/fiber/io'

module Rainbows::Fiber::Coolio
  autoload :Heartbeat, 'rainbows/fiber/coolio/heartbeat'
  autoload :Server, 'rainbows/fiber/coolio/server'
  autoload :Sleeper, 'rainbows/fiber/coolio/sleeper'
end
require 'rainbows/fiber/coolio/methods'
rainbows-4.6.2/lib/rainbows/fiber/base.rb0000644000004100000410000000367312354770160020335 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
require 'rainbows/fiber/io'

module Rainbows::Fiber::Base

  include Rainbows::Base

  # :stopdoc:
  RD = Rainbows::Fiber::RD
  WR = Rainbows::Fiber::WR
  ZZ = Rainbows::Fiber::ZZ
  # :startdoc:

  # the scheduler method that powers both FiberSpawn and FiberPool
  # concurrency models.  It times out idle clients and attempts to
  # schedules ones that were blocked on I/O.  At most it'll sleep
  # for one second (returned by the schedule_sleepers method) which
  # will cause it.
  def schedule
    begin
      Rainbows.tick
      t = schedule_sleepers
      ret = select(RD.compact.concat(LISTENERS), WR.compact, nil, t)
    rescue Errno::EINTR
      retry
    rescue Errno::EBADF, TypeError
      LISTENERS.compact!
      raise
    end or return

    # active writers first, then readers
    ret[1].concat(RD.compact & ret[0]).each { |c| c.f.resume }

    # accept is an expensive syscall, filter out listeners we don't want
    (ret[0] & LISTENERS).each { |x| yield x }
  end

  # wakes up any sleepers or keepalive-timeout violators that need to be
  # woken and returns an interval to IO.select on
  def schedule_sleepers
    max = nil
    now = Time.now
    fibs = []
    ZZ.delete_if { |fib, time|
      if now >= time
        fibs << fib
      else
        max = time
        false
      end
    }
    fibs.each { |fib| fib.resume }

    max_sleep = 1.0 # wake up semi-frequently to prevent SIGKILL from master
    if max
      max -= Time.now
      return 0 if max < 0.0
      return max_sleep if max > max_sleep
      max
    else
      max_sleep
    end
  end

  def process(client)
    Rainbows.cur += 1
    client.process_loop
  ensure
    Rainbows.cur -= 1
    ZZ.delete(client.f)
  end

  def self.setup(klass, app)
    Rainbows::Client.__send__(:include, Rainbows::Fiber::IO::Methods)
    require 'rainbows/fiber/body'
    Rainbows::Client.__send__(:include, Rainbows::Fiber::Body)
    self.const_set(:APP, app)
  end
end
rainbows-4.6.2/lib/rainbows/fiber/body.rb0000644000004100000410000000162012354770160020346 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# non-portable body handling for Fiber-based concurrency goes here
# this module is required and included in worker processes only
# this is meant to be included _after_ Rainbows::Response::Body
module Rainbows::Fiber::Body # :nodoc:

  # the sendfile 1.1.0+ gem includes IO#trysendfile
  if IO.method_defined?(:trysendfile)
    def write_body_file(body, range)
      sock, n, body = to_io, nil, body_to_io(body)
      offset, count = range ? range : [ 0, body.stat.size ]
      case n = sock.trysendfile(body, offset, count)
      when Integer
        offset += n
        return if 0 == (count -= n)
      when :wait_writable
        kgio_wait_writable
      else # nil
        return
      end while true
      ensure
        close_if_private(body)
    end
  end

  def self.included(klass)
    klass.__send__ :alias_method, :write_body_stream, :write_body_each
  end
end
rainbows-4.6.2/lib/rainbows/fiber/io.rb0000644000004100000410000000512312354770160020022 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# A \Fiber-aware IO class, gives users the illusion of a synchronous
# interface that yields away from the current \Fiber whenever
# the underlying descriptor is blocked on reads or write
#
# It's not recommended to use any of this in your applications
# unless you're willing to accept breakage.  Most of this is very
# difficult-to-use, fragile and we don't have much time to devote to
# supporting these in the future.
#
# This is a stable, legacy interface and should be preserved for all
# future versions of Rainbows!  However, new apps should use
# Rainbows::Fiber::IO::Socket or Rainbows::Fiber::IO::Pipe instead
# (or better yet, avoid any of the Rainbows::Fiber* stuff).
class Rainbows::Fiber::IO
  attr_accessor :to_io

  # :stopdoc:
  # see Rainbows::Fiber::IO::Compat for initialize implementation
  class << self
    alias :[] :new
  end
  # :startdoc:

  # no longer used internally within Rainbows!, only for compatibility
  def write_nonblock(buf)
    @to_io.write_nonblock(buf)
  end

  def kgio_addr
    @to_io.kgio_addr
  end

  # for wrapping output response bodies
  def each
    buf = readpartial(16384)
    yield buf
    yield buf while readpartial(16384, buf)
    rescue EOFError
      self
  end

  def closed?
    @to_io.closed?
  end

  def fileno
    @to_io.fileno
  end

  def write(buf)
    case rv = Kgio.trywrite(@to_io, buf)
    when String
      buf = rv
    when :wait_writable
      kgio_wait_writable
    end until nil == rv
  end

  # used for reading headers (respecting keepalive_timeout)
  def timed_read(buf)
    expire = nil
    case rv = Kgio.tryread(@to_io, 16384, buf)
    when :wait_readable
      return if expire && expire < Time.now
      expire ||= read_expire
      kgio_wait_readable
    else
      return rv
    end while true
  end

  def readpartial(length, buf = "")
    case rv = Kgio.tryread(@to_io, length, buf)
    when nil
      raise EOFError, "end of file reached", []
    when :wait_readable
      kgio_wait_readable
    else
      return rv
    end while true
  end

  def kgio_read(*args)
    @to_io.kgio_read(*args)
  end

  def kgio_read!(*args)
    @to_io.kgio_read!(*args)
  end

  def kgio_trywrite(*args)
    @to_io.kgio_trywrite(*args)
  end

  autoload :Socket, 'rainbows/fiber/io/socket'
  autoload :Pipe, 'rainbows/fiber/io/pipe'
end

# :stopdoc:
require 'rainbows/fiber/io/methods'
require 'rainbows/fiber/io/compat'
class Rainbows::Fiber::IO
  include Rainbows::Fiber::IO::Compat
  include Rainbows::Fiber::IO::Methods
  alias_method :wait_readable, :kgio_wait_readable
  alias_method :wait_writable, :kgio_wait_writable
end
rainbows-4.6.2/lib/rainbows/fiber/queue.rb0000644000004100000410000000147312354770160020543 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
# a self-sufficient Queue implementation for Fiber-based concurrency
# models.  This requires no external scheduler, so it may be used with
# Revactor as well as FiberSpawn and FiberPool.
class Rainbows::Fiber::Queue < Struct.new(:queue, :waiters)
  def initialize(queue = [], waiters = [])
    # move elements of the Queue into an Array
    if queue.class.name == "Queue"
      queue = queue.length.times.map { queue.pop }
    end
    super queue, waiters
  end

  def shift
    # ah the joys of not having to deal with race conditions
    if queue.empty?
      waiters << Fiber.current
      Fiber.yield
    end
    queue.shift
  end

  def <<(obj)
    queue << obj
    blocked = waiters.shift and blocked.resume
    queue # not quite 100% compatible but no-one's looking :>
  end
end
rainbows-4.6.2/lib/rainbows/thread_spawn.rb0000644000004100000410000000320712354770160021004 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'thread'

# Spawns a new thread for every client connection we accept().  This
# model is recommended for platforms like Ruby (MRI) 1.8 where spawning
# new threads is inexpensive, but still seems to work well enough with
# good native threading implementations such as NPTL under Linux on
# Ruby (MRI/YARV) 1.9
#
# This model should provide a high level of compatibility with all Ruby
# implementations, and most libraries and applications.  Applications
# running under this model should be thread-safe but not necessarily
# reentrant.
#
# If you're using green threads (MRI 1.8) and need to perform DNS lookups,
# consider using the "resolv-replace" library which replaces parts of the
# core Socket package with concurrent DNS lookup capabilities.

module Rainbows::ThreadSpawn
  include Rainbows::Base
  include Rainbows::WorkerYield

  def accept_loop(klass) #:nodoc:
    lock = Mutex.new
    limit = worker_connections
    nr = 0
    LISTENERS.each do |l|
      klass.new do
        begin
          if lock.synchronize { nr >= limit }
            worker_yield
          elsif client = l.kgio_accept
            klass.new(client) do |c|
              begin
                lock.synchronize { nr += 1 }
                c.process_loop
              ensure
                lock.synchronize { nr -= 1 }
              end
            end
          end
        rescue => e
          Rainbows::Error.listen_loop(e)
        end while Rainbows.alive
      end
    end
    sleep 1 while Rainbows.tick || lock.synchronize { nr > 0 }
  end

  def worker_loop(worker) #:nodoc:
    init_worker_process(worker)
    accept_loop(Thread)
  end
end
rainbows-4.6.2/lib/rainbows/version.rb0000644000004100000410000000005412354770160020007 0ustar  www-datawww-dataRainbows::Const::RAINBOWS_VERSION = '4.6.2'
rainbows-4.6.2/lib/rainbows/fiber.rb0000644000004100000410000000225412354770160017415 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
begin
  require 'fiber'
rescue LoadError
  defined?(NeverBlock) or raise
end

# core namespace for all things that use Fibers in \Rainbows!
#
# It's generally not recommended to use any of this in your applications
# unless you're willing to accept breakage.  Most of this is very
# difficult-to-use, fragile and we don't have much time to devote to
# supporting these in the future.
module Rainbows::Fiber

  # blocked readers (key: fileno, value: Rainbows::Fiber::IO object)
  RD = []

  # blocked writers (key: fileno, value: Rainbows::Fiber::IO object)
  WR = []

  # sleeping fibers go here (key: Fiber object, value: wakeup time)
  ZZ = {}

  # puts the current Fiber into uninterruptible sleep for at least
  # +seconds+.  Unlike Kernel#sleep, this it is not possible to sleep
  # indefinitely to be woken up (nobody wants that in a web server,
  # right?).  Calling this directly is deprecated, use
  # Rainbows.sleep(seconds) instead.
  def self.sleep(seconds)
    ZZ[Fiber.current] = Time.now + seconds
    Fiber.yield
  end

  autoload :Base, 'rainbows/fiber/base'
  autoload :Queue, 'rainbows/fiber/queue'
  autoload :IO, 'rainbows/fiber/io'
end
rainbows-4.6.2/lib/rainbows/http_server.rb0000644000004100000410000000617212354770160020676 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:

class Rainbows::HttpServer < Unicorn::HttpServer
  attr_accessor :copy_stream
  attr_accessor :worker_connections
  attr_accessor :keepalive_timeout
  attr_accessor :client_header_buffer_size
  attr_accessor :client_max_body_size
  attr_reader :use

  def self.setup(block)
    Rainbows.server.instance_eval(&block)
  end

  def initialize(app, options)
    Rainbows.server = self
    @logger = Unicorn::Configurator::DEFAULTS[:logger]
    super(app, options)
    defined?(@use) or self.use = Rainbows::Base
    @worker_connections ||= 50
    @worker_connections = 1 if @use == :Base
  end

  # Add one second to the timeout since our fchmod heartbeat is less
  # precise (and must be more conservative) than Unicorn does.  We
  # handle many clients per process and can't chmod on every
  # connection we accept without wasting cycles.  That added to the
  # fact that we let clients keep idle connections open for long
  # periods of time means we have to chmod at a fixed interval.
  def timeout=(nr)
    @timeout = nr + 1
  end

  def load_config!
    super
    @worker_connections = 1 if @use == :Base
  end

  def worker_loop(worker)
    Rainbows.forked = true
    orig = method(:worker_loop)
    extend(Rainbows.const_get(@use))
    m = method(:worker_loop)
    orig == m ? super(worker) : worker_loop(worker)
  end

  def spawn_missing_workers
    # 5: std{in,out,err} + heartbeat FD + per-process listener
    nofile = 5 + @worker_connections + LISTENERS.size
    trysetrlimit(:RLIMIT_NOFILE, nofile)

    case @use
    when :ThreadSpawn, :ThreadPool, :ActorSpawn,
         :CoolioThreadSpawn, :RevThreadSpawn,
         :XEpollThreadSpawn, :WriterThreadPool, :WriterThreadSpawn
      trysetrlimit(:RLIMIT_NPROC, @worker_connections + LISTENERS.size + 1)
    when :XEpollThreadPool, :CoolioThreadPool
      trysetrlimit(:RLIMIT_NPROC, Rainbows::O[:pool_size] + LISTENERS.size + 1)
    end
    super
  end

  def trysetrlimit(resource, want)
    var = Process.const_get(resource)
    cur, max = Process.getrlimit(var)
    cur <= want and Process.setrlimit(var, cur = max > want ? max : want)
    if cur == want
      @logger.warn "#{resource} rlim_cur=#{cur} is barely enough"
      @logger.warn "#{svc} may monopolize resources dictated by #{resource}" \
                   " and leave none for your app"
    end
    rescue => e
      @logger.error e.message
      @logger.error "#{resource} needs to be increased to >=#{want} before" \
                    " starting #{svc}"
  end

  def svc
    File.basename($0)
  end

  def use=(mod)
    @use = mod.to_s.split(/::/)[-1].to_sym
    new_defaults = {
      'rainbows.model' => @use,
      'rack.multithread' => !!(mod.to_s =~ /Thread/),
      'rainbows.autochunk' => [:Coolio,:Rev,:Epoll,:XEpoll,
                               :EventMachine,:NeverBlock].include?(@use),
    }
    Rainbows::Const::RACK_DEFAULTS.update(new_defaults)
  end

  def keepalive_requests=(nr)
    Unicorn::HttpRequest.keepalive_requests = nr
  end

  def keepalive_requests
    Unicorn::HttpRequest.keepalive_requests
  end

  def client_max_header_size=(bytes)
    Unicorn::HttpParser.max_header_len = bytes
  end
end
rainbows-4.6.2/lib/rainbows/writer_thread_pool/0000755000004100000410000000000012354770160021672 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/writer_thread_pool/client.rb0000644000004100000410000000325712354770160023504 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# used to wrap a BasicSocket to use with +q+ for all writes
# this is compatible with IO.select
class Rainbows::WriterThreadPool::Client < Struct.new(:to_io, :q)
  include Rainbows::SocketProxy
  include Rainbows::ProcessClient

  module Methods
    def write_body_each(body)
      return if @hp.hijacked?
      q << [ to_io, :write_body_each, body ]
    end

    def write_response_close(status, headers, body, alive)
      to_io.instance_variable_set(:@hp, @hp) # XXX ugh
      return if @hp.hijacked?
      Rainbows::SyncClose.new(body) { |sync_body|
        q << [ to_io, :write_response, status, headers, sync_body, alive ]
      }
    end

    if Rainbows::Response::COPY_STREAM || IO.method_defined?(:trysendfile)
      def write_response(status, headers, body, alive)
        if body.respond_to?(:close)
          write_response_close(status, headers, body, alive)
        elsif body.respond_to?(:to_path)
          write_response_path(status, headers, body, alive)
        else
          super
        end
      end

      def write_body_file(body, range)
        q << [ to_io, :write_body_file, body, range ]
      end

      def write_body_stream(body)
        q << [ to_io, :write_body_stream, body ]
      end
    else # each-only body response
      def write_response(status, headers, body, alive)
        if body.respond_to?(:close)
          write_response_close(status, headers, body, alive)
        else
          super
        end
      end
    end # each-only body response
  end # module Methods
  include Methods

  def write(buf)
    q << [ to_io, buf ]
  end

  def close
    q << [ to_io, :close ]
  end

  def closed?
    to_io.closed?
  end
end
rainbows-4.6.2/lib/rainbows/sendfile.rb0000644000004100000410000000517712354770160020126 0ustar  www-datawww-data# -*- encoding: binary -*-
# This middleware handles X-\Sendfile headers generated by applications
# or middlewares down the stack.  It should be placed at the top
# (outermost layer) of the middleware stack to avoid having its
# +to_path+ method clobbered by another middleware.
#
# This converts X-\Sendfile responses to bodies which respond to the
# +to_path+ method which allows certain concurrency models to serve
# efficiently using sendfile() or similar.  With multithreaded models
# under Ruby 1.9, IO.copy_stream will be used.
#
# This middleware is the opposite of Rack::Sendfile as it
# reverses the effect of Rack:::Sendfile.  Unlike many Ruby
# web servers, some configurations of \Rainbows! are capable of
# serving static files efficiently.
#
# === Compatibility (via IO.copy_stream in Ruby 1.9):
# * ThreadSpawn
# * ThreadPool
# * WriterThreadPool
# * WriterThreadSpawn
#
# === Compatibility (Ruby 1.8 and 1.9)
# * EventMachine
# * NeverBlock (using EventMachine)
#
# DO NOT use this middleware if you're proxying to \Rainbows! with a
# server that understands X-\Sendfile (e.g. Apache, Lighttpd) natively.
#
# This does NOT understand X-Accel-Redirect headers intended for nginx.
# X-Accel-Redirect requires the application to be highly coupled with
# the corresponding nginx configuration, and is thus too complicated to
# be worth supporting.
#
# Example config.ru:
#
#    use Rainbows::Sendfile
#    run lambda { |env|
#      path = "#{Dir.pwd}/random_blob"
#      [ 200,
#        {
#          'X-Sendfile' => path,
#          'Content-Type' => 'application/octet-stream'
#        },
#        []
#      ]
#    }

class Rainbows::Sendfile < Struct.new(:app)

  # Body wrapper, this allows us to fall back gracefully to
  # +each+ in case a given concurrency model does not optimize
  # +to_path+ calls.
  class Body < Struct.new(:to_path) # :nodoc: all
    CONTENT_LENGTH = 'Content-Length'.freeze

    def self.new(path, headers)
      unless headers[CONTENT_LENGTH]
        stat = File.stat(path)
        headers[CONTENT_LENGTH] = stat.size.to_s if stat.file?
      end
      super(path)
    end

    # fallback in case our +to_path+ doesn't get handled for whatever reason
    def each
      buf = ''
      File.open(to_path) do |fp|
        yield buf while fp.read(0x4000, buf)
      end
    end
  end

  # :stopdoc:
  X_SENDFILE = 'X-Sendfile'
  # :startdoc:

  def call(env) # :nodoc:
    status, headers, body = app.call(env)
    headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
    if path = headers.delete(X_SENDFILE)
      body = Body.new(path, headers) unless body.respond_to?(:to_path)
    end
    [ status, headers, body ]
  end
end
rainbows-4.6.2/lib/rainbows/rev.rb0000644000004100000410000000172312354770160017122 0ustar  www-datawww-data# -*- encoding: binary -*-
Rainbows.const_set(:Rev, Rainbows::Coolio)
# Coolio is the new version of this, use that instead.
#
# Implements a basic single-threaded event model with Rev.
# It is capable of handling
# thousands of simultaneous client connections, but with only a
# single-threaded app dispatch.  It is suited for slow clients and
# fast applications (applications that do not have slow network
# dependencies) or applications that use DevFdResponse for deferrable
# response bodies.  It does not require your Rack application to be
# thread-safe, reentrancy is only required for the DevFdResponse body
# generator.
#
# Compatibility: Whatever \Rev itself supports, currently Ruby
# 1.8/1.9.
#
# This model does not implement as streaming "rack.input" which
# allows the Rack application to process data as it arrives.  This
# means "rack.input" will be fully buffered in memory or to a
# temporary file before the application is entered.
module Rainbows::Rev; end
rainbows-4.6.2/lib/rainbows/ev_core/0000755000004100000410000000000012354770160017420 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/ev_core/cap_input.rb0000644000004100000410000000074012354770160021730 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::EvCore::CapInput

  def initialize(io, client, max)
    @io, @client, @bytes_left = io, client, max
  end

  def <<(buf)
    if (@bytes_left -= buf.size) < 0
      @io.close
      @client.err_413("chunked request body too big")
    end
    @io << buf
  end

  def gets; @io.gets; end
  def each; @io.each { |x| yield x }; end
  def size; @io.size; end
  def rewind; @io.rewind; end
  def read(*args); @io.read(*args); end
end
rainbows-4.6.2/lib/rainbows/join_threads.rb0000644000004100000410000000103212354770160020770 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# This module only gets loaded on shutdown
module Rainbows::JoinThreads

  # blocking acceptor threads must be forced to run
  def self.acceptors(threads)
    expire = Time.now + Rainbows.server.timeout
    threads.delete_if do |thr|
      Rainbows.tick
      begin
        # blocking accept() may not wake up properly
        thr.raise(Errno::EINTR) if Time.now > expire && thr.stop?

        thr.run
        thr.join(0.01)
      rescue
        true
      end
    end until threads.empty?
  end
end
rainbows-4.6.2/lib/rainbows/max_body.rb0000644000004100000410000000542012354770160020126 0ustar  www-datawww-data# -*- encoding: binary -*-

# Middleware used to enforce client_max_body_size for TeeInput users.
#
# There is no need to configure this middleware manually, it will
# automatically be configured for you based on the client_max_body_size
# setting.
#
# For more fine-grained control, you may also define it per-endpoint in
# your Rack config.ru like this:
#
#        map "/limit_1M" do
#          use Rainbows::MaxBody, 1024*1024
#          run MyApp
#        end
#        map "/limit_10M" do
#          use Rainbows::MaxBody, 1024*1024*10
#          run MyApp
#        end
#
# This is only compatible with concurrency models that expose a streaming
# "rack.input" to the Rack application.  Thus it is NOT compatible with
# any of the following as they fully buffer the request body before
# the application dispatch:
#
# * :Coolio
# * :CoolioThreadPool
# * :CoolioThreadSpawn
# * :Epoll
# * :EventMachine
# * :NeverBlock
# * :Rev
# * :RevThreadPool
# * :RevThreadSpawn
# * :XEpoll
#
# However, the global Rainbows::Configurator#client_max_body_size is compatible
# with all concurrency models \Rainbows! supports.
class Rainbows::MaxBody

  # This is automatically called when used with Rack::Builder#use
  def initialize(app, limit = nil)
    case limit
    when Integer, nil
    else
      raise ArgumentError, "limit not an Integer"
    end
    @app, @limit = app, limit
  end

  # :stopdoc:
  RACK_INPUT = "rack.input".freeze
  CONTENT_LENGTH = "CONTENT_LENGTH"
  HTTP_TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING"

  # our main Rack middleware endpoint
  def call(env)
    @limit = Rainbows.server.client_max_body_size if nil == @limit
    catch(:rainbows_EFBIG) do
      len = env[CONTENT_LENGTH]
      if len && len.to_i > @limit
        return err
      elsif /\Achunked\z/i =~ env[HTTP_TRANSFER_ENCODING]
        limit_input!(env)
      end
      @app.call(env)
    end || err
  end

  # this is called after forking, so it won't ever affect the master
  # if it's reconfigured
  def self.setup # :nodoc:
    Rainbows.server.client_max_body_size or return
    case Rainbows.server.use
    when :Rev, :Coolio, :EventMachine, :NeverBlock,
         :RevThreadSpawn, :RevThreadPool,
         :CoolioThreadSpawn, :CoolioThreadPool,
         :Epoll, :XEpoll
      return
    end

    # force ourselves to the outermost middleware layer
    Rainbows.server.app = self.new(Rainbows.server.app)
  end

  # Rack response returned when there's an error
  def err # :nodoc:
    [ 413, { 'Content-Length' => '0', 'Content-Type' => 'text/plain' }, [] ]
  end

  def limit_input!(env)
    input = env[RACK_INPUT]
    klass = input.respond_to?(:rewind) ? RewindableWrapper : Wrapper
    env[RACK_INPUT] = klass.new(input, @limit)
  end

  # :startdoc:
end
require 'rainbows/max_body/wrapper'
require 'rainbows/max_body/rewindable_wrapper'
rainbows-4.6.2/lib/rainbows/coolio_fiber_spawn.rb0000644000004100000410000000226512354770160022173 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'rainbows/fiber/coolio'

# A combination of the Coolio and FiberSpawn models.
#
# This concurrency model is difficult to use with existing applications,
# lacks third-party support, and is thus NOT recommended.
#
# This allows Ruby 1.9 Fiber-based concurrency for application
# processing while exposing a synchronous execution model and using
# scalable network concurrency provided by Cool.io.  A streaming
# "rack.input" is exposed.  Applications are strongly advised to wrap
# all slow IO objects (sockets, pipes) using the Rainbows::Fiber::IO or
# a Cool.io-compatible class whenever possible.
module Rainbows::CoolioFiberSpawn

  include Rainbows::Base
  include Rainbows::Fiber::Coolio

  def worker_loop(worker) # :nodoc:
    Rainbows::Response.setup
    init_worker_process(worker)
    Server.const_set(:MAX, @worker_connections)
    Rainbows::Fiber::Base.setup(Server, nil)
    Server.const_set(:APP, Rainbows.server.app)
    Heartbeat.new(1, true).attach(Coolio::Loop.default)
    LISTENERS.map! { |s| Server.new(s).attach(Coolio::Loop.default) }
    Rainbows::Client.__send__ :include, Rainbows::Fiber::Coolio::Methods
    Coolio::Loop.default.run
  end
end
rainbows-4.6.2/lib/rainbows/coolio_thread_spawn/0000755000004100000410000000000012354770160022021 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/coolio_thread_spawn/client.rb0000644000004100000410000000037112354770160023625 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::CoolioThreadSpawn::Client < Rainbows::Coolio::ThreadClient
  # MASTER will be set in worker_loop
  def app_dispatch
    Thread.new(self) { |client| MASTER << [ client, app_response ] }
  end
end
rainbows-4.6.2/lib/rainbows/stream_file.rb0000644000004100000410000000117512354770160020621 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:

# Used to keep track of file offsets in IO#trysendfile + evented
# models.  We always maintain our own file offsets in userspace because
# because sendfile() implementations offer pread()-like idempotency for
# concurrency (multiple clients can read the same underlying file handle).
class Rainbows::StreamFile
  attr_reader :to_io
  attr_accessor :offset, :count

  def initialize(offset, count, io, body)
    @offset, @count, @to_io, @body = offset, count, io, body
  end

  def close
    @body.close if @body.respond_to?(:close)
    @to_io.close unless @to_io.closed?
    @to_io = nil
  end
end
rainbows-4.6.2/lib/rainbows/coolio_support.rb0000644000004100000410000000034712354770160021407 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
begin
  require "coolio"
  Coolio::VERSION >= "1.0.0" or abort "cool.io >= 1.0.0 is required"
rescue LoadError
  require "rev"
  Rev::VERSION >= "0.3.0" or abort "rev >= 0.3.0 is required"
end
rainbows-4.6.2/lib/rainbows/xepoll_thread_pool.rb0000644000004100000410000000507412354770160022214 0ustar  www-datawww-data# -*- encoding: binary -*-
require "thread"
require "sleepy_penguin"
require "raindrops"

# This is an edge-triggered epoll concurrency model with blocking
# accept() in a (hopefully) native thread.  This is comparable to
# ThreadPool and CoolioThreadPool, but is Linux-only and able to exploit
# "wake one" accept() behavior of a blocking accept() call when used
# with native threads.
#
# This supports streaming "rack.input" and allows +:pool_size+ tuning
# independently of +worker_connections+
#
# === Disadvantages
#
# This is only supported under Linux 2.6 and later kernels.
#
# === Compared to CoolioThreadPool
#
# This does not buffer outgoing responses in userspace at all, meaning
# it can lower response latency to fast clients and also prevent
# starvation of other clients when reading slow disks for responses
# (when combined with native threads).
#
# CoolioThreadPool is likely better for trickling large static files or
# proxying responses to slow clients, but this is likely better for fast
# clients.
#
# Unlikely CoolioThreadPool, this supports streaming "rack.input" which
# is useful for reading large uploads from fast clients.
#
# This exposes no special API or extensions on top of Rack.
#
# === Compared to ThreadPool
#
# This can maintain idle connections without the memory overhead of an
# idle Thread.  The cost of handling/dispatching active connections is
# exactly the same for an equivalent number of active connections
# (but independently tunable).
#
# === :pool_size vs worker_connections
#
# Since +:pool_size+ and +worker_connections+ are independently tunable,
# it is possible to get into situations where active connections need
# to wait for an idle thread in the thread pool before being processed
#
# In your Rainbows! config block, you may specify a Thread pool size
# to limit your application concurrency independently of
# worker_connections.
#
#   Rainbows! do
#     use :XEpollThreadPool, :pool_size => 50
#     worker_connections 100
#   end
#
# In extremely rare cases, this may be combined with Rainbows::AppPool
# if you have different concurrency capabilities for different parts of
# your Rack application.
#
# === RubyGem Requirements
#
# * raindrops 0.6.0 or later
# * sleepy_penguin 3.0.1 or later
module Rainbows::XEpollThreadPool
  extend Rainbows::PoolSize

  # :stopdoc:
  include Rainbows::Base

  def init_worker_process(worker)
    super
    require "rainbows/xepoll_thread_pool/client"
    Rainbows::Client.__send__ :include, Client
  end

  def worker_loop(worker) # :nodoc:
    init_worker_process(worker)
    Client.loop
  end
  # :startdoc:
end
rainbows-4.6.2/lib/rainbows/stream_response_epoll/0000755000004100000410000000000012354770160022402 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/stream_response_epoll/client.rb0000644000004100000410000000260012354770160024203 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::StreamResponseEpoll::Client
  OUT = SleepyPenguin::Epoll::OUT
  N = Raindrops.new(1)
  EP = SleepyPenguin::Epoll.new
  timeout = Rainbows.server.timeout
  thr = Thread.new do
    begin
      EP.wait(nil, timeout) { |_,client| client.epoll_run }
    rescue Errno::EINTR
    rescue => e
      Rainbows::Error.listen_loop(e)
    end while Rainbows.alive || N[0] > 0
  end
  Rainbows.at_quit { thr.join(timeout) }

  attr_reader :to_io

  def initialize(io, unwritten)
    @finish = false
    @to_io = io
    @wr_queue = [ unwritten.dup ]
    EP.set(self, OUT)
  end

  def write(str)
    @wr_queue << str.dup
  end

  def close
    @finish = true
  end

  def hijack(hijack)
    @finish = hijack
  end

  def epoll_run
    return if @to_io.closed?
    buf = @wr_queue.shift or return on_write_complete
    case rv = @to_io.kgio_trywrite(buf)
    when nil
      buf = @wr_queue.shift or return on_write_complete
    when String # retry, socket buffer may grow
      buf = rv
    when :wait_writable
      return @wr_queue.unshift(buf)
    end while true
    rescue => err
      @to_io.close
      N.decr(0, 1)
  end

  def on_write_complete
    if true == @finish
      @to_io.shutdown
      @to_io.close
      N.decr(0, 1)
    elsif @finish.respond_to?(:call) # hijacked
      EP.delete(self)
      N.decr(0, 1)
      @finish.call(@to_io)
    end
  end
end
rainbows-4.6.2/lib/rainbows/pool_size.rb0000644000004100000410000000053312354770160020327 0ustar  www-datawww-data# -*- encoding: binary -*-
# :stopdoc:
module Rainbows::PoolSize
  DEFAULTS = {
    :pool_size => 50, # same as the default worker_connections
  }

  def setup
    o = Rainbows::O
    DEFAULTS.each { |k,v| o[k] ||= v }
    Integer === o[:pool_size] && o[:pool_size] > 0 or
      raise ArgumentError, "pool_size must a be an Integer > 0"
  end
end
rainbows-4.6.2/lib/rainbows/writer_thread_spawn/0000755000004100000410000000000012354770160022051 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/writer_thread_spawn/client.rb0000644000004100000410000000502212354770160023653 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# used to wrap a BasicSocket to use with +q+ for all writes
# this is compatible with IO.select
class Rainbows::WriterThreadSpawn::Client < Struct.new(:to_io, :q, :thr)
  include Rainbows::SocketProxy
  include Rainbows::ProcessClient
  include Rainbows::WorkerYield

  CUR = {} # :nodoc:

  module Methods
    def write_body_each(body)
      q << [ :write_body_each, body ]
    end

    def write_response_close(status, headers, body, alive)
      to_io.instance_variable_set(:@hp, @hp) # XXX ugh
      Rainbows::SyncClose.new(body) { |sync_body|
        q << [ :write_response, status, headers, sync_body, alive ]
      }
    end

    if Rainbows::Response::COPY_STREAM || IO.method_defined?(:trysendfile)
      def write_response(status, headers, body, alive)
        self.q ||= queue_writer
        if body.respond_to?(:close)
          write_response_close(status, headers, body, alive)
        elsif body.respond_to?(:to_path)
          write_response_path(status, headers, body, alive)
        else
          super
        end
      end

      def write_body_file(body, range)
        q << [ :write_body_file, body, range ]
      end

      def write_body_stream(body)
        q << [ :write_body_stream, body ]
      end
    else # each-only body response
      def write_response(status, headers, body, alive)
        self.q ||= queue_writer
        if body.respond_to?(:close)
          write_response_close(status, headers, body, alive)
        else
          super
        end
      end
    end # each-only body response
  end # module Methods
  include Methods

  def self.quit
    CUR.delete_if do |t,q|
      q << nil
      Rainbows.tick
      t.alive? ? t.join(0.01) : true
    end until CUR.empty?
  end

  def queue_writer
    until CUR.size < MAX
      CUR.delete_if { |t,_|
        t.alive? ? t.join(0) : true
      }.size >= MAX and worker_yield
    end

    q = Queue.new
    self.thr = Thread.new(to_io) do |io|
      while op = q.shift
        begin
          op, *rest = op
          case op
          when String
            io.kgio_write(op)
          when :close
            io.close unless io.closed?
            break
          else
            io.__send__ op, *rest
          end
        rescue => e
          Rainbows::Error.write(io, e)
        end
      end
      CUR.delete(Thread.current)
    end
    CUR[thr] = q
  end

  def write(buf)
    (self.q ||= queue_writer) << buf
  end

  def close
    if q
      q << :close
    else
      to_io.close
    end
  end

  def closed?
    to_io.closed?
  end
end
rainbows-4.6.2/lib/rainbows/queue_pool.rb0000644000004100000410000000120012354770160020471 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
require 'thread'

# Thread pool class based on pulling off a single Ruby Queue.
# This is NOT used for the ThreadPool class, since that class does not
# need a userspace Queue.
class Rainbows::QueuePool
  attr_reader :queue

  def initialize(size = 20)
    q = Queue.new
    @threads = (1..size).map do
      Thread.new do
        while job = q.shift
          yield job
        end
      end
    end
    @queue = q
  end

  def quit!
    @threads.each { |_| @queue << nil }
    @threads.delete_if do |t|
      Rainbows.tick
      t.alive? ? t.join(0.01) : true
    end until @threads.empty?
  end
end
rainbows-4.6.2/lib/rainbows/http_parser.rb0000644000004100000410000000032612354770160020657 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# avoid modifying Unicorn::HttpParser
class Rainbows::HttpParser < Unicorn::HttpParser
  def self.quit
    alias_method :next?, :never!
  end

  def never!
    false
  end
end
rainbows-4.6.2/lib/rainbows/stream_response_epoll.rb0000644000004100000410000000653112354770160022734 0ustar  www-datawww-data# -*- encoding: binary -*-
require "sleepy_penguin"
require "raindrops"

# Like Unicorn itself, this concurrency model is only intended for use
# behind nginx and completely unsupported otherwise.  Even further from
# Unicorn, this isn't even a good idea with normal LAN clients, only nginx!
#
# It does NOT require a thread-safe Rack application at any point, but
# allows streaming data asynchronously via nginx (using the
# "X-Accel-Buffering: no" header to disable buffering).
#
# Unlike Rainbows::Base, this does NOT support persistent
# connections or pipelining.  All \Rainbows! specific configuration
# options are ignored (except Rainbows::Configurator#use).
#
# === RubyGem Requirements
#
# * raindrops 0.6.0 or later
# * sleepy_penguin 3.0.1 or later
module Rainbows::StreamResponseEpoll
  # :stopdoc:
  CODES = Unicorn::HttpResponse::CODES
  HEADER_END = "X-Accel-Buffering: no\r\n\r\n"
  autoload :Client, "rainbows/stream_response_epoll/client"

  def http_response_write(socket, status, headers, body)
    status = CODES[status.to_i] || status
    hijack = ep_client = false

    if headers
      # don't set extra headers here, this is only intended for
      # consuming by nginx.
      buf = "HTTP/1.0 #{status}\r\nStatus: #{status}\r\n"
      headers.each do |key, value|
        case key
        when "rack.hijack"
          hijack = hijack_prepare(value)
          body = nil # ensure we do not close body
        else
          if /\n/ =~ value
            # avoiding blank, key-only cookies with /\n+/
            buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
          else
            buf << "#{key}: #{value}\r\n"
          end
        end
      end
      buf << HEADER_END

      case rv = socket.kgio_trywrite(buf)
      when nil then break
      when String # retry, socket buffer may grow
        buf = rv
      when :wait_writable
        ep_client = Client.new(socket, buf)
        if hijack
          ep_client.hijack(hijack)
        else
          body.each { |chunk| ep_client.write(chunk) }
          ep_client.close
        end
        # body is nil on hijack, in which case ep_client is never closed by us
        return
      end while true
    end

    if hijack
      hijack.call(socket)
      return
    end

    body.each do |chunk|
      if ep_client
        ep_client.write(chunk)
      else
        case rv = socket.kgio_trywrite(chunk)
        when nil then break
        when String # retry, socket buffer may grow
          chunk = rv
        when :wait_writable
          ep_client = Client.new(socket, chunk)
          break
        end while true
      end
    end
  ensure
    return if hijack
    body.respond_to?(:close) and body.close
    if ep_client
      ep_client.close
    else
      socket.shutdown
      socket.close
    end
  end

  # once a client is accepted, it is processed in its entirety here
  # in 3 easy steps: read request, call app, write app response
  def process_client(client)
    status, headers, body = @app.call(env = @request.read(client))

    if 100 == status.to_i
      client.write(Unicorn::Const::EXPECT_100_RESPONSE)
      env.delete(Unicorn::Const::HTTP_EXPECT)
      status, headers, body = @app.call(env)
    end
    @request.headers? or headers = nil
    return if @request.hijacked?
    http_response_write(client, status, headers, body)
  rescue => e
    handle_error(client, e)
  end

  # :startdoc:
end
rainbows-4.6.2/lib/rainbows/worker_yield.rb0000644000004100000410000000117712354770160021030 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::WorkerYield

  # Sleep if we're busy (and let other threads run).  Another less busy
  # worker process may take it for us if we sleep. This is gross but
  # other options still suck because they require expensive/complicated
  # synchronization primitives for _every_ case, not just this unlikely
  # one.  Since this case is (or should be) uncommon, just busy wait
  # when we have to.  We don't use Thread.pass because it needlessly
  # spins the CPU during I/O wait, CPU cycles that can be better used by
  # other worker _processes_.
  def worker_yield
    sleep(0.01)
  end
end
rainbows-4.6.2/lib/rainbows/epoll.rb0000644000004100000410000000325712354770160017445 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'sleepy_penguin'
require 'sendfile'

# Edge-triggered epoll concurrency model using
# {sleepy_penguin}[http://bogomips.org/sleepy_penguin/] for epoll.
#
# Unlike more portable options like Coolio and EventMachine, this
# is Linux-only, but uses edge-triggering instead of level-triggering,
# so it may perform better in some cases.  Coolio and EventMachine have
# better library support and may be widely-used, however.
#
# Consider using XEpoll instead of this if you are using Ruby 1.9,
# it will avoid accept()-scalability issues with many worker processes.
#
# When serving static files, this is extremely unfair and optimized
# for throughput at the expense of fairness.  This is not an issue
# if you're not serving static files, or if your working set is
# small enough to always be in your kernel page cache.  This concurrency
# model may starve clients if you have slow disks and large static files.
#
# Do not use this if you have slow external dependencies.
#
# === RubyGem Requirements
#
# * raindrops 0.6.0 or later
# * sleepy_penguin 3.0.1 or later
# * sendfile 1.1.0 or later
#
module Rainbows::Epoll
  # :stopdoc:
  include Rainbows::Base
  autoload :Server, 'rainbows/epoll/server'
  autoload :Client, 'rainbows/epoll/client'
  autoload :ResponsePipe, 'rainbows/epoll/response_pipe'
  autoload :ResponseChunkPipe, 'rainbows/epoll/response_chunk_pipe'

  def init_worker_process(worker)
    super
    Rainbows.const_set(:EP, SleepyPenguin::Epoll.new)
    Rainbows::Client.__send__ :include, Client
    LISTENERS.each { |io| io.extend(Server) }
  end

  def worker_loop(worker) # :nodoc:
    init_worker_process(worker)
    Client.loop
  end
  # :startdoc:
end
rainbows-4.6.2/lib/rainbows/writer_thread_pool.rb0000644000004100000410000000356512354770160022230 0ustar  www-datawww-data# -*- encoding: binary -*-

# This concurrency model implements a single-threaded app dispatch
# with a separate thread pool for writing responses.
#
# Unlike most \Rainbows! concurrency models, WriterThreadPool is
# designed to run behind nginx just like Unicorn is.  This concurrency
# model may be useful for existing Unicorn users looking for more
# output concurrency than socket buffers can provide while still
# maintaining a single-threaded application dispatch (though if the
# response body is dynamically generated, it must be thread safe).
#
# For serving large or streaming responses, using more threads (via
# the +worker_connections+ setting) and setting "proxy_buffering off"
# in nginx is recommended.  If your application does not handle
# uploads, then using any HTTP-aware proxy like haproxy is fine.
# Using a non-HTTP-aware proxy will leave you vulnerable to
# slow client denial-of-service attacks.
module Rainbows::WriterThreadPool
  # :stopdoc:
  include Rainbows::Base
  autoload :Client, 'rainbows/writer_thread_pool/client'

  @@nr = 0
  @@q = nil

  def process_client(client) # :nodoc:
    @@nr += 1
    Client.new(client, @@q[@@nr %= @@q.size]).process_loop
  end

  def worker_loop(worker) # :nodoc:
    # we have multiple, single-thread queues since we don't want to
    # interleave writes from the same client
    qp = (1..worker_connections).map do |n|
      Rainbows::QueuePool.new(1) do |response|
        begin
          io, arg, *rest = response
          case arg
          when String
            io.kgio_write(arg)
          when :close
            io.close unless io.closed?
          else
            io.__send__(arg, *rest)
          end
        rescue => err
          Rainbows::Error.write(io, err)
        end
      end
    end

    @@q = qp.map { |q| q.queue }
    super(worker) # accept loop from Unicorn
    qp.each { |q| q.quit! }
  end
  # :startdoc:
end
rainbows-4.6.2/lib/rainbows/base.rb0000644000004100000410000000356212354770160017243 0ustar  www-datawww-data# -*- encoding: binary -*-

# base class for \Rainbows! concurrency models, this is currently used by
# ThreadSpawn and ThreadPool models.  Base is also its own
# (non-)concurrency model which is basically Unicorn-with-keepalive, and
# not intended for production use, as keepalive with a pure prefork
# concurrency model is extremely expensive.
module Rainbows::Base
  # :stopdoc:

  def sig_receiver(worker)
    begin
      worker.to_io.kgio_wait_readable
      worker.kgio_tryaccept # Unicorn::Worker#kgio_tryaccept
    rescue => e
      Rainbows.alive or return
      Unicorn.log_error(Rainbows.server.logger, "signal receiver", e)
    end while true
  end

  # this method is called by all current concurrency models
  def init_worker_process(worker) # :nodoc:
    readers = super(worker)
    Rainbows::Response.setup
    Rainbows::MaxBody.setup
    Rainbows.worker = worker

    # spawn Threads since Logger takes a mutex by default and
    # we can't safely lock a mutex in a signal handler
    trap(:USR1) { Thread.new { reopen_worker_logs(worker.nr) } }
    trap(:QUIT) { Thread.new { Rainbows.quit! } }
    [:TERM, :INT].each { |sig| trap(sig) { exit!(0) } } # instant shutdown
    Rainbows::ProcessClient.const_set(:APP, Rainbows.server.app)
    Thread.new { sig_receiver(worker) }
    logger.info "Rainbows! #@use worker_connections=#@worker_connections"
    Rainbows.readers = readers # for Rainbows.quit
    readers # unicorn 4.8+ needs this
  end

  def process_client(client)
    client.process_loop
  end

  def self.included(klass) # :nodoc:
    klass.const_set :LISTENERS, Rainbows::HttpServer::LISTENERS
  end

  def reopen_worker_logs(worker_nr)
    logger.info "worker=#{worker_nr} reopening logs..."
    Unicorn::Util.reopen_logs
    logger.info "worker=#{worker_nr} done reopening logs"
    rescue
      Rainbows.quit! # let the master reopen and refork us
  end
  # :startdoc:
end
rainbows-4.6.2/lib/rainbows/thread_timeout.rb0000644000004100000410000001672012354770160021346 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'thread'

# Soft timeout middleware for thread-based concurrency models in \Rainbows!
# This timeout only includes application dispatch, and will not take into
# account the (rare) response bodies that are dynamically generated while
# they are being written out to the client.
#
# In your rackup config file (config.ru), the following line will
# cause execution to timeout in 1.5 seconds.
#
#    use Rainbows::ThreadTimeout, :timeout => 1.5
#    run MyApplication.new
#
# You may also specify a threshold, so the timeout does not take
# effect until there are enough active clients.  It does not make
# sense to set a +:threshold+ higher or equal to the
# +worker_connections+ \Rainbows! configuration parameter.
# You may specify a negative threshold to be an absolute
# value relative to the +worker_connections+ parameter, thus
# if you specify a threshold of -1, and have 100 worker_connections,
# ThreadTimeout will only activate when there are 99 active requests.
#
#    use Rainbows::ThreadTimeout, :timeout => 1.5, :threshold => -1
#    run MyApplication.new
#
# This middleware only affects elements below it in the stack, so
# it can be configured to ignore certain endpoints or middlewares.
#
# Timed-out requests will cause this middleware to return with a
# "408 Request Timeout" response.
#
# == Caveats
#
# Badly-written C extensions may not be timed out.  Audit and fix
# (or remove) those extensions before relying on this module.
#
# Do NOT, under any circumstances nest and load this in
# the same middleware stack.  You may load this in parallel in the
# same process completely independent middleware stacks, but DO NOT
# load this twice so it nests.  Things will break!
#
# This will behave badly if system time is changed since Ruby
# does not expose a monotonic clock for users, so don't change
# the system time while this is running.  All servers should be
# running ntpd anyways.
#
# "ensure" clauses may not fire properly or be interrupted during
# execution, so do not mix this module with code which relies on "ensure".
# (This is also true for the "Timeout" module in the Ruby standard library)
#
# "recursive locking" ThreadError exceptions may occur if
# ThreadTimeout fires while a Mutex is locked (because "ensure"
# clauses may not fire properly).

class Rainbows::ThreadTimeout

  # :stopdoc:
  #
  # we subclass Exception to get rid of normal StandardError rescues
  # in app-level code.  timeout.rb does something similar
  ExecutionExpired = Class.new(Exception)

  # The MRI 1.8 won't be usable in January 2038, we'll raise this
  # when we eventually drop support for 1.8 (before 2038, hopefully)
  NEVER = Time.at(0x7fffffff)

  def initialize(app, opts)
    # @timeout must be Numeric since we add this to Time
    @timeout = opts[:timeout]
    Numeric === @timeout or
      raise TypeError, "timeout=#{@timeout.inspect} is not numeric"

    if @threshold = opts[:threshold]
      Integer === @threshold or
        raise TypeError, "threshold=#{@threshold.inspect} is not an integer"
      @threshold == 0 and raise ArgumentError, "threshold=0 does not make sense"
      @threshold < 0 and @threshold += Rainbows.server.worker_connections
    end
    @app = app

    # This is the main datastructure for communicating Threads eligible
    # for expiration to the watchdog thread.  If the eligible thread
    # completes its job before its expiration time, it will delete itself
    # @active.  If the watchdog thread notices the thread is timed out,
    # the watchdog thread will delete the thread from this hash as it
    # raises the exception.
    #
    # key: Thread to be timed out
    # value: Time of expiration
    @active = {}

    # Protects all access to @active.  It is important since it also limits
    # safe points for asynchronously raising exceptions.
    @lock = Mutex.new

    # There is one long-running watchdog thread that watches @active and
    # kills threads that have been running too long
    # see start_watchdog
    @watchdog = nil
  end

  # entry point for Rack middleware
  def call(env)
    # Once we have this lock, we ensure two things:
    # 1) there is only one watchdog thread started
    # 2) we can't be killed once we have this lock, it's unlikely
    #    to happen unless @timeout is really low and the machine
    #    is very slow.
    @lock.lock

    # we're dead if anything in the next two lines raises, but it's
    # highly unlikely that they will, and anything such as NoMemoryError
    # is hopeless and we might as well just die anyways.
    # initialize guarantees @timeout will be Numeric
    start_watchdog(env) unless @watchdog
    @active[Thread.current] = Time.now + @timeout

    begin
      # It is important to unlock inside this begin block
      # Mutex#unlock really can't fail here since we did a successful
      # Mutex#lock before
      @lock.unlock

      # Once the Mutex was unlocked, we're open to Thread#raise from
      # the watchdog process.  This is the main place we expect to receive
      # Thread#raise.  @app is of course the next layer of the Rack
      # application stack
      @app.call(env)
    ensure
      # I's still possible to receive a Thread#raise here from
      # the watchdog, but that's alright, the "rescue ExecutionExpired"
      # line will catch that.
      @lock.synchronize { @active.delete(Thread.current) }
      # Thread#raise no longer possible here
    end
    rescue ExecutionExpired
      # If we got here, it's because the watchdog thread raised an exception
      # here to kill us.  The watchdog uses @active.delete_if with a lock,
      # so we guaranteed it's
      [ 408, { 'Content-Type' => 'text/plain', 'Content-Length' => '0' }, [] ]
  end

  # The watchdog thread is the one that does the job of killing threads
  # that have expired.
  def start_watchdog(env)
    @watchdog = Thread.new(env["rack.logger"]) do |logger|
      begin
        if @threshold
          # Hash#size is atomic in MRI 1.8 and 1.9 and we
          # expect that from other implementations.
          #
          # Even without a memory barrier, sleep(@timeout) vs
          # sleep(@timeout - time-for-SMP-to-synchronize-a-word)
          # is too trivial to worry about here.
          sleep(@timeout) while @active.size < @threshold
        end

        next_expiry = NEVER

        # We always lock access to @active, so we can't kill threads
        # that are about to release themselves from the eye of the
        # watchdog thread.
        @lock.synchronize do
          now = Time.now
          @active.delete_if do |thread, expire_at|
            # We also use this loop to get the maximum possible time to
            # sleep for if we're not killing the thread.
            if expire_at > now
              next_expiry = expire_at if next_expiry > expire_at
              false
            else
              # Terminate execution and delete this from the @active
              thread.raise(ExecutionExpired)
              true
            end
          end
        end

        # We always try to sleep as long as possible to avoid consuming
        # resources from the app.  So that's the user-configured @timeout
        # value.
        if next_expiry == NEVER
          sleep(@timeout)
        else
          # sleep until the next known thread is about to expire.
          sec = next_expiry - Time.now
          sec > 0.0 ? sleep(sec) : Thread.pass # give other threads a chance
        end
      rescue => e
        # just in case
        logger.error e
      end while true # we run this forever
    end
  end
  # :startdoc:
end
rainbows-4.6.2/lib/rainbows/server_token.rb0000644000004100000410000000205412354770160021032 0ustar  www-datawww-data# -*- encoding: binary -*-
module Rainbows

# An optional middleware to proudly display your usage of \Rainbows! in
# the "Server:" response header.  This means you can help tell the world
# you're using \Rainbows! and spread fun and joy all over the Internet!
#
#    ------ in your config.ru ------
#    require 'rainbows/server_token'
#    require 'rack/lobster'
#    use Rainbows::ServerToken
#    run Rack::Lobster.new
#
# If you're nervous about the exact version of \Rainbows! you're running,
# then you can actually specify anything you want:
#
#    use Rainbows::ServerToken, "netcat 1.0"
#

class ServerToken < Struct.new(:app, :token)

  # :stopdoc:
  #
  # Freeze constants as they're slightly faster when setting hashes
  SERVER = "Server".freeze

  def initialize(app, token = Const::RACK_DEFAULTS['SERVER_SOFTWARE'])
    super
  end

  def call(env)
    status, headers, body = app.call(env)
    headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
    headers[SERVER] = token
    [ status, headers, body ]
  end
  # :startdoc:
end
end
rainbows-4.6.2/lib/rainbows/error.rb0000644000004100000410000000216012354770160017453 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::Error

  # if we get any error, try to write something back to the client
  # assuming we haven't closed the socket, but don't get hung up
  # if the socket is already closed or broken.  We'll always ensure
  # the socket is closed at the end of this function
  def self.write(io, e)
    msg = response(e) and Kgio.trywrite(io, msg)
    rescue
  end

  def self.app(e)
    Unicorn.log_error(Rainbows.server.logger, "app error", e)
    rescue
  end

  def self.listen_loop(e)
    Rainbows.alive or return
    Unicorn.log_error(Rainbows.server.logger, "listen loop error", e)
    rescue
  end

  def self.response(e)
    case e
    when EOFError, Errno::ECONNRESET, Errno::EPIPE, Errno::EINVAL,
         Errno::EBADF, Errno::ENOTCONN, Errno::ETIMEDOUT, Errno::EHOSTUNREACH
      # swallow error if client shuts down one end or disconnects
    when Unicorn::HttpParserError
      Rainbows::Const::ERROR_400_RESPONSE # try to tell the client they're bad
    when IOError # HttpParserError is an IOError
    else
      app(e)
      Rainbows::Const::ERROR_500_RESPONSE
    end
  end
end
rainbows-4.6.2/lib/rainbows/fiber_spawn.rb0000644000004100000410000000201712354770160020622 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'rainbows/fiber'

# Simple Fiber-based concurrency model for 1.9.  This spawns a new Fiber
# for every incoming client connection and the root Fiber for scheduling
# and connection acceptance.
#
# This concurrency model is difficult to use with existing applications,
# lacks third-party support, and is thus NOT recommended.
#
# This exports a streaming "rack.input" with lightweight concurrency.
# Applications are strongly advised to wrap all slow IO objects
# (sockets, pipes) using the Rainbows::Fiber::IO class whenever
# possible.
module Rainbows::FiberSpawn
  include Rainbows::Fiber::Base

  def worker_loop(worker) # :nodoc:
    init_worker_process(worker)
    Rainbows::Fiber::Base.setup(self.class, app)
    limit = worker_connections

    begin
      schedule do |l|
        break if Rainbows.cur >= limit
        io = l.kgio_tryaccept or next
        Fiber.new { process(io) }.resume
      end
    rescue => e
      Rainbows::Error.listen_loop(e)
    end while Rainbows.cur_alive
  end
end
rainbows-4.6.2/lib/rainbows/sync_close.rb0000644000004100000410000000113212354770160020461 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
require 'thread'
class Rainbows::SyncClose
  def initialize(body)
    @body = body
    @mutex = Mutex.new
    @cv = ConditionVariable.new
    @mutex.synchronize do
      yield self
      @cv.wait(@mutex)
    end
  end

  def respond_to?(m)
    @body.respond_to?(m)
  end

  def to_path
    @body.to_path
  end

  def each
    @body.each { |x| yield x }
  end

  def to_io
    @body.to_io
  end

  # called by the writer thread to wake up the original thread (in #initialize)
  def close
    @body.close
    ensure
      @mutex.synchronize { @cv.signal }
  end
end
rainbows-4.6.2/lib/rainbows/thread_pool.rb0000644000004100000410000000414512354770160020627 0ustar  www-datawww-data# -*- encoding: binary -*-

# Implements a worker thread pool model.  This is suited for platforms
# like Ruby 1.9, where the cost of dynamically spawning a new thread for
# every new client connection is higher than with the ThreadSpawn model,
# but the cost of an idle thread is low (e.g. NPTL under Linux).
#
# This model should provide a high level of compatibility with all Ruby
# implementations, and most libraries and applications.  Applications
# running under this model should be thread-safe but not necessarily
# reentrant.
#
# Applications using this model are required to be thread-safe.  Threads
# are never spawned dynamically under this model.
#
# If you're using green threads (MRI 1.8) and need to perform DNS lookups,
# consider using the "resolv-replace" library which replaces parts of the
# core Socket package with concurrent DNS lookup capabilities.
module Rainbows::ThreadPool
  include Rainbows::Base

  def worker_loop(worker) # :nodoc:
    init_worker_process(worker)
    pool = (1..worker_connections).map do
      Thread.new { LISTENERS.size == 1 ? sync_worker : async_worker }
    end

    while Rainbows.alive
      # if any worker dies, something is serious wrong, bail
      pool.each do |thr|
        Rainbows.tick or break
        thr.join(1) and Rainbows.quit!
      end
    end
    Rainbows::JoinThreads.acceptors(pool)
  end

  def sync_worker # :nodoc:
    s = LISTENERS[0]
    begin
      c = s.kgio_accept and c.process_loop
    rescue => e
      Rainbows::Error.listen_loop(e)
    end while Rainbows.alive
  end

  def async_worker # :nodoc:
    begin
      # TODO: check if select() or accept() is a problem on large
      # SMP systems under Ruby 1.9.  Hundreds of native threads
      # all working off the same socket could be a thundering herd
      # problem.  On the other hand, a thundering herd may not
      # even incur as much overhead as an extra Mutex#synchronize
      ret = select(LISTENERS) and ret[0].each do |s|
        s = s.kgio_tryaccept and s.process_loop
      end
    rescue Errno::EINTR
    rescue => e
      Rainbows::Error.listen_loop(e)
    end while Rainbows.alive
  end
end
rainbows-4.6.2/lib/rainbows/coolio_thread_pool.rb0000644000004100000410000000424612354770160022175 0ustar  www-datawww-data# -*- encoding: binary -*-

# A combination of the Coolio and ThreadPool models.  This allows Ruby
# Thread-based concurrency for application processing.  It DOES NOT
# expose a streamable "rack.input" for upload processing within the
# app.  DevFdResponse should be used with this class to proxy
# asynchronous responses.  All network I/O between the client and
# server are handled by the main thread and outside of the core
# application dispatch.
#
# Unlike ThreadPool, Cool.io makes this model highly suitable for
# slow clients and applications with medium-to-slow response times
# (I/O bound), but less suitable for sleepy applications.
#
# This concurrency model is designed for Ruby 1.9, and Ruby 1.8
# users are NOT advised to use this due to high CPU usage.
#
# === :pool_size vs worker_connections
#
# In your Rainbows! config block, you may specify a Thread pool size
# to limit your application concurrency independently of
# worker_connections.
#
#   Rainbows! do
#     use :CoolioThreadPool, :pool_size => 50
#     worker_connections 100
#   end
#
# In extremely rare cases, this may be combined with Rainbows::AppPool
# if you have different concurrency capabilities for different parts of
# your Rack application.
#
# === RubyGem Requirements
# * cool.io 1.0.0 or later
module Rainbows::CoolioThreadPool
  # :stopdoc:
  autoload :Client, 'rainbows/coolio_thread_pool/client'
  extend Rainbows::PoolSize
  #:startdoc:
  include Rainbows::Coolio::Core

  def init_worker_threads(master, queue) # :nodoc:
    Rainbows::O[:pool_size].times.map do
      Thread.new do
        begin
          client = queue.pop
          master << [ client, client.app_response ]
        rescue => e
          Rainbows::Error.listen_loop(e)
        end while true
      end
    end
  end

  def init_worker_process(worker) # :nodoc:
    super
    cloop = Coolio::Loop.default
    master = Rainbows::Coolio::Master.new(Queue.new).attach(cloop)
    queue = Client.const_set(:QUEUE, Queue.new)
    threads = init_worker_threads(master, queue)
    Watcher.new(threads).attach(cloop)
    logger.info "CoolioThreadPool pool_size=#{Rainbows::O[:pool_size]}"
  end
end
# :enddoc:
require 'rainbows/coolio_thread_pool/watcher'
rainbows-4.6.2/lib/rainbows/event_machine/0000755000004100000410000000000012354770160020603 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/event_machine/response_chunk_pipe.rb0000644000004100000410000000074312354770160025177 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::EventMachine::ResponseChunkPipe
  include Rainbows::EventMachine::ResponsePipe

  def unbind
    @client.write("0\r\n\r\n")
    super
  end

  def notify_readable
    case data = Kgio.tryread(@io, 16384, RBUF)
    when String
      @client.write("#{data.size.to_s(16)}\r\n")
      @client.write(data)
      @client.write("\r\n")
    when :wait_readable
      return
    when nil
      return detach
    end while true
  end
end
rainbows-4.6.2/lib/rainbows/event_machine/try_defer.rb0000644000004100000410000000205712354770160023117 0ustar  www-datawww-data# -*- encoding: binary -*-

# Middleware that will run the app dispatch in a separate thread.
# This middleware is automatically loaded by Rainbows! when using
# EventMachine and if the app responds to the +deferred?+ method.
#
# Use EM.threadpool_size in your \Rainbows! config file to control
# the number of threads used by EventMachine.
#
# See http://brainspl.at/articles/2008/04/18/deferred-requests-with-merb-ebb-and-thin
# for more information.
class Rainbows::EventMachine::TryDefer
  # shortcuts
  ASYNC_CALLBACK = Rainbows::EvCore::ASYNC_CALLBACK # :nodoc:

  def initialize(app) # :nodoc:
    # the entire app becomes multithreaded, even the root (non-deferred)
    # thread since any thread can share processes with others
    Rainbows::Const::RACK_DEFAULTS['rack.multithread'] = true
    @app = app
  end

  def call(env) # :nodoc:
    if @app.deferred?(env)
      EM.defer(proc { catch(:async) { @app.call(env) } }, env[ASYNC_CALLBACK])
      # all of the async/deferred stuff breaks Rack::Lint :<
      nil
    else
      @app.call(env)
    end
  end
end
rainbows-4.6.2/lib/rainbows/event_machine/client.rb0000644000004100000410000000717712354770160022422 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
class Rainbows::EventMachine::Client < EM::Connection
  include Rainbows::EvCore
  Rainbows.config!(self, :keepalive_timeout)

  def initialize(io)
    @_io = io
    @deferred = nil
  end

  alias write send_data
  alias hijacked detach

  def receive_data(data)
    # To avoid clobbering the current streaming response
    # (often a static file), we do not attempt to process another
    # request on the same connection until the first is complete
    if @deferred
      if data
        @buf << data
        @_io.shutdown(Socket::SHUT_RD) if @buf.size > 0x1c000
      end
      EM.next_tick { receive_data(nil) } unless @buf.empty?
    else
      on_read(data || Z) if (@buf.size > 0) || data
    end
  end

  def quit
    super
    close_connection_after_writing if nil == @deferred
  end

  def app_call input
    set_comm_inactivity_timeout 0
    @env[RACK_INPUT] = input
    @env[REMOTE_ADDR] = @_io.kgio_addr
    @env[ASYNC_CALLBACK] = method(:write_async_response)
    @env[ASYNC_CLOSE] = EM::DefaultDeferrable.new
    @hp.hijack_setup(@env, @_io)
    status, headers, body = catch(:async) {
      APP.call(@env.merge!(RACK_DEFAULTS))
    }
    return hijacked if @hp.hijacked?

    if (nil == status || -1 == status)
      @deferred = true
    else
      ev_write_response(status, headers, body, @hp.next?)
    end
  end

  def deferred_errback(orig_body)
    @deferred.errback do
      orig_body.close if orig_body.respond_to?(:close)
      @deferred = nil
      quit
    end
  end

  def deferred_callback(orig_body, alive)
    @deferred.callback do
      orig_body.close if orig_body.respond_to?(:close)
      @deferred = nil
      alive ? receive_data(nil) : quit
    end
  end

  def ev_write_response(status, headers, body, alive)
    @state = :headers if alive
    if body.respond_to?(:errback) && body.respond_to?(:callback)
      write_headers(status, headers, alive, body) or return hijacked
      @deferred = body
      write_body_each(body)
      deferred_errback(body)
      deferred_callback(body, alive)
      return
    elsif body.respond_to?(:to_path)
      st = File.stat(path = body.to_path)

      if st.file?
        write_headers(status, headers, alive, body) or return hijacked
        @deferred = stream_file_data(path)
        deferred_errback(body)
        deferred_callback(body, alive)
        return
      elsif st.socket? || st.pipe?
        chunk = stream_response_headers(status, headers, alive, body)
        return hijacked if nil == chunk
        io = body_to_io(@deferred = body)
        m = chunk ? Rainbows::EventMachine::ResponseChunkPipe :
                    Rainbows::EventMachine::ResponsePipe
        return EM.watch(io, m, self).notify_readable = true
      end
      # char or block device... WTF? fall through to body.each
    end
    write_response(status, headers, body, alive) or return hijacked
    if alive
      if @deferred.nil?
        if @buf.empty?
          set_comm_inactivity_timeout(KEEPALIVE_TIMEOUT)
        else
          EM.next_tick { receive_data(nil) }
        end
      end
    else
      quit unless @deferred
    end
  end

  def next!
    @deferred.close if @deferred.respond_to?(:close)
    @deferred = nil
    @hp.keepalive? ? receive_data(nil) : quit
  end

  def unbind
    return if @hp.hijacked?
    async_close = @env[ASYNC_CLOSE] and async_close.succeed
    @deferred.respond_to?(:fail) and @deferred.fail
    begin
      @_io.close
    rescue Errno::EBADF
      # EventMachine's EventableDescriptor::Close() may close
      # the underlying file descriptor without invalidating the
      # associated IO object on errors, so @_io.closed? isn't
      # sufficient.
    end
  end
end
rainbows-4.6.2/lib/rainbows/event_machine/response_pipe.rb0000644000004100000410000000107012354770160024001 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::EventMachine::ResponsePipe
  # garbage avoidance, EM always uses this in a single thread,
  # so a single buffer for all clients will work safely
  RBUF = Rainbows::EvCore::RBUF

  def initialize(client)
    @client = client
  end

  def notify_readable
    case data = Kgio.tryread(@io, 16384, RBUF)
    when String
      @client.write(data)
    when :wait_readable
      return
    when nil
      return detach
    end while true
  end

  def unbind
    @client.next!
    @io.close unless @io.closed?
  end
end
rainbows-4.6.2/lib/rainbows/event_machine/server.rb0000644000004100000410000000053712354770160022443 0ustar  www-datawww-data# -*- encoding: binary -*-
module Rainbows::EventMachine::Server # :nodoc: all
  def close
    detach
    @io.close
  end

  # CL, CUR and MAX will be set when worker_loop starts
  def notify_readable
    return if CUR.size >= MAX
    io = @io.kgio_tryaccept or return
    sig = EM.attach_fd(io.fileno, false)
    CUR[sig] = CL.new(sig, io)
  end
end
rainbows-4.6.2/lib/rainbows/xepoll.rb0000644000004100000410000000156212354770160017632 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'raindrops'
require 'rainbows/epoll'

# Edge-triggered epoll concurrency model with blocking accept() in a
# (hopefully) native thread.  This is just like Epoll, but recommended
# for Ruby 1.9 users as it can avoid accept()-scalability issues on
# multicore machines with many worker processes.
#
# Do not use this if you have slow external dependencies.
#
# === RubyGem Requirements
#
# * raindrops 0.6.0 or later
# * sleepy_penguin 3.0.1 or later
# * sendfile 1.1.0 or later
module Rainbows::XEpoll
  # :stopdoc:
  include Rainbows::Base
  autoload :Client, 'rainbows/xepoll/client'

  def init_worker_process(worker)
    super
    Rainbows.const_set(:EP, SleepyPenguin::Epoll.new)
    Rainbows::Client.__send__ :include, Client
  end

  def worker_loop(worker) # :nodoc:
    init_worker_process(worker)
    Client.loop
  end
  # :startdoc:
end
rainbows-4.6.2/lib/rainbows/xepoll/0000755000004100000410000000000012354770160017301 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/xepoll/client.rb0000644000004100000410000000226212354770160021106 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:

module Rainbows::XEpoll::Client
  N = Raindrops.new(1)
  include Rainbows::Epoll::Client
  ACCEPTORS = Rainbows::HttpServer::LISTENERS.dup
  extend Rainbows::WorkerYield

  def self.included(klass)
    max = Rainbows.server.worker_connections
    ACCEPTORS.map! do |sock|
      Thread.new do
        begin
          if io = sock.kgio_accept(klass)
            N.incr(0, 1)
            io.epoll_once
          end
          worker_yield while N[0] >= max
        rescue => e
          Rainbows::Error.listen_loop(e)
        end while Rainbows.alive
      end
    end
  end

  def self.loop
    begin
      EP.wait(nil, 1000) { |_, obj| obj.epoll_run }
      Rainbows::Epoll::Client.expire
    rescue Errno::EINTR
    rescue => e
      Rainbows::Error.listen_loop(e)
    end while Rainbows.tick || N[0] > 0
    Rainbows::JoinThreads.acceptors(ACCEPTORS)
  end

  # only call this once
  def epoll_once
    @wr_queue = [] # may contain String, ResponsePipe, and StreamFile objects
    post_init
    EP.set(self, IN) # wake up the main thread
    rescue => e
      Rainbows::Error.write(self, e)
  end

  def on_close
    KATO.delete(self)
    N.decr(0, 1)
  end
end
rainbows-4.6.2/lib/rainbows/revactor/0000755000004100000410000000000012354770160017623 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/revactor/proxy.rb0000644000004100000410000000246312354770160021336 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
# Generic IO wrapper for proxying pipe and socket objects
# this behaves more like Rainbows::Fiber::IO than anything,
# making it highly suitable for proxying data from pipes/sockets
class Rainbows::Revactor::Proxy < Rev::IO
  def initialize(io)
    @receiver = Actor.current
    super(io)
    attach(Rev::Loop.default)
  end

  def close
    if @_io
      super
      @_io = nil
    end
  end

  def each
    # when yield-ing, Revactor::TCP#write may raise EOFError
    # (instead of Errno::EPIPE), so we need to limit the rescue
    # to just readpartial and let EOFErrors during yield bubble up
    begin
      buf = readpartial(INPUT_SIZE)
    rescue EOFError
      break
    end while yield(buf) || true
  end

  # this may return more than the specified length, Rainbows! won't care...
  def readpartial(length)
    @receiver = Actor.current
    enable if attached? && ! enabled?

    Actor.receive do |filter|
      filter.when(T[:rainbows_io_input, self]) do |_, _, data|
        return data
      end

      filter.when(T[:rainbows_io_closed, self]) do
        raise EOFError, "connection closed"
      end
    end
  end

  def on_close
    @receiver << T[:rainbows_io_closed, self]
  end

  def on_read(data)
    @receiver << T[:rainbows_io_input, self, data ]
    disable
  end
end
rainbows-4.6.2/lib/rainbows/revactor/client.rb0000644000004100000410000000240012354770160021422 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
require 'fcntl'
class Rainbows::Revactor::Client
  autoload :TeeSocket, 'rainbows/revactor/client/tee_socket'
  RD_ARGS = {}
  Rainbows.server.keepalive_timeout > 0 and
    RD_ARGS[:timeout] = Rainbows.server.keepalive_timeout
  attr_reader :kgio_addr

  def initialize(client)
    @client, @rd_args, @ts = client, [ nil ], nil
    io = client.instance_variable_get(:@_io)
    io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
    @kgio_addr = if Revactor::TCP::Socket === client
      @rd_args << RD_ARGS
      client.remote_addr
    else
      Kgio::LOCALHOST
    end
  end

  def kgio_read!(nr, buf)
    buf.replace(@client.read)
  end

  def write(buf)
    @client.write(buf)
  end

  def timed_read(buf2)
    buf2.replace(@client.read(*@rd_args))
  end

  def set_input(env, hp)
    env[RACK_INPUT] = 0 == hp.content_length ?
                      NULL_IO : IC.new(@ts = TeeSocket.new(@client), hp)
  end

  def to_io
    @client.instance_variable_get(:@_io)
  end

  def close
    @client.close
    @client = nil
  end

  def closed?
    @client.nil?
  end

  def self.setup
    self.const_set(:IC, Unicorn::HttpRequest.input_class)
    include Rainbows::ProcessClient
    include Methods
  end
end
require 'rainbows/revactor/client/methods'
rainbows-4.6.2/lib/rainbows/revactor/client/0000755000004100000410000000000012354770160021101 5ustar  www-datawww-datarainbows-4.6.2/lib/rainbows/revactor/client/tee_socket.rb0000644000004100000410000000242512354770160023556 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
#
# Revactor Sockets do not implement readpartial, so we emulate just
# enough to avoid mucking with TeeInput internals.  Fortunately
# this code is not heavily used so we can usually avoid the overhead
# of adding a userspace buffer.
class Rainbows::Revactor::Client::TeeSocket
  def initialize(socket)
    # IO::Buffer is used internally by Rev which Revactor is based on
    # so we'll always have it available
    @socket, @rbuf = socket, IO::Buffer.new
  end

  def leftover
    @rbuf.read
  end

  # Revactor socket reads always return an unspecified amount,
  # sometimes too much
  def kgio_read(length, dst = "")
    return dst.replace("") if length == 0

    # always check and return from the userspace buffer first
    @rbuf.size > 0 and return dst.replace(@rbuf.read(length))

    # read off the socket since there was nothing in rbuf
    tmp = @socket.read

    # we didn't read too much, good, just return it straight back
    # to avoid needlessly wasting memory bandwidth
    tmp.size <= length and return dst.replace(tmp)

    # ugh, read returned too much
    @rbuf << tmp[length, tmp.size]
    dst.replace(tmp[0, length])
    rescue EOFError
  end

  # just proxy any remaining methods TeeInput may use
  def close
    @socket.close
  end
end
rainbows-4.6.2/lib/rainbows/revactor/client/methods.rb0000644000004100000410000000271112354770160023072 0ustar  www-datawww-data# -*- encoding: binary -*-
# :enddoc:
module Rainbows::Revactor::Client::Methods
  if IO.method_defined?(:trysendfile)
    def write_body_file(body, range)
      body, client = body_to_io(body), @client
      sock = @client.instance_variable_get(:@_io)
      pfx = Revactor::TCP::Socket === client ? :tcp : :unix
      write_complete = T[:"#{pfx}_write_complete", client]
      closed = T[:"#{pfx}_closed", client]
      offset, count = range ? range : [ 0, body.stat.size ]
      case n = sock.trysendfile(body, offset, count)
      when Integer
        offset += n
        return if 0 == (count -= n)
      when :wait_writable
        # The @_write_buffer is empty at this point, trigger the
        # on_readable method which in turn triggers on_write_complete
        # even though nothing was written
        client.controller = Actor.current
        client.__send__(:enable_write_watcher)
        Actor.receive do |filter|
          filter.when(write_complete) {}
          filter.when(closed) { raise Errno::EPIPE }
        end
      else # nil
        return
      end while true
      ensure
        close_if_private(body)
    end
  end

  def handle_error(e)
    Revactor::TCP::ReadError === e or super
  end

  def write_response(status, headers, body, alive)
    super(status, headers, body, alive) or return
    alive && @ts and @hp.buf << @ts.leftover
  end

  def self.included(klass)
    klass.__send__ :alias_method, :write_body_stream, :write_body_each
  end
end
rainbows-4.6.2/lib/rainbows.rb0000644000004100000410000001053412354770160016326 0ustar  www-datawww-data# -*- encoding: binary -*-
require 'kgio'
require 'unicorn'
# the value passed to TCP_DEFER_ACCEPT actually matters in Linux 2.6.32+
Unicorn::SocketHelper::DEFAULTS[:tcp_defer_accept] = 60

# See http://rainbows.bogomips.org/ for documentation
module Rainbows
  # :stopdoc:
  O = {}

  # map of numeric file descriptors to IO objects to avoid using IO.new
  # and potentially causing race conditions when using /dev/fd/
  FD_MAP = {}
  FD_MAP.compare_by_identity if FD_MAP.respond_to?(:compare_by_identity)

  require 'rainbows/const'
  require 'rainbows/http_parser'
  require 'rainbows/http_server'
  autoload :Response, 'rainbows/response'
  autoload :ProcessClient, 'rainbows/process_client'
  autoload :Client, 'rainbows/client'
  autoload :Base, 'rainbows/base'
  autoload :Sendfile, 'rainbows/sendfile'
  autoload :AppPool, 'rainbows/app_pool'
  autoload :DevFdResponse, 'rainbows/dev_fd_response'
  autoload :MaxBody, 'rainbows/max_body'
  autoload :QueuePool, 'rainbows/queue_pool'
  autoload :EvCore, 'rainbows/ev_core'
  autoload :SocketProxy, 'rainbows/socket_proxy'

  # :startdoc:
  # Sleeps the current application dispatch.  This will pick the
  # optimal method to sleep depending on the concurrency model chosen
  # (which may still suck and block the entire process).  Using this
  # with the basic :Coolio or :EventMachine models is not recommended.
  # This should be used within your Rack application.
  def self.sleep(seconds)
    case Rainbows.server.use
    when :FiberPool, :FiberSpawn
      Rainbows::Fiber.sleep(seconds)
    when :RevFiberSpawn, :CoolioFiberSpawn
      Rainbows::Fiber::Coolio::Sleeper.new(seconds)
    when :Revactor
      Actor.sleep(seconds)
    else
      Kernel.sleep(seconds)
    end
  end
  # :stopdoc:

  class << self
    attr_accessor :server
    attr_accessor :cur # may not always be used
    attr_reader :alive
    attr_writer :worker
    attr_writer :forked
    attr_writer :readers
  end

  def self.config!(mod, *opts)
    @forked or abort "#{mod} should only be loaded in a worker process"
    opts.each do |opt|
      mod.const_set(opt.to_s.upcase, Rainbows.server.__send__(opt))
    end
  end

  @alive = true
  @cur = 0
  @expire = nil
  @at_quit = []

  def self.at_quit(&block)
    @at_quit << block
  end

  def self.tick
    @worker.tick = Time.now.to_i
    exit!(2) if @expire && Time.now >= @expire
    @alive && @server.master_pid == Process.ppid or quit!
  end

  def self.cur_alive
    @alive || @cur > 0
  end

  def self.quit!
    unless @expire
      @alive = false
      Rainbows::HttpParser.quit
      @expire = Time.now + (@server.timeout * 2.0)
      tmp = @readers.dup
      @readers.clear
      tmp.each { |s| s.close rescue nil }.clear
      @at_quit.each { |task| task.call }
    end
    false
  end

  autoload :Base, "rainbows/base"
  autoload :WriterThreadPool, "rainbows/writer_thread_pool"
  autoload :WriterThreadSpawn, "rainbows/writer_thread_spawn"
  autoload :Revactor, "rainbows/revactor"
  autoload :ThreadSpawn, "rainbows/thread_spawn"
  autoload :ThreadPool, "rainbows/thread_pool"
  autoload :Rev, "rainbows/rev"
  autoload :RevThreadSpawn, "rainbows/rev_thread_spawn"
  autoload :RevThreadPool, "rainbows/rev_thread_pool"
  autoload :RevFiberSpawn, "rainbows/rev_fiber_spawn"
  autoload :Coolio, "rainbows/coolio"
  autoload :CoolioThreadSpawn, "rainbows/coolio_thread_spawn"
  autoload :CoolioThreadPool, "rainbows/coolio_thread_pool"
  autoload :CoolioFiberSpawn, "rainbows/coolio_fiber_spawn"
  autoload :Epoll, "rainbows/epoll"
  autoload :XEpoll, "rainbows/xepoll"
  autoload :EventMachine, "rainbows/event_machine"
  autoload :FiberSpawn, "rainbows/fiber_spawn"
  autoload :FiberPool, "rainbows/fiber_pool"
  autoload :ActorSpawn, "rainbows/actor_spawn"
  autoload :NeverBlock, "rainbows/never_block"
  autoload :XEpollThreadSpawn, "rainbows/xepoll_thread_spawn"
  autoload :XEpollThreadPool, "rainbows/xepoll_thread_pool"
  autoload :StreamResponseEpoll, "rainbows/stream_response_epoll"

  autoload :Fiber, 'rainbows/fiber' # core class
  autoload :StreamFile, 'rainbows/stream_file'
  autoload :ThreadTimeout, 'rainbows/thread_timeout'
  autoload :WorkerYield, 'rainbows/worker_yield'
  autoload :SyncClose, 'rainbows/sync_close'
  autoload :ReverseProxy, 'rainbows/reverse_proxy'
  autoload :JoinThreads, 'rainbows/join_threads'
  autoload :PoolSize, 'rainbows/pool_size'
end

require 'rainbows/error'
require 'rainbows/configurator'
rainbows-4.6.2/.manifest0000644000004100000410000001702012354770160015213 0ustar  www-datawww-data.document
.gitignore
.manifest
.wrongdoc.yml
COPYING
ChangeLog
DEPLOY
Documentation/.gitignore
Documentation/GNUmakefile
Documentation/comparison.css
Documentation/comparison.haml
Documentation/rainbows.1.txt
FAQ
GIT-VERSION-FILE
GIT-VERSION-GEN
GNUmakefile
HACKING
LATEST
LICENSE
NEWS
README
Rakefile
SIGNALS
Sandbox
Static_Files
Summary
TODO
TUNING
Test_Suite
archive/.gitignore
archive/rfmig.rb
archive/slrnpull.conf
bin/rainbows
examples/reverse_proxy.ru
lib/rainbows.rb
lib/rainbows/actor_spawn.rb
lib/rainbows/app_pool.rb
lib/rainbows/base.rb
lib/rainbows/client.rb
lib/rainbows/configurator.rb
lib/rainbows/const.rb
lib/rainbows/coolio.rb
lib/rainbows/coolio/client.rb
lib/rainbows/coolio/core.rb
lib/rainbows/coolio/heartbeat.rb
lib/rainbows/coolio/master.rb
lib/rainbows/coolio/response_chunk_pipe.rb
lib/rainbows/coolio/response_pipe.rb
lib/rainbows/coolio/server.rb
lib/rainbows/coolio/thread_client.rb
lib/rainbows/coolio_fiber_spawn.rb
lib/rainbows/coolio_support.rb
lib/rainbows/coolio_thread_pool.rb
lib/rainbows/coolio_thread_pool/client.rb
lib/rainbows/coolio_thread_pool/watcher.rb
lib/rainbows/coolio_thread_spawn.rb
lib/rainbows/coolio_thread_spawn/client.rb
lib/rainbows/dev_fd_response.rb
lib/rainbows/epoll.rb
lib/rainbows/epoll/client.rb
lib/rainbows/epoll/response_chunk_pipe.rb
lib/rainbows/epoll/response_pipe.rb
lib/rainbows/epoll/server.rb
lib/rainbows/error.rb
lib/rainbows/ev_core.rb
lib/rainbows/ev_core/cap_input.rb
lib/rainbows/event_machine.rb
lib/rainbows/event_machine/client.rb
lib/rainbows/event_machine/response_chunk_pipe.rb
lib/rainbows/event_machine/response_pipe.rb
lib/rainbows/event_machine/server.rb
lib/rainbows/event_machine/try_defer.rb
lib/rainbows/fiber.rb
lib/rainbows/fiber/base.rb
lib/rainbows/fiber/body.rb
lib/rainbows/fiber/coolio.rb
lib/rainbows/fiber/coolio/heartbeat.rb
lib/rainbows/fiber/coolio/methods.rb
lib/rainbows/fiber/coolio/server.rb
lib/rainbows/fiber/coolio/sleeper.rb
lib/rainbows/fiber/io.rb
lib/rainbows/fiber/io/compat.rb
lib/rainbows/fiber/io/methods.rb
lib/rainbows/fiber/io/pipe.rb
lib/rainbows/fiber/io/socket.rb
lib/rainbows/fiber/queue.rb
lib/rainbows/fiber_pool.rb
lib/rainbows/fiber_spawn.rb
lib/rainbows/http_parser.rb
lib/rainbows/http_server.rb
lib/rainbows/join_threads.rb
lib/rainbows/max_body.rb
lib/rainbows/max_body/rewindable_wrapper.rb
lib/rainbows/max_body/wrapper.rb
lib/rainbows/never_block.rb
lib/rainbows/never_block/core.rb
lib/rainbows/never_block/event_machine.rb
lib/rainbows/pool_size.rb
lib/rainbows/process_client.rb
lib/rainbows/queue_pool.rb
lib/rainbows/response.rb
lib/rainbows/rev.rb
lib/rainbows/rev_fiber_spawn.rb
lib/rainbows/rev_thread_pool.rb
lib/rainbows/rev_thread_spawn.rb
lib/rainbows/revactor.rb
lib/rainbows/revactor/client.rb
lib/rainbows/revactor/client/methods.rb
lib/rainbows/revactor/client/tee_socket.rb
lib/rainbows/revactor/proxy.rb
lib/rainbows/reverse_proxy.rb
lib/rainbows/reverse_proxy/coolio.rb
lib/rainbows/reverse_proxy/ev_client.rb
lib/rainbows/reverse_proxy/event_machine.rb
lib/rainbows/reverse_proxy/multi_thread.rb
lib/rainbows/reverse_proxy/synchronous.rb
lib/rainbows/sendfile.rb
lib/rainbows/server_token.rb
lib/rainbows/socket_proxy.rb
lib/rainbows/stream_file.rb
lib/rainbows/stream_response_epoll.rb
lib/rainbows/stream_response_epoll/client.rb
lib/rainbows/sync_close.rb
lib/rainbows/thread_pool.rb
lib/rainbows/thread_spawn.rb
lib/rainbows/thread_timeout.rb
lib/rainbows/version.rb
lib/rainbows/worker_yield.rb
lib/rainbows/writer_thread_pool.rb
lib/rainbows/writer_thread_pool/client.rb
lib/rainbows/writer_thread_spawn.rb
lib/rainbows/writer_thread_spawn/client.rb
lib/rainbows/xepoll.rb
lib/rainbows/xepoll/client.rb
lib/rainbows/xepoll_thread_pool.rb
lib/rainbows/xepoll_thread_pool/client.rb
lib/rainbows/xepoll_thread_spawn.rb
lib/rainbows/xepoll_thread_spawn/client.rb
local.mk.sample
man/man1/rainbows.1
pkg.mk
rainbows.gemspec
setup.rb
t/.gitignore
t/GNUmakefile
t/README
t/app_deferred.ru
t/async-response-no-autochunk.ru
t/async-response.ru
t/async_chunk_app.ru
t/async_examples/README
t/async_examples/async_app.ru
t/async_examples/async_tailer.ru
t/async_sinatra.ru
t/bin/content-md5-put
t/bin/sha1sum.rb
t/bin/unused_listen
t/byte-range-common.sh
t/client_header_buffer_size.ru
t/close-has-env.ru
t/close-pipe-response.ru
t/close-pipe-to_path-response.ru
t/content-md5.ru
t/cramp/README
t/cramp/rainsocket.ru
t/cramp/streaming.ru
t/env.ru
t/env_rack_env.ru
t/fast-pipe-response.ru
t/file-wrap-to_path.ru
t/fork-sleep.ru
t/heartbeat-timeout.ru
t/hijack.ru
t/kgio-pipe-response.ru
t/large-file-response.ru
t/my-tap-lib.sh
t/rack-fiber_pool/app.ru
t/sha1-random-size.ru
t/sha1.ru
t/simple-http_ActorSpawn.ru
t/simple-http_Base.ru
t/simple-http_Coolio.ru
t/simple-http_CoolioFiberSpawn.ru
t/simple-http_CoolioThreadPool.ru
t/simple-http_CoolioThreadSpawn.ru
t/simple-http_Epoll.ru
t/simple-http_EventMachine.ru
t/simple-http_FiberPool.ru
t/simple-http_FiberSpawn.ru
t/simple-http_NeverBlock.ru
t/simple-http_Rev.ru
t/simple-http_RevFiberSpawn.ru
t/simple-http_RevThreadPool.ru
t/simple-http_RevThreadSpawn.ru
t/simple-http_Revactor.ru
t/simple-http_ThreadPool.ru
t/simple-http_ThreadSpawn.ru
t/simple-http_WriterThreadPool.ru
t/simple-http_WriterThreadSpawn.ru
t/simple-http_XEpoll.ru
t/simple-http_XEpollThreadPool.ru
t/simple-http_XEpollThreadSpawn.ru
t/sleep.ru
t/t0000-simple-http.sh
t/t0000.ru
t/t0001-unix-http.sh
t/t0002-graceful.sh
t/t0002-parser-error.sh
t/t0003-reopen-logs.sh
t/t0004-heartbeat-timeout.sh
t/t0005-large-file-response.sh
t/t0006-process-rack-env.sh
t/t0007-worker-follows-master-to-death.sh
t/t0008-ensure-usable-after-limit.sh
t/t0009-broken-app.sh
t/t0009.ru
t/t0010-keepalive-timeout-effective.sh
t/t0011-close-on-exec-set.sh
t/t0012-spurious-wakeups-quiet.sh
t/t0013-reload-bad-config.sh
t/t0014-config-conflict.sh
t/t0015-working_directory.sh
t/t0016-onenine-encoding-is-tricky.sh
t/t0016.rb
t/t0017-keepalive-timeout-zero.sh
t/t0018-reload-restore-settings.sh
t/t0019-keepalive-cpu-usage.sh
t/t0020-large-sendfile-response.sh
t/t0021-sendfile-wrap-to_path.sh
t/t0022-copy_stream-byte-range.sh
t/t0023-sendfile-byte-range.sh
t/t0024-pipelined-sendfile-response.sh
t/t0025-write-on-close.sh
t/t0026-splice-copy_stream-byte-range.sh
t/t0027-nil-copy_stream.sh
t/t0030-fast-pipe-response.sh
t/t0031-close-pipe-response.sh
t/t0032-close-pipe-to_path-response.sh
t/t0034-pipelined-pipe-response.sh
t/t0035-kgio-pipe-response.sh
t/t0040-keepalive_requests-setting.sh
t/t0041-optional-pool-size.sh
t/t0042-client_header_buffer_size.sh
t/t0043-quit-keepalive-disconnect.sh
t/t0044-autopush.sh
t/t0045-client_max_header_size.sh
t/t0050-response-body-close-has-env.sh
t/t0100-rack-input-hammer-chunked.sh
t/t0100-rack-input-hammer-content-length.sh
t/t0101-rack-input-trailer.sh
t/t0102-rack-input-short.sh
t/t0103-rack-input-limit.sh
t/t0104-rack-input-limit-tiny.sh
t/t0105-rack-input-limit-bigger.sh
t/t0106-rack-input-keepalive.sh
t/t0107-rack-input-limit-zero.sh
t/t0113-rewindable-input-false.sh
t/t0113.ru
t/t0114-rewindable-input-true.sh
t/t0114.ru
t/t0200-async-response.sh
t/t0201-async-response-no-autochunk.sh
t/t0202-async-response-one-oh.sh
t/t0300-async_sinatra.sh
t/t0400-em-async-app.sh
t/t0401-em-async-tailer.sh
t/t0402-async-keepalive.sh
t/t0500-cramp-streaming.sh
t/t0501-cramp-rainsocket.sh
t/t0600-rack-fiber_pool.sh
t/t0700-app-deferred.sh
t/t0800-rack-hijack.sh
t/t9000-rack-app-pool.sh
t/t9000.ru
t/t9001-sendfile-to-path.sh
t/t9001.ru
t/t9002-server-token.sh
t/t9002.ru
t/t9100-thread-timeout.sh
t/t9100.ru
t/t9101-thread-timeout-threshold.sh
t/t9101.ru
t/test-lib.sh
t/test_isolate.rb
t/test_isolate_cramp.rb
t/times.ru
t/worker-follows-master-to-death.ru
t/write-on-close.ru
vs_Unicorn
rainbows-4.6.2/metadata.yml0000644000004100000410000003025712354770160015716 0ustar  www-datawww-data--- !ruby/object:Gem::Specification
name: rainbows
version: !ruby/object:Gem::Version
  version: 4.6.2
platform: ruby
authors:
- Rainbows! hackers
autorequire: 
bindir: bin
cert_chain: []
date: 2014-05-12 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
  name: rack
  requirement: !ruby/object:Gem::Requirement
    requirements:
    - - "~>"
      - !ruby/object:Gem::Version
        version: '1.1'
  type: :runtime
  prerelease: false
  version_requirements: !ruby/object:Gem::Requirement
    requirements:
    - - "~>"
      - !ruby/object:Gem::Version
        version: '1.1'
- !ruby/object:Gem::Dependency
  name: kgio
  requirement: !ruby/object:Gem::Requirement
    requirements:
    - - "~>"
      - !ruby/object:Gem::Version
        version: '2.5'
  type: :runtime
  prerelease: false
  version_requirements: !ruby/object:Gem::Requirement
    requirements:
    - - "~>"
      - !ruby/object:Gem::Version
        version: '2.5'
- !ruby/object:Gem::Dependency
  name: unicorn
  requirement: !ruby/object:Gem::Requirement
    requirements:
    - - "~>"
      - !ruby/object:Gem::Version
        version: '4.8'
  type: :runtime
  prerelease: false
  version_requirements: !ruby/object:Gem::Requirement
    requirements:
    - - "~>"
      - !ruby/object:Gem::Version
        version: '4.8'
- !ruby/object:Gem::Dependency
  name: isolate
  requirement: !ruby/object:Gem::Requirement
    requirements:
    - - "~>"
      - !ruby/object:Gem::Version
        version: '3.1'
  type: :development
  prerelease: false
  version_requirements: !ruby/object:Gem::Requirement
    requirements:
    - - "~>"
      - !ruby/object:Gem::Version
        version: '3.1'
- !ruby/object:Gem::Dependency
  name: wrongdoc
  requirement: !ruby/object:Gem::Requirement
    requirements:
    - - "~>"
      - !ruby/object:Gem::Version
        version: '1.8'
  type: :development
  prerelease: false
  version_requirements: !ruby/object:Gem::Requirement
    requirements:
    - - "~>"
      - !ruby/object:Gem::Version
        version: '1.8'
description: |-
  \Rainbows! is an HTTP server for sleepy Rack applications.  It is based on
  Unicorn, but designed to handle applications that expect long
  request/response times and/or slow clients.
email: rainbows-public@bogomips.org
executables:
- rainbows
extensions: []
extra_rdoc_files:
- ChangeLog
- DEPLOY
- FAQ
- lib/rainbows.rb
- lib/rainbows/actor_spawn.rb
- lib/rainbows/app_pool.rb
- lib/rainbows/base.rb
- lib/rainbows/configurator.rb
- lib/rainbows/coolio.rb
- lib/rainbows/coolio_thread_pool.rb
- lib/rainbows/coolio_thread_spawn.rb
- lib/rainbows/dev_fd_response.rb
- lib/rainbows/epoll.rb
- lib/rainbows/event_machine.rb
- lib/rainbows/fiber_pool.rb
- lib/rainbows/fiber_spawn.rb
- lib/rainbows/max_body.rb
- lib/rainbows/never_block.rb
- lib/rainbows/rev.rb
- lib/rainbows/rev_fiber_spawn.rb
- lib/rainbows/rev_thread_pool.rb
- lib/rainbows/rev_thread_spawn.rb
- lib/rainbows/revactor.rb
- lib/rainbows/sendfile.rb
- lib/rainbows/server_token.rb
- lib/rainbows/stream_response_epoll.rb
- lib/rainbows/thread_pool.rb
- lib/rainbows/thread_spawn.rb
- lib/rainbows/thread_timeout.rb
- lib/rainbows/worker_yield.rb
- lib/rainbows/writer_thread_pool.rb
- lib/rainbows/writer_thread_spawn.rb
- lib/rainbows/xepoll.rb
- lib/rainbows/xepoll_thread_pool.rb
- lib/rainbows/xepoll_thread_spawn.rb
- LATEST
- LICENSE
- NEWS
- README
- SIGNALS
- TODO
- TUNING
- vs_Unicorn
- Summary
- Test_Suite
- Static_Files
- Sandbox
- HACKING
files:
- ".document"
- ".gitignore"
- ".manifest"
- ".wrongdoc.yml"
- COPYING
- ChangeLog
- DEPLOY
- Documentation/.gitignore
- Documentation/GNUmakefile
- Documentation/comparison.css
- Documentation/comparison.haml
- Documentation/rainbows.1.txt
- FAQ
- GIT-VERSION-FILE
- GIT-VERSION-GEN
- GNUmakefile
- HACKING
- LATEST
- LICENSE
- NEWS
- README
- Rakefile
- SIGNALS
- Sandbox
- Static_Files
- Summary
- TODO
- TUNING
- Test_Suite
- archive/.gitignore
- archive/rfmig.rb
- archive/slrnpull.conf
- bin/rainbows
- examples/reverse_proxy.ru
- lib/rainbows.rb
- lib/rainbows/actor_spawn.rb
- lib/rainbows/app_pool.rb
- lib/rainbows/base.rb
- lib/rainbows/client.rb
- lib/rainbows/configurator.rb
- lib/rainbows/const.rb
- lib/rainbows/coolio.rb
- lib/rainbows/coolio/client.rb
- lib/rainbows/coolio/core.rb
- lib/rainbows/coolio/heartbeat.rb
- lib/rainbows/coolio/master.rb
- lib/rainbows/coolio/response_chunk_pipe.rb
- lib/rainbows/coolio/response_pipe.rb
- lib/rainbows/coolio/server.rb
- lib/rainbows/coolio/thread_client.rb
- lib/rainbows/coolio_fiber_spawn.rb
- lib/rainbows/coolio_support.rb
- lib/rainbows/coolio_thread_pool.rb
- lib/rainbows/coolio_thread_pool/client.rb
- lib/rainbows/coolio_thread_pool/watcher.rb
- lib/rainbows/coolio_thread_spawn.rb
- lib/rainbows/coolio_thread_spawn/client.rb
- lib/rainbows/dev_fd_response.rb
- lib/rainbows/epoll.rb
- lib/rainbows/epoll/client.rb
- lib/rainbows/epoll/response_chunk_pipe.rb
- lib/rainbows/epoll/response_pipe.rb
- lib/rainbows/epoll/server.rb
- lib/rainbows/error.rb
- lib/rainbows/ev_core.rb
- lib/rainbows/ev_core/cap_input.rb
- lib/rainbows/event_machine.rb
- lib/rainbows/event_machine/client.rb
- lib/rainbows/event_machine/response_chunk_pipe.rb
- lib/rainbows/event_machine/response_pipe.rb
- lib/rainbows/event_machine/server.rb
- lib/rainbows/event_machine/try_defer.rb
- lib/rainbows/fiber.rb
- lib/rainbows/fiber/base.rb
- lib/rainbows/fiber/body.rb
- lib/rainbows/fiber/coolio.rb
- lib/rainbows/fiber/coolio/heartbeat.rb
- lib/rainbows/fiber/coolio/methods.rb
- lib/rainbows/fiber/coolio/server.rb
- lib/rainbows/fiber/coolio/sleeper.rb
- lib/rainbows/fiber/io.rb
- lib/rainbows/fiber/io/compat.rb
- lib/rainbows/fiber/io/methods.rb
- lib/rainbows/fiber/io/pipe.rb
- lib/rainbows/fiber/io/socket.rb
- lib/rainbows/fiber/queue.rb
- lib/rainbows/fiber_pool.rb
- lib/rainbows/fiber_spawn.rb
- lib/rainbows/http_parser.rb
- lib/rainbows/http_server.rb
- lib/rainbows/join_threads.rb
- lib/rainbows/max_body.rb
- lib/rainbows/max_body/rewindable_wrapper.rb
- lib/rainbows/max_body/wrapper.rb
- lib/rainbows/never_block.rb
- lib/rainbows/never_block/core.rb
- lib/rainbows/never_block/event_machine.rb
- lib/rainbows/pool_size.rb
- lib/rainbows/process_client.rb
- lib/rainbows/queue_pool.rb
- lib/rainbows/response.rb
- lib/rainbows/rev.rb
- lib/rainbows/rev_fiber_spawn.rb
- lib/rainbows/rev_thread_pool.rb
- lib/rainbows/rev_thread_spawn.rb
- lib/rainbows/revactor.rb
- lib/rainbows/revactor/client.rb
- lib/rainbows/revactor/client/methods.rb
- lib/rainbows/revactor/client/tee_socket.rb
- lib/rainbows/revactor/proxy.rb
- lib/rainbows/reverse_proxy.rb
- lib/rainbows/reverse_proxy/coolio.rb
- lib/rainbows/reverse_proxy/ev_client.rb
- lib/rainbows/reverse_proxy/event_machine.rb
- lib/rainbows/reverse_proxy/multi_thread.rb
- lib/rainbows/reverse_proxy/synchronous.rb
- lib/rainbows/sendfile.rb
- lib/rainbows/server_token.rb
- lib/rainbows/socket_proxy.rb
- lib/rainbows/stream_file.rb
- lib/rainbows/stream_response_epoll.rb
- lib/rainbows/stream_response_epoll/client.rb
- lib/rainbows/sync_close.rb
- lib/rainbows/thread_pool.rb
- lib/rainbows/thread_spawn.rb
- lib/rainbows/thread_timeout.rb
- lib/rainbows/version.rb
- lib/rainbows/worker_yield.rb
- lib/rainbows/writer_thread_pool.rb
- lib/rainbows/writer_thread_pool/client.rb
- lib/rainbows/writer_thread_spawn.rb
- lib/rainbows/writer_thread_spawn/client.rb
- lib/rainbows/xepoll.rb
- lib/rainbows/xepoll/client.rb
- lib/rainbows/xepoll_thread_pool.rb
- lib/rainbows/xepoll_thread_pool/client.rb
- lib/rainbows/xepoll_thread_spawn.rb
- lib/rainbows/xepoll_thread_spawn/client.rb
- local.mk.sample
- man/man1/rainbows.1
- pkg.mk
- rainbows.gemspec
- setup.rb
- t/.gitignore
- t/GNUmakefile
- t/README
- t/app_deferred.ru
- t/async-response-no-autochunk.ru
- t/async-response.ru
- t/async_chunk_app.ru
- t/async_examples/README
- t/async_examples/async_app.ru
- t/async_examples/async_tailer.ru
- t/async_sinatra.ru
- t/bin/content-md5-put
- t/bin/sha1sum.rb
- t/bin/unused_listen
- t/byte-range-common.sh
- t/client_header_buffer_size.ru
- t/close-has-env.ru
- t/close-pipe-response.ru
- t/close-pipe-to_path-response.ru
- t/content-md5.ru
- t/cramp/README
- t/cramp/rainsocket.ru
- t/cramp/streaming.ru
- t/env.ru
- t/env_rack_env.ru
- t/fast-pipe-response.ru
- t/file-wrap-to_path.ru
- t/fork-sleep.ru
- t/heartbeat-timeout.ru
- t/hijack.ru
- t/kgio-pipe-response.ru
- t/large-file-response.ru
- t/my-tap-lib.sh
- t/rack-fiber_pool/app.ru
- t/sha1-random-size.ru
- t/sha1.ru
- t/simple-http_ActorSpawn.ru
- t/simple-http_Base.ru
- t/simple-http_Coolio.ru
- t/simple-http_CoolioFiberSpawn.ru
- t/simple-http_CoolioThreadPool.ru
- t/simple-http_CoolioThreadSpawn.ru
- t/simple-http_Epoll.ru
- t/simple-http_EventMachine.ru
- t/simple-http_FiberPool.ru
- t/simple-http_FiberSpawn.ru
- t/simple-http_NeverBlock.ru
- t/simple-http_Rev.ru
- t/simple-http_RevFiberSpawn.ru
- t/simple-http_RevThreadPool.ru
- t/simple-http_RevThreadSpawn.ru
- t/simple-http_Revactor.ru
- t/simple-http_ThreadPool.ru
- t/simple-http_ThreadSpawn.ru
- t/simple-http_WriterThreadPool.ru
- t/simple-http_WriterThreadSpawn.ru
- t/simple-http_XEpoll.ru
- t/simple-http_XEpollThreadPool.ru
- t/simple-http_XEpollThreadSpawn.ru
- t/sleep.ru
- t/t0000-simple-http.sh
- t/t0000.ru
- t/t0001-unix-http.sh
- t/t0002-graceful.sh
- t/t0002-parser-error.sh
- t/t0003-reopen-logs.sh
- t/t0004-heartbeat-timeout.sh
- t/t0005-large-file-response.sh
- t/t0006-process-rack-env.sh
- t/t0007-worker-follows-master-to-death.sh
- t/t0008-ensure-usable-after-limit.sh
- t/t0009-broken-app.sh
- t/t0009.ru
- t/t0010-keepalive-timeout-effective.sh
- t/t0011-close-on-exec-set.sh
- t/t0012-spurious-wakeups-quiet.sh
- t/t0013-reload-bad-config.sh
- t/t0014-config-conflict.sh
- t/t0015-working_directory.sh
- t/t0016-onenine-encoding-is-tricky.sh
- t/t0016.rb
- t/t0017-keepalive-timeout-zero.sh
- t/t0018-reload-restore-settings.sh
- t/t0019-keepalive-cpu-usage.sh
- t/t0020-large-sendfile-response.sh
- t/t0021-sendfile-wrap-to_path.sh
- t/t0022-copy_stream-byte-range.sh
- t/t0023-sendfile-byte-range.sh
- t/t0024-pipelined-sendfile-response.sh
- t/t0025-write-on-close.sh
- t/t0026-splice-copy_stream-byte-range.sh
- t/t0027-nil-copy_stream.sh
- t/t0030-fast-pipe-response.sh
- t/t0031-close-pipe-response.sh
- t/t0032-close-pipe-to_path-response.sh
- t/t0034-pipelined-pipe-response.sh
- t/t0035-kgio-pipe-response.sh
- t/t0040-keepalive_requests-setting.sh
- t/t0041-optional-pool-size.sh
- t/t0042-client_header_buffer_size.sh
- t/t0043-quit-keepalive-disconnect.sh
- t/t0044-autopush.sh
- t/t0045-client_max_header_size.sh
- t/t0050-response-body-close-has-env.sh
- t/t0100-rack-input-hammer-chunked.sh
- t/t0100-rack-input-hammer-content-length.sh
- t/t0101-rack-input-trailer.sh
- t/t0102-rack-input-short.sh
- t/t0103-rack-input-limit.sh
- t/t0104-rack-input-limit-tiny.sh
- t/t0105-rack-input-limit-bigger.sh
- t/t0106-rack-input-keepalive.sh
- t/t0107-rack-input-limit-zero.sh
- t/t0113-rewindable-input-false.sh
- t/t0113.ru
- t/t0114-rewindable-input-true.sh
- t/t0114.ru
- t/t0200-async-response.sh
- t/t0201-async-response-no-autochunk.sh
- t/t0202-async-response-one-oh.sh
- t/t0300-async_sinatra.sh
- t/t0400-em-async-app.sh
- t/t0401-em-async-tailer.sh
- t/t0402-async-keepalive.sh
- t/t0500-cramp-streaming.sh
- t/t0501-cramp-rainsocket.sh
- t/t0600-rack-fiber_pool.sh
- t/t0700-app-deferred.sh
- t/t0800-rack-hijack.sh
- t/t9000-rack-app-pool.sh
- t/t9000.ru
- t/t9001-sendfile-to-path.sh
- t/t9001.ru
- t/t9002-server-token.sh
- t/t9002.ru
- t/t9100-thread-timeout.sh
- t/t9100.ru
- t/t9101-thread-timeout-threshold.sh
- t/t9101.ru
- t/test-lib.sh
- t/test_isolate.rb
- t/test_isolate_cramp.rb
- t/times.ru
- t/worker-follows-master-to-death.ru
- t/write-on-close.ru
- vs_Unicorn
homepage: http://rainbows.bogomips.org/
licenses:
- GPLv2+
- Ruby 1.8
metadata: {}
post_install_message: 
rdoc_options:
- "-t"
- Rainbows! - Unicorn for sleepy apps and slow clients
- "-W"
- http://bogomips.org/rainbows.git/tree/%s
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
  requirements:
  - - ">="
    - !ruby/object:Gem::Version
      version: '0'
required_rubygems_version: !ruby/object:Gem::Requirement
  requirements:
  - - ">="
    - !ruby/object:Gem::Version
      version: '0'
requirements: []
rubyforge_project: rainbows
rubygems_version: 2.2.2
signing_key: 
specification_version: 4
summary: "- Unicorn for sleepy apps and slow clients"
test_files: []
rainbows-4.6.2/rainbows.gemspec0000644000004100000410000000421612354770160016600 0ustar  www-datawww-data# -*- encoding: binary -*-
ENV["VERSION"] or abort "VERSION= must be specified"
manifest = File.readlines('.manifest').map! { |x| x.chomp! }
require 'wrongdoc'
extend Wrongdoc::Gemspec
name, summary, title = readme_metadata

Gem::Specification.new do |s|
  s.name = %q{rainbows}
  s.version = ENV["VERSION"].dup

  s.authors = ["#{name} hackers"]
  s.date = Time.now.utc.strftime('%Y-%m-%d')
  s.description = readme_description
  s.email = %q{rainbows-public@bogomips.org}
  s.executables = %w(rainbows)
  s.extra_rdoc_files = extra_rdoc_files(manifest)
  s.files = manifest
  s.homepage = Wrongdoc.config[:rdoc_url]
  s.summary = summary
  s.rdoc_options = rdoc_options
  s.rubyforge_project = %q{rainbows}

  # we want a newer Rack for a valid HeaderHash#each
  s.add_dependency(%q, ['~> 1.1'])

  # kgio 2.5 has kgio_wait_* methods that take optional timeout args
  s.add_dependency(%q, ['~> 2.5'])

  # we need Unicorn for the HTTP parser and process management
  # we need unicorn 4.8.0+ since we depend on undocumented/unsupported
  # unicorn internals.
  s.add_dependency(%q, ["~> 4.8"])

  s.add_development_dependency(%q, "~> 3.1")
  s.add_development_dependency(%q, "~> 1.8")

  # optional runtime dependencies depending on configuration
  # see t/test_isolate.rb for the exact versions we've tested with
  #
  # Revactor >= 0.1.5 includes UNIX domain socket support
  # s.add_dependency(%q, [">= 0.1.5"])
  #
  # Revactor depends on Rev, too, 0.3.0 got the ability to attach IOs
  # s.add_dependency(%q, [">= 0.3.2"])
  #
  # Cool.io is the new Rev, but it doesn't work with Revactor
  # s.add_dependency(%q, [">= 1.0"])
  #
  # Rev depends on IOBuffer, which got faster in 0.1.3
  # s.add_dependency(%q, [">= 0.1.3"])
  #
  # We use the new EM::attach/watch API in 0.12.10
  # s.add_dependency(%q, ["~> 0.12.10"])
  #
  # NeverBlock, currently only available on http://gems.github.com/
  # s.add_dependency(%q, ["~> 0.1.6.1"])

  # We inherited the Ruby 1.8 license from Mongrel, so we're stuck with it.
  # GPLv3 is preferred.
  s.licenses = ["GPLv2+", "Ruby 1.8"]
end
rainbows-4.6.2/man/0000755000004100000410000000000012354770160014157 5ustar  www-datawww-datarainbows-4.6.2/man/man1/0000755000004100000410000000000012354770160015013 5ustar  www-datawww-datarainbows-4.6.2/man/man1/rainbows.10000644000004100000410000001356312354770160016731 0ustar  www-datawww-data.TH rainbows 1 "December 3, 2009" "Rainbows! User Manual"
.SH NAME
.PP
rainbows - rackup-like command to launch Rainbows!
.SH SYNOPSIS
.PP
rainbows [-c CONFIG_FILE] [-E RACK_ENV] [-D] [RACKUP_FILE]
.SH DESCRIPTION
.PP
A rackup(1)-like command to launch Rack applications using
Rainbows!.
It is expected to be started in your application root (APP_ROOT),
but the \[lq]working_directory\[rq] directive may be used in the
CONFIG_FILE.
.PP
While Rainbows! takes a myriad of command-line options for
compatibility with ruby(1) and rackup(1), it is recommended to
stick to the few command-line options specified in the SYNOPSIS and
use the CONFIG_FILE as much as possible.
.SH RACKUP FILE
.PP
This defaults to "config.ru" in APP_ROOT.
It should be the same file used by rackup(1) and other Rack
launchers, it uses the \f[I]Rack::Builder\f[] DSL.
.PP
Embedded command-line options are mostly parsed for compatibility
with rackup(1) but strongly discouraged.
.SH UNICORN OPTIONS
.TP
.B -c, --config-file CONFIG_FILE
Path to the Unicorn-specific config file.
The config file is implemented as a Ruby DSL, so Ruby code may
executed.
See the RDoc/ri for the \f[I]Unicorn::Configurator\f[] class for
the full list of directives available from the DSL.
.RS
.RE
.TP
.B -D, --daemonize
Run daemonized in the background.
The process is detached from the controlling terminal and stdin is
redirected to \[lq]/dev/null\[rq].
Unlike many common UNIX daemons, we do not chdir to "/" upon
daemonization to allow more control over the startup/upgrade
process.
Unless specified in the CONFIG_FILE, stderr and stdout will also be
redirected to \[lq]/dev/null\[rq].
.RS
.RE
.TP
.B -E, --env RACK_ENV
Run under the given RACK_ENV.
See the RACK ENVIRONMENT section for more details.
.RS
.RE
.TP
.B -l, --listen ADDRESS
Listens on a given ADDRESS.
ADDRESS may be in the form of HOST:PORT or PATH, HOST:PORT is taken
to mean a TCP socket and PATH is meant to be a path to a UNIX
domain socket.
Defaults to \[lq]0.0.0.0:8080\[rq] (all addresses on TCP port 8080)
For production deployments, specifying the \[lq]listen\[rq]
directive in CONFIG_FILE is recommended as it allows fine-tuning of
socket options.
.RS
.RE
.SH RACKUP COMPATIBILITY OPTIONS
.TP
.B -o, --host HOST
Listen on a TCP socket belonging to HOST, default is
\[lq]0.0.0.0\[rq] (all addresses).
If specified multiple times on the command-line, only the
last-specified value takes effect.
This option only exists for compatibility with the rackup(1)
command, use of \[lq]-l\[rq]/\[lq]--listen\[rq] switch is
recommended instead.
.RS
.RE
.TP
.B -p, --port PORT
Listen on the specified TCP PORT, default is 8080.
If specified multiple times on the command-line, only the
last-specified value takes effect.
This option only exists for compatibility with the rackup(1)
command, use of \[lq]-l\[rq]/\[lq]--listen\[rq] switch is
recommended instead.
.RS
.RE
.TP
.B -s, --server SERVER
No-op, this exists only for compatibility with rackup(1).
.RS
.RE
.SH RUBY OPTIONS
.TP
.B -e, --eval LINE
Evaluate a LINE of Ruby code.
This evaluation happens immediately as the command-line is being
parsed.
.RS
.RE
.TP
.B -d, --debug
Turn on debug mode, the $DEBUG variable is set to true.
.RS
.RE
.TP
.B -w, --warn
Turn on verbose warnings, the $VERBOSE variable is set to true.
.RS
.RE
.TP
.B -I, --include PATH
specify $LOAD_PATH.
PATH will be prepended to $LOAD_PATH.
The \[aq]:\[aq] character may be used to delimit multiple
directories.
This directive may be used more than once.
Modifications to $LOAD_PATH take place immediately and in the order
they were specified on the command-line.
.RS
.RE
.TP
.B -r, --require LIBRARY
require a specified LIBRARY before executing the application.
The "require" statement will be executed immediately and in the
order they were specified on the command-line.
.RS
.RE
.SH SIGNALS
.PP
The following UNIX signals may be sent to the master process:
.IP \[bu] 2
HUP - reload config file, app, and gracefully restart all workers
.IP \[bu] 2
INT/TERM - quick shutdown, kills all workers immediately
.IP \[bu] 2
QUIT - graceful shutdown, waits for workers to finish their current
request before finishing.
.IP \[bu] 2
USR1 - reopen all logs owned by the master and all workers See
Unicorn::Util.reopen_logs for what is considered a log.
.IP \[bu] 2
USR2 - reexecute the running binary.
A separate QUIT should be sent to the original process once the
child is verified to be up and running.
.IP \[bu] 2
WINCH - gracefully stops workers but keep the master running.
This will only work for daemonized processes.
.IP \[bu] 2
TTIN - increment the number of worker processes by one
.IP \[bu] 2
TTOU - decrement the number of worker processes by one
.PP
See the SIGNALS (http://rainbows.rubyforge.org/SIGNALS.html)
document for full description of all signals used by Rainbows!.
.SH RACK ENVIRONMENT
.PP
Accepted values of RACK_ENV and the middleware they automatically
load (outside of RACKUP_FILE) are exactly as those in rackup(1):
.IP \[bu] 2
development - loads Rack::CommonLogger, Rack::ShowExceptions, and
Rack::Lint middleware
.IP \[bu] 2
deployment - loads Rack::CommonLogger middleware
.IP \[bu] 2
none - loads no middleware at all, relying entirely on RACKUP_FILE
.PP
All unrecognized values for RACK_ENV are assumed to be
\[lq]none\[rq].
Production deployments are strongly encouraged to use
\[lq]deployment\[rq] or \[lq]none\[rq] for maximum performance.
.PP
Note that the Rack::ContentLength and Rack::Chunked middlewares are
never loaded by default.
If needed, they should be individually specified in the
RACKUP_FILE, some frameworks do not require them.
.SH SEE ALSO
.IP \[bu] 2
unicorn(1)
.IP \[bu] 2
\f[I]Rack::Builder\f[] ri/RDoc
.IP \[bu] 2
\f[I]Unicorn::Configurator\f[] ri/RDoc
.IP \[bu] 2
Rainbows! RDoc (http://rainbows.rubyforge.org/)
.IP \[bu] 2
Rack RDoc (http://rack.rubyforge.org/doc/)
.IP \[bu] 2
Rackup HowTo (http://wiki.github.com/rack/rack/tutorial-rackup-howto)
.SH AUTHOR
Rainbows! Hackers 
rainbows-4.6.2/HACKING0000644000004100000410000000422712354770160014400 0ustar  www-datawww-data= Rainbows! Hacker's Guide

=== Tests

All tests are written in POSIX shell.  See README file in the t/ directory.

=== Documentation

We use RDoc 3.9.x with Darkfish for documentation as much as possible,
if you're on Ruby 1.8 you want to install the latest "rdoc" gem.  Due to
the lack of RDoc-to-manpage converters we know about, we're writing
manpages in Markdown and converting to troff/HTML with Pandoc.

Please wrap documentation at 72 characters-per-line or less (long URLs
are exempt) so it is comfortably readable from terminals.

When referencing mailing list posts, use
"http://bogomips.org/rainbows-public/m/$MESSAGE_ID" if possible since
the Message-ID remains searchable even if the website becomes unavailable.

== Contributing

Contributions are welcome in the form of patches, pull requests, code
review, testing, documentation, user support or any other feedback is
welcome.  The mailing list is the central coordination point for all
user and developer feedback and bug reports.

=== Submitting Patches

Follow conventions already established in the code and do not exceed 80
characters per line.

Inline patches (from "git format-patch -M") to the mailing list are
preferred because they allow code review and comments in the reply to
the patch.

We will adhere to mostly the same conventions for patch submissions as
git itself.  See the Documentation/SubmittingPatches document
distributed with git on on patch submission guidelines to follow.  Just
don't email the git mailing list or maintainer with Rainbows! patches :)

No subscription is required to post to the mailing list at
rainbows-public@bogomips.org

Please Cc: everyone on replies, as not everyone is subscribed.

== Building a Gem

In order to build the gem, you must install the following components:

 * wrongdoc
 * pandoc

You can build the Unicorn gem with the following command:

  gmake gem

== Running Development Versions

It is easy to install the contents of your git working directory:

Via RubyGems (recommended):

  gmake install-gem

Without RubyGems (via setup.rb):

  ruby setup.rb

It is not at all recommended to mix a RubyGems installation with an
installation done without RubyGems, however.
rainbows-4.6.2/archive/0000755000004100000410000000000012354770160015025 5ustar  www-datawww-datarainbows-4.6.2/archive/slrnpull.conf0000644000004100000410000000027512354770160017553 0ustar  www-datawww-data# group_name                         max        expire     headers_only
gmane.comp.lang.ruby.rainbows.general 1000000000 1000000000 0

# usage: slrnpull -d $PWD -h news.gmane.org --no-post
rainbows-4.6.2/archive/.gitignore0000644000004100000410000000002612354770160017013 0ustar  www-datawww-data/data
/news
/requests
rainbows-4.6.2/archive/rfmig.rb0000644000004100000410000000173412354770160016463 0ustar  www-datawww-data#!/usr/bin/env ruby
require 'find'
require 'fileutils'
rfdir = 'rubyforge.org:/var/www/gforge-projects/rainbows/'
newbase = 'http://rainbows.bogomips.org/'
refresh = ''
old = 'rf.old'
new = 'rf.new'
cmd = %W(rsync -av #{rfdir} #{old}/)
unless File.directory?(old)
  system(*cmd) or abort "#{cmd.inspect} failed: #$?"
end

Find.find(old) do |path|
  path =~ /\.html\z/ or next
  data = File.read(path)
  tmp = path.split(%r{/})
  tmp.shift == old or abort "BUG"
  dst = "#{new}/#{tmp.join('/')}"

  tmp[-1] = '' if tmp[-1] == "index.html"
  url = "#{newbase}#{tmp.join('/')}"
  meta = sprintf(refresh, url)
  data.sub!(/(]*>)/i, "#$1#{meta}")
  data.sub!(/(]*>)/i,
            "#{$1}Redirecting to #{url} ...
") FileUtils.mkdir_p(File.dirname(dst)) File.open(dst, "w") { |fp| fp.write(data) } end print "Verify results in #{new}/, then run:\n " puts %W(rsync -av #{new}/ #{rfdir}).join(' ') rainbows-4.6.2/TUNING0000644000004100000410000000375212354770160014342 0ustar www-datawww-data= Tuning \Rainbows! Most of the {tuning notes}[http://unicorn.bogomips.org/TUNING.html] apply to \Rainbows! as well. \Rainbows! is not particularly optimized at the moment and is designed for applications that spend large amounts of the time waiting on network activity. Thus memory usage and memory bandwidth for keeping connections open are often limiting factors as well. As of October 2009, absolutely ZERO work has been done for performance validation and tuning. Furthermore, \Rainbows! is NOT expected to do well on traditional benchmarks. Remember that \Rainbows! is only designed for applications that sleep and/or trickle network traffic. In the future, *may* do well in traditional benchmarks as a side effect, but that will never be the primary goal of the project. == \Rainbows! configuration * Don't set +worker_connections+ too high. It is often better to start denying requests and only serve the clients you can than to be completely bogged down and be unusable for everybody. * Increase +worker_processes+ if you have resources (RAM/DB connections) available. Additional worker processes can better utilize SMP, are more robust against crashes and are more likely to be fairly scheduled by the kernel. * If your workers do not seem to be releasing memory to the OS after traffic spikes, consider the {mall}[http://bogomips.org/mall/] library which allows access to the mallopt(3) function from Ruby. As of October 2009 tcmalloc (the default allocator for Ruby Enterprise Edition) does not release memory back to the kernel, the best it can do is use madvise(2) in an effort to swap out unused pages. == nginx configuration If you intend to use nginx as a reverse-proxy in front of \Rainbows! to handle Comet applications, make sure you disable proxy response buffering in nginx: proxy_buffering off; This can be disabled on a per-backend basis in nginx, so under no circumstances should you disable response buffering to Unicorn backends, only to \Rainbows! backends. rainbows-4.6.2/.gitignore0000644000004100000410000000031412354770160015372 0ustar www-datawww-data*.bundle *.log *.so *.rbc .DS_Store /.config /InstalledFiles /doc /local.mk /test/install-* log/ pkg/ /vendor /NEWS /ChangeLog /.manifest /GIT-VERSION-FILE /man /LATEST tags TAGS /lib/rainbows/version.rb rainbows-4.6.2/pkg.mk0000644000004100000410000001215612354770160014523 0ustar www-datawww-dataRUBY = ruby RAKE = rake RSYNC = rsync WRONGDOC = wrongdoc GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE @./GIT-VERSION-GEN -include GIT-VERSION-FILE -include local.mk DLEXT := $(shell $(RUBY) -rrbconfig -e 'puts RbConfig::CONFIG["DLEXT"]') RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION') RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))') lib := lib ifeq ($(shell test -f script/isolate_for_tests && echo t),t) isolate_libs := tmp/isolate/$(RUBY_ENGINE)-$(RUBY_VERSION)/isolate.mk $(isolate_libs): script/isolate_for_tests @$(RUBY) script/isolate_for_tests -include $(isolate_libs) lib := $(lib):$(ISOLATE_LIBS) endif ext := $(firstword $(wildcard ext/*)) ifneq ($(ext),) ext_pfx := tmp/ext/$(RUBY_ENGINE)-$(RUBY_VERSION) ext_h := $(wildcard $(ext)/*/*.h $(ext)/*.h) ext_src := $(wildcard $(ext)/*.c $(ext_h)) ext_pfx_src := $(addprefix $(ext_pfx)/,$(ext_src)) ext_d := $(ext_pfx)/$(ext)/.d $(ext)/extconf.rb: $(wildcard $(ext)/*.h) @>> $@ $(ext_d): @mkdir -p $(@D) @> $@ $(ext_pfx)/$(ext)/%: $(ext)/% $(ext_d) install -m 644 $< $@ $(ext_pfx)/$(ext)/Makefile: $(ext)/extconf.rb $(ext_d) $(ext_h) $(RM) -f $(@D)/*.o cd $(@D) && $(RUBY) $(CURDIR)/$(ext)/extconf.rb ext_sfx := _ext.$(DLEXT) ext_dl := $(ext_pfx)/$(ext)/$(notdir $(ext)_ext.$(DLEXT)) $(ext_dl): $(ext_src) $(ext_pfx_src) $(ext_pfx)/$(ext)/Makefile @echo $^ == $@ $(MAKE) -C $(@D) lib := $(lib):$(ext_pfx)/$(ext) build: $(ext_dl) else build: endif pkg_extra += GIT-VERSION-FILE NEWS ChangeLog LATEST ChangeLog: GIT-VERSION-FILE .wrongdoc.yml $(WRONGDOC) prepare NEWS LATEST: ChangeLog manifest: $(RM) .manifest $(MAKE) .manifest .manifest: $(pkg_extra) (git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \ LC_ALL=C sort > $@+ cmp $@+ $@ || mv $@+ $@ $(RM) $@+ doc:: .document .wrongdoc.yml $(pkg_extra) -find lib -type f -name '*.rbc' -exec rm -f '{}' ';' -find ext -type f -name '*.rbc' -exec rm -f '{}' ';' $(RM) -r doc $(WRONGDOC) all install -m644 COPYING doc/COPYING install -m644 $(shell LC_ALL=C grep '^[A-Z]' .document) doc/ ifneq ($(VERSION),) pkggem := pkg/$(rfpackage)-$(VERSION).gem pkgtgz := pkg/$(rfpackage)-$(VERSION).tgz release_notes := release_notes-$(VERSION) release_changes := release_changes-$(VERSION) release-notes: $(release_notes) release-changes: $(release_changes) $(release_changes): $(WRONGDOC) release_changes > $@+ $(VISUAL) $@+ && test -s $@+ && mv $@+ $@ $(release_notes): $(WRONGDOC) release_notes > $@+ $(VISUAL) $@+ && test -s $@+ && mv $@+ $@ # ensures we're actually on the tagged $(VERSION), only used for release verify: test x"$(shell umask)" = x0022 git rev-parse --verify refs/tags/v$(VERSION)^{} git diff-index --quiet HEAD^0 test $$(git rev-parse --verify HEAD^0) = \ $$(git rev-parse --verify refs/tags/v$(VERSION)^{}) fix-perms: -git ls-tree -r HEAD | awk '/^100644 / {print $$NF}' | xargs chmod 644 -git ls-tree -r HEAD | awk '/^100755 / {print $$NF}' | xargs chmod 755 gem: $(pkggem) install-gem: $(pkggem) gem install $(CURDIR)/$< $(pkggem): manifest fix-perms gem build $(rfpackage).gemspec mkdir -p pkg mv $(@F) $@ $(pkgtgz): distdir = $(basename $@) $(pkgtgz): HEAD = v$(VERSION) $(pkgtgz): manifest fix-perms @test -n "$(distdir)" $(RM) -r $(distdir) mkdir -p $(distdir) tar cf - $$(cat .manifest) | (cd $(distdir) && tar xf -) cd pkg && tar cf - $(basename $(@F)) | gzip -9 > $(@F)+ mv $@+ $@ package: $(pkgtgz) $(pkggem) test-release:: verify package $(release_notes) $(release_changes) # make tgz release on RubyForge @echo rubyforge add_release -f \ -n $(release_notes) -a $(release_changes) \ $(rfproject) $(rfpackage) $(VERSION) $(pkgtgz) @echo gem push $(pkggem) @echo rubyforge add_file \ $(rfproject) $(rfpackage) $(VERSION) $(pkggem) release:: verify package $(release_notes) $(release_changes) # make tgz release on RubyForge rubyforge add_release -f -n $(release_notes) -a $(release_changes) \ $(rfproject) $(rfpackage) $(VERSION) $(pkgtgz) # push gem to RubyGems.org gem push $(pkggem) # in case of gem downloads from RubyForge releases page rubyforge add_file \ $(rfproject) $(rfpackage) $(VERSION) $(pkggem) else gem install-gem: GIT-VERSION-FILE $(MAKE) $@ VERSION=$(GIT_VERSION) endif all:: test test_units := $(wildcard test/test_*.rb) test: test-unit test-unit: $(test_units) $(test_units): build $(RUBY) -I $(lib) $@ $(RUBY_TEST_OPTS) # this requires GNU coreutils variants ifneq ($(RSYNC_DEST),) publish_doc: -git set-file-times $(MAKE) doc find doc/images -type f | \ TZ=UTC xargs touch -d '1970-01-01 00:00:06' doc/rdoc.css $(MAKE) doc_gz $(RSYNC) -av doc/ $(RSYNC_DEST)/ git ls-files | xargs touch endif # Create gzip variants of the same timestamp as the original so nginx # "gzip_static on" can serve the gzipped versions directly. doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$') doc_gz: for i in $(docs); do \ gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done check-warnings: @(for i in $$(git ls-files '*.rb'| grep -v '^setup\.rb$$'); \ do $(RUBY) -d -W2 -c $$i; done) | grep -v '^Syntax OK$$' || : .PHONY: all .FORCE-GIT-VERSION-FILE doc test $(test_units) manifest .PHONY: check-warnings rainbows-4.6.2/.wrongdoc.yml0000644000004100000410000000056712354770160016037 0ustar www-datawww-data--- cgit_url: http://bogomips.org/rainbows.git git_url: git://bogomips.org/rainbows.git rdoc_url: http://rainbows.bogomips.org/ changelog_start: v0.97.0 merge_html: rainbows_1: Documentation/rainbows.1.html Summary: Documentation/comparison.html public_email: rainbows-public@bogomips.org private_email: rainbows@bogomips.org ml_url: http://bogomips.org/rainbows-public/ rainbows-4.6.2/GIT-VERSION-FILE0000644000004100000410000000002412354770160015606 0ustar www-datawww-dataGIT_VERSION = 4.6.2 rainbows-4.6.2/LICENSE0000644000004100000410000000564312354770160014421 0ustar www-datawww-data\Rainbows! is copyrighted Free Software by all contributors, see the logs in revision control for names and email addresses of all of them. You can redistribute it and/or modify it under either the terms of the GNU General Public License (GPL) as published by the Free Software Foundation (FSF), either version 2 of the License, or (at your option) any later version. We currently prefer the GPLv3 or later for derivative works, but the GPLv2 is fine. The complete texts of the GPLv2 and GPLv3 are below: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.txt GPLv3 - http://www.gnu.org/licenses/gpl-3.0.txt You may (against our _preference_) also use the Ruby 1.8 license terms. === Ruby 1.8-specific terms (if you're not using the GPLv2/GPLv3) 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b) use the modified software only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided. d) make other distribution arrangements with the author. 3. You may distribute the software in object code or executable form, provided that you do at least ONE of the following: a) distribute the executables and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b) accompany the distribution with the machine-readable source of the software. c) give non-standard executables non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under this terms. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. rainbows-4.6.2/setup.rb0000644000004100000410000010652612354770160015103 0ustar www-datawww-data# -*- encoding: binary -*- # # setup.rb # # Copyright (c) 2000-2005 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # unless Enumerable.method_defined?(:map) # Ruby 1.4.6 module Enumerable alias map collect end end unless File.respond_to?(:read) # Ruby 1.6 def File.read(fname) open(fname) {|f| return f.read } end end unless Errno.const_defined?(:ENOTEMPTY) # Windows? module Errno class ENOTEMPTY # We do not raise this exception, implementation is not needed. end end end def File.binread(fname) open(fname, 'rb') {|f| return f.read } end # for corrupted Windows' stat(2) def File.dir?(path) File.directory?((path[-1,1] == '/') ? path : path + '/') end class ConfigTable include Enumerable def initialize(rbconfig) @rbconfig = rbconfig @items = [] @table = {} # options @install_prefix = nil @config_opt = nil @verbose = true @no_harm = false end attr_accessor :install_prefix attr_accessor :config_opt attr_writer :verbose def verbose? @verbose end attr_writer :no_harm def no_harm? @no_harm end def [](key) lookup(key).resolve(self) end def []=(key, val) lookup(key).set val end def names @items.map {|i| i.name } end def each(&block) @items.each(&block) end def key?(name) @table.key?(name) end def lookup(name) @table[name] or setup_rb_error "no such config item: #{name}" end def add(item) @items.push item @table[item.name] = item end def remove(name) item = lookup(name) @items.delete_if {|i| i.name == name } @table.delete_if {|name, i| i.name == name } item end def load_script(path, inst = nil) if File.file?(path) MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path end end def savefile '.config' end def load_savefile begin File.foreach(savefile()) do |line| k, v = *line.split(/=/, 2) self[k] = v.strip end rescue Errno::ENOENT setup_rb_error $!.message + "\n#{File.basename($0)} config first" end end def save @items.each {|i| i.value } File.open(savefile(), 'w') {|f| @items.each do |i| f.printf "%s=%s\n", i.name, i.value if i.value? and i.value end } end def load_standard_entries standard_entries(@rbconfig).each do |ent| add ent end end def standard_entries(rbconfig) c = rbconfig rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) major = c['MAJOR'].to_i minor = c['MINOR'].to_i teeny = c['TEENY'].to_i version = "#{major}.#{minor}" # ruby ver. >= 1.4.4? newpath_p = ((major >= 2) or ((major == 1) and ((minor >= 5) or ((minor == 4) and (teeny >= 4))))) if c['rubylibdir'] # V > 1.6.3 libruby = "#{c['prefix']}/lib/ruby" librubyver = c['rubylibdir'] librubyverarch = c['archdir'] siteruby = c['sitedir'] siterubyver = c['sitelibdir'] siterubyverarch = c['sitearchdir'] elsif newpath_p # 1.4.4 <= V <= 1.6.3 libruby = "#{c['prefix']}/lib/ruby" librubyver = "#{c['prefix']}/lib/ruby/#{version}" librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" siteruby = c['sitedir'] siterubyver = "$siteruby/#{version}" siterubyverarch = "$siterubyver/#{c['arch']}" else # V < 1.4.4 libruby = "#{c['prefix']}/lib/ruby" librubyver = "#{c['prefix']}/lib/ruby/#{version}" librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" siterubyver = siteruby siterubyverarch = "$siterubyver/#{c['arch']}" end parameterize = lambda {|path| path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') } if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } makeprog = arg.sub(/'/, '').split(/=/, 2)[1] else makeprog = 'make' end [ ExecItem.new('installdirs', 'std/site/home', 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ {|val, table| case val when 'std' table['rbdir'] = '$librubyver' table['sodir'] = '$librubyverarch' when 'site' table['rbdir'] = '$siterubyver' table['sodir'] = '$siterubyverarch' when 'home' setup_rb_error '$HOME was not set' unless ENV['HOME'] table['prefix'] = ENV['HOME'] table['rbdir'] = '$libdir/ruby' table['sodir'] = '$libdir/ruby' end }, PathItem.new('prefix', 'path', c['prefix'], 'path prefix of target environment'), PathItem.new('bindir', 'path', parameterize.call(c['bindir']), 'the directory for commands'), PathItem.new('libdir', 'path', parameterize.call(c['libdir']), 'the directory for libraries'), PathItem.new('datadir', 'path', parameterize.call(c['datadir']), 'the directory for shared data'), PathItem.new('mandir', 'path', parameterize.call(c['mandir']), 'the directory for man pages'), PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), 'the directory for system configuration files'), PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), 'the directory for local state data'), PathItem.new('libruby', 'path', libruby, 'the directory for ruby libraries'), PathItem.new('librubyver', 'path', librubyver, 'the directory for standard ruby libraries'), PathItem.new('librubyverarch', 'path', librubyverarch, 'the directory for standard ruby extensions'), PathItem.new('siteruby', 'path', siteruby, 'the directory for version-independent aux ruby libraries'), PathItem.new('siterubyver', 'path', siterubyver, 'the directory for aux ruby libraries'), PathItem.new('siterubyverarch', 'path', siterubyverarch, 'the directory for aux ruby binaries'), PathItem.new('rbdir', 'path', '$siterubyver', 'the directory for ruby scripts'), PathItem.new('sodir', 'path', '$siterubyverarch', 'the directory for ruby extentions'), PathItem.new('rubypath', 'path', rubypath, 'the path to set to #! line'), ProgramItem.new('rubyprog', 'name', rubypath, 'the ruby program using for installation'), ProgramItem.new('makeprog', 'name', makeprog, 'the make program to compile ruby extentions'), SelectItem.new('shebang', 'all/ruby/never', 'ruby', 'shebang line (#!) editing mode'), BoolItem.new('without-ext', 'yes/no', 'no', 'does not compile/install ruby extentions') ] end private :standard_entries def load_multipackage_entries multipackage_entries().each do |ent| add ent end end def multipackage_entries [ PackageSelectionItem.new('with', 'name,name...', '', 'ALL', 'package names that you want to install'), PackageSelectionItem.new('without', 'name,name...', '', 'NONE', 'package names that you do not want to install') ] end private :multipackage_entries ALIASES = { 'std-ruby' => 'librubyver', 'stdruby' => 'librubyver', 'rubylibdir' => 'librubyver', 'archdir' => 'librubyverarch', 'site-ruby-common' => 'siteruby', # For backward compatibility 'site-ruby' => 'siterubyver', # For backward compatibility 'bin-dir' => 'bindir', 'bin-dir' => 'bindir', 'rb-dir' => 'rbdir', 'so-dir' => 'sodir', 'data-dir' => 'datadir', 'ruby-path' => 'rubypath', 'ruby-prog' => 'rubyprog', 'ruby' => 'rubyprog', 'make-prog' => 'makeprog', 'make' => 'makeprog' } def fixup ALIASES.each do |ali, name| @table[ali] = @table[name] end @items.freeze @table.freeze @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ end def parse_opt(opt) m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" m.to_a[1,2] end def dllext @rbconfig['DLEXT'] end def value_config?(name) lookup(name).value? end class Item def initialize(name, template, default, desc) @name = name.freeze @template = template @value = default @default = default @description = desc end attr_reader :name attr_reader :description attr_accessor :default alias help_default default def help_opt "--#{@name}=#{@template}" end def value? true end def value @value end def resolve(table) @value.gsub(%r<\$([^/]+)>) { table[$1] } end def set(val) @value = check(val) end private def check(val) setup_rb_error "config: --#{name} requires argument" unless val val end end class BoolItem < Item def config_type 'bool' end def help_opt "--#{@name}" end private def check(val) return 'yes' unless val case val when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' else setup_rb_error "config: --#{@name} accepts only yes/no for argument" end end end class PathItem < Item def config_type 'path' end private def check(path) setup_rb_error "config: --#{@name} requires argument" unless path path[0,1] == '$' ? path : File.expand_path(path) end end class ProgramItem < Item def config_type 'program' end end class SelectItem < Item def initialize(name, selection, default, desc) super @ok = selection.split('/') end def config_type 'select' end private def check(val) unless @ok.include?(val.strip) setup_rb_error "config: use --#{@name}=#{@template} (#{val})" end val.strip end end class ExecItem < Item def initialize(name, selection, desc, &block) super name, selection, nil, desc @ok = selection.split('/') @action = block end def config_type 'exec' end def value? false end def resolve(table) setup_rb_error "$#{name()} wrongly used as option value" end undef set def evaluate(val, table) v = val.strip.downcase unless @ok.include?(v) setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" end @action.call v, table end end class PackageSelectionItem < Item def initialize(name, template, default, help_default, desc) super name, template, default, desc @help_default = help_default end attr_reader :help_default def config_type 'package' end private def check(val) unless File.dir?("packages/#{val}") setup_rb_error "config: no such package: #{val}" end val end end class MetaConfigEnvironment def initialize(config, installer) @config = config @installer = installer end def config_names @config.names end def config?(name) @config.key?(name) end def bool_config?(name) @config.lookup(name).config_type == 'bool' end def path_config?(name) @config.lookup(name).config_type == 'path' end def value_config?(name) @config.lookup(name).config_type != 'exec' end def add_config(item) @config.add item end def add_bool_config(name, default, desc) @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) end def add_path_config(name, default, desc) @config.add PathItem.new(name, 'path', default, desc) end def set_config_default(name, default) @config.lookup(name).default = default end def remove_config(name) @config.remove(name) end # For only multipackage def packages raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer @installer.packages end # For only multipackage def declare_packages(list) raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer @installer.packages = list end end end # class ConfigTable # This module requires: #verbose?, #no_harm? module FileOperations def mkdir_p(dirname, prefix = nil) dirname = prefix + File.expand_path(dirname) if prefix $stderr.puts "mkdir -p #{dirname}" if verbose? return if no_harm? # Does not check '/', it's too abnormal. dirs = File.expand_path(dirname).split(%r<(?=/)>) if /\A[a-z]:\z/i =~ dirs[0] disk = dirs.shift dirs[0] = disk + dirs[0] end dirs.each_index do |idx| path = dirs[0..idx].join('') Dir.mkdir path unless File.dir?(path) end end def rm_f(path) $stderr.puts "rm -f #{path}" if verbose? return if no_harm? force_remove_file path end def rm_rf(path) $stderr.puts "rm -rf #{path}" if verbose? return if no_harm? remove_tree path end def remove_tree(path) if File.symlink?(path) remove_file path elsif File.dir?(path) remove_tree0 path else force_remove_file path end end def remove_tree0(path) Dir.foreach(path) do |ent| next if ent == '.' next if ent == '..' entpath = "#{path}/#{ent}" if File.symlink?(entpath) remove_file entpath elsif File.dir?(entpath) remove_tree0 entpath else force_remove_file entpath end end begin Dir.rmdir path rescue Errno::ENOTEMPTY # directory may not be empty end end def move_file(src, dest) force_remove_file dest begin File.rename src, dest rescue File.open(dest, 'wb') {|f| f.write File.binread(src) } File.chmod File.stat(src).mode, dest File.unlink src end end def force_remove_file(path) begin remove_file path rescue end end def remove_file(path) File.chmod 0777, path File.unlink path end def install(from, dest, mode, prefix = nil) $stderr.puts "install #{from} #{dest}" if verbose? return if no_harm? realdest = prefix ? prefix + File.expand_path(dest) : dest realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) str = File.binread(from) if diff?(str, realdest) verbose_off { rm_f realdest if File.exist?(realdest) } File.open(realdest, 'wb') {|f| f.write str } File.chmod mode, realdest File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| if prefix f.puts realdest.sub(prefix, '') else f.puts realdest end } end end def diff?(new_content, path) return true unless File.exist?(path) new_content != File.binread(path) end def command(*args) $stderr.puts args.join(' ') if verbose? system(*args) or raise RuntimeError, "system(#{args.map{|a| a.inspect }.join(' ')}) failed" end def ruby(*args) command config('rubyprog'), *args end def make(task = nil) command(*[config('makeprog'), task].compact) end def extdir?(dir) File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") end def files_of(dir) Dir.open(dir) {|d| return d.select {|ent| File.file?("#{dir}/#{ent}") } } end DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) def directories_of(dir) Dir.open(dir) {|d| return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT } end end # This module requires: #srcdir_root, #objdir_root, #relpath module HookScriptAPI def get_config(key) @config[key] end alias config get_config # obsolete: use metaconfig to change configuration def set_config(key, val) @config[key] = val end # # srcdir/objdir (works only in the package directory) # def curr_srcdir "#{srcdir_root()}/#{relpath()}" end def curr_objdir "#{objdir_root()}/#{relpath()}" end def srcfile(path) "#{curr_srcdir()}/#{path}" end def srcexist?(path) File.exist?(srcfile(path)) end def srcdirectory?(path) File.dir?(srcfile(path)) end def srcfile?(path) File.file?(srcfile(path)) end def srcentries(path = '.') Dir.open("#{curr_srcdir()}/#{path}") {|d| return d.to_a - %w(. ..) } end def srcfiles(path = '.') srcentries(path).select {|fname| File.file?(File.join(curr_srcdir(), path, fname)) } end def srcdirectories(path = '.') srcentries(path).select {|fname| File.dir?(File.join(curr_srcdir(), path, fname)) } end end class ToplevelInstaller Version = '3.4.1' Copyright = 'Copyright (c) 2000-2005 Minero Aoki' TASKS = [ [ 'all', 'do config, setup, then install' ], [ 'config', 'saves your configurations' ], [ 'show', 'shows current configuration' ], [ 'setup', 'compiles ruby extentions and others' ], [ 'install', 'installs files' ], [ 'test', 'run all tests in test/' ], [ 'clean', "does `make clean' for each extention" ], [ 'distclean',"does `make distclean' for each extention" ] ] def ToplevelInstaller.invoke config = ConfigTable.new(load_rbconfig()) config.load_standard_entries config.load_multipackage_entries if multipackage? config.fixup klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) klass.new(File.dirname($0), config).invoke end def ToplevelInstaller.multipackage? File.dir?(File.dirname($0) + '/packages') end def ToplevelInstaller.load_rbconfig if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } ARGV.delete(arg) load File.expand_path(arg.split(/=/, 2)[1]) $".push 'rbconfig.rb' else require 'rbconfig' end ::Config::CONFIG end def initialize(ardir_root, config) @ardir = File.expand_path(ardir_root) @config = config # cache @valid_task_re = nil end def config(key) @config[key] end def inspect "#<#{self.class} #{__id__()}>" end def invoke run_metaconfigs case task = parsearg_global() when nil, 'all' parsearg_config init_installers exec_config exec_setup exec_install else case task when 'config', 'test' ; when 'clean', 'distclean' @config.load_savefile if File.exist?(@config.savefile) else @config.load_savefile end __send__ "parsearg_#{task}" init_installers __send__ "exec_#{task}" end end def run_metaconfigs @config.load_script "#{@ardir}/metaconfig" end def init_installers @installer = Installer.new(@config, @ardir, File.expand_path('.')) end # # Hook Script API bases # def srcdir_root @ardir end def objdir_root '.' end def relpath '.' end # # Option Parsing # def parsearg_global while arg = ARGV.shift case arg when /\A\w+\z/ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) return arg when '-q', '--quiet' @config.verbose = false when '--verbose' @config.verbose = true when '--help' print_usage $stdout exit 0 when '--version' puts "#{File.basename($0)} version #{Version}" exit 0 when '--copyright' puts Copyright exit 0 else setup_rb_error "unknown global option '#{arg}'" end end nil end def valid_task?(t) valid_task_re() =~ t end def valid_task_re @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ end def parsearg_no_options unless ARGV.empty? task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" end end alias parsearg_show parsearg_no_options alias parsearg_setup parsearg_no_options alias parsearg_test parsearg_no_options alias parsearg_clean parsearg_no_options alias parsearg_distclean parsearg_no_options def parsearg_config evalopt = [] set = [] @config.config_opt = [] while i = ARGV.shift if /\A--?\z/ =~ i @config.config_opt = ARGV.dup break end name, value = *@config.parse_opt(i) if @config.value_config?(name) @config[name] = value else evalopt.push [name, value] end set.push name end evalopt.each do |name, value| @config.lookup(name).evaluate value, @config end # Check if configuration is valid set.each do |n| @config[n] if @config.value_config?(n) end end def parsearg_install @config.no_harm = false @config.install_prefix = '' while a = ARGV.shift case a when '--no-harm' @config.no_harm = true when /\A--prefix=/ path = a.split(/=/, 2)[1] path = File.expand_path(path) unless path[0,1] == '/' @config.install_prefix = path else setup_rb_error "install: unknown option #{a}" end end end def print_usage(out) out.puts 'Typical Installation Procedure:' out.puts " $ ruby #{File.basename $0} config" out.puts " $ ruby #{File.basename $0} setup" out.puts " # ruby #{File.basename $0} install (may require root privilege)" out.puts out.puts 'Detailed Usage:' out.puts " ruby #{File.basename $0} " out.puts " ruby #{File.basename $0} [] []" fmt = " %-24s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-q,--quiet', 'suppress message outputs' out.printf fmt, ' --verbose', 'output messages verbosely' out.printf fmt, ' --help', 'print this message' out.printf fmt, ' --version', 'print version and quit' out.printf fmt, ' --copyright', 'print copyright and quit' out.puts out.puts 'Tasks:' TASKS.each do |name, desc| out.printf fmt, name, desc end fmt = " %-24s %s [%s]\n" out.puts out.puts 'Options for CONFIG or ALL:' @config.each do |item| out.printf fmt, item.help_opt, item.description, item.help_default end out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" out.puts out.puts 'Options for INSTALL:' out.printf fmt, '--no-harm', 'only display what to do if given', 'off' out.printf fmt, '--prefix=path', 'install path prefix', '' out.puts end # # Task Handlers # def exec_config @installer.exec_config @config.save # must be final end def exec_setup @installer.exec_setup end def exec_install @installer.exec_install end def exec_test @installer.exec_test end def exec_show @config.each do |i| printf "%-20s %s\n", i.name, i.value if i.value? end end def exec_clean @installer.exec_clean end def exec_distclean @installer.exec_distclean end end # class ToplevelInstaller class ToplevelInstallerMulti < ToplevelInstaller include FileOperations def initialize(ardir_root, config) super @packages = directories_of("#{@ardir}/packages") raise 'no package exists' if @packages.empty? @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) end def run_metaconfigs @config.load_script "#{@ardir}/metaconfig", self @packages.each do |name| @config.load_script "#{@ardir}/packages/#{name}/metaconfig" end end attr_reader :packages def packages=(list) raise 'package list is empty' if list.empty? list.each do |name| raise "directory packages/#{name} does not exist"\ unless File.dir?("#{@ardir}/packages/#{name}") end @packages = list end def init_installers @installers = {} @packages.each do |pack| @installers[pack] = Installer.new(@config, "#{@ardir}/packages/#{pack}", "packages/#{pack}") end with = extract_selection(config('with')) without = extract_selection(config('without')) @selected = @installers.keys.select {|name| (with.empty? or with.include?(name)) \ and not without.include?(name) } end def extract_selection(list) a = list.split(/,/) a.each do |name| setup_rb_error "no such package: #{name}" unless @installers.key?(name) end a end def print_usage(f) super f.puts 'Inluded packages:' f.puts ' ' + @packages.sort.join(' ') f.puts end # # Task Handlers # def exec_config run_hook 'pre-config' each_selected_installers {|inst| inst.exec_config } run_hook 'post-config' @config.save # must be final end def exec_setup run_hook 'pre-setup' each_selected_installers {|inst| inst.exec_setup } run_hook 'post-setup' end def exec_install run_hook 'pre-install' each_selected_installers {|inst| inst.exec_install } run_hook 'post-install' end def exec_test run_hook 'pre-test' each_selected_installers {|inst| inst.exec_test } run_hook 'post-test' end def exec_clean rm_f @config.savefile run_hook 'pre-clean' each_selected_installers {|inst| inst.exec_clean } run_hook 'post-clean' end def exec_distclean rm_f @config.savefile run_hook 'pre-distclean' each_selected_installers {|inst| inst.exec_distclean } run_hook 'post-distclean' end # # lib # def each_selected_installers Dir.mkdir 'packages' unless File.dir?('packages') @selected.each do |pack| $stderr.puts "Processing the package `#{pack}' ..." if verbose? Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") Dir.chdir "packages/#{pack}" yield @installers[pack] Dir.chdir '../..' end end def run_hook(id) @root_installer.run_hook id end # module FileOperations requires this def verbose? @config.verbose? end # module FileOperations requires this def no_harm? @config.no_harm? end end # class ToplevelInstallerMulti class Installer FILETYPES = %w( bin lib ext data conf man ) include FileOperations include HookScriptAPI def initialize(config, srcroot, objroot) @config = config @srcdir = File.expand_path(srcroot) @objdir = File.expand_path(objroot) @currdir = '.' end def inspect "#<#{self.class} #{File.basename(@srcdir)}>" end def noop(rel) end # # Hook Script API base methods # def srcdir_root @srcdir end def objdir_root @objdir end def relpath @currdir end # # Config Access # # module FileOperations requires this def verbose? @config.verbose? end # module FileOperations requires this def no_harm? @config.no_harm? end def verbose_off begin save, @config.verbose = @config.verbose?, false yield ensure @config.verbose = save end end # # TASK config # def exec_config exec_task_traverse 'config' end alias config_dir_bin noop alias config_dir_lib noop def config_dir_ext(rel) extconf if extdir?(curr_srcdir()) end alias config_dir_data noop alias config_dir_conf noop alias config_dir_man noop def extconf ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt end # # TASK setup # def exec_setup exec_task_traverse 'setup' end def setup_dir_bin(rel) files_of(curr_srcdir()).each do |fname| update_shebang_line "#{curr_srcdir()}/#{fname}" end end alias setup_dir_lib noop def setup_dir_ext(rel) make if extdir?(curr_srcdir()) end alias setup_dir_data noop alias setup_dir_conf noop alias setup_dir_man noop def update_shebang_line(path) return if no_harm? return if config('shebang') == 'never' old = Shebang.load(path) if old $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 new = new_shebang(old) return if new.to_s == old.to_s else return unless config('shebang') == 'all' new = Shebang.new(config('rubypath')) end $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? open_atomic_writer(path) {|output| File.open(path, 'rb') {|f| f.gets if old # discard output.puts new.to_s output.print f.read } } end def new_shebang(old) if /\Aruby/ =~ File.basename(old.cmd) Shebang.new(config('rubypath'), old.args) elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' Shebang.new(config('rubypath'), old.args[1..-1]) else return old unless config('shebang') == 'all' Shebang.new(config('rubypath')) end end def open_atomic_writer(path, &block) tmpfile = File.basename(path) + '.tmp' begin File.open(tmpfile, 'wb', &block) File.rename tmpfile, File.basename(path) ensure File.unlink tmpfile if File.exist?(tmpfile) end end class Shebang def Shebang.load(path) line = nil File.open(path) {|f| line = f.gets } return nil unless /\A#!/ =~ line parse(line) end def Shebang.parse(line) cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') new(cmd, args) end def initialize(cmd, args = []) @cmd = cmd @args = args end attr_reader :cmd attr_reader :args def to_s "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") end end # # TASK install # def exec_install rm_f 'InstalledFiles' exec_task_traverse 'install' end def install_dir_bin(rel) install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 end def install_dir_lib(rel) install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 end def install_dir_ext(rel) return unless extdir?(curr_srcdir()) install_files rubyextentions('.'), "#{config('sodir')}/#{File.dirname(rel)}", 0555 end def install_dir_data(rel) install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 end def install_dir_conf(rel) # FIXME: should not remove current config files # (rename previous file to .old/.org) install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 end def install_dir_man(rel) install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 end def install_files(list, dest, mode) mkdir_p dest, @config.install_prefix list.each do |fname| install fname, dest, mode, @config.install_prefix end end def libfiles glob_reject(%w(*.y *.output), targetfiles()) end def rubyextentions(dir) ents = glob_select("*.#{@config.dllext}", targetfiles()) if ents.empty? setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" end ents end def targetfiles mapdir(existfiles() - hookfiles()) end def mapdir(ents) ents.map {|ent| if File.exist?(ent) then ent # objdir else "#{curr_srcdir()}/#{ent}" # srcdir end } end # picked up many entries from cvs-1.11.1/src/ignore.c JUNK_FILES = %w( core RCSLOG tags TAGS .make.state .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb *~ *.old *.bak *.BAK *.orig *.rej _$* *$ *.org *.in .* ) def existfiles glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) end def hookfiles %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| %w( config setup install clean ).map {|t| sprintf(fmt, t) } }.flatten end def glob_select(pat, ents) re = globs2re([pat]) ents.select {|ent| re =~ ent } end def glob_reject(pats, ents) re = globs2re(pats) ents.reject {|ent| re =~ ent } end GLOB2REGEX = { '.' => '\.', '$' => '\$', '#' => '\#', '*' => '.*' } def globs2re(pats) /\A(?:#{ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') })\z/ end # # TASK test # TESTDIR = 'test' def exec_test unless File.directory?('test') $stderr.puts 'no test in this package' if verbose? return end $stderr.puts 'Running tests...' if verbose? begin require 'test/unit' rescue LoadError setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' end runner = Test::Unit::AutoRunner.new(true) runner.to_run << TESTDIR runner.run end # # TASK clean # def exec_clean exec_task_traverse 'clean' rm_f @config.savefile rm_f 'InstalledFiles' end alias clean_dir_bin noop alias clean_dir_lib noop alias clean_dir_data noop alias clean_dir_conf noop alias clean_dir_man noop def clean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'clean' if File.file?('Makefile') end # # TASK distclean # def exec_distclean exec_task_traverse 'distclean' rm_f @config.savefile rm_f 'InstalledFiles' end alias distclean_dir_bin noop alias distclean_dir_lib noop def distclean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'distclean' if File.file?('Makefile') end alias distclean_dir_data noop alias distclean_dir_conf noop alias distclean_dir_man noop # # Traversing # def exec_task_traverse(task) run_hook "pre-#{task}" FILETYPES.each do |type| if type == 'ext' and config('without-ext') == 'yes' $stderr.puts 'skipping ext/* by user option' if verbose? next end traverse task, type, "#{task}_dir_#{type}" end run_hook "post-#{task}" end def traverse(task, rel, mid) dive_into(rel) { run_hook "pre-#{task}" __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') directories_of(curr_srcdir()).each do |d| traverse task, "#{rel}/#{d}", mid end run_hook "post-#{task}" } end def dive_into(rel) return unless File.dir?("#{@srcdir}/#{rel}") dir = File.basename(rel) Dir.mkdir dir unless File.dir?(dir) prevdir = Dir.pwd Dir.chdir dir $stderr.puts '---> ' + rel if verbose? @currdir = rel yield Dir.chdir prevdir $stderr.puts '<--- ' + rel if verbose? @currdir = File.dirname(rel) end def run_hook(id) path = [ "#{curr_srcdir()}/#{id}", "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } return unless path begin instance_eval File.read(path), path, 1 rescue raise if $DEBUG setup_rb_error "hook #{path} failed:\n" + $!.message end end end # class Installer class SetupError < StandardError; end def setup_rb_error(msg) raise SetupError, msg end if $0 == __FILE__ begin ToplevelInstaller.invoke rescue SetupError raise if $DEBUG $stderr.puts $!.message $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." exit 1 end end rainbows-4.6.2/Summary0000644000004100000410000000000012354770160014752 0ustar www-datawww-datarainbows-4.6.2/DEPLOY0000644000004100000410000000311512354770160014323 0ustar www-datawww-data= Deploying \Rainbows! == \Rainbows! only For the daring, you should consider deploying \Rainbows! in a standalone configuration. This will be more highly recommended as \Rainbows! stabilizes, especially if static file performance improves (or you don't need them). You will need to do this to support things like BOSH or do real-time processing of the request body as it is being uploaded. In this case, haproxy or any similar (non-request-body-buffering) load balancer should be used to balance requests between different machines. == nginx proxying to \Rainbows! or Unicorn For high-traffic applications, routing slow actions to \Rainbows! with nginx is recommended as nginx can serve static files faster and nginx can forward fast actions to Unicorn. static files | nginx |--> slow actions --> Rainbows! | `--> fast actions --> Unicorn Be sure to set proxy_buffering off in nginx for "slow actions" if you have Comet applications (but not for Unicorn). == Denial-of-Service Concerns Since \Rainbows! is designed to talk to slow clients with long-held connections, it may be subject to brute force denial-of-service attacks. In Unicorn and Mongrel, we've already enabled the "httpready" accept filter for FreeBSD and the TCP_DEFER_ACCEPT option in Linux; but it is still possible to build clients that work around and fool these mechanisms. \Rainbows! itself does not feature any explicit protection against brute force denial-of-service attacks. We believe this is best handled by dedicated firewalls provided by the operating system. rainbows-4.6.2/TODO0000644000004100000410000000061712354770160014100 0ustar www-datawww-data= TODO items for Rainbows! We're lazy and pick the easy items to do first, then the ones people care about. * investigate non-Rack frameworks (e.g. Goliath) * documentation improvements * Improve test suite coverage. We won't waste cycles with puny unit tests, only integration tests that exercise externally visible parts. * test and improve performance (throughput/latency/memory usage) rainbows-4.6.2/Documentation/0000755000004100000410000000000012354770160016215 5ustar www-datawww-datarainbows-4.6.2/Documentation/GNUmakefile0000644000004100000410000000123012354770160020263 0ustar www-datawww-dataall:: PANDOC = pandoc PANDOC_OPTS = -f markdown --email-obfuscation=none pandoc = $(PANDOC) $(PANDOC_OPTS) pandoc_html = $(pandoc) --toc -t html --no-wrap man1 := $(addsuffix .1,rainbows) html1 := $(addsuffix .html,$(man1)) all:: html man html: $(html1) man: $(man1) install-html: html mkdir -p ../doc/man1 install -m 644 $(html1) ../doc/man1 install-man: man mkdir -p ../man/man1 install -m 644 $(man1) ../man/man1 %.1: %.1.txt $(pandoc) -s -t man < $< > $@+ && mv $@+ $@ %.1.html: %.1.txt $(pandoc_html) < $< > $@+ && mv $@+ $@ comparison.html: comparison.haml haml -t ugly < $< > $@+ && mv $@+ $@ clean:: $(RM) $(man1) $(html1) comparison.html rainbows-4.6.2/Documentation/.gitignore0000644000004100000410000000003012354770160020176 0ustar www-datawww-data*.1 *.5 *.7 *.gz *.html rainbows-4.6.2/Documentation/rainbows.1.txt0000644000004100000410000001366712354770160020756 0ustar www-datawww-data% rainbows(1) Rainbows! User Manual % Rainbows! Hackers % December 3, 2009 # NAME rainbows - rackup-like command to launch Rainbows! # SYNOPSIS rainbows [-c CONFIG_FILE] [-E RACK_ENV] [-D] [RACKUP_FILE] # DESCRIPTION A rackup(1)-like command to launch Rack applications using Rainbows!. It is expected to be started in your application root (APP_ROOT), but the "working_directory" directive may be used in the CONFIG_FILE. While Rainbows! takes a myriad of command-line options for compatibility with ruby(1) and rackup(1), it is recommended to stick to the few command-line options specified in the SYNOPSIS and use the CONFIG_FILE as much as possible. # RACKUP FILE This defaults to \"config.ru\" in APP_ROOT. It should be the same file used by rackup(1) and other Rack launchers, it uses the *Rack::Builder* DSL. Embedded command-line options are mostly parsed for compatibility with rackup(1) but strongly discouraged. # UNICORN OPTIONS -c, \--config-file CONFIG_FILE : Path to the Unicorn-specific config file. The config file is implemented as a Ruby DSL, so Ruby code may executed. See the RDoc/ri for the *Unicorn::Configurator* class for the full list of directives available from the DSL. -D, \--daemonize : Run daemonized in the background. The process is detached from the controlling terminal and stdin is redirected to "/dev/null". Unlike many common UNIX daemons, we do not chdir to \"/\" upon daemonization to allow more control over the startup/upgrade process. Unless specified in the CONFIG_FILE, stderr and stdout will also be redirected to "/dev/null". -E, \--env RACK_ENV : Run under the given RACK_ENV. See the RACK ENVIRONMENT section for more details. -l, \--listen ADDRESS : Listens on a given ADDRESS. ADDRESS may be in the form of HOST:PORT or PATH, HOST:PORT is taken to mean a TCP socket and PATH is meant to be a path to a UNIX domain socket. Defaults to "0.0.0.0:8080" (all addresses on TCP port 8080) For production deployments, specifying the "listen" directive in CONFIG_FILE is recommended as it allows fine-tuning of socket options. -N, \--no-default-middleware : Disables loading middleware implied by RACK_ENV. This bypasses the configuration documented in the RACK ENVIRONMENT section, but still allows RACK_ENV to be used for application/framework-specific purposes. # RACKUP COMPATIBILITY OPTIONS -o, \--host HOST : Listen on a TCP socket belonging to HOST, default is "0.0.0.0" (all addresses). If specified multiple times on the command-line, only the last-specified value takes effect. This option only exists for compatibility with the rackup(1) command, use of "-l"/"\--listen" switch is recommended instead. -p, \--port PORT : Listen on the specified TCP PORT, default is 8080. If specified multiple times on the command-line, only the last-specified value takes effect. This option only exists for compatibility with the rackup(1) command, use of "-l"/"\--listen" switch is recommended instead. -s, \--server SERVER : No-op, this exists only for compatibility with rackup(1). # RUBY OPTIONS -e, \--eval LINE : Evaluate a LINE of Ruby code. This evaluation happens immediately as the command-line is being parsed. -d, \--debug : Turn on debug mode, the $DEBUG variable is set to true. -w, \--warn : Turn on verbose warnings, the $VERBOSE variable is set to true. -I, \--include PATH : specify $LOAD_PATH. PATH will be prepended to $LOAD_PATH. The \':\' character may be used to delimit multiple directories. This directive may be used more than once. Modifications to $LOAD_PATH take place immediately and in the order they were specified on the command-line. -r, \--require LIBRARY : require a specified LIBRARY before executing the application. The \"require\" statement will be executed immediately and in the order they were specified on the command-line. # SIGNALS The following UNIX signals may be sent to the master process: * HUP - reload config file, app, and gracefully restart all workers * INT/TERM - quick shutdown, kills all workers immediately * QUIT - graceful shutdown, waits for workers to finish their current request before finishing. * USR1 - reopen all logs owned by the master and all workers See Unicorn::Util.reopen_logs for what is considered a log. * USR2 - reexecute the running binary. A separate QUIT should be sent to the original process once the child is verified to be up and running. * WINCH - gracefully stops workers but keep the master running. This will only work for daemonized processes. * TTIN - increment the number of worker processes by one * TTOU - decrement the number of worker processes by one See the [SIGNALS][4] document for full description of all signals used by Rainbows!. # RACK ENVIRONMENT Accepted values of RACK_ENV and the middleware they automatically load (outside of RACKUP_FILE) are exactly as those in rackup(1): * development - loads Rack::CommonLogger, Rack::ShowExceptions, and Rack::Lint middleware * deployment - loads Rack::CommonLogger middleware * none - loads no middleware at all, relying entirely on RACKUP_FILE All unrecognized values for RACK_ENV are assumed to be "none". Production deployments are strongly encouraged to use "deployment" or "none" for maximum performance. Note the Rack::ContentLength and Rack::Chunked middlewares are also loaded by "deployment" and "development", but no other values of RACK_ENV. If needed, they must be individually specified in the RACKUP_FILE, some frameworks do not require them. # SEE ALSO * unicorn(1) * *Rack::Builder* ri/RDoc * *Unicorn::Configurator* ri/RDoc * [Rainbows! RDoc][1] * [Rack RDoc][2] * [Rackup HowTo][3] [1]: http://rainbows.bogomips.org/ [2]: http://rdoc.info/gems/r#/gems/rack/frames [3]: http://wiki.github.com/rack/rack/tutorial-rackup-howto [4]: http://rainbows.bogomips.org/SIGNALS.html rainbows-4.6.2/Documentation/comparison.css0000644000004100000410000000017712354770160021106 0ustar www-datawww-data/* help to make this look nice would be greatly appreciated */ .comparison { margin: 1em; border: 1px solid; width: 100%; } rainbows-4.6.2/Documentation/comparison.haml0000644000004100000410000002415612354770160021242 0ustar www-datawww-data%h1 Rainbows! at a glance %p Confused by all the options we give you? So are we! Here's some tables to help keep your head straight. Remember, engineering is all about trade-offs. %h2 core features and compatibility %br %table.comp %tr.comp_header %th.mod module %th.tee rack.input streaming %th.r18 Ruby 1.8 %th.r19 Ruby 1.9 %th.rbx Rubinius %th.slow slow clients %tr.comp_base %td.mod Unicorn/Base %td.tee Yes %td.r18 Yes %td.r19 Yes %td.rbx Yes %td.slow No %tr.comp_row %td.mod Revactor %td.tee Yes %td.r18 No %td.r19 Yes %td.rbx No %td.slow Yes %tr.comp_row %td.mod ThreadPool %td.tee Yes %td.r18 Yes %td.r19 Yes %td.rbx Yes %td.slow Yes %tr.comp_row %td.mod Coolio %td.tee No %td.r18 Yes %td.r19 Yes %td.rbx No %td.slow Yes %tr.comp_row %td.mod ThreadSpawn %td.tee Yes %td.r18 Yes %td.r19 Yes %td.rbx Yes %td.slow Yes %tr.comp_row %td.mod EventMachine %td.tee No %td.r18 Yes %td.r19 Yes %td.rbx No %td.slow Yes %tr.comp_row %td.mod CoolioThreadSpawn %td.tee No %td.r18 No %td.r19 Yes %td.rbx No %td.slow Yes %tr.comp_row %td.mod FiberSpawn %td.tee Yes %td.r18 No %td.r19 Yes %td.rbx No %td.slow Yes %tr.comp_row %td.mod FiberPool %td.tee Yes %td.r18 No %td.r19 Yes %td.rbx No %td.slow Yes %tr.comp_base %td.mod ActorSpawn %td.tee Yes %td.r18 Not yet %td.r19 No %td.rbx Yes %td.slow Yes %tr.comp_base %td.mod NeverBlock %td.tee No %td.r18 Yes %td.r19 Yes %td.rbx No %td.slow Yes %tr.comp_row %td.mod CoolioThreadPool %td.tee No %td.r18 Yes %td.r19 No %td.rbx No %td.slow Yes %tr.comp_row %td.mod CoolioFiberSpawn %td.tee Yes %td.r18 No %td.r19 Yes %td.rbx No %td.slow Yes %tr.comp_row %td.mod WriterThreadPool %td.tee Yes %td.r18 Yes %td.r19 Yes %td.rbx Yes %td.slow no %tr.comp_row %td.mod WriterThreadSpawn %td.tee Yes %td.r18 Yes %td.r19 Yes %td.rbx Yes %td.slow no %tr.comp_row %td.mod Epoll %td.tee no %td.r18 Yes %td.r19 Yes %td.rbx Yes %td.slow Yes %tr.comp_row %td.mod XEpoll %td.tee no %td.r18 Yes %td.r19 Yes %td.rbx Yes %td.slow Yes %tr.comp_row %td.mod XEpollThreadSpawn %td.tee Yes %td.r18 Yes %td.r19 Yes %td.rbx Yes %td.slow Yes %tr.comp_row %td.mod XEpollThreadPool %td.tee Yes %td.r18 Yes %td.r19 Yes %td.rbx Yes %td.slow Yes %ul %li Cool.io should also work with Rubinius (though we haven't had time to test). %li CoolioThread* and CoolioThread* requires Ruby 1.9 reasonable performance %li rack.input streaming is what makes %a(href="http://upr.bogomips.org/") upload progress, and BOSH possible %li rack.input streaming is NOT compatible with current versions of nginx or any proxy that fully buffers request bodies before proxying. Keep in mind request body buffering in nginx is a good thing in all other cases where rack.input streaming is not needed. %h2 application requirements %br %table.comp %tr.comp_header %th.mod module %th.slowio slow I/O (backend, not client) %th.thr thread safety %th.reent single thread reentrant %tr.comp_base %td.mod Unicorn/Base %td.slowio avoid %td.thr No %td.reent No %tr.comp_row %td.mod Revactor %td.slowio %a(href="http://coolio.github.com/")Coolio, %a(href="http://revactor.org/")Revactor, %b not %a(href="Rainbows/Fiber/IO.html")Fiber::IO %td.thr No %td.reent Yes %tr.comp_row %td.mod ThreadPool %td.slowio thread-safe Ruby %td.thr Yes %td.reent No %tr.comp_row %td.mod Coolio %td.slowio %a(href="http://coolio.github.com/") Coolio %td.thr No %td.reent No %tr.comp_row %td.mod ThreadSpawn %td.slowio thread-safe Ruby %td.thr Yes %td.reent No %tr.comp_row %td.mod EventMachine %td.slowio %a(href="http://rubyeventmachine.com") EventMachine %td.thr No %td.reent No %tr.comp_row %td.mod CoolioThreadSpawn %td.slowio thread-safe Ruby, %a(href="http://coolio.github.com/") Coolio %td.thr Yes %td.reent No %tr.comp_row %td.mod FiberSpawn %td.slowio %a(href="Rainbows/Fiber/IO.html") Rainbows::Fiber::IO %td.thr No %td.reent Yes %tr.comp_row %td.mod FiberPool %td.slowio %a(href="Rainbows/Fiber/IO.html") Rainbows::Fiber::IO %td.thr No %td.reent Yes %tr.comp_base %td.mod ActorSpawn %td.slowio thread-safe Ruby %td.thr Yes %td.reent Yes %tr.comp_base %td.mod NeverBlock %td.slowio %a(href="http://www.espace.com.eg/neverblock") NeverBlock, %a(href="http://rubyeventmachine.com") EventMachine %td.thr No %td.reent Yes %tr.comp_row %td.mod CoolioThreadPool %td.slowio thread-safe Ruby, %a(href="http://coolio.github.com/") Coolio %td.thr Yes %td.reent No %tr.comp_row %td.mod CoolioFiberSpawn %td.slowio %a(href="Rainbows/Fiber/IO.html") Rainbows::Fiber::IO %td.thr No %td.reent Yes %tr.comp_base %td.mod WriterThreadPool %td.slowio avoid %td.thr Maybe %td.reent Maybe %tr.comp_base %td.mod WriterThreadSpawn %td.slowio avoid %td.thr Maybe %td.reent Maybe %tr.comp_base %td.mod Epoll %td.slowio No %td.thr No %td.reent No %tr.comp_base %td.mod XEpoll %td.slowio No %td.thr No %td.reent No %tr.comp_base %td.mod XEpollThreadSpawn %td.slowio thread-safe Ruby %td.thr Yes %td.reent No %tr.comp_base %td.mod XEpollThreadPool %td.slowio thread-safe Ruby %td.thr Yes %td.reent No %ul %li Requirements for single thread reentrancy are loose in that there is no risk of race conditions and potentially mutually exclusive to thread-safety. In the case where a Fiber yields while holding a resource and another Fiber attempting to acquire it may raise an error or worse, deadlock the entire process. %li Slow I/O means anything that can block/stall on sockets including 3rd-party APIs (OpenID providers included) or slow database queries. Properly run Memcached (within the same LAN) is fast and not a blocker. Slow I/O on POSIX filesystems only includes a few operations, namely on UNIX domain sockets and named pipes. Nearly all other operations on POSIX filesystems can be considered "fast", or at least uninterruptible. %li WriterThread{Pool,Spawn} will require thread safety if your response body is dynamically generated during the body#each call. %h2 middlewares and frameworks %br %table.comp %tr.comp_header %th.mod model %th.devfd %a(href="Rainbows/DevFdResponse.html") DevFdResponse %th.app_pool %a(href="Rainbows/AppPool.html") AppPool %th.lock %a(href="http://rdoc.info/gems/rack/Rack/Lock") Rack::Lock %th.async async %tr.comp_row %td.mod Unicorn/Base %td.devfd no-op %td.app_pool no-op %td.lock no-op %td.async lots of RAM :P %tr.comp_row %td.mod Revactor %td.devfd no-op %td.app_pool Yes %td.lock No! %td.async Revactor itself %tr.comp_row %td.mod ThreadPool %td.devfd Yes %td.app_pool Yes %td.lock Yes %td.async thread-safe Ruby %tr.comp_row %td.mod Coolio %td.devfd Yes %td.app_pool no-op %td.lock no-op %td.async DevFdResponse %tr.comp_row %td.mod ThreadSpawn %td.devfd Yes %td.app_pool Yes %td.lock Yes %td.async thread-safe Ruby %tr.comp_row %td.mod EventMachine %td.devfd Yes %td.app_pool no-op %td.lock no-op %td.async async_sinatra, Cramp, rack-fiber_pool %tr.comp_row %td.mod CoolioThreadSpawn %td.devfd Yes %td.app_pool Yes %td.lock Dumb %td.async thread-safe Ruby %tr.comp_row %td.mod FiberSpawn %td.devfd Yes %td.app_pool Yes %td.lock No! %td.async Rainbows::Fiber::IO, Rainbows.sleep %tr.comp_row %td.mod FiberPool %td.devfd Yes %td.app_pool Yes %td.lock No! %td.async Rainbows::Fiber::IO, Rainbows.sleep %tr.comp_row %td.mod ActorSpawn %td.devfd no-op %td.app_pool Yes %td.lock Yes %td.async thread-safe Ruby %tr.comp_row %td.mod NeverBlock %td.devfd Yes %td.app_pool Yes* %td.lock Yes* %td.async NeverBlock, async_sinatra %tr.comp_row %td.mod CoolioThreadPool %td.devfd Yes %td.app_pool Yes %td.lock Dumb %td.async Coolio, thread-safe Ruby %tr.comp_row %td.mod CoolioFiberSpawn %td.devfd Yes %td.app_pool Yes %td.lock No! %td.async Coolio, Rainbows::Fiber::IO, Rainbows.sleep %tr.comp_row %td.mod WriterThreadPool %td.devfd Yes %td.app_pool no-op %td.lock no-op %td.async thread-safe Ruby in response body only %tr.comp_row %td.mod WriterThreadSpawn %td.devfd Yes %td.app_pool no-op %td.lock no-op %td.async thread-safe Ruby in response body only %tr.comp_row %td.mod Epoll %td.devfd Yes %td.app_pool no-op %td.lock no-op %td.async DevFdResponse %tr.comp_row %td.mod XEpoll %td.devfd Yes %td.app_pool no-op %td.lock no-op %td.async DevFdResponse %tr.comp_row %td.mod XEpollThreadPool %td.devfd Yes %td.app_pool Yes %td.lock Yes %td.async thread-safe Ruby %tr.comp_row %td.mod XEpollThreadSpawn %td.devfd Yes %td.app_pool Yes %td.lock Yes %td.async thread-safe Ruby %ul %li "No!" means it's fundamentally incompatible, use an %a(href="Rainbows/AppPool.html") AppPool %b :size of one instead. %li NeverBlock also supports a :pool_size option which is one less layer of complexity than using AppPool. %li NeverBlock can neuter the Mutex class so Rack::Lock effectively becomes a no-op with: %br %code require "never_block/frameworks/rails" (before Rails is loaded) %li Everything that's DevFdResponse-compatible can use it for passing async responses through rainbows-4.6.2/SIGNALS0000644000004100000410000001132112354770160014425 0ustar www-datawww-data== Signal handling In general, signals need only be sent to the master process. However, the signals Rainbows! uses internally to communicate with the worker processes are documented here as well. With the exception of TTIN/TTOU, signal handling matches the behavior of and {nginx}[http://nginx.net/] so it should be possible to easily share process management scripts between \Rainbows!, Unicorn and nginx. === Master Process * HUP - reload config file, app, and gracefully restart all workers If the "preload_app" directive is false (the default), then workers will also pick up any application code changes when restarted. If "preload_app" is true, then application code changes will have no effect; USR2 + QUIT (see below) must be used to load newer code in this case. When reloading the application, +Gem.refresh+ will be called so updated code for your application can pick up newly installed RubyGems. It is not recommended that you uninstall libraries your application depends on while Rainbows! is running, as respawned workers may enter a spawn loop when they fail to load an uninstalled dependency. * INT/TERM - quick shutdown, kills all workers immediately * QUIT - graceful shutdown, waits for workers to finish their current request before finishing. This currently does not wait for requests deferred to a separate thread when using EventMachine (when app.deferred?(env) => true) * USR1 - reopen all logs owned by the master and all workers See Unicorn::Util.reopen_logs for what is considered a log. * USR2 - reexecute the running binary. A separate QUIT should be sent to the original process once the child is verified to be up and running. * WINCH - gracefully stops workers but keep the master running. This will only work for daemonized processes. * TTIN - increment the number of worker processes by one * TTOU - decrement the number of worker processes by one === Worker Processes Sending signals directly to the worker processes should not normally be needed. If the master process is running, any exited worker will be automatically respawned. * INT/TERM - Quick shutdown, immediately exit. Unless WINCH has been sent to the master (or the master is killed), the master process will respawn a worker to replace this one. * QUIT - Gracefully exit after finishing the current request. Unless WINCH has been sent to the master (or the master is killed), the master process will respawn a worker to replace this one. This currently does not wait for requests deferred to a separate thread when using EventMachine (when app.deferred?(env) => true) * USR1 - Reopen all logs owned by the worker process. See Unicorn::Util.reopen_logs for what is considered a log. Unlike Unicorn, log files are reopened immediately in \Rainbows! since worker processes are likely to be serving multiple clients simutaneously, we can't wait for all of them to finish. === Procedure to replace a running rainbows executable You may replace a running instance of rainbows with a new one without losing any incoming connections. Doing so will reload all of your application code, Unicorn/Rainbows! config, Ruby executable, and all libraries. The only things that will not change (due to OS limitations) are: 1. The path to the rainbows executable script. If you want to change to a different installation of Ruby, you can modify the shebang line to point to your alternative interpreter. The procedure is exactly like that of nginx: 1. Send USR2 to the master process 2. Check your process manager or pid files to see if a new master spawned successfully. If you're using a pid file, the old process will have ".oldbin" appended to its path. You should have two master instances of rainbows running now, both of which will have workers servicing requests. Your process tree should look something like this: rainbows master (old) \_ rainbows worker[0] \_ rainbows worker[1] \_ rainbows worker[2] \_ rainbows worker[3] \_ rainbows master \_ rainbows worker[0] \_ rainbows worker[1] \_ rainbows worker[2] \_ rainbows worker[3] 3. You can now send WINCH to the old master process so only the new workers serve requests. If your rainbows process is bound to an interactive terminal, you can skip this step. Step 5 will be more difficult but you can also skip it if your process is not daemonized. 4. You should now ensure that everything is running correctly with the new workers as the old workers die off. 5. If everything seems ok, then send QUIT to the old master. You're done! If something is broken, then send HUP to the old master to reload the config and restart its workers. Then send QUIT to the new master process. rainbows-4.6.2/checksums.yaml.gz0000444000004100000410000000041412354770160016671 0ustar www-datawww-data‹]ypSe=R@…ûœ"ˆ ËO:;{O,keã¤òôn,u`†âñø€Ûívy{ÅûåzýìG¬xÄËÇ÷ýŠ–,¼ªÙ:+e€`rBZ$Ñ. Ç÷ëyÄׯoçtö=ÉAU©×5ö–J&…Žpƒ'uâøÇµ^[åðTœ[±ÁYC{±SíeÌ:·Í3É'É,—£×á¬æ8±».‹,’Mh„ (­'1I Date: Mon May 12 07:01:27 2014 +0000 Rainbows! 4.6.2 - see you on the other side This release updates documentation to reflect the migration of the mailing list to a new public-inbox[1] instance. This is necessary due to the impending RubyForge shutdown on May 15, 2014. The public-inbox address is: rainbows-public@bogomips.org (no subscription required, plain text only) ssoma[2] git archives: git://bogomips.org/rainbows-public browser-friendly archives: http://bogomips.org/rainbows-public/ As evidenced by our git history, Rainbows! development has stagnated over the years. Rainbows! was designed to be an unopinionated exploration into various concurrency options offered in the Ruby ecosystem. In recent years, I have come to favor the one-shot-based, worst-of-all-worlds design of yahns: http://yahns.yhbt.net/README Without the exploration from Rainbows!, yahns may not exist today. Disclaimer: Rainbows! has always been intolerant of buggy/broken code in libraries and apps. yahns is even less tolerant of buggy/broken code, as the SIGKILL-based timeout mechanism inherited unicorn is completely gone. On the other hand, yahns has reasonable defaults so you do not have to read documentation to configure it. [1] policy: http://public-inbox.org/ - git://80x24.org/public-inbox an "archives first" approach to mailing lists [2] mechanism: http://ssoma.public-inbox.org/ - git://80x24.org/ssoma some sort of mail archiver (using git) commit ba419772ac228e8775b1659eb93f968b9e887b29 Author: Eric Wong Date: Mon May 12 06:59:17 2014 +0000 documentation updates for Rubyforge death We're migrating to a new public-inbox[1] + mailing list rainbows-public@bogomips.org [1] http://public-inbox.org/ commit d4ba676aab09d3d4c51146661dcea11e5aa99f0c Author: Eric Wong Date: Tue May 6 21:12:56 2014 +0000 add script for redirecting to new site commit 3dd351a325a599fda1011ebc12cc2fbacbf7905f Author: Eric Wong Date: Mon Apr 21 19:16:46 2014 +0000 add slrnpull.conf example for gmane archives In case anybody else wants to verify/check the archive or use this for other projects, we'll document what we did here. commit eda61534c8203cf0e0fcc7c4d9564917c4552127 Author: Eric Wong Date: Sun Feb 2 00:29:10 2014 +0000 rainbows 4.6.1 - EventMachine fixes * event_machine: update for unicorn 4.8.x * disable cramp tests for now * update EventMachine tests * set executable bit rainbows executable Nothing relevant for non-EM users. commit 41087ce5f3e6042ea30a9ea659cfbcf7afe1f5ae Author: Eric Wong Date: Wed Jan 29 21:57:57 2014 +0000 set executable bit rainbows executable Folks don't usually run from the source directory, but RubyGems complains otherwise. commit b8cba3a5fd92fc9365702b8987230b27e5fcfcf2 Author: Eric Wong Date: Wed Jan 29 21:56:32 2014 +0000 update EventMachine tests EM 1.0.3 got released and seems to work under Ruby 2.1, so re-enable EM and NeverBlock tests again. commit 332ed08d87f881d2c44ad30b9e5da7434cce9b69 Author: Eric Wong Date: Wed Jan 29 21:52:17 2014 +0000 disable cramp tests for now Cramp seems broken for now, we'll deal with it at another time and I'm not sure if it ever took off... commit a3cb0c37d1e865778a515ccaff4c2af31e556ee1 Author: Eric Wong Date: Wed Jan 29 21:48:25 2014 +0000 event_machine: update for unicorn 4.8.x unicorn 4.8.x shutdown handling is different and no longer removes items from the event loop. So we must do that ourselves to enable graceful shutdown. Otherwise, we'll time out on shutdowns and the master will forcibly kill us. commit 2dc55e417708d411987b4fb4233c87fe9f59ba0a Author: Eric Wong Date: Fri Jan 17 20:13:42 2014 +0000 Rainbows! 4.6.0 - fix unicorn 4.8.0 compatibility The unicorn 4.8.0 internal changes unfortunately broke some unoffically supported behavior we depended on. This release fixes that, but as a result, we lose compatibility of older unicorn versions. (Oops!, oh well... :x) There's also minor bugfixes and documentation updates. In order to ease transitions to future versions of the GPL, we are now "GPLv2 or later" instead of explicitly GPLv2 + GPLv3(-only). The old Ruby 1.8 license remains an option. If the FSF turns out a horrible GPLv4, users are free to continue using GPLv2 or GPLv3. commit 169d506907a711254802a8e86cc343c6322dfae3 Author: Eric Wong Date: Fri Jan 17 20:25:06 2014 +0000 switch homepage to http://rainbows.bogomips.org/ RubyForge is going away, so we must migrate the homepage. The mailing list will be migrated, soon. commit 3e701ee74ff29bdf5daa3249acede5a85a2b0097 Author: Eric Wong Date: Fri Jan 17 02:03:04 2014 +0000 unicorn 4.8 compatibility + test version bumps Internal reworking of unicorn 4.8.0 completely broke us(!). This commit fixes things, but it means we no longer support unicorn <= 4.7. Sorry about that. commit 1b3e68e3eac0c3fbb2a32e4fddd26ee8a50ec627 Author: Eric Wong Date: Fri Jan 17 01:31:43 2014 +0000 error: silence ETIMEDOUT and EHOSTUNREACH errors There's nothing we can do about these errors due to network failures and bad clients, either, so do not spew a backtrace for them. commit 877f44605ec1768e279843e36c214b656d191011 Author: Eric Wong Date: Sat Oct 26 08:01:24 2013 +0000 license: allow all future versions of the GNU GPL There is currently no GPLv4, so this change has no effect at the moment. In case the GPLv4 arrives and I am not alive to approve/review it, the lesser of evils is have give blanket approval of all future GPL versions (as published by the FSF). The worse evil is to be stuck with a license which cannot guarantee the Free-ness of this project in the future. This unfortunately means the FSF can theoretically come out with license terms I do not agree with, but the GPLv2 and GPLv3 will always be an option to all users. commit 008269885bcf1bbf99059d752970969f573d8867 Author: Eric Wong Date: Mon Sep 30 18:19:49 2013 +0000 Rakefile: kill raa_update task RAA is dead. commit af09f23d530dd018bfd074a81d679a6ac815165e Author: Lin Jen-Shin Date: Fri Sep 13 00:41:16 2013 +0800 Match against newer curl. My curl 7.32.0 would actually print 400 Bad Request Hope this would make it match against newer or older curl. Signed-off-by: Eric Wong commit d4988043825d1082666b4c219f2d66659b0c350b Author: Eric Wong Date: Wed Sep 4 17:09:33 2013 +0000 SIGNALS: document SIGHUP/preload_app incompatibility This was documented in the unicorn docs, but not Rainbows! (The major difference between unicorn and Rainbows! signal handling is the deferred handling of SIGUSR1 in unicorn vs the immediate handling in Rainbows!) commit 45261c67658d57f98b51d6bb37890f03b60240a9 Author: Eric Wong Date: Wed Apr 24 23:08:32 2013 +0000 test_isolate: update raindrops to 0.11.0 raindrops 0.11.0 was released several days ago and contains minor improvements + fixes. commit 2a39832fdbf8054c514562735b1ab6c6544065fe Author: Eric Wong Date: Fri Mar 1 23:29:00 2013 +0000 epoll+xepoll: clarify intent of these concurrency options Via private communication, I learned of a user using XEpoll when he should not have been. commit 4be3e386118969345ee5c94049efa7faae338e4d Author: Eric Wong Date: Wed Feb 27 10:25:21 2013 +0000 Rainbows! 4.5.0 - hijacking support This release adds hijacking support for Rack 1.5.x users. See Rack documentation for more information about hijacking. Lin Jen-Shin also provided the -N/--no-default-middleware option. Minor packaging cleanups and new HACKING document. There are also some corner-case bugfixes for *Epoll* users (sleepy_penguin, these bugs do not affect EM or Cool.io users) and test suite portability improvements. commit 85e228de580f2ffff0025695a523349cef79d9c7 Author: Eric Wong Date: Tue Feb 26 21:16:27 2013 +0000 manpage: update middleware-related documentation -N/--no-default-middleware needs a corresponding manpage entry. Additionally, the Rack::Chunked/ContentLength middleware comment is out-of-date as of unicorn v4.1.0 commit 1bdb4eda0d868223bd6b3d92db4545a02f5e14f9 Author: Eric Wong Date: Tue Feb 26 21:02:34 2013 +0000 epoll/*: remove user-space array as active queue This prevents pathological starvation cases where the user-space ready-list can be repopulated infinitely. With EPOLLONESHOT, epoll itself may be used for this task (at a slightly higher cost) by enabling read/write checks, as the epoll ready-list preserves event ordering when used with EPOLLONESHOT. commit 552c4ca305092911ca7613de4b1ba310e5481775 Author: Eric Wong Date: Tue Feb 26 20:07:30 2013 +0000 xepoll_thread_*/client: EPOLLONESHOT implies EPOLLET No need to specify both flags for epoll (confirmed by reading fs/eventpoll.c in the Linux kernel). commit 81e555569fc254ed9d592c8734695b22d4f9bc12 Author: Eric Wong Date: Tue Feb 26 08:19:05 2013 +0000 doc: add HACKING document Thanks to Alex Wolfe for noticing. commit f8b5181e744848468bf3bb0fe469795f9113cf4c Author: Eric Wong Date: Tue Feb 26 07:42:51 2013 +0000 tests: update checks for Ruby 2.0.0 EventMachine/NeverBlock currently do not build on Ruby 2.0.0 commit 95d6719722f31987c72052b000f67d99989a81c8 Author: Eric Wong Date: Tue Feb 26 03:16:46 2013 +0000 bump unicorn dependency to 4.6.2+ for Ruby 2.0.0 This ensures we're compatible with the latest stable Ruby version. commit 83a3b49dd3a1f1885ca56e9889ff22aa7b7c6136 Author: Eric Wong Date: Tue Feb 26 03:14:26 2013 +0000 gemspec: set licenses= attribute Old versions of RubyGems which could not handle this attribute are likely no longer in use. commit 7dd14b2780b87be58c7afbd03df8d1f7cc3e9575 Author: Eric Wong Date: Sat Feb 16 00:04:53 2013 +0000 GIT-VERSION-GEN: rewrite to manage RAINBOWS_VERSION const This DRYs up versioning and makes packages easier to distribute. commit e166cfe5e8d648b544b1291ec157bd234a425e21 Author: Eric Wong Date: Fri Feb 8 22:45:20 2013 +0000 hijacking support for Rack 1.5.x users This requires Rack 1.5.x and unicorn 4.6.0 for hijacking support. Older versions of Rack continue to work fine, but we must use unicorn 4.6.0 features to support this. commit e6faf9e26bcb172026a4984ecadbaa8b6789bcb7 Author: Eric Wong Date: Mon Feb 4 12:39:09 2013 +0000 tests: "wc -l" portability for *BSDs On FreeBSD 9.0, "wc -l" emits leading whitespace, so filter it through tr -d '[:space:]' to eliminate it. commit f4e3bac180ff01256832f98655636f79b52f9d5b Author: Eric Wong Date: Fri Jan 25 02:10:05 2013 +0000 tests: bump version dependencies for Isolate Most of these test dependencies may be safely bumped. commit 8a6117a22a7d01eeb5adc63d3152acf435cd3176 Author: Eric Wong Date: Thu Jan 24 23:33:54 2013 +0000 tests: "wc -c" portability for *BSDs On FreeBSD 9.0, "wc -c" emits leading whitespace, so filter it through tr -d '[:space:]' to eliminate it. commit 0ba6fc3c30b9cf530faf7fcf5ce7be519ec13fe7 Author: Eric Wong Date: Thu Jan 24 23:13:41 2013 +0000 tests: replace non-portable "date +%s" with ruby equivalent "date +%s" is not in POSIX (it is in GNU, and at least FreeBSD 9.0, possibly earlier). The Ruby equivalent should be sufficiently portable between different Ruby versions. This change was automated via: perl -i -p -e 's/date \+%s/unix_time/' t/*.sh commit cbff7b0892148b037581541184364e0e91d2a138 Author: Eric Wong Date: Thu Jan 24 22:59:16 2013 +0000 tests: remove utee and use tee(1) instead POSIX already stipulates tee(1) must be unbuffered. I think my decision to use utee was due to my being misled by a bug in older curl where -N did not work as advertised (but --no-buffer did). commit a552fb90230eb65c28897398c7786dc9a13446ca Author: Lin Jen-Shin Date: Tue Jan 29 11:44:06 2013 +0800 Add -N or --no-default-middleware option. This would prevent Unicorn (Rainbows) from adding default middleware, as if RACK_ENV were always none. (not development nor deployment) This is implemented in Unicorn, so we only need to update the option parser here. Discussion thread on Unicorn mailing list: http://rubyforge.org/pipermail/mongrel-unicorn/2013-January/001675.html Signed-off-by: Eric Wong commit 49f70632e2f4a9b84fd4fced7439d0844fed1bbd Author: Eric Wong Date: Thu Jan 24 19:47:41 2013 +0000 epoll: ensure closing of pipelined clients if required on_read normally relies on the close checking in on_readable, but on_deferred_write_complete may be called from on_writable instead (bypassing the close check of on_readable). This affects both Epoll and XEpoll users, but only if they were sending responses via body#to_path and triggering on_deferred_write_complete. While we're at it, favor non-recursive want_more instead of calling on_readable recursively in ev_write_responses to prevent stack growth. commit ad5ab9fe21d57b78b7c6681bbc0907c934e898b0 Author: Eric Wong Date: Fri Jan 18 11:21:45 2013 +0000 Rainbows! 4.4.3 - bugfixes for EventMachine users This release fixes two EventMachine bugfixes from Lin Jen-Shin and Mark J. Titorenko. There are also some minor cleanups. Lin Jen-Shin (1): event_machine: avoid close on deferred response Mark J. Titorenko (1): event_machine: join reactor_thread if it is already running Eric Wong (2): event_machine: cleanup confusing assignment t/GNUmakefile: cleanup test dependencies commit 5eaec3184b49d3c1068b1f011e7afe43b33dd2d5 Author: Eric Wong Date: Thu Jan 17 10:17:29 2013 +0000 t/GNUmakefile: cleanup test dependencies The missing random_blob dependency was causing the following to fail on a fresh clone: make -C t ThreadPool.t0005-large-file-response.sh commit a12d2e64e04b5098f31e63a72ea8330b6c8f3a19 Author: Mark J. Titorenko Date: Fri Jan 11 11:57:12 2013 +0000 event_machine: join reactor_thread if it is already running ...rather than falling through worker_loop Prior to the application of this patch, if an EventMachine reactor_thread has already been started elsewhere before the worker_loop is entered, the worker_loop exits as a second call to EM.run does not block the current thread. This patch causes the worker_loop thread to join the reactor_thread if it is running. [ew: commit message formatting] Signed-off-by: Eric Wong commit 28eaf3e766f5dddc6ca073a155b8005afdaecfba Author: Eric Wong Date: Wed Dec 19 03:39:58 2012 +0000 event_machine: cleanup confusing assignment commit 42bf1f6de55b82af46fd8255453036c6582b7f19 Author: Lin Jen-Shin Date: Wed Dec 19 04:03:55 2012 +0800 event_machine: avoid close on deferred response close_connection_after_writing only if not deferred, as in cool.io Deferred responses may buffer more data down the line, so keep the connection alive if we have a deferred response body. [ew: clear @deferred when we really want to quit, updated commit message] Acked-by: Eric Wong commit f7f9c6b8470e54e25a17cd1a9517f5030e43fa66 Author: Eric Wong Date: Thu Dec 6 11:40:21 2012 +0000 Rainbows! 4.4.2 - EventMachine async.callback fix One bugfix allows stream(:keep_open) in Sinatra to work properly. Thanks to W. Andrew Loe III for the informative bug report and reproducible test case. ref: http://mid.gmane.org/CA+-9oNd1EFqsniPkkPTwu5opTCinbM7-2KHoXov7+y3LE4s4Tg@mail.gmail.com commit ef74449fdf7a2c487d07a1aa8b4e34cc3be77aab Author: Eric Wong Date: Wed Dec 5 03:34:02 2012 +0000 test_isolate: bump eventmachine and sinatra deps Always ensuring we work with the latest versions. commit d5c8cc8b51619f0d33f75036c53e3936ad2749b2 Author: Eric Wong Date: Wed Dec 5 03:08:19 2012 +0000 event_machine: properly defer body.close for async Calling body.close in the normal write_response() code path is incorrect, and only worked out of sheer luck with Cramp and async_sinata. This change allows stream(:keep_open) in Sinatra to work properly. Thanks to W. Andrew Loe III for the informative bug report and reproducible test case. ref: http://mid.gmane.org/CA+-9oNd1EFqsniPkkPTwu5opTCinbM7-2KHoXov7+y3LE4s4Tg@mail.gmail.com commit fb671a2d316029169940d00a3f9307c3113541c4 Author: Eric Wong Date: Fri Aug 31 01:53:43 2012 +0000 Rainbows! 4.4.1 - a minor bugfix for Fiber users Fiber-based concurrency options avoids negative sleep intervals. Thanks to Lin Jen-Shin for pointing this out. commit e794a40049959a23ba311c572e518bb7c2861812 Author: Eric Wong Date: Fri Aug 24 23:03:17 2012 +0000 fiber/base: avoid negative sleep interval Also clarify the code while we're at it. Thanks to Lin Jen-Shin for pointing this out. ref: http://mid.gmane.org/CAA2_N1unOXb7Z4Jr8oKoSLu266O9Ko4o=oWzAcMA1w3=9X74KA@mail.gmail.com commit 12281e4cee86496588814cd1851e8764caea024c Author: Eric Wong Date: Sat Aug 18 08:10:14 2012 +0000 Rakefile: freshmeat.net -> freecode.com freecode.com now requires HTTPS, too. commit 7450ebd46b6e7cf0eb59a3d6d561d5d6252bef16 Author: Eric Wong Date: Sat Aug 18 07:04:11 2012 +0000 Rainbows! 4.4.0 - minor improvements For epoll/Cool.io-based concurrency models, shutdown() is now used to timeout keepalive clients to avoid race conditions. Minor documentation improvements. commit 5bcbe397f4457fa6c76443cc77d662139ef464f2 Author: Eric Wong Date: Thu Jul 19 13:10:37 2012 -0700 thread_timeout: document additional caveats Again, for the one thousandth time, timing out threads is very tricky business :< commit 73a76531b7f4826cdb3bad0a0286038fb6ade4f3 Author: Eric Wong Date: Sun Jul 1 03:56:39 2012 +0000 test_isolate: bump raindrops dependency As usual, test with the latest released version to avoid surprises. commit 36ce10c49ce7aa0df9d47f5a5f870c23c1891258 Author: Eric Wong Date: Sat Jun 30 19:57:08 2012 +0000 coolio+xepoll_thread*: use shutdown() for keepalive timeout Triggering Errno::EBADF is tricky in multithreaded situations due to possible race conditions and yet-to-be discovered bugs. shutdown(2) is also safe against apps the fork() internally but do not execve(2) nor set FD_CLOEXEC. n.b. calling fork() after pthreads are spawned may not be safe on all platforms w.r.t. malloc, but /is/ the case for glibc on GNU/Linux. Follow-up-to: commit a5b987619f4b793203f6a50e424fe98c5b0794ba commit a5b987619f4b793203f6a50e424fe98c5b0794ba Author: Eric Wong Date: Mon Jun 18 20:32:39 2012 +0000 epoll*: favor shutdown(2) for keepalive timeout Triggering Errno::EBADF is tricky in multithreaded situations due to possible race conditions and yet-to-be discovered bugs. shutdown(2) is also safe against apps the fork() internally but do not execve(2) nor set FD_CLOEXEC. n.b. calling fork() after pthreads are spawned may not be safe on all platforms w.r.t. malloc, but /is/ the case for glibc on GNU/Linux. commit 604f64f624d562f480dc8424a6597ec5b32947df Author: Eric Wong Date: Wed May 9 21:11:39 2012 +0000 fiber_pool: take Fibers from larger pools faster Array#pop can be significantly faster than Array#shift on large arrays (especially since we push into the Array). This is because Array#shift needs to shift all elements in the array, and Array#pop only needs to shorten the array by one element. The Fiber stack may also be hotter in CPU caches when we choose the most-frequently used stack. commit 3e61265d4193340721dd53178c43c4fa24debb79 Author: Eric Wong Date: Wed May 9 21:11:13 2012 +0000 test_isolate: update deps for EM tests async_sinatra and rack-fiber_pool had new versions since we last updated. commit bdd64821feba82d9fce45c8dac64ba7b7bb530ac Author: Eric Wong Date: Wed May 9 02:20:42 2012 +0000 test_isolate: bump unicorn and kgio versions for testing Users will pull the latest upstream, ensure things keep working. commit bbd55c8bcd0d2833c99a2c23856ff07be7f46f9a Author: Eric Wong Date: Wed May 9 02:18:08 2012 +0000 stream_response_epoll: fix for unicorn 4.3.x unicorn 4.3.x now calls shutdown() explicitly on the socket, so we can't just rely on a dup()-ed FD to keep a socket around. commit 9573d9bc6c63aced28300a9d7346b5575eed8b7d Author: Eric Wong Date: Tue May 8 23:18:41 2012 +0000 tests: require sleepy_penguin 3.1.0 or later We'll be making the XEpollThreadPool users depend on this, too. commit 335b383fa43a6d21d473ebc81668dae9a3c09055 Author: Eric Wong Date: Wed Apr 11 21:48:36 2012 +0000 doc: some spelling fixes Found with rdoc-spellcheck commit f2c27e950b3314b4a647c482d506ba4c7314cac4 Author: Eric Wong Date: Thu Feb 23 08:07:13 2012 +0000 t/test_isolate: update test dependencies At least for the gems I'm most familiar with... commit b5756023920bc48bd879b419f80849ded38173c8 Author: Eric Wong Date: Wed Oct 12 16:29:23 2011 -0700 t0501: fix ambiguous shell construct for capture + subshells Starting with "$((" can be ambiguous and confused for shell arithmetic. commit db46699c438c2d1287563587fddea84e34c53482 Author: Eric Wong Date: Wed Oct 12 16:14:49 2011 -0700 http_server: fix log message if using Base (no concurrency) Telling the user worker_connections=50 when using the Base concurrency model is misleading. commit 0b955487e44011d80a457618c2e8148c8ec05e05 Author: Eric Wong Date: Fri Sep 2 02:14:03 2011 +0000 Rainbows! 4.3.1 - small bugfix This release fixes a potential reentrancy deadlock when using the default logger from the Ruby standard library. commit 71b4507755f2bd279adb14e6cdc90e1380efa247 Author: Eric Wong Date: Tue Aug 30 16:18:21 2011 -0700 avoid potential Logger deadlock in SIGQUIT and SIGUSR1 If any combination of SIGQUIT and SIGUSR1 are sent to a Rainbows! worker in a /very/ short period of time, the Mutex used by the default Logger implementation may deadlock since Mutex synchronization is not reentrant-safe. Users of alternative logger implementations (or monkey-patched ones) are possibly not affected. Users of the logger_mp_safe.rb monkey-patch distributed[1] with unicorn are not affected. [1] http://unicorn.bogomips.org/examples/logger_mp_safe.rb commit 58a09dccfa425811fa3b66e097c38bb303c2aa1e Author: Eric Wong Date: Sat Aug 20 01:15:30 2011 +0000 Rainbows! 4.3.0 - pull in changes from unicorn 4.1.0 The deprecated Rainbows::HttpResponse class is finally gone thanks to Pratik Naik. Logging of errors is more consistent with the changes in unicorn 4.1.0. There are also minor documentation updates. See the unicorn 4.1.0 release notes for more details: http://bogomips.org/unicorn.git/tag/?id=v4.1.0 commit b6c07eb14f008361caca6fcc642df60b29ef192c Author: Eric Wong Date: Sat Aug 20 00:55:56 2011 +0000 bump unicorn dependency version We want the stricter parser the error log filtering in unicorn 4.1.0 commit cd40e197ab6ef6d0baa219497cea7458457f3343 Author: Eric Wong Date: Sat Aug 20 00:50:52 2011 +0000 TODO: remove splice(2) item Rainbows! 3.3.0 added the copy_stream Configurator directive commit d74a4b4520f89ec2dbf539b02dea1f938c63d74c Author: Eric Wong Date: Sat Aug 20 00:46:28 2011 +0000 rdoc: cleanup and stop depending on :enddoc: It doesn't seem to work at the top of the file... commit f782ab61830ee92942abfcca7c7cb2cdd274e69d Author: Eric Wong Date: Sat Aug 20 00:37:03 2011 +0000 update development wrongdoc dep to 1.6 Hopefully this points more folks to email us. commit fbc144d57e83dc8ef5d9e653d38f88ef4b74cb62 Author: Eric Wong Date: Fri Aug 19 23:13:10 2011 +0000 event_machine: update rdoc for new Cramp homepage Cramp has a homepage and mailing list now, yay! commit 3b987ec182cbe919646ea1cb9cd903a31bd85d45 Author: Eric Wong Date: Fri Aug 19 23:10:28 2011 +0000 cramp: update test dependency to version to 0.15 Everything appears to be working... commit 371fbd9e1e189510c825063770ff4f5ea79bfead Author: Eric Wong Date: Fri Aug 19 23:07:59 2011 +0000 t/test_isolate: bump kgio test version We always try to test with the latest and greatest. commit ff70afb97c7f41e28b3d3f6df665daeb4d415989 Author: Eric Wong Date: Tue Aug 16 19:00:37 2011 -0700 more consistent logging for errors The Unicorn.log_error method exists since 4.0.0 commit 93e06d33193ee9fd4a58275a4909f008b4bcc4dc Author: Eric Wong Date: Fri Aug 12 07:53:21 2011 +0000 test_isolate: bump cool.io test version Everything appears to work as expected under cool.io 1.1.0 commit a8949f91b323cf478f6aa99dc144f672d382e55e Author: Pratik Naik Date: Fri Aug 5 22:24:21 2011 +0100 Remove deprecated Rainbows::HttpResponse Acked-by: Eric Wong commit 017b95be08a702c9faae1949db9328f7e82c2d8c Author: Eric Wong Date: Fri Aug 5 23:31:18 2011 +0000 Rainbows! 4.2.0 - Cramp WebSocket updates! This release includes updates to support WebSockets under Cramp 0.14 and later. This will be the last release which supports Cramp 0.13. There are no changes in this release for non-Cramp users. commit 73a8847fac9939e1b42a471f7537b5f89bd5ca94 Author: Eric Wong Date: Fri Aug 5 23:17:07 2011 +0000 bump Cramp test dependency to 0.14 commit ab175517f8fd22acada6daa46c89edda080fa6a9 Author: Eric Wong Date: Fri Aug 5 19:08:12 2011 +0000 event_machine: APP needs to be in the main Client class Oops, testing against new changes against cramp.git here commit 7223b868624d19f3421045c2bc5b075bacd83694 Author: Eric Wong Date: Fri Aug 5 18:44:32 2011 +0000 event_machine: add :em_client_class option This can allow Cramp (and potentially other libraries) to subclass or implement duck-type compatible versions of Rainbows::EventMachine::Client. commit c6bb07a6475f71dc1e2b46b87a34fbbf46c52b5d Author: Eric Wong Date: Fri Aug 5 18:15:45 2011 +0000 cramp: bump test version to 0.13 cramp was just released a few days ago and all the tested pieces seem to work... commit 8134763f3cc8403ef0e718aa511b192d305a0182 Author: Eric Wong Date: Sat Jul 30 22:25:31 2011 +0000 Rainbows! 4.1.0 - minor internal cleanups There are only some minor cleanups in this release and a bump to kgio 2.5 to remove the dependency on io/wait. kgio 2.5 or later is now required (kgio 2.6+ will be required in the next release). commit 1e57c4c846ba9a3b736078b860583b0e7dd33d6c Author: Eric Wong Date: Thu Jul 21 23:27:18 2011 +0000 remove tcp_nodelay/tcp_nopush tweaking Unicorn 4.x already defaults match those of Rainbows! to favor lower latency instead of lowered bandwidth usage. commit 175727bc349ec0f7b94b4de936c7e9de1aaffd2d Author: Eric Wong Date: Thu Jul 21 23:17:48 2011 +0000 remove unnecessary io/wait require kgio 2.5 added kgio_wait_*able methods commit 19a7c3afcb22af75ac3afc358e96325d00415ace Author: Eric Wong Date: Wed Jun 29 19:57:05 2011 +0000 Revert "http_server: cap timeout at 32-bit LONG_MAX seconds" Unicorn (> 4.0.1) already handles this for us, not that it affects many people... This reverts commit 37c376a9253ed62d134cbb4dbc6eaecc6076c77e. commit c3982b7c043effcfa897086a221567c035603352 Author: Eric Wong Date: Tue Jun 28 23:32:26 2011 +0000 t0044: do not assume setsockopt() finished Race conditions abound in the world of concurrency! commit 65f251070ce9b61a9049cb413fccffdfa11e7a70 Author: Eric Wong Date: Tue Jun 28 08:17:01 2011 +0000 Fix Fiber* and WriterThread* breakage from removing io/wait This fixes up breakage introduced in commit 905f0ff393629ddb4d70e3dc221b016128c47415 to switch to kgio for timed, synchronous waiting. commit 905f0ff393629ddb4d70e3dc221b016128c47415 Author: Eric Wong Date: Tue Jun 28 07:39:35 2011 +0000 require kgio 2.5 for kgio_wait_readable(timeout) Since kgio_wait_*able in kgio 2.5 takes an optional timeout argument, we no longer have to load the extra "io/wait" module. This saves us a small amount of some memory and also removes the extra ioctl(FIONREAD) syscall IO#wait enforces. Like IO#wait in Ruby 1.9.3dev, kgio_wait_readable may use ppoll() to wait on high-numbered file descriptors as efficiently as it waits on low-numbered descriptors. commit 9684ed46e78dab1ae787fe135a3cec1c4490ddd6 Author: Eric Wong Date: Tue Jun 28 07:20:27 2011 +0000 bin/rainbows: release the OptionParser object from stack No point in keeping it around to eat memory. commit 37c376a9253ed62d134cbb4dbc6eaecc6076c77e Author: Eric Wong Date: Mon Jun 27 21:20:42 2011 +0000 http_server: cap timeout at 32-bit LONG_MAX seconds We can't wait for longer than 68 years. commit 1f8ebc013e8b0e9a62587fb95a81e8c5f541a9d4 Author: Eric Wong Date: Mon Jun 27 09:20:37 2011 +0000 Rainbows! 4.0.0 - MOAR concurrency for MOAR COARS Rainbows! now scales to more than 1024 worker processes without special privileges. To enable this, Rainbows! now depends on Unicorn 4.x and thus raindrops[1]. client_max_header_size directive is added to limit per-client memory usage in headers. An experimental StreamResponseEpoll concurrency option now exists to buffer outgoing responses without any thread-safe dependencies. Unlike the rest of Rainbows! which works fine without nginx, this concurrency option is /only/ supported behind nginx, even more strongly so than Unicorn itself. non-nginx LAN clients are NOT supported for this. This relies on the sleepy_penguin[2] RubyGem (and Linux). There are some minor bug fixes and cleanups all around. See "git log v3.4.0.." for details. [1] http://raindrops.bogomips.org/ [2] http://bogomips.org/sleepy_penguin/ commit cc74623dddb9d28d7d5a2479aafdb7a754645697 Author: Eric Wong Date: Mon Jun 27 09:18:39 2011 +0000 bump dependencies (kgio, unicorn, raindrops) We now rely on Unicorn 4.0.0. We'll use the latest kgio and raindrops versions anyways. commit 0e9d8ac759f7c5d783f055df0fb5d4f08c68db71 Author: Eric Wong Date: Mon Jun 27 09:15:24 2011 +0000 remove unused arg in Rainbows::Response.setup It hasn't been used in a while, but we kept it for Zbatery version compatibility. commit 8c1e3da65d7a709c6578652fa57e5ca0e7a9a459 Author: Eric Wong Date: Wed Jun 22 07:40:03 2011 +0000 epoll/client: properly close on all pipe responses Some pipe responses can trigger the on_deferred_write_complete method without ever re-running the event loop. This appears to be the result of the occasional t0050 failures. commit 63aff8f1cffebaeb6320a5f6d2ec8cb9c7d8ea54 Author: Eric Wong Date: Wed Jun 22 07:05:37 2011 +0000 t0050: improve diagnostics for this test This test seems to fail sometimes with Epoll and XEpoll... commit cdbec31577cae4fcaa3c4dcd0492d2ab81307fe4 Author: Eric Wong Date: Wed Jun 22 02:25:56 2011 +0000 t/test_isolate: remove unneeded comment That's been around forever, and we think Rubinius supports that... commit a831f5685ac03f7f0f3d341df89eade6134b55b4 Author: Eric Wong Date: Wed Jun 22 02:25:11 2011 +0000 dev_fd_response: enable under Rubinius Untested, but it should work nowadays... commit 6858c79a49a5d25edb6c06ffd131749eb2f23b2b Author: Eric Wong Date: Wed Jun 22 02:21:58 2011 +0000 Unicorn 4.x resync for ticker This removes the extra per-process file descriptor and replaces it with Raindrops. commit 51859dfd9b9f11f6cc0af3fc252312b8018a88e2 Author: Eric Wong Date: Wed Jun 22 02:21:33 2011 +0000 t/test_isolate: drop Unicorn test dependency for now It's already a runtime dependency commit be9f24aa1392c67294187844a620e3a9974e0948 Author: Eric Wong Date: Wed Jun 15 22:42:49 2011 +0000 remove IO_PURGATORY dependency We no longer need to put all listeners away since Unicorn uses kgio. commit 281ef555c40cf292809ad10d623d0571fafc790e Author: Eric Wong Date: Sat Jun 11 06:35:12 2011 +0000 configurator: add client_max_header_size directive Lowering this will lower worst-case memory usage and mitigate some denial-of-service attacks. This should be larger than client_header_buffer_size. The default value is carried over from Mongrel and Unicorn. commit 1f3e7b1c7ee89aefdcadd4aebd7b0527baa188a3 Author: Eric Wong Date: Sat Jun 11 05:46:52 2011 +0000 doc: remove docs for Rainbows::Fiber::* APIs Do not encourage their use, really. commit d01c5df960546462e8f5f458131d78689342d6aa Author: Eric Wong Date: Fri Jun 10 22:39:01 2011 -0700 stream_response_epoll: update documentation Yes, this concurrency model is our strangest yet. commit b065cab4fa5ca25302220c20563398a57069ae08 Author: Eric Wong Date: Sat Jun 11 00:34:42 2011 +0000 remove Rainbows.run method We can get away with a single stack frame reduction. Unicorn itself has more stack reductions, but Rainbows! is further behind in this area. commit 1468604be898d17c2cf2da519dccd493c58f4282 Author: Eric Wong Date: Fri Jun 10 22:08:04 2011 +0000 ev_core: do not autochunk HTTP/1.0 (and 0.9) responses Do not assume middlewares/applications are stupid and blindly add chunking to responses (we have precedence set by Rack::Chunked). commit 1aa7eb6608d04a21d1143e7ac09e5219a1208123 Author: Eric Wong Date: Fri Jun 10 21:35:48 2011 +0000 ev_core: use add_parse for trailers, too HttpParser#trailers and #headers are actually the same method, so we'll just continue on. commit 27f54de4b83e20c5d68c6dd225c1d350aeee5cd3 Author: Eric Wong Date: Fri Jun 10 20:27:22 2011 +0000 gemspec: add GPLv3 to commented-out licenses section Reduces inconsistency. commit 7bb7c4469fb744135377d68e7c29cde3fbc635dc Author: Eric Wong Date: Fri Jun 10 02:13:55 2011 +0000 use HttpParser#add_parse API in Unicorn 3.7.0 It's easier-to-use in some cases. commit 772a5da573a661df6421afa6398b9e4023e77ce4 Author: Eric Wong Date: Thu Jun 9 23:47:37 2011 +0000 tests: more fixes for StreamResponseEpoll Oops. commit be467481fe774e60f5a349c6447769f090e57e06 Author: Eric Wong Date: Thu Jun 9 21:29:02 2011 +0000 response: sendfile_range fis for Rack::File in rack 1.3.0 Rack::File already sets Content-Range, so don't repeat work and reparse Content-Length. commit 5bd92111979d968fc9b982f1e529b8044e718f71 Author: Eric Wong Date: Thu Jun 9 21:08:38 2011 +0000 t/t9002: use Rack::Head We send HEAD requests and expect body-less responses. Noticed while running a newer rack version after re-isolating. commit 879968a88559c434b4f117e86e61ccdb62263b26 Author: Eric Wong Date: Thu Jun 9 21:01:00 2011 +0000 update various RubyGem dependencies Gotta keep using the latest and greatest. commit 40edc84784864063a38ba38bf854a2119c243ce4 Author: Eric Wong Date: Thu Jun 9 00:34:00 2011 +0000 stream_response_epoll: our most "special" concurrency option yet This doesn't use Rainbows::Base so we have no keepalive support at all. This could eventually be an option for streaming applications. commit 54deb6a9a0e868c0958c9ec145e311661ce90e54 Author: Eric Wong Date: Thu Jun 9 00:23:28 2011 +0000 move reopen_worker_logs to base We may not always use Rainbows! :Base since we don't want keepalive/immediate log reopening in some cases. commit 99244e8e30eded813bd8eaf7f2136a3871bd486c Author: Eric Wong Date: Mon Jun 6 10:24:05 2011 -0700 doc: remove --sanitize-html for pandoc pandoc 1.8 no longer supports this, and we don't need it anyways since we only generate documentation from our repository. commit d40aff8c3fb6ecce314897837c28d9400fbf38a8 Author: Eric Wong Date: Tue May 31 08:33:04 2011 -0700 xepoll_thread_*: update docs for Linux 3 Linux 3.0.0 is just around the corner and of course newer than 2.6. commit cfe9a6d38533d380112f2b99bea9a1a3664f0395 Author: Eric Wong Date: Mon May 30 21:31:23 2011 +0000 test-lib: enable splice(2) for Linux 3.x The latest Linux series is now 3.x, not 2.6.x commit 983722912028f35348c8ff276df48855281be715 Author: Eric Wong Date: Sat May 21 03:02:09 2011 +0000 Rainbows 3.4.0 - minor updates and fixes SIGQUIT (graceful shutdown) now drops idle keepalive clients for the concurrency models where maintaining an idle client is relatively inexpensive: Coolio, CoolioThreadPool, CoolioThreadSpawn, Epoll, EventMachine, XEpoll, XEpollThreadPool, XEpollThreadSpawn. Kgio.autopush now works properly for all multi-threaded concurrency models (if you're using :tcp_nopush). commit e5eda7790806d6d7709d02db04c75f09340e9a24 Author: Eric Wong Date: Sat May 21 03:01:50 2011 +0000 pkg.mk: update to the latest version * locale fix for grep commit 7bd77ef78055af5d5232d16f00568ce02b947ae0 Author: Eric Wong Date: Fri May 20 18:23:50 2011 -0700 use and recommend sleepy_penguin 3.0.1 It's better under 1.9.3 (sleepy_penguin 3.0.1 was bogus) commit 07cb79c14399a292ae2a50db007180941c758c69 Author: Eric Wong Date: Fri May 20 18:23:50 2011 -0700 use and recommend sleepy_penguin 3.0.0 It's better under 1.9.3 commit 9009074ae535438696b706b4d6b8d4c72187c33e Author: Eric Wong Date: Fri May 20 18:15:30 2011 -0700 t0044: increase test reliability commit f83fe5edb79e51616afcf2a9f991cfe153eabd48 Author: Eric Wong Date: Fri May 20 15:44:48 2011 -0700 try_defer: enable documentation It should hopefully give this more visibility even though it's an internal feature. commit 0b9b4edc63c0021e567c68137e1ee7f9e99ddcc5 Author: Eric Wong Date: Fri May 20 18:13:21 2011 -0700 xepoll_thread_pool/client: improve autopush support We need to trigger a recv() to uncork the response. This won't affect fairness (much) since all recv()s are non-blocking and a successful header parse will put us in the back of the queue. commit a5419a5fd57a231e40b7a3ba48a02cdef15909be Author: Eric Wong Date: Fri May 20 15:30:11 2011 -0700 .gitignore: add tags/TAGS files Some folks use them. commit f4b084225b7115efecfbfb2b5d16da1f1da5c39e Author: Eric Wong Date: Fri May 20 15:17:21 2011 -0700 add tests for Kgio autopush on Linux We can support it fully for a subset of concurrency models where we have full control over buffering and HTTP/1.1 keepalive clients. commit a396de855e291d27d43454618031432634fe7d37 Author: Eric Wong Date: Fri May 20 14:24:14 2011 -0700 add test for SIGQUIT disconnect This will only be supported for certain concurency models, but it's probably good enough. commit 5d5033afa9052dc0d60e743f07bc694f008182b3 Author: Eric Wong Date: Fri May 20 14:22:37 2011 -0700 event_machine: disconnect idle clients at on SIGQUIT Since it's cheap to maintain keepalive clients with EM, we need a way of disconnecting them in a timely fashion on rare SIGQUIT events. commit bba079d8a605b546bc262e80234c3c233a386e44 Author: Eric Wong Date: Fri May 20 18:58:04 2011 +0000 client: use kgio_write across the board This should enable Kgio "autopush" support for ThreadSpawn, ThreadPool, XEpollThreadSpawn, and XEpollThreadPool. (still needs tests) commit ea6d0eab436c78113f8a61e7fcc34db4117d14c0 Author: Eric Wong Date: Fri May 20 09:19:51 2011 +0000 coolio* + *epoll*: drop keepalive clients on SIGQUIT In concurrency models long keepalive times are cheap (and thus more likely to be used), this allows Rainbows! to gracefully shut down more quickly. commit 90af18f6884857704d72fd6b2bb91718aad72117 Author: Eric Wong Date: Thu May 19 23:54:59 2011 -0700 epoll/xepoll: more consistent client implementations There's less logic in the server this way and easier to potentially share code this way. commit 5e4f790847198e1267b2fbd5decfa09e5cc3d618 Author: Eric Wong Date: Tue May 17 17:38:12 2011 -0700 doc: recommend io_splice 4.1.1 or later io_splice 4.1.1 works around issues with socket buffers filling up pipe buffers on blocking splice. See http://lkml.org/lkml/2009/1/13/478 for a better explanation. commit bc4f5510cd0e5ccf85f077cca019bca70309bf01 Author: Eric Wong Date: Mon May 16 14:12:27 2011 -0700 Rainbows! 3.3.0 - doc improvements and more * improved documentation all around, suggestions/comments to further improve documentation is greatly welcome at: rainbows-talk@rubyforge.org * added GPLv3 option to the license (now (Ruby|GPLv2|GPLv3), though Unicorn is still (Ruby|GPLv2) for now) * added client_header_buffer_size config directive (default 1K) * small default header buffer size (16K => 1K) to reduce memory usage, Rails apps with cookie sessions may want to increase this (~2K) * all concurrency models default to 50 connections per process * all concurrency models with a secondary :pool_size parameter also default to 50 (threads/fibers/whatever) * RLIMIT_NOFILE and RLIMIT_NPROC are automatically increased if needed * Rainbows::ThreadTimeout middleware rewritten, still not recommended, lazy people should be using Unicorn anyways :) * Several experimental Linux-only edge-triggered epoll options: XEpollThreadSpawn, XEpollThreadPool, XEpoll, and Epoll. The latter two were in previous releases but never announced. These require the "sleepy_penguin", "raindrops", and "sendfile" RubyGems === Deprecations * Rainbows::Fiber::IO* APIs all deprecated, Rainbows! will avoid having any concurrency model-specific APIs in the future and also avoid introducing new APIs for applications. * Fiber-based concurrency models are no longer recommended, they're too fragile for most apps, use at your own risk (they'll continue to be supported, however). Linux NPTL + Ruby 1.9 is pretty lightweight and will be even lighter in Ruby 1.9.3 if you're careful with stack usage in your C extensions. commit bcd0825fc77a2986aebb3f1f5a653a64faf6a8c5 Author: Eric Wong Date: Mon May 16 21:01:47 2011 +0000 writer_thread_*: fix sendfile detection under Ruby 1.8 I can't wait until I stop supporting Ruby 1.8 commit 912c328ecfccf189239c4ad2bb7863d64342868f Author: Eric Wong Date: Mon May 16 20:42:10 2011 +0000 document RubyGem requirements Hopefully makes things easier to try out. commit 46ac7ca9f3e5c7736aa1c9430d2de218e27bf730 Author: Eric Wong Date: Mon May 16 20:32:29 2011 +0000 doc: cleanup rdoc for Rainbows module The only supported method is Rainbows.sleep in here commit 362ac7801d4f6ddd85cbafdc7e9118f88089564a Author: Eric Wong Date: Mon May 16 20:13:00 2011 +0000 use :pool_size for RLIMIT_NPROC with thread pool models Only needed for Ruby 1.9 commit f9de85b9a35c9f95b390249ea855858c5e4a3ae2 Author: Eric Wong Date: Mon May 16 20:05:12 2011 +0000 more reliable shutdown for epoll concurrency models Just close the epoll descriptor, since the sleepy_penguin epoll_wait wrapper may not return EINTR in the future. commit 49b60d6f67a94560a802c6ff80c16c92093de877 Author: Eric Wong Date: Mon May 16 19:43:38 2011 +0000 tests: unify checks for copy_stream and threaded app dispatch This makes things easier to maintain as we add more concurrency options. commit 39f625fff05d457b01f088017f463a86d3b6c626 Author: Eric Wong Date: Mon May 16 19:04:06 2011 +0000 add "copy_stream" config directive This allows using IO::Splice.copy_stream from the "io_splice" RubyGem on recent Linux systems. This also allows users to disable copy_stream usage entirely and use traditional response_body.each calls which are compatible with all Rack servers (to workaround bugs in IO.copy_stream under 1.9.2-p180). commit d520222050ce88388ae5e446c63642d79cc9796e Author: Eric Wong Date: Tue May 10 16:34:51 2011 -0700 configurator: update user-facing documentation Finally, we have all methods in configurator and it's much easier to document! commit 95cec890409ac07d51c87ab157fe7e101d6f8432 Author: Eric Wong Date: Tue May 10 16:33:07 2011 -0700 max_body: documentation updates It can't be used as middleware for fully-buffering concurrency models. commit 382d3cabfda9fdd391228d24e8a8d4cb014648b9 Author: Eric Wong Date: Tue May 10 15:35:21 2011 -0700 LICENSE: add GPLv3 to license terms GPLv2 and Ruby-specific terms remain intact, but this means we can be combined and redistributed with GPLv3-only software (once Unicorn has GPLv3 added to its license). commit 704f843054f3ca32941d42972a1c7d1b144d06ad Author: Eric Wong Date: Mon May 9 04:39:54 2011 +0000 configurator: move validation logic over There's actually no reason we can't have these methods in Rainbows::Configurator where it's easier to document nowadays. commit 598525843ee1d120fd9878011ca2b6328c2cf95f Author: Eric Wong Date: Mon May 9 02:24:26 2011 +0000 doc: better document :pool_size options CoolioThreadPool has had it supported forever, but only NeverBlock had it documented. commit 1e3b7e9bfef992b337a9a113da6c99071dce60e1 Author: Eric Wong Date: Mon May 9 02:15:55 2011 +0000 TODO: misc updates Some things were never going to get done due to lack of interest from users. commit 87632aac323bd41701900a03dc78bc307fa8229a Author: Eric Wong Date: Mon May 9 02:13:51 2011 +0000 doc: update comparison and README Clearly users need to know about more options commit d0336075a772d040b9fe3100098e1815ce609cb4 Author: Eric Wong Date: Mon May 9 01:57:16 2011 +0000 xepoll_thread_*: add rdoc for users It's good to describe what they're useful for. commit a62e9240fffe544d171c87f94946ecf50888a6df Author: Eric Wong Date: Mon May 9 01:56:46 2011 +0000 xepoll_thread_spawn/client: remove rdoc It's an internal implementation detail. commit 6b430295177c4689a6a5d421d575867c5da8ba2b Author: Eric Wong Date: Mon May 9 01:55:32 2011 +0000 max_body: rdoc updates speling ficks and less confusing #initialize documentation commit 9afcdde9a92f817330e4dfd5e1ef77a7bf461253 Author: Eric Wong Date: Mon May 9 00:21:23 2011 +0000 xepoll_thread_pool: add optional :pool_size argument We're now able to configure the number of threads independently of worker_connections. commit 725adcc6986bc2658d92ea3ebebe61d2b83060ad Author: Eric Wong Date: Mon May 9 00:17:06 2011 +0000 split out pool_size module coolio_thread_pool, neverblock both use it, and xepoll_thread_pool will support it next, too. commit 3c1649226d2371ce09d14f3fcce18fec0af1ba02 Author: Eric Wong Date: Mon May 9 00:16:23 2011 +0000 t0041: less confusing skip message Just the test name is irrelevant commit 380ef63bc2c8f7b6f1cab7387aa9343bc5720c9c Author: Eric Wong Date: Mon May 9 00:02:51 2011 +0000 add XEpollThreadPool concurrency option This is probably friendlier on server resources in the worst case than XEpollThreadSpawn but may perform worse in the client client-visible way, too. commit df00f2b1028ba95450246c82e468878b6ec903a3 Author: Eric Wong Date: Sun May 8 07:54:30 2011 +0000 Revert "epoll: fixes for Ruby 1.9.3dev" Fixed in kgio 2.4.0 now This reverts commit a1168e7d2bfe182896f139d051ef099616fd1646. commit ed869514211a908748f04a881e6fb18a395914ae Author: Eric Wong Date: Sun May 8 07:47:03 2011 +0000 join_threads: simplify thread stoppage check No need for a string comparison commit 8af417bbacb285c90a376c55a42fe39a981a328d Author: Eric Wong Date: Sun May 8 07:39:38 2011 +0000 require kgio 2.4.0 kgio 2.4.0 has some 1.9.3dev fixes which affect us commit ec3f871c9a34734b865365a43979a0b459654b05 Author: Eric Wong Date: Sun May 8 07:26:55 2011 +0000 epoll_wait: flags argument is unused We only poll for one event (EPOLLIN/EPOLLOUT) at a time, so there's no need to actually check since they're too rare. commit e64e2e0045d63c4edd291839febba978534be652 Author: Eric Wong Date: Sun May 8 07:22:36 2011 +0000 xepoll: cleanup acceptor logic worker_yield is safer than setting a threshold with multiple acceptors when thread limits are hit. Also, avoid sleep + Thread#run since it's potentially racy if threads are extremely unfairly scheduled. Same things applied to xepoll_thread_spawn. commit 07a0bee9dd6f2c366d11284b7e9ab09d66b411e4 Author: Eric Wong Date: Sun May 8 04:06:12 2011 +0000 xepoll_thread_spawn: rework acceptor logic Infinite sleep is too dangerous due to possible race conditions, so use worker_yield which is safer and cheaper in the general case. We can also avoid sleeping on new threads by only spawning when the client module is included. commit c543b295ff2108623f3748a141e04e5530d06377 Author: Eric Wong Date: Sun May 8 03:58:21 2011 +0000 xepoll_thread_spawn/client: close returns nil Otherwise pipeline_ready can false positive on us commit 497ce74310baaa7ac4835b8d6aa76acb40d44b7c Author: Eric Wong Date: Sun May 8 01:28:43 2011 +0000 xepoll_thread_spawn: inline needless method shorter line and 3 lines of code killed! commit ef9376775b1a6eeae8807160b2b8ea23fbfd3607 Author: Eric Wong Date: Sun May 8 01:27:25 2011 +0000 process_client: fix pipeline_ready arity Not that it's actually used, right now. commit 3decb4375847b97c117440e5167dbba33d5f33b5 Author: Eric Wong Date: Fri May 6 01:04:39 2011 +0000 remove unnecessary variable assignments commit bcca2d3bc609e9ecaa77ec93bef861b1ddcc01dd Author: Eric Wong Date: Fri May 6 00:52:18 2011 +0000 test_isolate: update to latest raindrops and unicorn Newer versions should be better commit 21a1a20223986af8c8962cafe2e0f1eae0dcfd19 Author: Eric Wong Date: Fri May 6 00:38:53 2011 +0000 ensure some requires get loaded in master More sharing, faster startups, and most importantly, better error reporting if some things are missing. commit 0efe7b677b076a32ef54e5f2f0a9861eb1f7f3d9 Author: Eric Wong Date: Thu May 5 07:16:48 2011 +0000 http_server: XEpollThreadSpawn sets RLIMIT_NPROC It *can* have as many threads as it does idle connections. commit 8fb350e00c1421907cbc36cbf29c499b8ad051e4 Author: Eric Wong Date: Tue May 3 01:18:45 2011 +0000 Rainbows! defaults more DRY We won't forget to reset defaults on SIGHUP anymore. commit e9a229c639dd6bda08a3fa8ce14af3688dc2ba63 Author: Eric Wong Date: Tue May 3 01:15:37 2011 +0000 s/max_bytes/client_max_body_size/ for consistency Too confusing otherwise... commit a7f5f17ba2047ef7143465f612007ea81871a79e Author: Eric Wong Date: Tue May 3 01:05:11 2011 +0000 add client_header_buffer_size tuning parameter We're changing our default to 1K buffers to save memory. This should reduce memory usage of idle clients and lower pressure on the MRI GC. Rails applications using session cookies (the default) may want to up this to 2K or more. commit 01381ab4dfeb032c85e72724df889c7aa381aa32 Author: Eric Wong Date: Mon May 2 22:05:52 2011 +0000 rainbows: get rid of MODEL_WORKER_CONNECTIONS constant Just use regular autoload, now. Less guesswork. commit 25c5930988d6deab8f885981fdaf6c1eb12fdf98 Author: Eric Wong Date: Mon May 2 08:05:45 2011 -0700 http_server: default all options to 50 connections pavinging the way for changing MODEL_WORKER_CONNECTIONS commit ce4f5cd7197fb99ed99399b22cbece1684532f85 Author: Eric Wong Date: Sun May 1 01:44:28 2011 +0000 doc: add Sandbox document Rainbows! is a bit bigger than Unicorn and usually requires being sandboxed, too. commit 8c086f095a2f3be0f71829af9037d99c79604a37 Author: Eric Wong Date: Sat Apr 30 06:56:47 2011 +0000 lower header buffer sizes for synchronous models HTTP headers are usually smaller than 4K, so 16K was way too much for most users and often caused unnecessary GC runs. EventMachine and Coolio models can all share the same initial read buffer, so it's less urgent that they get this lowered for now... commit bfb58da95e13f3061feb2c1f284efc80a13ba23c Author: Eric Wong Date: Sat Apr 30 06:50:17 2011 +0000 xepoll_thread_spawn: lower memory usage This one actually has a realistic chance of running hundreds/thousands of clients, and 32K per-client is a huge amount. commit ca8021d4177e5f7131f88daf6b56402ff39c3a35 Author: Eric Wong Date: Fri Apr 29 18:14:10 2011 +0000 epoll: fix timeout Epoll#wait and epoll_wait(2) timeouts are in milliseconds, not seconds. This affects xepoll, too. commit 73ed28f1863d5ae3048a284e25a918255bdafe8a Author: Eric Wong Date: Fri Apr 29 18:11:04 2011 +0000 xepoll_thread_spawn: fix race condition with acceptors Noticed under 1.8.7 commit aff36865d5e738babdbf36f34fd0693b67bb3d90 Author: Eric Wong Date: Fri Apr 29 05:45:44 2011 +0000 xepoll_thread_spawn: initial implementation Whee! This is going to be awesome. commit 40cf3eb79054caa4b7d81120a736491aca8259eb Author: Eric Wong Date: Thu Apr 28 18:42:46 2011 +0000 document epoll and xepoll They're probably ready for general use in a very limited capacity... commit 6ea50dd6866a7b4eda5134cb2c8980710285e127 Author: Eric Wong Date: Tue Apr 26 14:04:26 2011 -0700 doc: stop recommending Fiber* stuff Too much NIH and too fragile. commit 5bd08f7bb06367823412bd3410bcb41babd6d57b Author: Eric Wong Date: Tue Apr 26 14:00:25 2011 -0700 revactor: remove documentation for internal methods commit 00b9b46b2a49ab96f74fd21807ee9c93b218bc2d Author: Eric Wong Date: Tue Apr 26 13:30:28 2011 -0700 revactor: do not recommend, upstream is dormant commit e3c8152e016fe29f76a996b8802df7158c081db0 Author: Eric Wong Date: Tue Apr 26 12:56:20 2011 -0700 stream_file: hide internals No need to expose things that don't need exposing. commit 012d3e3d1ae9186434efcaa746f25118683fe8b3 Author: Eric Wong Date: Mon Apr 25 19:44:13 2011 -0700 make all concurrency options use 50 by default This may change again in the future, but it paves the way for simplifying this code... commit f6471ed9cafe6e65e72fa9486ecdcc4b2f8d1373 Author: Eric Wong Date: Thu Apr 21 15:38:20 2011 -0700 thread_timeout: annotate as much as possible This should make code review easier. commit 63b3b40abf054bce2846bc9e36770a6ed2791a8b Author: Eric Wong Date: Sat Apr 9 18:58:23 2011 -0700 increase RLIMIT_NPROC for thread-crazy folks Might as well, threads are free and cheap on 64-bit commit 066bbf930d023ecf61ed4af363aebadbce2c51db Author: Eric Wong Date: Fri Apr 8 13:38:11 2011 -0700 http_server: attempt to increase RLIMIT_NOFILE In case people try to do C10K, make life easier for them. commit e5c4125c348104444714b5111c90016d520461d6 Author: Eric Wong Date: Thu Apr 21 21:22:06 2011 +0000 thread_timeout: document Thread.pass usage Thread-switching sometimes takes too long under YARV, so we force a switch since the watchdog thread is lower-priority. commit 71d48a01a7c257dffa22c10781c55deff64037d2 Author: Eric Wong Date: Thu Apr 21 21:21:13 2011 +0000 join_threads: workaround blocking accept() issues Should be fixed in 1.9.3 and/or future Kgio... commit 7f368d2cf9e456e37f1489dc275ed0de007175dd Author: Eric Wong Date: Thu Apr 21 06:48:46 2011 +0000 bump dependency to Unicorn 3.6.0 Might as well use the latest and greatest. commit 15505863ce2080bdf342930530acc35c0d112df1 Author: Eric Wong Date: Tue Apr 19 04:35:43 2011 +0000 http_server: less hacky loading of concurrency model Ugh the old way was gross. commit 367ba00d2f3a4e9a0fa848d076f97f754a9d7cc5 Author: Eric Wong Date: Mon Apr 11 07:54:50 2011 +0000 t: only enable Revactor tests under 1.9.2 for now Revactor doesn't seem to work under 1.9.3dev, and Revactor is dead upstream. commit a1168e7d2bfe182896f139d051ef099616fd1646 Author: Eric Wong Date: Mon Apr 11 07:53:57 2011 +0000 epoll: fixes for Ruby 1.9.3dev commit 321e3dfbd5c4a585c00a8d5221ebdf9b934aaea1 Author: Eric Wong Date: Mon Apr 11 04:16:31 2011 +0000 thread_timeout: rewrite for safety This attempts to fix all the danger associated with using Thread#raise. Hopefully I succeeded. Note: this logic _cannot_ be used to fix the timeout.rb module in the Ruby standard library, that one needs to allow nested timeouts (which seems impossible). commit 5170b767ef1bbc94554920959d1792fce43899a9 Author: Eric Wong Date: Sun Apr 10 07:42:31 2011 +0000 thread_pool: get used of dead thread_join method It's in the JoinThreads module now commit 777beb3e27785d2da2865cb5fc0d43f1c158cb5e Author: Eric Wong Date: Tue Mar 22 11:08:31 2011 -0700 queue_pool: switch to ivars to protect internals commit dd6d5168e4f3dcb4555264265a05e5b61273893d Author: Eric Wong Date: Tue Mar 22 17:22:00 2011 +0000 thread_pool+thread_spawn: update documentation They're not bad with slow clients a previously thought. commit 53fdbda1ba1d88e93d662a040fe4c7fb52810155 Author: Eric Wong Date: Mon Mar 21 02:45:02 2011 +0000 simplify LISTENERS closing No need to obfuscate what it's doing commit c7e75cfe241a621f50c750720c47149a156e8e7f Author: Eric Wong Date: Sun Mar 20 03:07:31 2011 +0000 fix various warnings with "check-warnings" target Run under 1.9.3dev commit d327a0d468e5b5cfb05bb56621fb22d156ce3a40 Author: Eric Wong Date: Thu Mar 17 09:59:55 2011 +0000 pkg.mk: new task for checking Ruby warnings commit ee9e7886d9101a6376d268a931c645026e9f6b0f Author: Eric Wong Date: Tue Mar 15 21:44:14 2011 +0000 fiber/io: fix broken call to Kgio.trywrite Fortunately it's a deprecated class that nobody uses... commit e9cb419a7887fce13fc7b185177c87fc45279383 Author: Eric Wong Date: Tue Mar 15 12:36:10 2011 +0000 Rainbows! 3.2.0 - trying to send files to slow clients We now use IO#trysendfile in the sendfile 1.1.0 to reduce the cost of generating backtraces for slow clients (from EAGAIN). Nothing new for people not serving static files (but more on the way). Existing "sendfile" gem users must upgrade to 1.1.0 or risk being left without sendfile support at all: http://bogomips.org/rainbows.git/patch?id=cd8a874d commit f550b2ac5cdfedba53564609af19db38c302bbf4 Author: Eric Wong Date: Tue Mar 15 12:35:06 2011 +0000 bump Unicorn dependency to 3.5.0 Latest and greatest :D commit c158f140e6a402278602b3be890c2c1dc494465d Author: Eric Wong Date: Thu Mar 10 15:22:06 2011 -0800 doc: update Static_Files for new sendfile gem Ugh, and still no usable IO.copy_stream under 1.9.2 :< commit cd8a874d18fe01e11bb57b91186b6c9f712a4b3f Author: Eric Wong Date: Thu Mar 10 15:06:10 2011 -0800 switch from IO#sendfile_nonblock to IO#trysendfile IO#trysendfile does not raise exceptions for common EAGAIN errors, making it far less expensive to use with the following concurrency models: * Coolio * CoolioFiberSpawn * Revactor * FiberSpawn * FiberPool This requires the new sendfile 1.1.0 RubyGem and removes support for the sendfile 1.0.0. All sendfile users must upgrade or be left without sendfile(2) support. IO#sendfile behaves the same if you're using a multi-threaded concurrency option, but we don't detect nor use it unless IO#trysendfile exists. commit afea5cd7c691de95b37d29728ab4880e3b737a42 Author: Eric Wong Date: Tue Mar 8 14:08:43 2011 -0800 test_isolate: bump dependencies New sendfile gem will give us IO#trysendfile. We might as well use and test the latest and greatest versions of everything else since thats what users pull in by default. commit 85784e1b5fca7bbadc7fb5dba1f100785188954f Author: Eric Wong Date: Mon Feb 28 03:10:55 2011 +0000 use IO#wait instead of IO.select for single readers It's a simpler interface and avoids allocating an array which is nice. commit 829d58c261ba64bb4f512c6591eba2f468df15a6 Author: Eric Wong Date: Wed Feb 16 10:47:27 2011 -0800 README: clarify license terms and versions Ruby 1.9.3dev switched to BSD but we remain under the same terms as the old Ruby 1.8 license. Mongrel2 exists now and also uses the BSD, so don't confuse people with that, either. commit 23be173e2506f8a39d3e7b3097cef4e3b8a31ccc Author: Eric Wong Date: Tue Feb 15 09:44:23 2011 -0800 tests: updates for cramp 0.12 The Cramp::Controller namespace is gone. commit 061cb38ee10c431a015276c2cf3db37e5e3e66ef Author: Eric Wong Date: Fri Feb 11 10:49:56 2011 +0000 Rainbows! 3.1.0 - minor updates Small bug fixes that have been sitting around, not much but it's already been one month since our last release. * Unicorn dependency updated to 3.4.0, so we get IPv6 support and Kgio.autopush support for ":tcp_nopush => true" users. * Optional :pool_size argument is fixed for NeverBlock and CoolioThreadPool users. * Mostly minor internal code cleanups * Sunshowers support removed, it was out-of-date and unmaintained. Cramp remains supported for now. * X-Rainbows-* response headers support removed, nobody used it. There are severalnew features in this release not documented here. Consider any new features not mentioned in these release notes to be subject to removal/renaming in future releases. commit 93625a24f0b20a89f4ab732c92f76640e387c1a0 Author: Eric Wong Date: Fri Feb 11 11:12:17 2011 +0000 pkg.mk: update to the latest * Fixes Ruby 1.9.3dev deprecation warnings * Fixes some documentation dependency issues commit 3b3047f2f60a5f9c4f526fe8fcfc0d7897422abb Author: Eric Wong Date: Fri Feb 11 10:49:07 2011 +0000 reverse_proxy: document as "not ready for production" Of course some folks believe nothing in Rainbows! is :. commit 04b8de2cf6c3037c45e60d3720472b09dd23efd5 Author: Eric Wong Date: Mon Feb 7 19:48:46 2011 -0800 Revert t/bin/unused_listen simplification This conflicts with ports clients may automatically use in the ephemeral range. This reverts commit c9a7560bb684bbdadb641ebc7597303f38c37d4f. commit 662a146457689110c83bfd18987938475966f454 Author: Eric Wong Date: Mon Feb 7 19:26:04 2011 -0800 new test for optional :pool_size handling This will help prevent us from breaking :pool_size in the future. commit a34b1048d05397f72ad579fcef72cbb1a9e9d8bc Author: Graham Hughes Date: Mon Feb 7 16:49:31 2011 -0800 fix optional "use" argument handling Actually use the Rainbows::O constant for use [ew: rewritten commit message subject] Acked-by: Eric Wong commit b7d974960b58d732149d5fabf870b2332b6877f7 Author: Eric Wong Date: Mon Feb 7 18:52:51 2011 -0800 gemspec: remove unnecessary statements No need for these commit 40871775044a88662b9593d86a631755ca516bab Author: Eric Wong Date: Mon Feb 7 18:51:50 2011 -0800 doc: rdoc cleanups and fixes Don't need to document things that aren't ready, yet. commit 53bac4f65d9430495c8043b239cc936012ea7a8d Author: Eric Wong Date: Sun Feb 6 06:19:09 2011 +0000 minimize &block usage for yield No need to allocate a proc every time when we can just yield much more efficiently. commit 44eb53f5a5f1ea2e5aee93d0caf995f42b3179f7 Author: Eric Wong Date: Sun Feb 6 01:42:31 2011 +0000 kill some unnecessary &block usage We were needlessly allocating objects even when using yield. commit 90a86c9822238f01e8d60c9303b9a0da64351c7f Author: Eric Wong Date: Sat Feb 5 10:44:52 2011 +0000 *epoll: refactor common loop code acceptor thread pools could use some work, still commit c9ca82c76f1eb0e06b0aa204a243d00f382579c5 Author: Eric Wong Date: Sat Feb 5 10:11:56 2011 +0000 *epoll: consolidate re-run logic There's a lot of code duplication here :< commit d8e3edadd128a2ee5712b87a6b660295de2df6be Author: Eric Wong Date: Sat Feb 5 09:22:05 2011 +0000 writer_thread_pool: needless use of Array#map Array#each will do.. commit fa7a282502c253ad3203f28bb10eede8b3a3cd3e Author: Eric Wong Date: Sat Feb 5 09:06:37 2011 +0000 less expensive QUIT processing Avoid constantly nuking the method cache and raising repeated exceptions while our master process is shutting us down. commit 723ca7b440b1cf161d999aedcfadba8498adaa7c Author: Eric Wong Date: Sat Feb 5 08:46:40 2011 +0000 http_server: kill a warning Ugh, I need to think of a way to clean this up... commit 7246d2f2d1601dbb5486ce7f9ddbebd1bb975b58 Author: Eric Wong Date: Fri Feb 4 20:39:41 2011 -0800 bump required Unicorn dependency for Kgio We want to use the singleton methods in Kgio to reduce conditionals. commit e00c2e8c53ad5b47baa5bc6a8765b7c3c92296b9 Author: Eric Wong Date: Fri Feb 4 18:59:18 2011 -0800 bump wrongdoc development dependency 1.5 is nicer than older versions commit 8e2e8adda8adbadee8ab31cde700b7e486b1154c Author: Eric Wong Date: Fri Feb 4 18:58:26 2011 -0800 rename XAcceptEpoll to XEpoll It's too long especially since XEpollThreadPool is planned :> commit 0f4f015737297ac5245d0be9ee83553770ade0a9 Author: Eric Wong Date: Fri Feb 4 15:05:25 2011 -0800 test_isolate: use latest Unicorn It supports IPv6 and pulls in a better Kgio. Since Unicorn defaults to ":tcp_nopush => true", we need to flip it back to false to be compatible with the types of apps Rainbows! is targetted as. commit f015a843fcfa120286628ec75829575ee2391380 Author: Eric Wong Date: Fri Feb 4 17:48:34 2011 -0800 tests: replace several sed invocations with ed ed can do in-place editing portably, unlike sed. commit 82579a5f20531e69eb40e6a58a931b6c5f77d548 Author: Eric Wong Date: Fri Feb 4 15:06:23 2011 -0800 reverse_proxy: small reorganization Put all of our constants in one place for easy reference commit d20bb75518669444ad65fffe4c6e5d2654436193 Author: Eric Wong Date: Fri Feb 4 15:01:03 2011 -0800 reverse_proxy: properly read IPv6 addreses in upstreams Yes it's fugly commit ba9a22b6926db058199d44a626b923376c8a850f Author: Eric Wong Date: Fri Feb 4 22:38:31 2011 +0000 epoll: handle EINTR properly in Ruby-space We can't work around it effectively in the C extension itself. This requires the latest sleepy_penguin gem. commit da93ad1c07ff9deb4d3a2e765b26f87ccf68aa23 Author: Eric Wong Date: Fri Feb 4 05:41:14 2011 +0000 test_isolate: only load sleepy_penguin under Linux It's Linux-only, after all commit c9a7560bb684bbdadb641ebc7597303f38c37d4f Author: Eric Wong Date: Fri Feb 4 04:20:15 2011 +0000 t/bin/unused_listen: simplify this Binding to a random port is much easier this way commit 130d086666ccd825fcb13cf02fcf941b8fe661af Author: Eric Wong Date: Tue Feb 1 15:05:52 2011 -0800 preliminary reverse proxy Rack application This can be a starting point for developing Cool.io or EventMachine-based reverse proxy applications on Rainbows! Eventually Rainbows! could replace nginx for Unicorn users! Just don't consider this code production ready, yet, at all, it doesn't handle any sort of failover and has no automated tests, yet. commit 9bf406655a84090426a62ab60677df529e408f17 Author: Eric Wong Date: Mon Jan 31 14:21:18 2011 -0800 coolio/client: on_write_complete triggers read This allows us to more aggressively handle pipelining as well as trigger future Kgio autopush behavior. commit 5cbe276ef1e797aef77e7401593f4a7827c37fe3 Author: Eric Wong Date: Thu Jan 27 12:52:15 2011 -0800 test for client_max_body_size being zero Some users never, ever accept uploads, so we should test for it. commit 7d623489dd51667e0a2eec9d44aec57cb4c5ba44 Author: Eric Wong Date: Wed Jan 26 17:42:55 2011 +0000 epoll/client: avoid unnecessary Epoll#set calls We should only attempt to modify the descriptor when we block, and not for subsequent events. commit bc8a71ea4fee6e4ed35dad245511508ac0b7bf4b Author: Eric Wong Date: Wed Jan 26 17:40:01 2011 +0000 GNUmakefile: only enable epoll-based models in Linux epoll is Linux-only right now. kqueue probably isn't worth supporting directly (and even direct epoll support is debatable given the current GVL situation) commit 667b05819b85165061c445fb2c75ef161a5de5b6 Author: Eric Wong Date: Mon Jan 24 20:04:20 2011 -0800 initial XAcceptEpoll concurrency model Edge-triggered epoll concurrency model with blocking accept() in a (hopefully) native thread. This is recommended over Epoll for Ruby 1.9 users as it can workaround accept()-scalability issues on multicore machines. commit 9f7f497dc21d904a1af20465318d85811daf0652 Author: Eric Wong Date: Mon Jan 24 17:13:14 2011 -0800 epoll/client: thread-safety for write queuing We're living on the EDGE and mixing epoll with threads :D commit 04b8649b3750a0002094e90a62f7566059c8a4e9 Author: Eric Wong Date: Mon Jan 24 17:00:51 2011 -0800 epoll: make Epoll.quit more reusable We'll be using this more in the future commit 85ccfbc75abc78f977583db6303e3fb270fd2ec9 Author: Eric Wong Date: Mon Jan 24 13:20:23 2011 -0800 neverblock: fix app_call under 1.8.7 super doesn't seem to capture arguments inside a block under 1.8.7 :< commit 9e146021e17056976e61d824d228922bf81a4f24 Author: Eric Wong Date: Mon Jan 24 11:47:43 2011 -0800 clear LISTENERS array on close No reason to keep it around commit 6f6a95b6e3b4f76278cd19b787c8487453dae4d4 Author: Eric Wong Date: Fri Jan 21 22:56:29 2011 -0800 epoll/client: minor optimization We know @wr_queue is empty since we just initialized it and the first thing an HTTP client does is read. commit 01fbc4a7f2fd5b751b9183d11aa74aadaffd145b Author: Eric Wong Date: Fri Jan 21 17:34:21 2011 -0800 ev_core: force input to be given to app_call No need to setting an ivar for most requests commit 9719da13b8aabeabbf2eebc37f127e88550c3121 Author: Eric Wong Date: Fri Jan 21 17:27:25 2011 -0800 epoll/client: factor out on_close method We'll override it, maybe... commit 97d8f9b49e3f4637c7c573a971cdbc54b5698dce Author: Eric Wong Date: Fri Jan 21 17:10:00 2011 -0800 epoll/client: remove unused client We don't do Level-Triggered I/O around here commit cc08315b7821cf7178d233451fd6bbe84eb90dcc Author: Eric Wong Date: Fri Jan 21 15:34:15 2011 -0800 extract common tasks to pkg.mk to simplify our makefile commit fb24ca8f30edbf5086dbe288d4dd4fa213684236 Author: Eric Wong Date: Fri Jan 21 15:11:16 2011 -0800 epoll: reduce expiration calls and Time objects We'll lower our precision for keepalive timeouts a little and and reduce our Time object allocation rate. commit 43d6ec33c3dd7497e27127adfffeb94722fd4b8d Author: Eric Wong Date: Fri Jan 21 15:08:07 2011 -0800 ev_core: garbage reduction We don't need to allocate new string objects for short-lived strings. We'll pay the price of a constant lookup instead. commit 247ce76b4aabfa42157b9cbf9ebae824819cfff6 Author: Eric Wong Date: Fri Jan 21 13:21:07 2011 -0800 epoll: use newer sleepy_penguin We can eliminate the State module to simplify our code since 1.3.x keeps better track of things. commit b33ab73a984660ab2d741b32725ab21d7860bef3 Author: Eric Wong Date: Fri Jan 21 13:50:12 2011 -0800 max_body: disable for epoll It's almost just like Coolio commit 6750d3b50a9d4e66cbdb3b3ce295a1f16a54c678 Author: Eric Wong Date: Fri Jan 21 12:35:05 2011 -0800 doc: git.bogomips.org => bogomips.org bogomips.org is slimming down and losing URL weight :) commit 805f0eba4f63d0c9020708e1122f64776933c06f Author: Eric Wong Date: Fri Jan 21 00:16:57 2011 -0800 epoll: use sleepy_penguin default size for epoll_wait It was based off the nginx window of 64 events. Not that any servers are really that busy... commit 1b024d48169442a5498d490699ea1eee41563587 Author: Eric Wong Date: Thu Jan 20 15:27:46 2011 -0800 epoll: close epoll descriptor on graceful shutdown This allows us to gracefully shutdown more quickly. commit ea4a01df8b1fa29e2088816f4734bb0073eb9380 Author: Eric Wong Date: Thu Jan 20 22:15:12 2011 +0000 remove support for Sunshowers The WebSocket protocol is still undergoing changes and unused. We won't waste time supporting it until it's finalized and doesn't break HTTP. commit 7ec2e407206036a993aadbcd39b13a2d86b7a288 Author: Eric Wong Date: Thu Jan 20 13:26:59 2011 -0800 epoll: ignore ECONNRESET errors Nothing we can do about that from clients. Perhaps kgio should just return nil for those... commit 7ad4e300c29b06fd53aca9f9bad18f97f9a34633 Author: Eric Wong Date: Thu Jan 20 20:32:24 2011 +0000 ev_core: simplify setup steps ev_core is always loaded after forking, so eliminate the need for extra setup steps for each concurrency model that uses it commit 72cbecfe01b8c4b2c7bb7e362401805374036dc2 Author: Eric Wong Date: Thu Jan 20 02:28:12 2011 -0800 merge rack_input into process_client It turns out to be less-used than previous anticipated, so there's no point in having yet another module. commit ed33b9c0d060806b41e952a50e0ab65a0f5fe21a Author: Eric Wong Date: Thu Jan 20 02:20:19 2011 -0800 ev_core: localize 413 error constant It's the only place we ever use it commit e0ad4353f0c0f8ae27301df3e694384f687a4264 Author: Eric Wong Date: Thu Jan 20 02:16:04 2011 -0800 remove unused 416 error constants/exceptions We handle that locally in rainbows/response now commit fa0b3774dd9cd73331e83b3517c37964ab265074 Author: Eric Wong Date: Thu Jan 20 02:13:38 2011 -0800 dev_fd_response: garbage reduction Constant strings mean the runtime won't have to allocate new objects all the time since GC is currently the biggest performance problem of Ruby 1.9.x in my experience. commit fc355db99bdbe72644e4691a7a107b6a29c77be6 Author: Eric Wong Date: Wed Jan 19 18:07:13 2011 -0800 dev_fd_response: do not send chunks to 1.0 clients chunked Transfer-Encoding is only valid for HTTP/1.1 commit d59ea817d624478176212f27bb79976c371a9c6f Author: Eric Wong Date: Wed Jan 19 17:47:37 2011 -0800 t0035: kgio-pipe-response works everywhere Or at least it should :) commit 5d5d31e7ed12a232e405a8456d08e974ac74299a Author: Eric Wong Date: Wed Jan 19 17:46:52 2011 -0800 t0023: use skip_models helper we need to get in the habit of using this more commit 1a449c8abafbb17f9e7c2d68363957c80dc1ab86 Author: Eric Wong Date: Wed Jan 19 16:23:54 2011 -0800 remove support for X-Rainbows-* headers We guarantee the Rack env will exist for the duration of the request/response cycle, so we can just tweak "rainbows.autochunk". commit 9424b13255a238dfa44952ebeb07bea3acee999c Author: Eric Wong Date: Wed Jan 19 15:06:10 2011 -0800 initial edge-triggered epoll model Coolio and EventMachine only use level-triggered epoll, but being Rainbows!, we live on the EDGE! commit 7a04133c1ab57923cac8a9de04b00bfe89bcce2d Author: Eric Wong Date: Wed Jan 19 02:58:43 2011 +0000 tests: content-md5 tests shut down connection This makes content-md5 tests much faster since we no longer wait for the server to to timeout. commit 810701d939114a5fcdacc55ec383de112c5e3fa4 Author: Eric Wong Date: Mon Jan 17 03:42:33 2011 +0000 ev_core: reuse buffer to avoid GC thrashing Single-threaded concurrency models can reuse a single buffer to avoid thrashing memory and causing unnecessary GC activity. commit c7f4ade8edcfa7422d41c4272250b292b141b427 Author: Eric Wong Date: Fri Jan 14 18:51:54 2011 +0000 t0050: improve test reliability normal signals can get lost easily :< commit 18db44fe89b8ba61f21b92f6efece37baa8bd6c7 Author: Eric Wong Date: Fri Jan 14 18:49:58 2011 +0000 tests: bump rack-fiber_pool version to 0.9.1 We always try to track the latest and greatest. We've also updated the test to actually test concurrency since rack-fiber_pool reuses recent fibers now. commit 2eb58a62a5e738c287db8239f4f4e8d1d02ed328 Author: Eric Wong Date: Tue Jan 11 16:43:10 2011 -0800 Rainbows! 3.0.0 - serving the fastest apps to slow clients faster! There is one incompatible change: We no longer assume application authors are crazy and use strangely-cased headers for "Content-Length", "Transfer-Encoding", and "Range". This allows us to avoid the case-insensitivity of Rack::Utils::HeaderHash for a speed boost on the few apps that already serve thousands of requests/second per-worker. :Coolio got "async.callback" support like :EventMachine, but it currently lacks EM::Deferrables which would allow us to call "succeed"/"fail" callbacks. This means only one-shot response writes are supported. There are numerous internal code cleanups and several bugfixes for handling partial static file responses. commit 9caff8ecc7912306366cebb9c57b65ab12fde5ad Author: Eric Wong Date: Tue Jan 11 16:36:08 2011 -0800 add write-on-close test from Unicorn We need to ensure this esoteric feature keeps working for some people. commit 89f948aeaef34114ec61291ff8ffbebcf85a748e Author: Eric Wong Date: Tue Jan 11 16:01:20 2011 -0800 event_machine: buffer reads when waiting for async.callback We cannot trigger on_read events and invoke the HTTP parser and modify @env while we're waiting for an application to run async.callback. We also need to clear (and *maybe* re-set) @deferred if we're writing from async.callback commit 0515dee246536cb3942f51f8d264737f106d3985 Author: Eric Wong Date: Tue Jan 11 15:46:56 2011 -0800 event_machine/client: rename ivar for consistency with Coolio Both use @deferred to refer to the state where there's a deferred response body in the queue. commit f9256a6897645e709753f37b723ed1c6388c0c84 Author: Eric Wong Date: Tue Jan 11 15:44:57 2011 -0800 event_machine/client: remove unused :body accessor Nobody uses it anymore commit 69d8ec2f167eb99a1efec4a770ba5951e7c366f0 Author: Eric Wong Date: Mon Jan 10 18:07:21 2011 -0800 coolio: enable async.callback for one-shot body responses The lack of an equivlent to EM::Deferrable prevents us from doing streaming/trickling responses, but a one-shot body should work fine for Coolio and generating dynamic responses. commit bf6eb8f1f79ed57ee317b2e6f73c70a59708739f Author: Eric Wong Date: Fri Jan 7 18:16:27 2011 -0800 rainbows/coolio/client: set LOOP constant in module It's more natural and easier to maintain this way since Coolio::Client is always lazy loaded. commit 4747aa0d07950b9abbb036bc02491c4639dc9f80 Author: Eric Wong Date: Fri Jan 7 17:10:46 2011 -0800 coolio/client: small optimizations * lazy load uncommon classes: StreamFile, Response{Chunk,}Pipe * remove needless rainbows/coolio/sendfile module since we're lazily loaded now along with the rest of rainbows/coolio/client and only in the worker. commit 37ec9ef3272931f42b4b0bb3f04a3855a8702a05 Author: Eric Wong Date: Fri Jan 7 18:06:54 2011 -0800 coolio_thread_*: lazy load Rainbows::Coolio::Client We don't want that loaded in the parent process since we want config reloadability. commit f6d448bc21c3bde3ab39b55664722b40f5801c20 Author: Eric Wong Date: Fri Jan 7 18:05:28 2011 -0800 redirect unexpected test output to /dev/null We check the return code anyways, and spewing random binary data to the terminal with verbosity on is not a good idea. commit 25c9cf0d8420a971840297d9ca62e7dd9c05b09e Author: Eric Wong Date: Fri Jan 7 17:07:48 2011 -0800 event_machine: fold write_response back into client No point in having too many modules to search around (for both hackers and the runtime). commit 7e4a7225dbd01df27a6b3ec44e53c013b889a724 Author: Eric Wong Date: Fri Jan 7 16:12:26 2011 -0800 event_machine: cleanup async logic Since we support keepalive now, setting @state can be harmful and the comment is out-of-date. commit 9d2fdee38f990e44b3a232c27f51287173e60e91 Author: Eric Wong Date: Fri Jan 7 15:49:02 2011 -0800 ev_core: small organization cleanup We want to put all constants in one place. commit 63c32b35cccd5f2358565f828db240200e8bffa7 Author: Eric Wong Date: Fri Jan 7 15:47:33 2011 -0800 more consistent use/avoidance of HeaderHash Rack::Utils::HeaderHash is still expensive, so avoid forcing it on users since we can assume app/library authors use normally-cased HTTP headers. commit 58dbf0952b94b01d4a434fa880755f9a320c6103 Author: Eric Wong Date: Fri Jan 7 10:27:30 2011 -0800 favor Hash#include? for some existence checks Hash#[] is slightly slower on the miss case due to calling Hash#default (but faster for the hit case, probably because it is inlined in 1.9). commit 2d2416daa554dd530b5f2cfeffe3e0e31505c824 Author: Eric Wong Date: Fri Jan 7 11:59:42 2011 -0800 test_isolate: bump versions and simplify We can't possibly keep track of all sub-dependencies, so only declare primary dependencies until we find a known problem with a sub-dependency. commit fbedef35186609f9e1cdedaa3a9d2a016423fe82 Author: Eric Wong Date: Fri Jan 7 11:55:12 2011 -0800 test_isolate: prevent concurrent execution I realize this lock overly covers different versions of Ruby, but it's simple and we don't need to invoke isolate too often (we hope). commit efbdc44a5a301e5ea16d5135afaa9cda3a33f5d3 Author: Eric Wong Date: Fri Jan 7 11:54:35 2011 -0800 tests: bump async_sinatra dependency to 0.4.0 It's out and it works, so why not. commit f1893ae9786db250fd95def1cf958f2351cb84c5 Author: Eric Wong Date: Fri Jan 7 11:47:05 2011 -0800 bump dependency on Rack to 1.2.1 We need to split out Cramp tests to Isolate to a different path, which sucks, but oh well. Cramp hasn't had a release in a while... commit 360ba90a9a50b5aabd1c65d51034c7ebe77e36ef Author: Eric Wong Date: Fri Jan 7 10:20:24 2011 -0800 response: do not skip Status header set by app Rack::Lint already stops apps from using it. If a developer insists on it, then users who inspect their HTTP headers can point and laugh at them for not using Rack::Lint! commit c162bd4d620bc1421f104d9d0dfba21fb9b43068 Author: Eric Wong Date: Fri Jan 7 10:10:20 2011 -0800 response: fix skipping of Status: header from app We already set a Status: header by default for compatibility with some existing, broken libraries out there. commit b788a0f1eedfb358f9d57f40cec6ba0960dea3fe Author: Eric Wong Date: Fri Jan 7 10:02:52 2011 -0800 ev_core: garbage reduction with const strings Reading headers is common and we don't want to create new String objects (even if they're tiny or copy-on-write) for the GC to munch on. commit 370fb8c7811704ed65384f599b52ac1b6d0c36c9 Author: Eric Wong Date: Wed Jan 5 18:01:36 2011 -0800 event_machine: refactor async.callback for keepalive async.callback will be useful with Coolio (and more!) soon, so ensure it works as well as the rest of Rainbows! commit 2cb26ba8084cd37996330616b885de1c780d848e Author: Eric Wong Date: Wed Jan 5 17:39:11 2011 -0800 event_machine: factor out async.callback handling This will allow Coolio to use it, too. commit 31a93152c8977f31045bd182ae99df4ebd088abf Author: Eric Wong Date: Wed Jan 5 17:18:05 2011 -0800 minor cleanups following state cleanups We noticed a few more things that could be cleaned up after the last commit. commit 6bde32081338ce8075854f4c47ce8ca5347df919 Author: Eric Wong Date: Wed Jan 5 17:06:20 2011 -0800 eliminate G constant and just use the Rainbows! module Code organization is hard :< commit d6e4975937a9590f48dc39b1a4aefa9d62f34616 Author: Eric Wong Date: Wed Jan 5 16:38:46 2011 -0800 rainbows.rb: unindent commit ea6831e1eaeeb862afa7ed9213e2d9bc0180e706 Author: Eric Wong Date: Wed Jan 5 16:33:04 2011 -0800 eliminate timed_read module No need to split it out when there's only a single class using it. commit ed7669ced3aba5c0ba6f5fbee9411546b32c96df Author: Eric Wong Date: Wed Jan 5 16:29:53 2011 -0800 simplify keepalive_timeout accounting Easier just to use an instance variable commit 4060b7742d047c0000fd1bf4ac2c3b9cae95585a Author: Eric Wong Date: Wed Jan 5 16:19:17 2011 -0800 disable Nagle's algorithm by default Nagle's algorithm is harmful with the write-write-read sequence during keepalive, so we disable it performance for users using keepalive. We always write headers with a separate write because Rack response bodies may not always be ready for writing when headers are. This requires Unicorn 3.3.0 commit 9b6679ec50ecd184fccd195bd14bda3c6ea2070f Author: Eric Wong Date: Wed Jan 5 16:15:29 2011 -0800 simplify Rainbows.max_bytes implementation Yes, I'm still learning Ruby. commit 950b4ba1454543de492efffdf4022f481ca7e4c2 Author: Eric Wong Date: Wed Jan 5 16:14:07 2011 -0800 response: use optimized httpdate implementation Believe it or not, Time#httpdate showed up at the top of my profiler output for the past couple of years now. I guess that's what happens when all HTTP applications I write are less complex than Rack::Lobster :P commit 4768850390bb1d1e3c2e36273e9aad953e18de19 Author: Eric Wong Date: Wed Jan 5 16:08:09 2011 -0800 Bump unicorn dependency to 3.3.0 This means we can remove Time.now.httpdate in the next commit commit e8ae2e483e75ad163212cc8d3a7107eb2a014a9c Author: Eric Wong Date: Wed Jan 5 10:56:50 2011 -0800 response: allow normal Hash for crafting Range headers HeaderHash is quite expensive, and Rack::File currently uses a regular Ruby Hash with properly-cased headers the same way they're presented in rfc2616. commit 16b24c86cecec0697b35ac321cddd500bc9cfdea Author: Eric Wong Date: Wed Jan 5 10:54:45 2011 -0800 tests: ensure 416 responses do keepalive This is useful for clients that specify a bad range, we can preserve the connection for them to specify a good response. commit 89e1f00cd540bca54c2cbcb7d9b6cad5e0e3cf34 Author: Eric Wong Date: Wed Jan 5 10:22:11 2011 -0800 send proper 416 responses 416 responses without a body should respond with a zero Content-Length and a Content-Range that allows clients to specify a proper range in the future. rfc2616, section 14.16 says: > A server sending a response with status code 416 (Requested > range not satisfiable) SHOULD include a Content-Range field > with a byte-range- resp-spec of "*". The instance-length > specifies the current length of the selected resource. commit 429e9948d91fa159c2daa9944a5026cebb78bade Author: Eric Wong Date: Tue Jan 4 18:13:45 2011 -0800 writer_thread_pool: remove unnecesary debug messages Oops commit 104f7bd76696e42c0b8a97f3780cabea9a7dd44c Author: Eric Wong Date: Tue Jan 4 17:12:22 2011 -0800 prefer Hash#merge! to Hash#update for consistency It's more likely to be in the method cache since Rack::URLMap uses it and also is more consistent when looking at profiling output against various concurrency models. commit e21939d776673b2f8887adf7a5c64812b7d2e98e Author: Eric Wong Date: Thu Dec 30 08:33:15 2010 +0000 globally refactor Range handling for responses Rack::Utils::HeaderHash is still very expensive in Rack 1.2, especially for simple things that we want to run as fast as possible with minimal interference. HeaderHash is unnecessary for most requests that do not send Content-Range in responses. commit 4a76da1833922c74e147be5def9bfe04fd0c16a2 Author: Eric Wong Date: Thu Dec 30 08:32:28 2010 +0000 coolio: rename deferred_response => response_pipe For consistency with the EventMachine code commit ed28feabd79697cb27722036622aeca1fbf0723d Author: Eric Wong Date: Thu Dec 30 08:32:27 2010 +0000 event_machine: cleanup response_pipe No need to pass unnecessary variables to response_pipe, just let the client handle it all. commit c38bc88363d4c4f5f639540ca86f4dd1225df79e Author: Eric Wong Date: Thu Dec 30 08:32:26 2010 +0000 refactor response sendfile body handling for easier debugging Unique method names makes it easier to follow code and determine where our methods come from. commit 1a03f9a525faf4c997546c419de6854223b645c4 Author: Eric Wong Date: Thu Dec 30 08:32:25 2010 +0000 t9000: disable this test for CoolioThread* models It's not appropriate to use AppPool middleware with these. It was disabled for RevThread*, too. commit 72a315441937f9e0531112f2b7080da39ca6064d Author: Eric Wong Date: Thu Dec 30 08:32:24 2010 +0000 simplify per-client keepalive state checks This lets us simplify repetitive checks worry less about properly maintaining/closing client connections for each concurrency model we support. commit 1f28534fce2b30bf3099ea0d014b62ac4a3205f0 Author: Eric Wong Date: Thu Dec 30 08:32:23 2010 +0000 coolio/master: small garbage reduction Creating unnecessary string objects for every response is not a good idea. commit a471d10cd793c4dc7182b4e588a21d6ac7c41de7 Author: Eric Wong Date: Thu Dec 30 08:32:22 2010 +0000 return 206 status for partial sendfile responses Although curl did not complain, 206 is the correct error code for partial HTTP responses. commit dc77752eb7bfe528c60dc0ac028717877342e22a Author: Eric Wong Date: Thu Dec 30 08:32:21 2010 +0000 coolio_thread_*: fix 416 response handling After beefing up and enabling byte range tests for "sendfile" (and no just IO.copy_stream), we noticed threaded-Coolio variants did not handle invalid byte ranges correctly. commit 5c5aea99a57d07c1d0e0ea471b62acf2852526c7 Author: Eric Wong Date: Thu Dec 30 08:32:20 2010 +0000 t0022: fix up broken/incomplete tests Tests for checking the Content-Range were totally broken, but fortunately the code itself works. commit 0e7d1a8b2923a20c452a300a48d25f7224b0b35b Author: Eric Wong Date: Tue Jan 4 16:18:46 2011 -0800 *gemspec: fix build, we have no unit tests commit 558abace15eefb06163b741cc7090893d532fb1d Author: Eric Wong Date: Wed Dec 29 09:29:13 2010 +0000 Packaging cleanups, reinstate generated files for the tarball commit 074b2ddc09517274b058a68fd1d40c59e35ca3a5 Author: Eric Wong Date: Wed Dec 29 09:03:37 2010 +0000 Rakefile: fix fm_update task Oops commit e98836e165fa46b67fc57adc1ce59620f4f3fc4d Author: Eric Wong Date: Wed Dec 29 02:13:04 2010 +0000 Rainbows! 2.1.0 - Cool.io, bugfixes and more! Cool.io (new version of Rev) support is explicitly added (it always worked before). ":Coolio" may be used in place of ":Rev" anywhere in your Rainbows! config file. There is a new "keepalive_requests" config directive to limit the number of requests a single connection may make (default: 100, same as nginx). This may be useful for better load-balancing characteristics. The old "Rev" prefixes remain supported as long as Cool.io remains compatible with Rev (likely forever). Bug fixes: * Rainbows::ThreadTimeout middleware with multiple clients * large, pipelined upload errors with Revactor+Coolio(Rev) * high CPU usage for maintaining idle keepalive on *Fiber* * needless ThreadPool wakeups * request env prematurely cleared keepalive requests, breaking some middlewares such as Clogger. * "close" not called on body if wrapper and sendfile used together Various code cleanups, and our RDoc website is JavaScript-free. See the ChangeLog or git for all changes. commit 40445641f11f01c6a24bf96c8b80eed5fd33a512 Author: Eric Wong Date: Tue Dec 28 17:59:27 2010 -0800 complete Rev => Coolio renaming We use Cool.io internally everywhere now, but preserve Rev-based models for anybody using them. commit 3495d59763e6159975debf32728dc53fc41c5ea1 Author: Eric Wong Date: Mon Dec 27 20:25:39 2010 -0800 several response body#close fixes Some middlewares require the Rack env to be preserved all the way through to close, so we'll ensure all request models preserve it. We also need to better response body wrappers/proxies always get fired properly when returning. IO.copy_stream and "sendfile" gem users could hit cases where wrappers did not fire properly. commit 53afe0b23fc67c5b25541cddbd68f905c649e756 Author: Eric Wong Date: Tue Dec 28 06:54:06 2010 +0000 dev_fd_response: pass files straight through No need to wrap regular files commit 46d79be0ad3de48ef0a677537becb3508ccad31e Author: Eric Wong Date: Tue Dec 28 01:14:43 2010 +0000 enable the keepalive_requests config option This will allow servers to limit the number of keepalive requests that can be made over a single connection to prevent denial-of-service and also to improve fairness in load-balancing. commit ef66567984780b2ce8daa155c367bcf7e049ab77 Author: Eric Wong Date: Mon Dec 27 23:25:12 2010 +0000 coolio*: favor Coolio over Rev We still use and define Rev internally, but that's mostly just manual labor of converting stuff over. commit 728496a31f34234b46d7025a23933aa06dd824f5 Author: Eric Wong Date: Mon Dec 27 13:10:57 2010 -0800 coolio_fiber_spawn: decrease CPU usage for keepalive Blindly resuming fibers every second is a waste of cycles, we can use the ZZ hash in regular FiberSpawn to resume expired fibers on an as-needed basis. While we're at it, merge the keepalive-timeout class into the heartbeat, there's no reason to have separate timers and classes here. commit 0766c1eb631190ee514a90e4d20a941f0a310054 Author: Eric Wong Date: Mon Dec 27 13:10:56 2010 -0800 t0019: add CPU usage test This requires manual verification :< commit d430bc3a481f897b4f515d5b7d0a0e239f8d2bb2 Author: Eric Wong Date: Mon Dec 27 13:10:55 2010 -0800 test-lib: fix bug in setting worker_connections We need to be able to set this with keepalive_timeout simultaneously. commit 4e7ee4b8a451edf78b0eeac47b8a24c591986e91 Author: Eric Wong Date: Mon Dec 27 13:10:54 2010 -0800 t0015: even less racy fix We need to ensure the first worker has started and is running before attempting to signal the reload. commit 2b85ea421197af84a13f6062b21ddcf61490ebcf Author: Eric Wong Date: Mon Dec 27 08:57:31 2010 +0000 doc: misc cleanups and additions for RDoc This is also our website, so we need to document the new Cool.io-based concurrency options for users and point existing Rev* users to it. commit 87a38d1a5658938a3540b45ab04cc85a9135c8f0 Author: Eric Wong Date: Mon Dec 27 07:45:28 2010 +0000 t0015: increase reliability of test The worker process may fork before the original process is killed during daemonization. commit 3a250fcfb9fcfa0ab3a8105821e670563025faa4 Author: Eric Wong Date: Mon Dec 27 07:18:49 2010 +0000 initial cool.io support Cool.io is the new name for Rev. We'll continue to support Rev until Cool.io breaks backwards compatibility. Rev may not be supported if Cool.io is. commit 2873361069dc2f8c793875316a0a2c9b8fa54761 Author: Eric Wong Date: Mon Dec 27 04:23:34 2010 +0000 fiber/io: avoid allocating Range objects for slicing It's slightly faster this way, but string slicing sucks anyways :< commit 40c22b3f0a2ace6e4b51a39624ac5ed5ee3d0162 Author: Eric Wong Date: Mon Dec 27 03:56:30 2010 +0000 fiber/*: more efficient keepalive_timeout expiry We can use the same interface as Rainbows::Fiber.sleep to avoid blindly waking up readers at ever scheduler invocation. commit abb3f7e057bd5fb0aa97cae1410ce2f55ba12b9c Author: Eric Wong Date: Mon Dec 27 03:01:47 2010 +0000 fiber/base: use bare "select" where possible Less visual noise commit 2d25a86d1d17bd966eea59e5666e41d9da562811 Author: Eric Wong Date: Mon Dec 27 02:59:10 2010 +0000 thread_pool: avoid needless wakeups from select No point in waking up when our ticker runs in a separate thread. commit a3d3d13711869d420b4473d492bd788ebe493053 Author: Eric Wong Date: Mon Dec 27 02:58:31 2010 +0000 thread_*: unindent Hopefully this will make our code easier to follow. commit 94b848a8f9120bce8b0abd776b1a9b7e2f4fa30d Author: Eric Wong Date: Mon Dec 27 02:43:44 2010 +0000 introduce worker_yield method This lets Rainbows! yield the current worker process when busy in the hopes another worker will pick up the slack. We can also override this for the single worker process case later if people care enough. commit a310302708faa19042282e94525544cfbb23eba5 Author: Eric Wong Date: Mon Dec 27 02:36:58 2010 +0000 writer_thread_spawn: factor out Client.quit Self-documenting code is easier to follow commit 6ae020c9ac483d822902b5d33f038f79b44d3a50 Author: Eric Wong Date: Mon Dec 27 02:30:58 2010 +0000 writer_thread_*: split out classes into separate files Use a consistent "Client" naming to reduce confusion commit e7d295fd8e3628eba7a1ba52e95b7dee11532e98 Author: Eric Wong Date: Mon Dec 27 02:20:00 2010 +0000 writer_thread_*: split out common socket_proxy code Needless duplication sucks commit 7f2cb1b56afda847c29e1e65fe0608a6f20a0fe6 Author: Eric Wong Date: Mon Dec 27 02:13:32 2010 +0000 writer_thread_*: unindent commit a5ff497e57bc6e8793c38bdd94ea9f1cfefd17fd Author: Eric Wong Date: Sun Dec 26 23:54:49 2010 +0000 revactor: split out tee_socket and use autoload Some applications never need TeeSocket, and we don't have to worry about thread-safety with Revactor. commit c4d92b384dd3f926fa12eb704eeef663a9cb7b66 Author: Eric Wong Date: Sun Dec 26 23:52:43 2010 +0000 more :: prefix elimination This should make things easier on the eyes. commit 2e131bfd21f5ec5acc3c86233e5e292cec7aa67d Author: Eric Wong Date: Sun Dec 26 23:52:02 2010 +0000 fiber/queue: unindent This also cleans up some constant resolution for the root Fiber class. commit 712ef17547291fed37e79d37d0b6e0128ed43e0d Author: Eric Wong Date: Sun Dec 26 23:41:51 2010 +0000 remove unnecessary "::" constant prefixing It's ugly to look at. commit 6733af0546a759b73fd63880e8ef2549caf4c4f2 Author: Eric Wong Date: Sun Dec 26 23:16:41 2010 +0000 never_block: simplify and split out code alias_method is a mess, super is superb! commit 20e8d57127f16da8e4242582dee3b99d54cbb729 Author: Eric Wong Date: Sun Dec 26 23:12:18 2010 +0000 event_machine: split out server and client classes This should make things easier to find commit a50c9d312b9d5274a95f2816b5f53a3738d0cb92 Author: Eric Wong Date: Sun Dec 26 22:48:33 2010 +0000 rev_thread_*: unindent and split out This should make classes easier to find and hopefully make our code easier to follow. commit 6741aa27e4c35724b5306644d3a391fce415b0c7 Author: Eric Wong Date: Sun Dec 26 21:46:46 2010 +0000 rainbows/rev/* require/autoload cleanup One line of code saved! We'll also avoid loading DeferredChunkResponse which is rarely needed (unlike DeferredResponse). commit 869e0d5da8cbe8959983ed66e2b4c31dc7a37d92 Author: Eric Wong Date: Sun Dec 26 21:40:34 2010 +0000 rename rev/thread => rev/thread_client While we're at it, unindent commit c1655a501fc26f7100dd788b42a1914be833fea4 Author: Eric Wong Date: Sun Dec 26 21:33:21 2010 +0000 rev_thread_spawn: disable under Ruby 1.8 It still burns CPU at the first sign of doing anything interesting, so stop it. Ruby 1.9 is the future :P commit fb7d5dbf06f8ce92a23b50a0cc45be3e7b55fccd Author: Eric Wong Date: Sun Dec 26 21:27:08 2010 +0000 GNUmakefile: fix packaging task Oops commit 7bfd7995fd403f80940e3f6ac36f9ae58b7040cb Author: Eric Wong Date: Sun Dec 26 03:30:36 2010 +0000 avoid HttpParser#keepalive? and HttpParser#reset The HttpParser#next? method will come with keepalive protection for Rainbows!, which can prevent clients from monopolizing a server with excessive pipelining/keepalive requests. commit 92a11cdfe00c5e551388c2cc1a62bfc59d568c6f Author: Eric Wong Date: Sun Dec 26 09:50:06 2010 +0000 bump Unicorn dependency Unicorn 3.2.1 gives us an improved HttpParser#next? that preserves state until the next HttpParser#parse call. commit 17156f6f561c6d697a83e3b9beae2d58eb796428 Author: Eric Wong Date: Sun Dec 26 03:29:16 2010 +0000 rainbows/rev/*: uninident some more This makes constant resolution more predictable, we hope. commit 68accc9930b0653b702553790d4ccd626a8dfdfe Author: Eric Wong Date: Sun Dec 26 03:09:47 2010 +0000 rev: split out Rainbows::Rev::Server One file per class/module should be easier for new hackers to find. Unindent rainbows/rev/core while we're at it, too. commit a35fd37ff0c81ca8130c18b7b77957bafe686f83 Author: Eric Wong Date: Sun Dec 26 03:00:11 2010 +0000 rev: unindent, needless autoload Deferred* classes will get loaded anyways since Rainbows::Rev::Client hit them in case statements. commit 43e3d3f7a8dd2b184c8485469c2acff3dac34009 Author: Eric Wong Date: Fri Dec 24 08:52:55 2010 +0000 doc: switch documentation generation to wrongdoc It is a common base so we can share documentation tasks more easily with Unicorn and it ensures our RDoc no longer has JavaScript in it! commit 7e0dc42f7084e1719456a80b2e44049133c2e8b7 Author: Eric Wong Date: Mon Dec 20 00:54:17 2010 +0000 thread_timeout: fix bad comparison This was causing unrelated requests to get killed every +timeout+ seconds, instead of only the ones that were running too long. Noticed-by: ghazel@gmail.com ref: http://mid.gmane.org/AANLkTi=7OhyTwkHsp_rXU7Gp1PokihiQ9bJigpO-BfN6@mail.gmail.com commit 886e0a006d9e8e9c586beae28ed4dc5097064e90 Author: Eric Wong Date: Mon Dec 20 03:41:48 2010 +0000 thread_timeout: avoid a threading bug under 1.9 Because of the lack of GVL-releasing syscalls in this branch of the thread loop, we need Thread.pass to ensure other threads get scheduled appropriately under 1.9. This is likely a threading bug in 1.9 that warrants further investigation when we're in a better mood. commit 7e2bb251228a30c0d4e66029b20bbbf85bc99a09 Author: Eric Wong Date: Tue Dec 14 18:36:34 2010 -0800 FAQ: add a note about config.threadsafe! At least one user ran into it: http://mid.gmane.org/AANLkTikegPX2-6Q93++bz_aLt+9nLPJXjg+NkL8tDjeE@mail.gmail.com commit 2bb3f8fd600bd0aabe5e4d7c3d1f99d745fc8f49 Author: Eric Wong Date: Thu Dec 9 17:14:04 2010 -0800 respect client_body_buffer_size in Unicorn 3.1.0 This is only needed for concurrency options that do not use TeeInput, since TeeInput automatically handles this for us. commit af3629e6bd88b5744016a1c6217c00c37ef5a376 Author: Eric Wong Date: Thu Dec 9 14:42:18 2010 -0800 bump Unicorn dependency to 3.1.0 We'll be taking advantage of configurable client_buffer_body_size soon. commit 7b51a4b5e16734bae6fe60d180c3906f96235da4 Author: Eric Wong Date: Thu Dec 9 14:22:16 2010 -0800 thread_timeout: de-Struct-ify Avoid exposing internals, it's also slightly faster to access ivars directly rather than using method calls. commit 6b750f5f952963009a2e6e8702fc8f3d8adc94ea Author: Eric Wong Date: Wed Dec 8 18:35:27 2010 -0800 respect "rewindable_input false" in Unicorn config This was completely overlooked for the Rainbows 2.0.x releases. commit 10d96a76a1ea4431dd10ba181d747169c22c1cec Author: Eric Wong Date: Wed Dec 8 13:38:52 2010 -0800 t/*.sh: indentation fixes We use real tabs for indenting shell code since it is not Ruby. commit 1562a19a021a72a78ba495328d2d37ba0dc83b8c Author: Eric Wong Date: Tue Dec 7 12:11:51 2010 -0800 rev+revactor: fix LARGE pipelined uploads Large uploads behave differently with regard to buffering, and there were bugs in the way the Rev and Revactor backends handled uploads. commit b4835c6d542c6369f2523ab68fc41b0202d7c6dc Author: Eric Wong Date: Fri Dec 3 01:23:11 2010 +0000 Rainbows! 2.0.1 - upload pipelining fixes For HTTP clients living on the edge and pipelining uploads, we now fully support pipelined requests (as long as the application consumes each request in its entirety). commit c096e735efea5050b0559748633403f0387ea3b3 Author: Eric Wong Date: Fri Dec 3 01:12:08 2010 +0000 fix pipelining of requests with bodies All synchronous models have this fixed in unicorn 3.0.1, so only Rev and EventMachine-based concurrency models require code changes. commit 64889d9136fa5466269232c26a2f235dd763d8f0 Author: Eric Wong Date: Thu Dec 2 07:38:14 2010 +0000 ev_core: refactor and split cap_input out Hopefully it makes more sense now and is easier to digest for new hackers. commit 945b5760d86b9dd00e65bd0625b98cf75f6a8257 Author: Eric Wong Date: Wed Dec 1 21:29:34 2010 -0800 ev_core: split out prepare_request_body We may have other uses for this in the future... commit 90789761f0cc78d3726f3a2eda1c5fe95c015ac2 Author: Eric Wong Date: Fri Nov 19 19:09:59 2010 -0800 Rainbows! 2.0.0 - minority rules! This release is targeted at the minority of web applications that deal heavily with uploads. Thanks to Unicorn 3.x, we now support HTTP keepalive for requests with bodies as long as the application consumes them. Unicorn 3.x also allows disabling the rewindability requirement of "rack.input" (in violation of the Rack 1.x spec). The global client_body_max_size may also be applied per-endpoint using the Rainbows::MaxBody middleware described in: http://rainbows.rubyforge.org/Rainbows/MaxBody.html commit c33878ac0f9f990aaa2054f8f00674d61bf5c848 Author: Eric Wong Date: Sat Nov 20 03:10:09 2010 +0000 tests: depend on Unicorn 3.0.0 Unicorn 3.0.0 is final and released, so we will use it in our tests commit a5986295bfb7bd7c44c863e4670f16481097c7fc Author: Eric Wong Date: Fri Nov 19 18:55:06 2010 -0800 revactor: fix braindamaged commit/coding style Oops, last commit was rushed commit da9dfc1dece4fbcdf2a8e8ccdb4914ce4aa3a998 Author: Eric Wong Date: Fri Nov 19 18:39:02 2010 -0800 simpler keepalive check for synchronous models Unicorn 3.x includes HttpParser#next? which will reset the parser for keepalive requests without extra steps. commit c6ffae22748bc22d5ef88fea2a3ca67f480ee74b Author: Eric Wong Date: Fri Nov 19 10:19:45 2010 +0000 max_body: rewrite wrappers to be safer To avoid denial-of-service attacks, the wrappers need to intercept requests *before* they hit the memory allocator, so we need to reimplement the read(all) and gets cases to use smaller buffers whenever the application does not specify one. commit 3cee07d750f678af92318c14110c803be3f9b97f Author: Eric Wong Date: Fri Nov 19 10:19:44 2010 +0000 max_body: do not enable for RevThread* models Those already use CapInput, just like the rest of the evented Rainbows! world. commit 00b854e37391322c05cc16115b245d855b4970be Author: Eric Wong Date: Fri Nov 19 10:19:43 2010 +0000 upgrade to Kgio 2.x and Unicorn 3.x Kgio 2.0.0 has a superior API and less likely to conflict or blow up with other applications. Unicorn 3.x requires Kgio 2.x, too. commit 0f00424be24806791f2b253ddac6d35102842646 Author: Eric Wong Date: Wed Nov 17 10:06:45 2010 -0800 test_isolate: depend on newer Unicorn It allows disabling rewindable input and contains simpler code for upload processing. commit 31cf77e7aa2f2e6065e7ace44e55c3f042b51f1b Author: Eric Wong Date: Tue Nov 16 16:16:42 2010 -0800 reimplement client_max_body_size handlers This allows the client_max_body_size implementation to not rely on Unicorn::TeeInput internals, allowing it to be used with Unicorn::StreamInput (or any other (nearly) Rack::Lint-compatible input object). commit 42747db815ad668b20849afb2a9dcdd1319713ae Author: Eric Wong Date: Tue Nov 2 12:32:23 2010 -0700 avoid Errno::EAGAIN, harder Errno::EAGAIN is still a problem under Ruby 1.9.2, so try harder to avoid it and use kgio methods. Even when 1.9.3 is available, kgio will still be faster as exceptions are slower than normal return values. commit 427ef4a2953a4b2d34f7dd89566a0cb5ee6e734d Author: Eric Wong Date: Tue Nov 2 11:49:17 2010 -0700 avoid Kgio::WaitReadable/WaitWritable constants The underlying symbolic names are easier to type and recommended. commit ed3a30dcfb5489447dec9c3f73e8bec9dbf7713a Author: Eric Wong Date: Thu Nov 4 19:52:35 2010 -0700 process_client: fix attempted keepalive on HTTP 0.9 The long-term goal is to make the Unicorn API more terse when handling keepalive. commit 9f185041fb7af4cda21ba2d547fd4d16d9b2e453 Author: Eric Wong Date: Thu Oct 28 08:42:09 2010 +0000 Rainbows! 1.0.0 - internal cleanups This release is merely a milestone in our evolving internal API. Use of kgio may result in performance improvements under Ruby 1.9.2 with non-blocking I/O-intensive workloads. The only bugfix is that SIGHUP reloads restores defaults on unset settings. A similar fix is included in Unicorn 2.0.0 as well. commit 015635f877084cc05a9e6e4c1430c70279d0a04e Author: Eric Wong Date: Thu Oct 28 08:40:12 2010 +0000 tests: avoid race conditions on reload tests We need to ensure the old worker is really dead before sending requests after reloading. commit b06c6b3ac214bc598d499c994884113d5b106e90 Author: Eric Wong Date: Thu Oct 28 08:29:53 2010 +0000 t0018: wait for old worker to be reaped On busy machines, old workers may not shutdown quickly enough and may still be processing requests. commit f84f138233be0607b0151a5a28c3f9190ba336a2 Author: Eric Wong Date: Thu Oct 28 08:20:37 2010 +0000 rev+event_machine: small cleanups with new parser API These allow for small reductions in the amount of variables we have to manage, more changes coming with later Unicorns. commit c4579db76b9ed5b0286fad852e798e8a890f093c Author: Eric Wong Date: Thu Oct 28 02:17:25 2010 +0000 HUP reload restores defaults on unset settings For consistency, changed settings are reset back to their default values if they are removed or commented out from the config file. commit 83644e02068311c7ff9cdbc63efd4764d1518138 Author: Eric Wong Date: Wed Oct 27 23:52:28 2010 +0000 use Unicorn 2.0.0 final Unicorn 2.0.0 has CPU wakeup reductions. commit 2b9503566759678c9fbd7b01d1b5f487854208db Author: Eric Wong Date: Tue Oct 26 21:36:02 2010 +0000 Rakefile: updates for prereleases We do prereleases, now. commit a47cd4a7f392a76357ed4f3e458797ae1f9c8f25 Author: Eric Wong Date: Tue Oct 26 21:26:03 2010 +0000 Rainbows! 1.0.0pre1 - kinder, gentler I/O Mostly internal changes for kgio (and Unicorn) integration. There should be no (supported) user-visible changes from Rainbows! 0.97.0. kgio should improve performance for concurrency models that use non-blocking I/O internally, especially under Ruby 1.9.2 commit 0298a6743f2cadf8c8e47a298c5b35505b74af46 Author: Eric Wong Date: Tue Oct 26 21:20:45 2010 +0000 gemspec: bump development dependency of Isolate Might as well go with the latest and greatest, it has saner defaults at least. commit 567e6ce5dba5ad2cca2cca8c64123e27939bff2b Author: Eric Wong Date: Tue Oct 26 21:13:10 2010 +0000 doc: RDoc updates Once again we avoid documenting internals on the public website and use code comments for other developers. commit 894cb73887c106acc793f0317ee849ae215ead56 Author: Eric Wong Date: Mon Oct 25 22:15:47 2010 +0000 reduce dependency on IO#write_nonblock kgio_trywrite is superior if it is available. commit 4ee6e0dafeb1b7af28fa90ae27c1a1a04aa8e852 Author: Eric Wong Date: Sat Oct 23 00:42:04 2010 +0000 http_request: remove this (sub)class It does not appear to be needed, for now, since the parser and Unicorn::HttpRequest are one and the same. commit 180485d49ea858f83ef2a28a9e07224aa514edc7 Author: Eric Wong Date: Fri Oct 22 16:21:03 2010 -0700 unindent most files This simplifies and disambiguates most constant resolution issues as well as lowering our identation level. Hopefully this makes code easier to understand. commit 41145ed4d335718ac43aec9313b7571a12fe96ee Author: Eric Wong Date: Fri Oct 22 15:31:47 2010 -0700 local.mk.sample: remove testing under 1.9.1 Ruby 1.9.2 has been out for a while and is the stable release nowadays. commit b595ad7333ff85452b229674c6726e40d2cf7bb9 Author: Eric Wong Date: Fri Oct 22 18:55:46 2010 +0000 README: update copyright year This project is over 1 year old! commit 4a568eeb7d17885579790d0ae004f04aa13479cb Author: Eric Wong Date: Fri Oct 22 02:50:09 2010 +0000 dev_fd_response: do not wrap for Fiber-aware IOs Applications may use wait_readable-aware methods directly to work with Rainbows! commit 6d46978bdc8d2ee4263431ecdcada53389b12597 Author: Eric Wong Date: Fri Oct 22 02:51:18 2010 +0000 fiber_{pool,spawn}: unindent Reduces confusion for constant resolution/scoping rules and lowers LoC. commit 03806d2b44c2d3cee75258ee9e83d671e751baeb Author: Eric Wong Date: Fri Oct 22 02:38:40 2010 +0000 fiber_pool: no need for old Fiber::IO Rainbows::Client takes care of the I/O wait/read-ability for us already. commit 15631717fce044fbad2f386a7b1c7daf4bdd83d2 Author: Eric Wong Date: Thu Oct 21 16:25:39 2010 -0700 code shuffling for kgio Despite the large number of changes, most of it is code movement here. commit d4a2b5dd2b85f4b2d3bb120ee1e1b0dde31bc25c Author: Eric Wong Date: Wed Oct 20 17:48:58 2010 -0700 unicorn 2.x updates + kgio We get basic internal API changes from Unicorn, code simplifications coming next. commit a085ba3586756ac1f778d2862f75889de2449b0e Author: Eric Wong Date: Wed Oct 20 17:48:57 2010 -0700 http_server: more descriptive error for debugging Sometimes we have stupid syntax or constant resolution errors in our code. commit ad821f70a2488a91f2be1ac53cb2e64f50743989 Author: Eric Wong Date: Tue Sep 28 17:40:01 2010 -0700 start using kgio library It removes the burden of byte slicing and setting file descriptor flags. In some cases, we can remove unnecessary peeraddr calls, too. commit 11c75ec06ce72cea0c760161dc01a196500aa293 Author: Eric Wong Date: Fri Sep 17 08:55:35 2010 +0000 event_machine: remove unnecessary "return" Noise is bad. rainbows-4.6.2/GIT-VERSION-GEN0000755000004100000410000000205612354770160015512 0ustar www-datawww-data#!/usr/bin/env ruby DEF_VER = "v4.6.2" CONSTANT = "Rainbows::Const::RAINBOWS_VERSION" RVF = "lib/rainbows/version.rb" GVF = "GIT-VERSION-FILE" vn = DEF_VER # First see if there is a version file (included in release tarballs), # then try git-describe, then default. if File.exist?(".git") describe = `git describe --abbrev=4 HEAD 2>/dev/null`.strip case describe when /\Av[0-9]*/ vn = describe system(*%w(git update-index -q --refresh)) unless `git diff-index --name-only HEAD --`.chomp.empty? vn << "-dirty" end vn.tr!('-', '.') end end vn = vn.sub!(/\Av/, "") # generate the Ruby constant new_ruby_version = "#{CONSTANT} = '#{vn}'\n" cur_ruby_version = File.read(RVF) rescue nil if new_ruby_version != cur_ruby_version File.open(RVF, "w") { |fp| fp.write(new_ruby_version) } end # generate the makefile snippet new_make_version = "GIT_VERSION = #{vn}\n" cur_make_version = File.read(GVF) rescue nil if new_make_version != cur_make_version File.open(GVF, "w") { |fp| fp.write(new_make_version) } end puts vn if $0 == __FILE__ rainbows-4.6.2/vs_Unicorn0000644000004100000410000000573112354770160015462 0ustar www-datawww-data= \Rainbows! is like Unicorn, but Different... While \Rainbows! depends on Unicorn for its process/socket management, HTTP parser and configuration language; \Rainbows! is more ambitious. == Architectural Diagrams === Unicorn uses a 1:1 mapping of processes to clients unicorn master \_ unicorn worker[0] | \_ client[0] \_ unicorn worker[1] | \_ client[1] \_ unicorn worker[2] | \_ client[2] ... \_ unicorn worker[M] \_ client[M] === \Rainbows! uses a M:N mapping of processes to clients rainbows master \_ rainbows worker[0] | \_ client[0,0] | \_ client[0,1] | \_ client[0,2] | ... | \_ client[0,N] \_ rainbows worker[1] | \_ client[1,0] | \_ client[1,1] | \_ client[1,2] | \_ client[1,3] | ... | \_ client[1,N] \_ rainbows worker[2] | \_ client[2,0] | \_ client[2,1] | \_ client[2,2] | ... | \_ client[2,N] ... \_ rainbows worker[M] \_ client[M,0] \_ client[M,1] \_ client[M,2] ... \_ client[M,N] In both cases, workers share common listen sockets with the master and pull connections off the listen queue only if the worker has resources available. == Differences from Unicorn * log rotation is handled immediately in \Rainbows! whereas Unicorn has the luxury of delaying it until the current request is finished processing to prevent log entries for one request to be split across files. * load balancing between workers is imperfect, certain worker processes may be servicing more requests than others so it is important to not set +worker_connections+ too high. Unicorn worker processes can never be servicing more than one request at once. * speculative, non-blocking accept() is not used, this is to help load balance between multiple worker processes. * HTTP pipelining and keepalive may be used for GET and HEAD requests. * Less heavily-tested and inherently more complex. == Similarities with Unicorn While some similarities are obvious (we depend on and subclass off Unicorn code), some things are not: * Does not attempt to accept() connections when pre-configured limits are hit (+worker_connections+). This will first help balance load to different worker processes, and if your listen() +:backlog+ is overflowing: to other machines in your cluster. * Accepts the same {signals}[http://unicorn.bogomips.org/SIGNALS.html] for process management, so you can share scripts to manage them (and nginx, too). * supports per-process listeners, allowing an external load balancer like haproxy or nginx to be used to balance between multiple worker processes. * Exposes a streaming "rack.input" to the Rack application that reads data off the socket as the application reads it (while retaining rewindable semantics as required by Rack). This allows Rack-compliant apps/middleware to implement things such as real-time upload progress monitoring. rainbows-4.6.2/.document0000644000004100000410000000204312354770160015222 0ustar www-datawww-dataChangeLog DEPLOY FAQ lib/rainbows.rb lib/rainbows/actor_spawn.rb lib/rainbows/app_pool.rb lib/rainbows/base.rb lib/rainbows/configurator.rb lib/rainbows/coolio.rb lib/rainbows/coolio_thread_pool.rb lib/rainbows/coolio_thread_spawn.rb lib/rainbows/dev_fd_response.rb lib/rainbows/epoll.rb lib/rainbows/event_machine.rb lib/rainbows/fiber_pool.rb lib/rainbows/fiber_spawn.rb lib/rainbows/max_body.rb lib/rainbows/never_block.rb lib/rainbows/rev.rb lib/rainbows/rev_fiber_spawn.rb lib/rainbows/rev_thread_pool.rb lib/rainbows/rev_thread_spawn.rb lib/rainbows/revactor.rb lib/rainbows/sendfile.rb lib/rainbows/server_token.rb lib/rainbows/stream_response_epoll.rb lib/rainbows/thread_pool.rb lib/rainbows/thread_spawn.rb lib/rainbows/thread_timeout.rb lib/rainbows/worker_yield.rb lib/rainbows/writer_thread_pool.rb lib/rainbows/writer_thread_spawn.rb lib/rainbows/xepoll.rb lib/rainbows/xepoll_thread_pool.rb lib/rainbows/xepoll_thread_spawn.rb LATEST LICENSE NEWS rainbows_1 README SIGNALS TODO TUNING vs_Unicorn Summary Test_Suite Static_Files Sandbox HACKING rainbows-4.6.2/Sandbox0000644000004100000410000000130512354770160014724 0ustar www-datawww-data= Tips for using \Rainbows! with Sandbox installation tools Most {tips for Unicorn}[http://unicorn.bogomips.org/Sandbox.html] for Bundler and Isolate apply to \Rainbows! as well. == TLDR (Bundler) You need to add "rainbows" to your Gemfile for Bundler and start \Rainbows! with: bundle exec rainbows ... == TLDR (Isolate) Isolate "rainbows" and execute the "rainbows" launcher in your isolated GEM_PATH: $APP_ROOT/tmp/ruby-1.9/bin/rainbows ... == Explanation Due to the variety of potential dependencies, \Rainbows! lazy loads many of its internals, often after the application itself is loaded. This results in more potential to interact badly with sandbox tools that modify the gem environment. rainbows-4.6.2/COPYING0000644000004100000410000010436712354770160014452 0ustar www-datawww-data GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .