rainbows 0.1.1 → 0.2.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.
Files changed (48) hide show
  1. data/.document +6 -5
  2. data/DEPLOY +13 -0
  3. data/GIT-VERSION-GEN +1 -1
  4. data/GNUmakefile +1 -1
  5. data/README +32 -6
  6. data/SIGNALS +11 -7
  7. data/TODO +2 -3
  8. data/lib/rainbows.rb +10 -3
  9. data/lib/rainbows/app_pool.rb +90 -0
  10. data/lib/rainbows/base.rb +41 -4
  11. data/lib/rainbows/const.rb +1 -6
  12. data/lib/rainbows/http_server.rb +1 -1
  13. data/lib/rainbows/rev.rb +174 -0
  14. data/lib/rainbows/revactor.rb +40 -37
  15. data/lib/rainbows/thread_pool.rb +31 -57
  16. data/lib/rainbows/thread_spawn.rb +32 -45
  17. data/local.mk.sample +4 -3
  18. data/t/.gitignore +1 -2
  19. data/t/GNUmakefile +21 -7
  20. data/t/README +42 -0
  21. data/t/bin/content-md5-put +36 -0
  22. data/t/bin/unused_listen +1 -1
  23. data/t/content-md5.ru +23 -0
  24. data/t/sleep.ru +11 -0
  25. data/t/t0000-basic.sh +29 -3
  26. data/t/t1000-thread-pool-basic.sh +5 -6
  27. data/t/t1000.ru +5 -1
  28. data/t/t1002-thread-pool-graceful.sh +37 -0
  29. data/t/t2000-thread-spawn-basic.sh +4 -6
  30. data/t/t2000.ru +5 -1
  31. data/t/t2002-thread-spawn-graceful.sh +37 -0
  32. data/t/t3000-revactor-basic.sh +4 -6
  33. data/t/t3000.ru +5 -1
  34. data/t/t3001-revactor-pipeline.sh +46 -0
  35. data/t/t3002-revactor-graceful.sh +38 -0
  36. data/t/t3003-revactor-reopen-logs.sh +54 -0
  37. data/t/t3100-revactor-tee-input.sh +8 -13
  38. data/t/t4000-rev-basic.sh +51 -0
  39. data/t/t4000.ru +9 -0
  40. data/t/t4002-rev-graceful.sh +52 -0
  41. data/t/t4003-rev-parser-error.sh +34 -0
  42. data/t/t4100-rev-rack-input.sh +44 -0
  43. data/t/t4101-rev-rack-input-trailer.sh +51 -0
  44. data/t/t9000-rack-app-pool.sh +37 -0
  45. data/t/t9000.ru +14 -0
  46. data/t/test-lib.sh +29 -2
  47. data/vs_Unicorn +50 -1
  48. metadata +28 -6
