rainbows 0.3.0 → 0.4.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 (86) hide show
  1. data/GIT-VERSION-GEN +1 -1
  2. data/GNUmakefile +12 -6
  3. data/README +3 -2
  4. data/Rakefile +3 -3
  5. data/TODO +2 -9
  6. data/lib/rainbows.rb +1 -0
  7. data/lib/rainbows/app_pool.rb +10 -6
  8. data/lib/rainbows/base.rb +1 -1
  9. data/lib/rainbows/const.rb +1 -1
  10. data/lib/rainbows/ev_core.rb +88 -0
  11. data/lib/rainbows/event_machine.rb +218 -0
  12. data/lib/rainbows/http_server.rb +4 -1
  13. data/lib/rainbows/rev.rb +21 -89
  14. data/lib/rainbows/revactor.rb +4 -10
  15. data/local.mk.sample +13 -7
  16. data/rainbows.gemspec +17 -2
  17. data/t/.gitignore +1 -0
  18. data/t/GNUmakefile +63 -40
  19. data/t/README +6 -2
  20. data/t/async_sinatra.ru +13 -0
  21. data/t/bin/unused_listen +1 -1
  22. data/t/large-file-response.ru +1 -0
  23. data/t/my-tap-lib.sh +200 -0
  24. data/t/simple-http_Base.ru +3 -0
  25. data/t/simple-http_EventMachine.ru +9 -0
  26. data/t/simple-http_Rev.ru +9 -0
  27. data/t/simple-http_Revactor.ru +10 -0
  28. data/t/simple-http_ThreadPool.ru +10 -0
  29. data/t/simple-http_ThreadSpawn.ru +10 -0
  30. data/t/t0000-simple-http.sh +142 -0
  31. data/t/t0001-unix-http.sh +103 -0
  32. data/t/t0002-graceful.sh +32 -0
  33. data/t/t0002-parser-error.sh +31 -0
  34. data/t/t0003-reopen-logs.sh +97 -0
  35. data/t/t0005-large-file-response.sh +83 -0
  36. data/t/t0100-rack-input-hammer.sh +45 -0
  37. data/t/t0101-rack-input-trailer.sh +68 -0
  38. data/t/t0200-async-response.sh +66 -0
  39. data/t/t0201-async-response-no-autochunk.sh +3 -0
  40. data/t/t0300-async_sinatra.sh +65 -0
  41. data/t/t9000-rack-app-pool.sh +45 -33
  42. data/t/test-lib.sh +67 -56
  43. metadata +26 -56
  44. data/t/lib-async-response-no-autochunk.sh +0 -6
  45. data/t/lib-async-response.sh +0 -45
  46. data/t/lib-graceful.sh +0 -40
  47. data/t/lib-input-trailer.sh +0 -63
  48. data/t/lib-large-file-response.sh +0 -45
  49. data/t/lib-parser-error.sh +0 -29
  50. data/t/lib-rack-input-hammer.sh +0 -38
  51. data/t/lib-reopen-logs.sh +0 -60
  52. data/t/lib-simple-http.sh +0 -92
  53. data/t/t0000-basic.sh +0 -2
  54. data/t/t1000-thread-pool-basic.sh +0 -2
  55. data/t/t1002-thread-pool-graceful.sh +0 -2
  56. data/t/t1003-thread-pool-reopen-logs.sh +0 -2
  57. data/t/t1004-thread-pool-async-response.sh +0 -45
  58. data/t/t1005-thread-pool-large-file-response.sh +0 -45
  59. data/t/t1006-thread-pool-async-response-no-autochunk.sh +0 -6
  60. data/t/t1100-thread-pool-rack-input.sh +0 -2
  61. data/t/t1101-thread-pool-input-trailer.sh +0 -2
  62. data/t/t2000-thread-spawn-basic.sh +0 -2
  63. data/t/t2002-thread-spawn-graceful.sh +0 -2
  64. data/t/t2003-thread-spawn-reopen-logs.sh +0 -2
  65. data/t/t2004-thread-spawn-async-response.sh +0 -45
  66. data/t/t2005-thread-spawn-large-file-response.sh +0 -45
  67. data/t/t2006-thread-spawn-async-response-no-autochunk.sh +0 -6
  68. data/t/t2100-thread-spawn-rack-input.sh +0 -2
  69. data/t/t2101-thread-spawn-input-trailer.sh +0 -2
  70. data/t/t3000-revactor-basic.sh +0 -2
  71. data/t/t3002-revactor-graceful.sh +0 -2
  72. data/t/t3003-revactor-reopen-logs.sh +0 -2
  73. data/t/t3004-revactor-async-response.sh +0 -45
  74. data/t/t3005-revactor-large-file-response.sh +0 -2
  75. data/t/t3006-revactor-async-response-no-autochunk.sh +0 -6
  76. data/t/t3100-revactor-rack-input.sh +0 -2
  77. data/t/t3101-revactor-rack-input-trailer.sh +0 -2
  78. data/t/t4000-rev-basic.sh +0 -2
  79. data/t/t4002-rev-graceful.sh +0 -2
  80. data/t/t4003-rev-parser-error.sh +0 -2
  81. data/t/t4003-rev-reopen-logs.sh +0 -2
  82. data/t/t4004-rev-async-response.sh +0 -45
  83. data/t/t4005-rev-large-file-response.sh +0 -2
  84. data/t/t4006-rev-async-response-no-autochunk.sh +0 -6
  85. data/t/t4100-rev-rack-input.sh +0 -2
  86. data/t/t4101-rev-rack-input-trailer.sh +0 -2
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 %Q(listen=#{addr}:#{port} _TEST_RM_LIST="$_TEST_RM_LIST #{lock_path}")
39
+ puts %Q(listen=#{addr}:#{port} T_RM_LIST="$T_RM_LIST #{lock_path}")
@@ -4,6 +4,7 @@ use Rack::ContentType
4
4
  map "/rss" do
5
5
  run lambda { |env|
6
6
  # on Linux, this is in kilobytes
7
+ GC.start if GC.respond_to?(:start)
7
8
  ::File.read("/proc/self/status") =~ /^VmRSS:\s+(\d+)/
8
9
  [ 200, {}, [ ($1.to_i * 1024).to_s ] ]
9
10
  }
data/t/my-tap-lib.sh ADDED
@@ -0,0 +1,200 @@
1
+ #!/bin/sh
2
+ # Copyright (c) 2009 Eric Wong <normalperson@yhbt.net>
3
+ #
4
+ # TAP-producing shell library for POSIX-compliant Bourne shells We do
5
+ # not _rely_ on Bourne Again features, though we will use "set -o
6
+ # pipefail" from ksh93 or bash 3 if available
7
+ #
8
+ # Only generic, non-project/non-language-specific stuff goes here. We
9
+ # only have POSIX dependencies for the core tests (without --verbose),
10
+ # though we'll enable useful non-POSIX things if they're available.
11
+ #
12
+ # This test library is intentionally unforgiving, it does not support
13
+ # skipping tests nor continuing after any failure. Any failures
14
+ # immediately halt execution as do any references to undefined
15
+ # variables.
16
+ #
17
+ # When --verbose is specified, we always prefix stdout/stderr
18
+ # output with "#" to avoid confusing TAP consumers. Otherwise
19
+ # the normal stdout/stderr streams are redirected to /dev/null
20
+
21
+ # dup normal stdout(fd=1) and stderr (fd=2) to fd=3 and fd=4 respectively
22
+ # normal TAP output goes to fd=3, nothing should go to fd=4
23
+ exec 3>&1 4>&2
24
+
25
+ # ensure a sane environment
26
+ TZ=UTC LC_ALL=C LANG=C
27
+ export LANG LC_ALL TZ
28
+ unset CDPATH
29
+
30
+ # pipefail is non-POSIX, but very useful in ksh93/bash
31
+ ( set -o pipefail 2>/dev/null ) && set -o pipefail
32
+
33
+ SED=${SED-sed}
34
+
35
+ # Unlike other test frameworks, we are unforgiving and bail immediately
36
+ # on any failures. We do this because we're lazy about error handling
37
+ # and also because we believe anything broken should not be allowed to
38
+ # propagate throughout the rest of the test
39
+ set -e
40
+ set -u
41
+
42
+ # name of our test
43
+ T=${0##*/}
44
+
45
+ t_expect_nr=-1
46
+ t_nr=0
47
+ t_current=
48
+ t_complete=false
49
+
50
+ # list of files to remove unconditionally on exit
51
+ T_RM_LIST=
52
+
53
+ # list of files to remove only on successful exit
54
+ T_OK_RM_LIST=
55
+
56
+ # emit output to stdout, it'll be parsed by the TAP consumer
57
+ # so it must be TAP-compliant output
58
+ t_echo () {
59
+ echo >&3 "$@"
60
+ }
61
+
62
+ # emits non-parsed information to stdout, it will be prefixed with a '#'
63
+ # to not throw off TAP consumers
64
+ t_info () {
65
+ t_echo '#' "$@"
66
+ }
67
+
68
+ # exit with an error and print a diagnostic
69
+ die () {
70
+ echo >&2 "$@"
71
+ exit 1
72
+ }
73
+
74
+ # our at_exit handler, it'll fire for all exits except SIGKILL (unavoidable)
75
+ t_at_exit () {
76
+ code=$?
77
+ set +e
78
+ if test $code -eq 0
79
+ then
80
+ $t_complete || {
81
+ t_info "t_done not called"
82
+ code=1
83
+ }
84
+ elif test -n "$t_current"
85
+ then
86
+ t_echo "not ok $t_nr - $t_current"
87
+ fi
88
+ if test $t_expect_nr -ne -1
89
+ then
90
+ test $t_expect_nr -eq $t_nr || {
91
+ t_info "planned $t_expect_nr tests but ran $t_nr"
92
+ test $code -ne 0 || code=1
93
+ }
94
+ fi
95
+ $t_complete || {
96
+ t_info "unexpected test failure"
97
+ test $code -ne 0 || code=1
98
+ }
99
+ rm -f $T_RM_LIST
100
+ test $code -eq 0 && rm -f $T_OK_RM_LIST
101
+ set +x
102
+ exec >&3 2>&4
103
+ t_close_fds
104
+ exit $code
105
+ }
106
+
107
+ # close test-specific extra file descriptors
108
+ t_close_fds () {
109
+ exec 3>&- 4>&-
110
+ }
111
+
112
+ # call this at the start of your test to specify the number of tests
113
+ # you plan to run
114
+ t_plan () {
115
+ test "$1" -ge 1 || die "must plan at least one test"
116
+ test $t_expect_nr -eq -1 || die "tried to plan twice in one test"
117
+ t_expect_nr=$1
118
+ shift
119
+ t_echo 1..$t_expect_nr "#" "$@"
120
+ trap t_at_exit EXIT
121
+ }
122
+
123
+ _t_checkup () {
124
+ test $t_expect_nr -le 0 && die "no tests planned"
125
+ test -n "$t_current" && t_echo "ok $t_nr - $t_current"
126
+ true
127
+ }
128
+
129
+ # finalizes any previously test and starts a new one
130
+ t_begin () {
131
+ _t_checkup
132
+ t_nr=$(( $t_nr + 1 ))
133
+ t_current="$1"
134
+
135
+ # just in case somebody wanted to cheat us:
136
+ set -e
137
+ }
138
+
139
+ # finalizes the current test without starting a new one
140
+ t_end () {
141
+ _t_checkup
142
+ t_current=
143
+ }
144
+
145
+ # run this to signify the end of your test
146
+ t_done () {
147
+ _t_checkup
148
+ t_current=
149
+ t_complete=true
150
+ test $t_expect_nr -eq $t_nr || exit 1
151
+ exit 0
152
+ }
153
+
154
+ # create and assign named-pipes to variable _names_ passed to this function
155
+ t_fifos () {
156
+ for _id in "$@"
157
+ do
158
+ _name=$_id
159
+ _tmp=$(mktemp -t $T.$$.$_id.XXXXXXXX)
160
+ eval "$_id=$_tmp"
161
+ rm -f $_tmp
162
+ mkfifo $_tmp
163
+ T_RM_LIST="$T_RM_LIST $_tmp"
164
+ done
165
+ }
166
+
167
+ t_verbose=false t_trace=false
168
+
169
+ while test "$#" -ne 0
170
+ do
171
+ arg="$1"
172
+ shift
173
+ case $arg in
174
+ -v|--verbose) t_verbose=true ;;
175
+ --trace) t_trace=true t_verbose=true ;;
176
+ *) die "Unknown option: $arg" ;;
177
+ esac
178
+ done
179
+
180
+ # we always only setup stdout, nothing should end up in the "real" stderr
181
+ if $t_verbose
182
+ then
183
+ if test x"$(which mktemp 2>/dev/null)" = x
184
+ then
185
+ die "mktemp(1) not available for --verbose"
186
+ fi
187
+ t_fifos t_stdout t_stderr
188
+
189
+ (
190
+ # use a subshell so seds are not waitable
191
+ $SED -e 's/^/#: /' $t_stdout &
192
+ $SED -e 's/^/#! /' $t_stderr &
193
+ ) &
194
+ exec > $t_stdout 2> $t_stderr
195
+ else
196
+ exec > /dev/null 2> /dev/null
197
+ fi
198
+
199
+ $t_trace && set -x
200
+ true
@@ -0,0 +1,3 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ run lambda { |env| [ 200, {}, [ env.inspect << "\n" ] ] }
@@ -0,0 +1,9 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ run lambda { |env|
4
+ if env['rack.multithread'] == false && env['rainbows.model'] == :EventMachine
5
+ [ 200, {}, [ env.inspect << "\n" ] ]
6
+ else
7
+ raise "rack.multithread is true"
8
+ end
9
+ }
@@ -0,0 +1,9 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ run lambda { |env|
4
+ if env['rack.multithread'] == false && env['rainbows.model'] == :Rev
5
+ [ 200, {}, [ env.inspect << "\n" ] ]
6
+ else
7
+ raise "rack.multithread is true"
8
+ end
9
+ }
@@ -0,0 +1,10 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ run lambda { |env|
4
+ Actor.sleep 1
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
10
+ }
@@ -0,0 +1,10 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ run lambda { |env|
4
+ sleep 1
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
10
+ }
@@ -0,0 +1,10 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ run lambda { |env|
4
+ sleep 1
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
10
+ }
@@ -0,0 +1,142 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ t_plan 24 "simple HTTP connection keepalive/pipelining tests for $model"
4
+
5
+ t_begin "checking for config.ru for $model" && {
6
+ tbase=simple-http_$model.ru
7
+ test -f "$tbase"
8
+ }
9
+
10
+ t_begin "setup and start" && {
11
+ rainbows_setup
12
+ rainbows -D $tbase -c $unicorn_config
13
+ rainbows_wait_start
14
+ }
15
+
16
+ t_begin "pid file exists" && {
17
+ test -f $pid
18
+ }
19
+
20
+ t_begin "single request" && {
21
+ curl -sSfv http://$listen/
22
+ }
23
+
24
+ dbgcat r_err
25
+
26
+ t_begin "two requests with keepalive" && {
27
+ curl -sSfv http://$listen/a http://$listen/b > $tmp 2>&1
28
+ }
29
+
30
+ dbgcat r_err
31
+ dbgcat tmp
32
+
33
+ t_begin "reused existing connection" && {
34
+ grep 'Re-using existing connection' < $tmp
35
+ }
36
+
37
+ t_begin "pipelining partial requests" && {
38
+ req='GET / HTTP/1.1\r\nHost: example.com\r\n'
39
+ (
40
+ cat $fifo > $tmp &
41
+ printf "$req"'\r\n'"$req"
42
+ sleep 1
43
+ printf 'Connection: close\r\n\r\n'
44
+ wait
45
+ echo ok > $ok
46
+ ) | socat - TCP:$listen > $fifo
47
+ }
48
+ dbgcat tmp
49
+
50
+ t_begin "two HTTP/1.1 responses" && {
51
+ test 2 -eq $(grep '^HTTP/1.1' $tmp | wc -l)
52
+ }
53
+
54
+ t_begin "two HTTP/1.1 200 OK responses" && {
55
+ test 2 -eq $(grep '^HTTP/1.1 200 OK' $tmp | wc -l)
56
+ }
57
+
58
+ t_begin 'one "Connection: keep-alive" response' && {
59
+ test 1 -eq $(grep '^Connection: keep-alive' $tmp | wc -l)
60
+ }
61
+
62
+ t_begin 'one "Connection: close" response' && {
63
+ test 1 -eq $(grep '^Connection: close' $tmp | wc -l)
64
+ }
65
+
66
+ t_begin 'check subshell success' && {
67
+ test x"$(cat $ok)" = xok
68
+ }
69
+
70
+
71
+ t_begin "check stderr" && {
72
+ check_stderr
73
+ }
74
+
75
+ t_begin "burst pipelining requests" && {
76
+ req='GET / HTTP/1.1\r\nHost: example.com\r\n'
77
+ (
78
+ cat $fifo > $tmp &
79
+ printf "$req"'\r\n'"$req"'Connection: close\r\n\r\n'
80
+ wait
81
+ echo ok > $ok
82
+ ) | socat - TCP:$listen > $fifo
83
+ }
84
+
85
+ dbgcat tmp
86
+ dbgcat r_err
87
+
88
+ t_begin "got 2 HTTP/1.1 responses from pipelining" && {
89
+ test 2 -eq $(grep '^HTTP/1.1' $tmp | wc -l)
90
+ }
91
+
92
+ t_begin "got 2 HTTP/1.1 200 OK responses" && {
93
+ test 2 -eq $(grep '^HTTP/1.1 200 OK' $tmp | wc -l)
94
+ }
95
+
96
+ t_begin "one keepalive connection" && {
97
+ test 1 -eq $(grep '^Connection: keep-alive' $tmp | wc -l)
98
+ }
99
+
100
+ t_begin "second request closes connection" && {
101
+ test 1 -eq $(grep '^Connection: close' $tmp | wc -l)
102
+ }
103
+
104
+ t_begin "subshell exited correctly" && {
105
+ test x"$(cat $ok)" = xok
106
+ }
107
+
108
+ t_begin "stderr log has no errors" && {
109
+ check_stderr
110
+ }
111
+
112
+ t_begin "HTTP/0.9 request should not return headers" && {
113
+ (
114
+ printf 'GET /\r\n'
115
+ cat $fifo > $tmp &
116
+ wait
117
+ echo ok > $ok
118
+ ) | socat - TCP:$listen > $fifo
119
+ }
120
+
121
+ dbgcat tmp
122
+ dbgcat r_err
123
+
124
+ t_begin "env.inspect should've put everything on one line" && {
125
+ test 1 -eq $(wc -l < $tmp)
126
+ }
127
+
128
+ t_begin "no headers in output" && {
129
+ if grep ^Connection: $tmp
130
+ then
131
+ die "Connection header found in $tmp"
132
+ elif grep ^HTTP/ $tmp
133
+ then
134
+ die "HTTP/ found in $tmp"
135
+ fi
136
+ }
137
+
138
+ t_begin "killing succeeds" && {
139
+ kill $rainbows_pid
140
+ }
141
+
142
+ t_done
@@ -0,0 +1,103 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ t_plan 18 "simple HTTP connection keepalive/pipelining tests for $model"
4
+
5
+ t_begin "checking for config.ru for $model" && {
6
+ tbase=simple-http_$model.ru
7
+ test -f "$tbase"
8
+ }
9
+
10
+ t_begin "setup and start" && {
11
+ rtmpfiles unix_socket
12
+ rainbows_setup
13
+ echo "listen '$unix_socket'" >> $unicorn_config
14
+ rainbows -D $tbase -c $unicorn_config
15
+ rainbows_wait_start
16
+ }
17
+
18
+ t_begin "pid file exists" && {
19
+ test -f $pid
20
+ }
21
+
22
+ t_begin "single TCP request" && {
23
+ curl -sSfv http://$listen/
24
+ }
25
+
26
+ dbgcat r_err
27
+
28
+ t_begin "pipelining partial requests" && {
29
+ req='GET / HTTP/1.1\r\nHost: example.com\r\n'
30
+ (
31
+ cat $fifo > $tmp &
32
+ printf "$req"'\r\n'"$req"
33
+ sleep 1
34
+ printf 'Connection: close\r\n\r\n'
35
+ wait
36
+ echo ok > $ok
37
+ ) | socat - UNIX:$unix_socket > $fifo
38
+ }
39
+ dbgcat tmp
40
+
41
+ t_begin "two HTTP/1.1 responses" && {
42
+ test 2 -eq $(grep '^HTTP/1.1' $tmp | wc -l)
43
+ }
44
+
45
+ t_begin "two HTTP/1.1 200 OK responses" && {
46
+ test 2 -eq $(grep '^HTTP/1.1 200 OK' $tmp | wc -l)
47
+ }
48
+
49
+ t_begin 'one "Connection: keep-alive" response' && {
50
+ test 1 -eq $(grep '^Connection: keep-alive' $tmp | wc -l)
51
+ }
52
+
53
+ t_begin 'one "Connection: close" response' && {
54
+ test 1 -eq $(grep '^Connection: close' $tmp | wc -l)
55
+ }
56
+
57
+ t_begin 'check subshell success' && {
58
+ test x"$(cat $ok)" = xok
59
+ }
60
+
61
+
62
+ t_begin "check stderr" && {
63
+ check_stderr
64
+ }
65
+
66
+ t_begin "burst pipelining requests" && {
67
+ req='GET / HTTP/1.1\r\nHost: example.com\r\n'
68
+ (
69
+ cat $fifo > $tmp &
70
+ printf "$req"'\r\n'"$req"'Connection: close\r\n\r\n'
71
+ wait
72
+ echo ok > $ok
73
+ ) | socat - UNIX:$unix_socket > $fifo
74
+ }
75
+
76
+ dbgcat tmp
77
+ dbgcat r_err
78
+
79
+ t_begin "two HTTP/1.1 responses" && {
80
+ test 2 -eq $(grep '^HTTP/1.1' $tmp | wc -l)
81
+ }
82
+
83
+ t_begin "two HTTP/1.1 200 OK responses" && {
84
+ test 2 -eq $(grep '^HTTP/1.1 200 OK' $tmp | wc -l)
85
+ }
86
+
87
+ t_begin 'one "Connection: keep-alive" response' && {
88
+ test 1 -eq $(grep '^Connection: keep-alive' $tmp | wc -l)
89
+ }
90
+
91
+ t_begin 'one "Connection: close" response' && {
92
+ test 1 -eq $(grep '^Connection: close' $tmp | wc -l)
93
+ }
94
+
95
+ t_begin 'check subshell success' && {
96
+ test x"$(cat $ok)" = xok
97
+ }
98
+
99
+ t_begin "killing succeeds" && {
100
+ kill $rainbows_pid
101
+ }
102
+
103
+ t_done