rainbows 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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.