data/t/README ADDED
@@ -0,0 +1,42 @@
1
+ = Rainbows! test suite
2
+
3
+ These are all integration tests that start the server on random, unused
4
+ TCP ports or Unix domain sockets. They're all designed to run
5
+ concurrently with other tests to minimize test time, but tests may be
6
+ run independently as well.
7
+
8
+ We write our tests in Bourne shell because that's what we're
9
+ comfortable writing integration tests with.
10
+
11
+ == Requirements
12
+
13
+ * {Ruby 1.8 or 1.9}[http://www.ruby-lang.org/] (duh!)
14
+ * {GNU make}[http://www.gnu.org/software/make/]
15
+ * {socat}[http://www.dest-unreach.org/socat/]
16
+ * {curl}[http://curl.haxx.se/]
17
+ * standard UNIX shell utilities (Bourne sh, awk, sed, grep, tee, ...)
18
+
19
+ We do not use bashisms or any non-portable, non-POSIX constructs
20
+ in our shell code. We use the "pipefail" option if available and
21
+ mainly test with {ksh}[http://kornshell.com/], but occasionally
22
+ with {dash}[http://gondor.apana.org.au/~herbert/dash/] and
23
+ {bash}[http://www.gnu.org/software/bash/], too.
24
+
25
+ == Running Tests
26
+
27
+ To run the entire test suite with 8 tests running at once:
28
+
29
+ make -j8
30
+
31
+ To run one individual test:
32
+
33
+ make t0000-basic.sh
34
+
35
+ You may also increase verbosity by setting the "V" variable for
36
+ GNU make. To disable trapping of stdout/stderr:
37
+
38
+ make V=1
39
+
40
+ To enable the "set -x" option in shell scripts to trace execution
41
+
42
+ make V=2
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ # simple chunked HTTP PUT request generator (and just that),
3
+ # it reads stdin and writes to stdout so socat can write to a
4
+ # UNIX or TCP socket (or to another filter or file) along with
5
+ # a Content-MD5 trailer.
6
+ # -*- encoding: binary -*-
7
+ require 'digest/md5'
8
+ $stdout.sync = $stderr.sync = true
9
+ $stdout.binmode
10
+ $stdin.binmode
11
+
12
+ bs = ENV['bs'] ? ENV['bs'].to_i : 4096
13
+
14
+ if ARGV.grep("--no-headers").empty?
15
+ $stdout.write(
16
+ "PUT / HTTP/1.1\r\n" \
17
+ "Host: example.com\r\n" \
18
+ "Transfer-Encoding: chunked\r\n" \
19
+ "Trailer: Content-MD5\r\n" \
20
+ "\r\n"
21
+ )
22
+ end
23
+
24
+ digest = Digest::MD5.new
25
+ if buf = $stdin.read(bs)
26
+ begin
27
+ digest.update(buf)
28
+ $stdout.write("%x\r\n" % [ buf.size ])
29
+ $stdout.write(buf)
30
+ $stdout.write("\r\n")
31
+ end while $stdin.read(bs, buf)
32
+ end
33
+
34
+ digest = [ digest.digest ].pack('m').strip
35
+ $stdout.write("0\r\n")
36
+ $stdout.write("Content-MD5: #{digest}\r\n\r\n")
data/t/bin/unused_listen CHANGED
@@ -36,4 +36,4 @@ rescue Errno::EEXIST
36
36
  retry
37
37
  end
38
38
  sock.close rescue nil
39
- puts "listen=#{addr}:#{port} lock_path=#{lock_path}"
39
+ puts %Q(listen=#{addr}:#{port} _TEST_RM_LIST="$_TEST_RM_LIST #{lock_path}")
data/t/content-md5.ru ADDED
@@ -0,0 +1,23 @@
1
+ # SHA1 checksum generator
2
+ bs = ENV['bs'] ? ENV['bs'].to_i : 4096
3
+ require 'digest/md5'
4
+ use Rack::ContentLength
5
+ app = lambda do |env|
6
+ /\A100-continue\z/i =~ env['HTTP_EXPECT'] and
7
+ return [ 100, {}, [] ]
8
+ digest = Digest::MD5.new
9
+ input = env['rack.input']
10
+ if buf = input.read(bs)
11
+ begin
12
+ digest.update(buf)
13
+ end while input.read(bs, buf)
14
+ end
15
+
16
+ expect = env['HTTP_CONTENT_MD5']
17
+ readed = [ digest.digest ].pack('m').strip
18
+ body = "expect=#{expect}\nreaded=#{readed}\n"
19
+ status = expect == readed ? 200 : 500
20
+
21
+ [ status, {'Content-Type' => 'text/plain'}, [ body ] ]
22
+ end
23
+ run app
data/t/sleep.ru ADDED
@@ -0,0 +1,11 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ sleep_class = ENV['SLEEP_CLASS']
4
+ sleep_class = sleep_class ? Object.const_get(sleep_class) : Kernel
5
+ $stderr.puts "sleep_class=#{sleep_class.inspect}"
6
+ run lambda { |env|
7
+ nr = 1
8
+ env["PATH_INFO"] =~ %r{/([\d\.]+)\z} and nr = $1.to_f
9
+ sleep_class.sleep(nr)
10
+ [ 200, {}, [ "Hello\n" ] ]
11
+ }
data/t/t0000-basic.sh CHANGED
@@ -2,10 +2,36 @@
2
2
  . ./test-lib.sh
3
3
 
4
4
  eval $(unused_listen)
5
- pid=$(mktemp -t rainbows.$$.pid.XXXXXXXX)
6
- TEST_RM_LIST="$TEST_RM_LIST $lock_path $pid"
5
+ rtmpfiles pid tmp ok fifo
7
6
 
8
- rainbows t0000.ru -l $listen --pid $pid &
7
+ rm -f $fifo
8
+ mkfifo $fifo
9
+
10
+ rainbows -D t0000.ru -l $listen --pid $pid &
9
11
  wait_for_pid $pid
12
+
13
+ echo "single request"
10
14
  curl -sSfv http://$listen/
15
+
16
+ echo "two requests with keepalive"
17
+ curl -sSfv http://$listen/a http://$listen/b > $tmp 2>&1
18
+ grep 'Re-using existing connection' < $tmp
19
+
20
+ echo "pipelining partial requests"
21
+ req='GET / HTTP/1.1\r\nHost: foo\r\n'
22
+ (
23
+ printf "$req"'\r\n'"$req"
24
+ cat $fifo > $tmp &
25
+ sleep 1
26
+ printf 'Connection: close\r\n\r\n'
27
+ echo ok > $ok
28
+ ) | socat - TCP:$listen > $fifo
29
+
11
30
  kill $(cat $pid)
31
+
32
+ # sed -ne 's/^/------/p' < $tmp
33
+ test 2 -eq $(grep '^HTTP/1.1' $tmp | wc -l)
34
+ test 2 -eq $(grep '^HTTP/1.1 200 OK' $tmp | wc -l)
35
+ test 1 -eq $(grep '^Connection: keep-alive' $tmp | wc -l)
36
+ test 1 -eq $(grep '^Connection: close' $tmp | wc -l)
37
+ test x"$(cat $ok)" = xok
@@ -2,17 +2,14 @@
2
2
  . ./test-lib.sh
3
3
 
4
4
  eval $(unused_listen)
5
- unicorn_config=$(mktemp -t rainbows.$$.unicorn.rb.XXXXXXXX)
6
- curl_out=$(mktemp -t rainbows.$$.curl.out.XXXXXXXX)
7
- curl_err=$(mktemp -t rainbows.$$.curl.err.XXXXXXXX)
8
- pid=$(mktemp -t rainbows.$$.pid.XXXXXXXX)
9
- TEST_RM_LIST="$TEST_RM_LIST $unicorn_config $lock_path $pid"
10
- TEST_RM_LIST="$TEST_RM_LIST $curl_out $curl_err"
5
+ rtmpfiles unicorn_config curl_out curl_err pid r_err r_out
11
6
 
12
7
  nr_client=30
13
8
  nr_thread=10
14
9
 
15
10
  cat > $unicorn_config <<EOF
11
+ stderr_path "$r_err"
12
+ stdout_path "$r_out"
16
13
  listen "$listen"
17
14
  pid "$pid"
18
15
  Rainbows! do
@@ -41,3 +38,5 @@ nr=$(sort < $curl_out | uniq | wc -l)
41
38
 
42
39
  test "$nr" -le $nr_thread
43
40
  test "$nr" -gt 1
41
+
42
+ ! grep Error $r_err
data/t/t1000.ru CHANGED
@@ -2,5 +2,9 @@ use Rack::ContentLength
2
2
  use Rack::ContentType
3
3
  run lambda { |env|
4
4
  sleep 1
5
- [ 200, {}, [ Thread.current.inspect << "\n" ] ]
5
+ if env['rack.multithread'] && env['rainbows.model'] == :ThreadPool
6
+ [ 200, {}, [ Thread.current.inspect << "\n" ] ]
7
+ else
8
+ raise "rack.multithread is not true"
9
+ end
6
10
  }
@@ -0,0 +1,37 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+
4
+ eval $(unused_listen)
5
+ rtmpfiles unicorn_config curl_out curl_err pid r_err r_out
6
+ nr_thread=10
7
+ nr_client=10
8
+ cat > $unicorn_config <<EOF
9
+ listen "$listen"
10
+ stderr_path "$r_err"
11
+ stdout_path "$r_out"
12
+ pid "$pid"
13
+ Rainbows! do
14
+ use :ThreadPool
15
+ worker_connections $nr_thread
16
+ end
17
+ EOF
18
+
19
+ rainbows -D sleep.ru -c $unicorn_config
20
+ wait_for_pid $pid
21
+
22
+ for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" </dev/null)
23
+ do
24
+ curl -sSf http://$listen/5 >> $curl_out 2>> $curl_err &
25
+ done
26
+ sleep 2
27
+ kill -QUIT $(cat $pid)
28
+ wait
29
+
30
+ dbgcat r_err
31
+ ! test -s $curl_err
32
+ test x"$(wc -l < $curl_out)" = x$nr_client
33
+ nr=$(sort < $curl_out | uniq | wc -l)
34
+
35
+ test "$nr" -eq 1
36
+ test x$(sort < $curl_out | uniq) = xHello
37
+ ! grep Error $r_err
@@ -2,17 +2,14 @@
2
2
  . ./test-lib.sh
3
3
 
4
4
  eval $(unused_listen)
5
- unicorn_config=$(mktemp -t rainbows.$$.unicorn.rb.XXXXXXXX)
6
- curl_out=$(mktemp -t rainbows.$$.curl.out.XXXXXXXX)
7
- curl_err=$(mktemp -t rainbows.$$.curl.err.XXXXXXXX)
8
- pid=$(mktemp -t rainbows.$$.pid.XXXXXXXX)
9
- TEST_RM_LIST="$TEST_RM_LIST $unicorn_config $lock_path $pid"
10
- TEST_RM_LIST="$TEST_RM_LIST $curl_out $curl_err"
5
+ rtmpfiles unicorn_config curl_out curl_err pid r_err r_out
11
6
 
12
7
  nr_client=30
13
8
  nr_thread=10
14
9
 
15
10
  cat > $unicorn_config <<EOF
11
+ stderr_path "$r_err"
12
+ stdout_path "$r_out"
16
13
  listen "$listen"
17
14
  pid "$pid"
18
15
  Rainbows! do
@@ -38,3 +35,4 @@ kill $(cat $pid)
38
35
  test x"$(wc -l < $curl_out)" = x$nr_client
39
36
  nr=$(sort < $curl_out | uniq | wc -l)
40
37
  test "$nr" -eq $nr_client
38
+ ! grep Error $r_err
data/t/t2000.ru CHANGED
@@ -2,5 +2,9 @@ use Rack::ContentLength
2
2
  use Rack::ContentType
3
3
  run lambda { |env|
4
4
  sleep 1
5
- [ 200, {}, [ Thread.current.inspect << "\n" ] ]
5
+ if env['rack.multithread'] && env['rainbows.model'] == :ThreadSpawn
6
+ [ 200, {}, [ Thread.current.inspect << "\n" ] ]
7
+ else
8
+ raise "rack.multithread is not true"
9
+ end
6
10
  }
@@ -0,0 +1,37 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+
4
+ eval $(unused_listen)
5
+ rtmpfiles unicorn_config curl_out curl_err pid r_err r_out
6
+ nr_thread=10
7
+ nr_client=10
8
+ cat > $unicorn_config <<EOF
9
+ listen "$listen"
10
+ stderr_path "$r_err"
11
+ stdout_path "$r_out"
12
+ pid "$pid"
13
+ Rainbows! do
14
+ use :ThreadSpawn
15
+ worker_connections $nr_thread
16
+ end
17
+ EOF
18
+
19
+ rainbows -D sleep.ru -c $unicorn_config
20
+ wait_for_pid $pid
21
+
22
+ for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" </dev/null)
23
+ do
24
+ curl -sSf http://$listen/5 >> $curl_out 2>> $curl_err &
25
+ done
26
+ sleep 2
27
+ kill -QUIT $(cat $pid)
28
+ wait
29
+
30
+ dbgcat r_err
31
+ ! test -s $curl_err
32
+ test x"$(wc -l < $curl_out)" = x$nr_client
33
+ nr=$(sort < $curl_out | uniq | wc -l)
34
+
35
+ test "$nr" -eq 1
36
+ test x$(sort < $curl_out | uniq) = xHello
37
+ ! grep Error $r_err
@@ -3,12 +3,7 @@
3
3
  require_revactor
4
4
 
5
5
  eval $(unused_listen)
6
- unicorn_config=$(mktemp -t rainbows.$$.unicorn.rb.XXXXXXXX)
7
- curl_out=$(mktemp -t rainbows.$$.curl.out.XXXXXXXX)
8
- curl_err=$(mktemp -t rainbows.$$.curl.err.XXXXXXXX)
9
- pid=$(mktemp -t rainbows.$$.pid.XXXXXXXX)
10
- TEST_RM_LIST="$TEST_RM_LIST $pid $unicorn_config $lock_path"
11
- TEST_RM_LIST="$TEST_RM_LIST $curl_out $curl_err"
6
+ rtmpfiles unicorn_config curl_out curl_err pid r_err r_out
12
7
 
13
8
  nr_client=30
14
9
  nr_actor=10
@@ -16,6 +11,8 @@ nr_actor=10
16
11
  cat > $unicorn_config <<EOF
17
12
  listen "$listen"
18
13
  pid "$pid"
14
+ stderr_path "$r_err"
15
+ stdout_path "$r_out"
19
16
  Rainbows! do
20
17
  use :Revactor
21
18
  worker_connections $nr_actor
@@ -40,3 +37,4 @@ test x"$(wc -l < $curl_out)" = x$nr_client
40
37
  nr=$(sort < $curl_out | uniq | wc -l)
41
38
 
42
39
  test "$nr" -eq 1
40
+ ! grep Error $r_err
data/t/t3000.ru CHANGED
@@ -2,5 +2,9 @@ use Rack::ContentLength
2
2
  use Rack::ContentType
3
3
  run lambda { |env|
4
4
  Actor.sleep 1
5
- [ 200, {}, [ Thread.current.inspect << "\n" ] ]
5
+ if env['rack.multithread'] == false && env['rainbows.model'] == :Revactor
6
+ [ 200, {}, [ Thread.current.inspect << "\n" ] ]
7
+ else
8
+ raise "rack.multithread is true"
9
+ end
6
10
  }
@@ -0,0 +1,46 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ require_revactor
4
+
5
+ eval $(unused_listen)
6
+ rtmpfiles unicorn_config curl_out curl_err pid fifo tmp ok r_err r_out
7
+
8
+ rm -f $fifo
9
+ mkfifo $fifo
10
+
11
+ cat > $unicorn_config <<EOF
12
+ stderr_path "$r_err"
13
+ stdout_path "$r_out"
14
+ listen "$listen"
15
+ pid "$pid"
16
+ Rainbows! do
17
+ use :Revactor
18
+ end
19
+ EOF
20
+
21
+ rainbows -D t0000.ru -c $unicorn_config
22
+ wait_for_pid $pid
23
+
24
+ echo "two requests with keepalive"
25
+ curl -sSfv http://$listen/a http://$listen/b > $tmp 2>&1
26
+ grep 'Re-using existing connection' < $tmp
27
+
28
+ echo "pipelining partial requests"
29
+ req='GET / HTTP/1.1\r\nHost: foo\r\n'
30
+ (
31
+ printf "$req"'\r\n'"$req"
32
+ cat $fifo > $tmp &
33
+ sleep 1
34
+ printf 'Connection: close\r\n\r\n'
35
+ echo ok > $ok
36
+ ) | socat - TCP:$listen > $fifo
37
+
38
+ kill $(cat $pid)
39
+
40
+ dbgcat tmp
41
+ test 2 -eq $(grep '^HTTP/1.1' $tmp | wc -l)
42
+ test 2 -eq $(grep '^HTTP/1.1 200 OK' $tmp | wc -l)
43
+ test 1 -eq $(grep '^Connection: keep-alive' $tmp | wc -l)
44
+ test 1 -eq $(grep '^Connection: close' $tmp | wc -l)
45
+ test x"$(cat $ok)" = xok
46
+ ! grep Error $r_err
@@ -0,0 +1,38 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ require_revactor
4
+
5
+ eval $(unused_listen)
6
+ rtmpfiles unicorn_config curl_out curl_err pid r_err r_out
7
+ nr_actor=10
8
+ nr_client=10
9
+ cat > $unicorn_config <<EOF
10
+ listen "$listen"
11
+ stderr_path "$r_err"
12
+ stdout_path "$r_out"
13
+ pid "$pid"
14
+ Rainbows! do
15
+ use :Revactor
16
+ worker_connections $nr_actor
17
+ end
18
+ EOF
19
+
20
+ SLEEP_CLASS=Actor rainbows -D sleep.ru -c $unicorn_config
21
+ wait_for_pid $pid
22
+
23
+ for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" </dev/null)
24
+ do
25
+ curl -sSf http://$listen/5 >> $curl_out 2>> $curl_err &
26
+ done
27
+ sleep 2
28
+ kill -QUIT $(cat $pid)
29
+ wait
30
+
31
+ dbgcat r_err
32
+ ! test -s $curl_err
33
+ test x"$(wc -l < $curl_out)" = x$nr_client
34
+ nr=$(sort < $curl_out | uniq | wc -l)
35
+
36
+ test "$nr" -eq 1
37
+ test x$(sort < $curl_out | uniq) = xHello
38
+ ! grep Error $r_err