rainbows 0.1.1 → 0.2.0

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