rainbows 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/t/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ /test-results-*
2
+ /test-bin-*
3
+ /*.code
4
+ /*.log
data/t/GNUmakefile ADDED
@@ -0,0 +1,64 @@
1
+ # we can run tests in parallel with GNU make
2
+
3
+ all::
4
+
5
+ ruby = ruby
6
+ rainbows_lib := $(shell cd ../lib && pwd)
7
+ -include ../local.mk
8
+ ifeq ($(RUBY_VERSION),)
9
+ RUBY_VERSION := $(shell $(ruby) -e 'puts RUBY_VERSION')
10
+ endif
11
+
12
+ ifeq ($(RUBYLIB),)
13
+ RUBYLIB := $(rainbows_lib)
14
+ else
15
+ RUBYLIB := $(rainbows_lib):$(RUBYLIB)
16
+ endif
17
+ export RUBYLIB
18
+
19
+ T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
20
+
21
+ all:: $(T)
22
+
23
+ # can't rely on "set -o pipefail" since we don't require bash or ksh93 :<
24
+ t_code = $@-$(RUBY_VERSION).code
25
+ t_log = $@-$(RUBY_VERSION).log
26
+ t_run = $(TRACER) $(SHELL) $(TEST_OPTS) $@
27
+
28
+ # prefix stdout messages with ':', and stderr messages with '!'
29
+ t_wrap = ( ( ( $(RM) $(t_code); \
30
+ $(t_run); \
31
+ echo $$? > $(t_code) ) \
32
+ | sed 's/^/$(pfx):/' 1>&3 ) 2>&1 \
33
+ | sed 's/^/$(pfx)!/' 1>&2 ) 3>&1
34
+
35
+ ifndef V
36
+ quiet_pre = @echo '* $@';
37
+ quiet_post = > $(t_log) 2>&1; exit $$(cat $(t_code))
38
+ pfx =
39
+ else
40
+ quiet_pre = @echo '* $@';
41
+ quiet_post = 2>&1 | tee $(t_log); exit $$(cat $(t_code))
42
+ pfx = $@
43
+ endif
44
+
45
+ # TRACER='strace -f -o $@.strace -s 100000'
46
+ run_test = $(quiet_pre) ( $(t_wrap) ) $(quiet_post)
47
+
48
+ test-bin-$(RUBY_VERSION)/rainbows: ruby_bin = $(shell which $(ruby))
49
+ test-bin-$(RUBY_VERSION)/rainbows: ../bin/rainbows
50
+ mkdir -p $(@D)
51
+ install -m 755 $^ $@+
52
+ $(ruby) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@+
53
+ cmp $@+ $@ || mv $@+ $@
54
+ $(RM) $@+
55
+
56
+ $(T): export ruby := $(ruby)
57
+ $(T): export PATH := $(CURDIR)/test-bin-$(RUBY_VERSION):$(PATH)
58
+ $(T): test-bin-$(RUBY_VERSION)/rainbows
59
+ $(run_test)
60
+
61
+ clean:
62
+ $(RM) -r *.log *.code test-bin-$(RUBY_VERSION)
63
+
64
+ .PHONY: $(T) clean
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ # this is to remain compatible with the unused_port function in the
3
+ # Unicorn test/test_helper.rb file
4
+ require 'socket'
5
+ require 'tmpdir'
6
+
7
+ default_port = 8080
8
+ addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
9
+ retries = 100
10
+ base = 5000
11
+ port = sock = lock_path = nil
12
+
13
+ begin
14
+ begin
15
+ port = base + rand(32768 - base)
16
+ while port == default_port
17
+ port = base + rand(32768 - base)
18
+ end
19
+
20
+ sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
21
+ sock.bind(Socket.pack_sockaddr_in(port, addr))
22
+ sock.listen(5)
23
+ rescue Errno::EADDRINUSE, Errno::EACCES
24
+ sock.close rescue nil
25
+ retry if (retries -= 1) >= 0
26
+ end
27
+
28
+ # since we'll end up closing the random port we just got, there's a race
29
+ # condition could allow the random port we just chose to reselect itself
30
+ # when running tests in parallel with gmake. Create a lock file while
31
+ # we have the port here to ensure that does not happen.
32
+ lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
33
+ lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
34
+ rescue Errno::EEXIST
35
+ sock.close rescue nil
36
+ retry
37
+ end
38
+ sock.close rescue nil
39
+ puts "listen=#{addr}:#{port} lock_path=#{lock_path}"
data/t/sha1.ru ADDED
@@ -0,0 +1,17 @@
1
+ # SHA1 checksum generator
2
+ bs = ENV['bs'] ? ENV['bs'].to_i : 4096
3
+ require 'digest/sha1'
4
+ use Rack::ContentLength
5
+ app = lambda do |env|
6
+ /\A100-continue\z/i =~ env['HTTP_EXPECT'] and
7
+ return [ 100, {}, [] ]
8
+ digest = Digest::SHA1.new
9
+ input = env['rack.input']
10
+ buf = input.read(bs)
11
+ begin
12
+ digest.update(buf)
13
+ end while input.read(bs, buf)
14
+
15
+ [ 200, {'Content-Type' => 'text/plain'}, [ digest.hexdigest << "\n" ] ]
16
+ end
17
+ run app
data/t/t0000-basic.sh ADDED
@@ -0,0 +1,18 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+
4
+ eval $(unused_listen)
5
+ config_ru=$(mktemp -t rainbows.$$.XXXXXXXX.config.ru)
6
+ pid=$(mktemp -t rainbows.$$.XXXXXXXX.pid)
7
+ TEST_RM_LIST="$TEST_RM_LIST $config_ru $lock_path"
8
+
9
+ cat > $config_ru <<\EOF
10
+ use Rack::ContentLength
11
+ use Rack::ContentType
12
+ run lambda { |env| [ 200, {}, [ env.inspect << "\n" ] ] }
13
+ EOF
14
+
15
+ rainbows $config_ru -l $listen --pid $pid &
16
+ wait_for_pid $pid
17
+ curl -sSfv http://$listen/
18
+ kill $(cat $pid)
@@ -0,0 +1,53 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+
4
+ eval $(unused_listen)
5
+ config_ru=$(mktemp -t rainbows.$$.XXXXXXXX.config.ru)
6
+ unicorn_config=$(mktemp -t rainbows.$$.XXXXXXXX.unicorn.rb)
7
+ curl_out=$(mktemp -t rainbows.$$.XXXXXXXX.curl.out)
8
+ curl_err=$(mktemp -t rainbows.$$.XXXXXXXX.curl.err)
9
+ pid=$(mktemp -t rainbows.$$.XXXXXXXX.pid)
10
+ TEST_RM_LIST="$TEST_RM_LIST $config_ru $unicorn_config $lock_path"
11
+ TEST_RM_LIST="$TEST_RM_LIST $curl_out $curl_err"
12
+
13
+ cat > $config_ru <<\EOF
14
+ use Rack::ContentLength
15
+ use Rack::ContentType
16
+ run lambda { |env|
17
+ sleep 1
18
+ [ 200, {}, [ Thread.current.inspect << "\n" ] ]
19
+ }
20
+ EOF
21
+
22
+ nr_client=30
23
+ nr_thread=10
24
+
25
+ cat > $unicorn_config <<EOF
26
+ listen "$listen"
27
+ pid "$pid"
28
+ Rainbows! do
29
+ use :ThreadPool
30
+ worker_connections $nr_thread
31
+ end
32
+ EOF
33
+
34
+ rainbows -D $config_ru -c $unicorn_config
35
+ wait_for_pid $pid
36
+
37
+ start=$(date +%s)
38
+ for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" </dev/null)
39
+ do
40
+ ( curl -sSf http://$listen/$i >> $curl_out 2>> $curl_err ) &
41
+ done
42
+ wait
43
+ echo elapsed=$(( $(date +%s) - $start ))
44
+
45
+ kill $(cat $pid)
46
+
47
+ ! test -s $curl_err
48
+ test x"$(wc -l < $curl_out)" = x$nr_client
49
+
50
+ nr=$(sort < $curl_out | uniq | wc -l)
51
+
52
+ test "$nr" -le $nr_thread
53
+ test "$nr" -gt 1
@@ -0,0 +1,50 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+
4
+ eval $(unused_listen)
5
+ config_ru=$(mktemp -t rainbows.$$.XXXXXXXX.config.ru)
6
+ unicorn_config=$(mktemp -t rainbows.$$.XXXXXXXX.unicorn.rb)
7
+ curl_out=$(mktemp -t rainbows.$$.XXXXXXXX.curl.out)
8
+ curl_err=$(mktemp -t rainbows.$$.XXXXXXXX.curl.err)
9
+ pid=$(mktemp -t rainbows.$$.XXXXXXXX.pid)
10
+ TEST_RM_LIST="$TEST_RM_LIST $config_ru $unicorn_config $lock_path"
11
+ TEST_RM_LIST="$TEST_RM_LIST $curl_out $curl_err"
12
+
13
+ cat > $config_ru <<\EOF
14
+ use Rack::ContentLength
15
+ use Rack::ContentType
16
+ run lambda { |env|
17
+ sleep 1
18
+ [ 200, {}, [ Thread.current.inspect << "\n" ] ]
19
+ }
20
+ EOF
21
+
22
+ nr_client=30
23
+ nr_thread=10
24
+
25
+ cat > $unicorn_config <<EOF
26
+ listen "$listen"
27
+ pid "$pid"
28
+ Rainbows! do
29
+ use :ThreadSpawn
30
+ worker_connections $nr_thread
31
+ end
32
+ EOF
33
+
34
+ rainbows -D $config_ru -c $unicorn_config
35
+ wait_for_pid $pid
36
+
37
+ start=$(date +%s)
38
+ for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" </dev/null)
39
+ do
40
+ ( curl -sSf http://$listen/$i >> $curl_out 2>> $curl_err ) &
41
+ done
42
+ wait
43
+ echo elapsed=$(( $(date +%s) - $start ))
44
+
45
+ kill $(cat $pid)
46
+
47
+ ! test -s $curl_err
48
+ test x"$(wc -l < $curl_out)" = x$nr_client
49
+ nr=$(sort < $curl_out | uniq | wc -l)
50
+ test "$nr" -eq $nr_client
@@ -0,0 +1,52 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ require_revactor
4
+
5
+ eval $(unused_listen)
6
+ config_ru=$(mktemp -t rainbows.$$.XXXXXXXX.config.ru)
7
+ unicorn_config=$(mktemp -t rainbows.$$.XXXXXXXX.unicorn.rb)
8
+ curl_out=$(mktemp -t rainbows.$$.XXXXXXXX.curl.out)
9
+ curl_err=$(mktemp -t rainbows.$$.XXXXXXXX.curl.err)
10
+ pid=$(mktemp -t rainbows.$$.XXXXXXXX.pid)
11
+ TEST_RM_LIST="$TEST_RM_LIST $config_ru $unicorn_config $lock_path"
12
+ TEST_RM_LIST="$TEST_RM_LIST $curl_out $curl_err"
13
+
14
+ cat > $config_ru <<\EOF
15
+ use Rack::ContentLength
16
+ use Rack::ContentType
17
+ run lambda { |env|
18
+ Actor.sleep 1
19
+ [ 200, {}, [ Thread.current.inspect << "\n" ] ]
20
+ }
21
+ EOF
22
+
23
+ nr_client=30
24
+ nr_actor=10
25
+
26
+ cat > $unicorn_config <<EOF
27
+ listen "$listen"
28
+ pid "$pid"
29
+ Rainbows! do
30
+ use :Revactor
31
+ worker_connections $nr_actor
32
+ end
33
+ EOF
34
+
35
+ rainbows -D $config_ru -c $unicorn_config
36
+ wait_for_pid $pid
37
+
38
+ start=$(date +%s)
39
+ for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" </dev/null)
40
+ do
41
+ ( curl -sSf http://$listen/$i >> $curl_out 2>> $curl_err ) &
42
+ done
43
+ wait
44
+ echo elapsed=$(( $(date +%s) - $start ))
45
+
46
+ kill $(cat $pid)
47
+
48
+ ! test -s $curl_err
49
+ test x"$(wc -l < $curl_out)" = x$nr_client
50
+ nr=$(sort < $curl_out | uniq | wc -l)
51
+
52
+ test "$nr" -eq 1
@@ -0,0 +1,49 @@
1
+ #!/bin/sh
2
+ nr_client=${nr_client-25}
3
+ nr_actor=${nr_actor-50}
4
+
5
+ . ./test-lib.sh
6
+ require_revactor
7
+
8
+ eval $(unused_listen)
9
+ unicorn_config=$(mktemp -t rainbows.$$.unicorn.rb.XXXXXXXX)
10
+ curl_out=$(mktemp -t rainbows.$$.curl.out.XXXXXXXX)
11
+ curl_err=$(mktemp -t rainbows.$$.curl.err.XXXXXXXX)
12
+ r_err=$(mktemp -t rainbows.$$.r.err.XXXXXXXX)
13
+ r_out=$(mktemp -t rainbows.$$.r.out.XXXXXXXX)
14
+ pid=$(mktemp -t rainbows.$$.pid.XXXXXXXX)
15
+ blob=$(mktemp -t rainbows.$$.blob.XXXXXXXX)
16
+ TEST_RM_LIST="$TEST_RM_LIST $unicorn_config $lock_path $r_err $r_out"
17
+ TEST_RM_LIST="$TEST_RM_LIST $curl_out $curl_err $blob"
18
+
19
+ cat > $unicorn_config <<EOF
20
+ listen "$listen"
21
+ pid "$pid"
22
+ stderr_path "$r_err"
23
+ stdout_path "$r_out"
24
+ Rainbows! do
25
+ use :Revactor
26
+ worker_connections $nr_actor
27
+ end
28
+ EOF
29
+
30
+ echo pid=$pid
31
+ rainbows -D sha1.ru -c $unicorn_config
32
+ wait_for_pid $pid
33
+
34
+ dd if=/dev/urandom bs=1M count=10 of=$blob 2>/dev/null
35
+
36
+ start=$(date +%s)
37
+ for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" </dev/null)
38
+ do
39
+ ( curl -sSf -T- < $blob http://$listen/$i >> $curl_out 2>> $curl_err ) &
40
+ done
41
+ wait
42
+ echo elapsed=$(( $(date +%s) - $start ))
43
+
44
+ kill $(cat $pid)
45
+ test $nr_client -eq $(wc -l < $curl_out)
46
+ test 1 -eq $(sort < $curl_out | uniq | wc -l)
47
+ blob_sha1=$( expr "$(sha1sum < $blob)" : '\([a-f0-9]\+\)')
48
+ echo blob_sha1=$blob_sha1
49
+ test x"$blob_sha1" = x"$(sort < $curl_out | uniq)"
data/t/test-lib.sh ADDED
@@ -0,0 +1,41 @@
1
+ #!/bin/sh
2
+ # Copyright (c) 2009 Eric Wong
3
+ set -e
4
+ set -u
5
+ T=$(basename $0)
6
+ ruby="${ruby-ruby}"
7
+
8
+ # ensure a sane environment
9
+ TZ=UTC LC_ALL=C LANG=C
10
+ export LANG LC_ALL TZ
11
+ unset CDPATH
12
+
13
+ die () {
14
+ echo >&2 "$@"
15
+ exit 1
16
+ }
17
+
18
+ TEST_RM_LIST=""
19
+ trap 'rm -f $TEST_RM_LIST' 0
20
+ PATH=$PWD/bin:$PATH
21
+ export PATH
22
+
23
+ test -x $PWD/bin/unused_listen || die "must be run in 't' directory"
24
+
25
+ wait_for_pid () {
26
+ path="$1"
27
+ nr=30
28
+ while ! test -s "$path" && test $nr -gt 0
29
+ do
30
+ nr=$(($nr - 1))
31
+ sleep 1
32
+ done
33
+ }
34
+
35
+ require_revactor () {
36
+ if ! $ruby -rrevactor -e "puts Revactor::VERSION" >/dev/null 2>&1
37
+ then
38
+ echo >&2 "skipping $T since we don't have Revactor"
39
+ exit 0
40
+ fi
41
+ }
data/vs_Unicorn ADDED
@@ -0,0 +1,48 @@
1
+ = \Rainbows! is like Unicorn, but Different...
2
+
3
+ While \Rainbows! depends on Unicorn for its process/socket management,
4
+ HTTP parser and configuration language; \Rainbows! is more ambitious.
5
+
6
+ == Differences from Unicorn
7
+
8
+ * log rotation is handled immediately in \Rainbows! whereas Unicorn has
9
+ the luxury of delaying it until the current request is finished
10
+ processing to prevent log entries for one request to be split across
11
+ files.
12
+
13
+ * load balancing between workers is imperfect, certain worker processes
14
+ may be servicing more requests than others so it is important to not
15
+ set +worker_connections+ too high. Unicorn worker processes can never
16
+ be servicing more than one request at once.
17
+
18
+ * speculative, non-blocking accept() is not used, this is to help
19
+ load balance between multiple worker processes.
20
+
21
+ * HTTP pipelining and keepalive may be used for GET and HEAD requests.
22
+
23
+ * Less heavily-tested and inherently more complex.
24
+
25
+
26
+ == Similarities with Unicorn
27
+
28
+ While some similarities are obvious (we depend on and subclass of
29
+ Unicorn code), some things are not:
30
+
31
+ * Does not attempt to accept() connections when pre-configured limits
32
+ are hit (+worker_connections+). This will first help balance load
33
+ to different worker processes, and if your listen() +:backlog+ is
34
+ overflowing: to other machines in your cluster.
35
+
36
+ * Accepts the same {signals}[http://unicorn.bogomips.org/SIGNALS.html]
37
+ for process management, so you can share scripts to manage them (and
38
+ nginx, too).
39
+
40
+ * supports per-process listeners, allowing an external load balancer
41
+ like haproxy or nginx to be used to balance between multiple
42
+ worker processes.
43
+
44
+ * Exposes a streaming "rack.input" to the Rack application that reads
45
+ data off the socket as the application reads it (while retaining
46
+ rewindable semantics as required by Rack). This allows Rack-compliant
47
+ apps/middleware to implement things such as real-time upload progress
48
+ monitoring.