unicorn 3.6.0 → 4.0.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/.document +1 -0
- data/.manifest +13 -0
- data/ChangeLog +783 -1
- data/DESIGN +0 -8
- data/Documentation/GNUmakefile +1 -1
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -2
- data/HACKING +11 -0
- data/KNOWN_ISSUES +2 -2
- data/LATEST +24 -24
- data/Links +53 -0
- data/NEWS +66 -0
- data/PHILOSOPHY +49 -49
- data/Sandbox +13 -4
- data/TODO +0 -2
- data/TUNING +31 -9
- data/bin/unicorn +2 -1
- data/bin/unicorn_rails +2 -1
- data/examples/big_app_gc.rb +2 -33
- data/examples/nginx.conf +17 -4
- data/ext/unicorn_http/ext_help.h +16 -0
- data/ext/unicorn_http/extconf.rb +1 -0
- data/ext/unicorn_http/global_variables.h +9 -3
- data/ext/unicorn_http/unicorn_http.c +357 -259
- data/ext/unicorn_http/unicorn_http.rl +148 -50
- data/lib/unicorn/configurator.rb +36 -8
- data/lib/unicorn/const.rb +5 -3
- data/lib/unicorn/http_request.rb +1 -3
- data/lib/unicorn/http_server.rb +82 -95
- data/lib/unicorn/oob_gc.rb +61 -50
- data/lib/unicorn/socket_helper.rb +23 -8
- data/lib/unicorn/worker.rb +45 -4
- data/lib/unicorn.rb +8 -6
- data/script/isolate_for_tests +4 -2
- data/t/broken-app.ru +12 -0
- data/t/heartbeat-timeout.ru +12 -0
- data/t/oob_gc.ru +21 -0
- data/t/oob_gc_path.ru +21 -0
- data/t/t0001-reload-bad-config.sh +1 -0
- data/t/t0002-parser-error.sh +64 -1
- data/t/t0004-heartbeat-timeout.sh +69 -0
- data/t/t0009-broken-app.sh +56 -0
- data/t/t0019-max_header_len.sh +49 -0
- data/t/t0020-at_exit-handler.sh +49 -0
- data/t/t9001-oob_gc.sh +47 -0
- data/t/t9002-oob_gc-path.sh +75 -0
- data/test/benchmark/stack.ru +8 -0
- data/test/unit/test_droplet.rb +28 -0
- data/test/unit/test_http_parser.rb +60 -4
- data/test/unit/test_http_parser_ng.rb +54 -0
- data/test/unit/test_response.rb +1 -1
- data/test/unit/test_server.rb +1 -1
- data/test/unit/test_signals.rb +1 -1
- data/test/unit/test_socket_helper.rb +8 -0
- data/test/unit/test_upload.rb +1 -1
- data/unicorn.gemspec +3 -2
- metadata +44 -16
@@ -0,0 +1,12 @@
|
|
1
|
+
use Rack::ContentLength
|
2
|
+
headers = { 'Content-Type' => 'text/plain' }
|
3
|
+
run lambda { |env|
|
4
|
+
case env['PATH_INFO']
|
5
|
+
when "/block-forever"
|
6
|
+
Process.kill(:STOP, $$)
|
7
|
+
sleep # in case STOP signal is not received in time
|
8
|
+
[ 500, headers, [ "Should never get here\n" ] ]
|
9
|
+
else
|
10
|
+
[ 200, headers, [ "#$$\n" ] ]
|
11
|
+
end
|
12
|
+
}
|
data/t/oob_gc.ru
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#\-E none
|
2
|
+
require 'unicorn/oob_gc'
|
3
|
+
use Rack::ContentLength
|
4
|
+
use Rack::ContentType, "text/plain"
|
5
|
+
use Unicorn::OobGC
|
6
|
+
$gc_started = false
|
7
|
+
|
8
|
+
# Mock GC.start
|
9
|
+
def GC.start
|
10
|
+
ObjectSpace.each_object(BasicSocket) do |x|
|
11
|
+
next if Unicorn::HttpServer::LISTENERS.include?(x)
|
12
|
+
x.closed? or abort "not closed #{x}"
|
13
|
+
end
|
14
|
+
$gc_started = true
|
15
|
+
end
|
16
|
+
run lambda { |env|
|
17
|
+
if "/gc_reset" == env["PATH_INFO"] && "POST" == env["REQUEST_METHOD"]
|
18
|
+
$gc_started = false
|
19
|
+
end
|
20
|
+
[ 200, {}, [ "#$gc_started\n" ] ]
|
21
|
+
}
|
data/t/oob_gc_path.ru
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#\-E none
|
2
|
+
require 'unicorn/oob_gc'
|
3
|
+
use Rack::ContentLength
|
4
|
+
use Rack::ContentType, "text/plain"
|
5
|
+
use Unicorn::OobGC, 5, /BAD/
|
6
|
+
$gc_started = false
|
7
|
+
|
8
|
+
# Mock GC.start
|
9
|
+
def GC.start
|
10
|
+
ObjectSpace.each_object(BasicSocket) do |x|
|
11
|
+
next if Unicorn::HttpServer::LISTENERS.include?(x)
|
12
|
+
x.closed? or abort "not closed #{x}"
|
13
|
+
end
|
14
|
+
$gc_started = true
|
15
|
+
end
|
16
|
+
run lambda { |env|
|
17
|
+
if "/gc_reset" == env["PATH_INFO"] && "POST" == env["REQUEST_METHOD"]
|
18
|
+
$gc_started = false
|
19
|
+
end
|
20
|
+
[ 200, {}, [ "#$gc_started\n" ] ]
|
21
|
+
}
|
data/t/t0002-parser-error.sh
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/bin/sh
|
2
2
|
. ./test-lib.sh
|
3
|
-
t_plan
|
3
|
+
t_plan 11 "parser error test"
|
4
4
|
|
5
5
|
t_begin "setup and startup" && {
|
6
6
|
unicorn_setup
|
@@ -24,6 +24,69 @@ t_begin "response should be a 400" && {
|
|
24
24
|
grep -F 'HTTP/1.1 400 Bad Request' $tmp
|
25
25
|
}
|
26
26
|
|
27
|
+
t_begin "send a huge Request URI (REQUEST_PATH > (12 * 1024))" && {
|
28
|
+
rm -f $tmp
|
29
|
+
cat $fifo > $tmp &
|
30
|
+
(
|
31
|
+
set -e
|
32
|
+
trap 'echo ok > $ok' EXIT
|
33
|
+
printf 'GET /'
|
34
|
+
for i in $(awk </dev/null 'BEGIN{for(i=0;i<1024;i++) print i}')
|
35
|
+
do
|
36
|
+
printf '0123456789ab'
|
37
|
+
done
|
38
|
+
printf ' HTTP/1.1\r\nHost: example.com\r\n\r\n'
|
39
|
+
) | socat - TCP:$listen > $fifo || :
|
40
|
+
test xok = x$(cat $ok)
|
41
|
+
wait
|
42
|
+
}
|
43
|
+
|
44
|
+
t_begin "response should be a 414 (REQUEST_PATH)" && {
|
45
|
+
grep -F 'HTTP/1.1 414 Request-URI Too Long' $tmp
|
46
|
+
}
|
47
|
+
|
48
|
+
t_begin "send a huge Request URI (QUERY_STRING > (10 * 1024))" && {
|
49
|
+
rm -f $tmp
|
50
|
+
cat $fifo > $tmp &
|
51
|
+
(
|
52
|
+
set -e
|
53
|
+
trap 'echo ok > $ok' EXIT
|
54
|
+
printf 'GET /hello-world?a'
|
55
|
+
for i in $(awk </dev/null 'BEGIN{for(i=0;i<1024;i++) print i}')
|
56
|
+
do
|
57
|
+
printf '0123456789'
|
58
|
+
done
|
59
|
+
printf ' HTTP/1.1\r\nHost: example.com\r\n\r\n'
|
60
|
+
) | socat - TCP:$listen > $fifo || :
|
61
|
+
test xok = x$(cat $ok)
|
62
|
+
wait
|
63
|
+
}
|
64
|
+
|
65
|
+
t_begin "response should be a 414 (QUERY_STRING)" && {
|
66
|
+
grep -F 'HTTP/1.1 414 Request-URI Too Long' $tmp
|
67
|
+
}
|
68
|
+
|
69
|
+
t_begin "send a huge Request URI (FRAGMENT > 1024)" && {
|
70
|
+
rm -f $tmp
|
71
|
+
cat $fifo > $tmp &
|
72
|
+
(
|
73
|
+
set -e
|
74
|
+
trap 'echo ok > $ok' EXIT
|
75
|
+
printf 'GET /hello-world#a'
|
76
|
+
for i in $(awk </dev/null 'BEGIN{for(i=0;i<64;i++) print i}')
|
77
|
+
do
|
78
|
+
printf '0123456789abcdef'
|
79
|
+
done
|
80
|
+
printf ' HTTP/1.1\r\nHost: example.com\r\n\r\n'
|
81
|
+
) | socat - TCP:$listen > $fifo || :
|
82
|
+
test xok = x$(cat $ok)
|
83
|
+
wait
|
84
|
+
}
|
85
|
+
|
86
|
+
t_begin "response should be a 414 (FRAGMENT)" && {
|
87
|
+
grep -F 'HTTP/1.1 414 Request-URI Too Long' $tmp
|
88
|
+
}
|
89
|
+
|
27
90
|
t_begin "server stderr should be clean" && check_stderr
|
28
91
|
|
29
92
|
t_begin "term signal sent" && kill $unicorn_pid
|
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
|
4
|
+
t_plan 11 "heartbeat/timeout test"
|
5
|
+
|
6
|
+
t_begin "setup and startup" && {
|
7
|
+
unicorn_setup
|
8
|
+
echo timeout 3 >> $unicorn_config
|
9
|
+
echo preload_app true >> $unicorn_config
|
10
|
+
unicorn -D heartbeat-timeout.ru -c $unicorn_config
|
11
|
+
unicorn_wait_start
|
12
|
+
}
|
13
|
+
|
14
|
+
t_begin "read worker PID" && {
|
15
|
+
worker_pid=$(curl -sSf http://$listen/)
|
16
|
+
t_info "worker_pid=$worker_pid"
|
17
|
+
}
|
18
|
+
|
19
|
+
t_begin "sleep for a bit, ensure worker PID does not change" && {
|
20
|
+
sleep 4
|
21
|
+
test $(curl -sSf http://$listen/) -eq $worker_pid
|
22
|
+
}
|
23
|
+
|
24
|
+
t_begin "block the worker process to force it to die" && {
|
25
|
+
rm $ok
|
26
|
+
t0=$(date +%s)
|
27
|
+
err="$(curl -sSf http://$listen/block-forever 2>&1 || > $ok)"
|
28
|
+
t1=$(date +%s)
|
29
|
+
elapsed=$(($t1 - $t0))
|
30
|
+
t_info "elapsed=$elapsed err=$err"
|
31
|
+
test x"$err" != x"Should never get here"
|
32
|
+
test x"$err" != x"$worker_pid"
|
33
|
+
}
|
34
|
+
|
35
|
+
t_begin "ensure worker was killed" && {
|
36
|
+
test -e $ok
|
37
|
+
test 1 -eq $(grep timeout $r_err | grep killing | wc -l)
|
38
|
+
}
|
39
|
+
|
40
|
+
t_begin "ensure timeout took at least 3 seconds" && {
|
41
|
+
test $elapsed -ge 3
|
42
|
+
}
|
43
|
+
|
44
|
+
t_begin "we get a fresh new worker process" && {
|
45
|
+
new_worker_pid=$(curl -sSf http://$listen/)
|
46
|
+
test $new_worker_pid -ne $worker_pid
|
47
|
+
}
|
48
|
+
|
49
|
+
t_begin "truncate the server error log" && {
|
50
|
+
> $r_err
|
51
|
+
}
|
52
|
+
|
53
|
+
t_begin "SIGSTOP and SIGCONT on unicorn master does not kill worker" && {
|
54
|
+
kill -STOP $unicorn_pid
|
55
|
+
sleep 4
|
56
|
+
kill -CONT $unicorn_pid
|
57
|
+
sleep 2
|
58
|
+
test $new_worker_pid -eq $(curl -sSf http://$listen/)
|
59
|
+
}
|
60
|
+
|
61
|
+
t_begin "stop server" && {
|
62
|
+
kill -QUIT $unicorn_pid
|
63
|
+
}
|
64
|
+
|
65
|
+
t_begin "check stderr" && check_stderr
|
66
|
+
|
67
|
+
dbgcat r_err
|
68
|
+
|
69
|
+
t_done
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
|
4
|
+
t_plan 9 "graceful handling of broken apps"
|
5
|
+
|
6
|
+
t_begin "setup and start" && {
|
7
|
+
unicorn_setup
|
8
|
+
unicorn -E none -D broken-app.ru -c $unicorn_config
|
9
|
+
unicorn_wait_start
|
10
|
+
}
|
11
|
+
|
12
|
+
t_begin "normal response is alright" && {
|
13
|
+
test xOK = x"$(curl -sSf http://$listen/)"
|
14
|
+
}
|
15
|
+
|
16
|
+
t_begin "app raised exception" && {
|
17
|
+
curl -sSf http://$listen/raise 2> $tmp || :
|
18
|
+
grep -F 500 $tmp
|
19
|
+
> $tmp
|
20
|
+
}
|
21
|
+
|
22
|
+
t_begin "app exception logged and backtrace not swallowed" && {
|
23
|
+
grep -F 'app error' $r_err
|
24
|
+
grep -A1 -F 'app error' $r_err | tail -1 | grep broken-app.ru:
|
25
|
+
dbgcat r_err
|
26
|
+
> $r_err
|
27
|
+
}
|
28
|
+
|
29
|
+
t_begin "trigger bad response" && {
|
30
|
+
curl -sSf http://$listen/nil 2> $tmp || :
|
31
|
+
grep -F 500 $tmp
|
32
|
+
> $tmp
|
33
|
+
}
|
34
|
+
|
35
|
+
t_begin "app exception logged" && {
|
36
|
+
grep -F 'app error' $r_err
|
37
|
+
> $r_err
|
38
|
+
}
|
39
|
+
|
40
|
+
t_begin "normal responses alright afterwards" && {
|
41
|
+
> $tmp
|
42
|
+
curl -sSf http://$listen/ >> $tmp &
|
43
|
+
curl -sSf http://$listen/ >> $tmp &
|
44
|
+
curl -sSf http://$listen/ >> $tmp &
|
45
|
+
curl -sSf http://$listen/ >> $tmp &
|
46
|
+
wait
|
47
|
+
test xOK = x$(sort < $tmp | uniq)
|
48
|
+
}
|
49
|
+
|
50
|
+
t_begin "teardown" && {
|
51
|
+
kill $unicorn_pid
|
52
|
+
}
|
53
|
+
|
54
|
+
t_begin "check stderr" && check_stderr
|
55
|
+
|
56
|
+
t_done
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
t_plan 5 "max_header_len setting (only intended for Rainbows!)"
|
4
|
+
|
5
|
+
t_begin "setup and start" && {
|
6
|
+
unicorn_setup
|
7
|
+
req='GET / HTTP/1.0\r\n\r\n'
|
8
|
+
len=$(printf "$req" | wc -c)
|
9
|
+
echo Unicorn::HttpParser.max_header_len = $len >> $unicorn_config
|
10
|
+
unicorn -D -c $unicorn_config env.ru
|
11
|
+
unicorn_wait_start
|
12
|
+
}
|
13
|
+
|
14
|
+
t_begin "minimal request succeeds" && {
|
15
|
+
rm -f $tmp
|
16
|
+
(
|
17
|
+
cat $fifo > $tmp &
|
18
|
+
printf "$req"
|
19
|
+
wait
|
20
|
+
echo ok > $ok
|
21
|
+
) | socat - TCP:$listen > $fifo
|
22
|
+
test xok = x$(cat $ok)
|
23
|
+
|
24
|
+
fgrep "HTTP/1.1 200 OK" $tmp
|
25
|
+
}
|
26
|
+
|
27
|
+
t_begin "big request fails" && {
|
28
|
+
rm -f $tmp
|
29
|
+
(
|
30
|
+
cat $fifo > $tmp &
|
31
|
+
printf 'GET /xxxxxx HTTP/1.0\r\n\r\n'
|
32
|
+
wait
|
33
|
+
echo ok > $ok
|
34
|
+
) | socat - TCP:$listen > $fifo
|
35
|
+
test xok = x$(cat $ok)
|
36
|
+
fgrep "HTTP/1.1 413" $tmp
|
37
|
+
}
|
38
|
+
|
39
|
+
dbgcat tmp
|
40
|
+
|
41
|
+
t_begin "killing succeeds" && {
|
42
|
+
kill $unicorn_pid
|
43
|
+
}
|
44
|
+
|
45
|
+
t_begin "check stderr" && {
|
46
|
+
check_stderr
|
47
|
+
}
|
48
|
+
|
49
|
+
t_done
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
|
4
|
+
t_plan 5 "at_exit/END handlers work as expected"
|
5
|
+
|
6
|
+
t_begin "setup and startup" && {
|
7
|
+
unicorn_setup
|
8
|
+
cat >> $unicorn_config <<EOF
|
9
|
+
at_exit { \$stdout.syswrite("#{Process.pid} BOTH\\n") }
|
10
|
+
END { \$stdout.syswrite("#{Process.pid} END BOTH\\n") }
|
11
|
+
after_fork do |_,_|
|
12
|
+
at_exit { \$stdout.syswrite("#{Process.pid} WORKER ONLY\\n") }
|
13
|
+
END { \$stdout.syswrite("#{Process.pid} END WORKER ONLY\\n") }
|
14
|
+
end
|
15
|
+
EOF
|
16
|
+
|
17
|
+
unicorn -D pid.ru -c $unicorn_config
|
18
|
+
unicorn_wait_start
|
19
|
+
}
|
20
|
+
|
21
|
+
t_begin "read worker PID" && {
|
22
|
+
worker_pid=$(curl -sSf http://$listen/)
|
23
|
+
t_info "worker_pid=$worker_pid"
|
24
|
+
}
|
25
|
+
|
26
|
+
t_begin "issue graceful shutdown (SIGQUIT) and wait for termination" && {
|
27
|
+
kill -QUIT $unicorn_pid
|
28
|
+
|
29
|
+
while kill -0 $unicorn_pid >/dev/null 2>&1
|
30
|
+
do
|
31
|
+
sleep 1
|
32
|
+
done
|
33
|
+
}
|
34
|
+
|
35
|
+
t_begin "check stderr" && check_stderr
|
36
|
+
|
37
|
+
dbgcat r_err
|
38
|
+
dbgcat r_out
|
39
|
+
|
40
|
+
t_begin "all at_exit handlers ran" && {
|
41
|
+
grep "$worker_pid BOTH" $r_out
|
42
|
+
grep "$unicorn_pid BOTH" $r_out
|
43
|
+
grep "$worker_pid END BOTH" $r_out
|
44
|
+
grep "$unicorn_pid END BOTH" $r_out
|
45
|
+
grep "$worker_pid WORKER ONLY" $r_out
|
46
|
+
grep "$worker_pid END WORKER ONLY" $r_out
|
47
|
+
}
|
48
|
+
|
49
|
+
t_done
|
data/t/t9001-oob_gc.sh
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
t_plan 9 "OobGC test"
|
4
|
+
|
5
|
+
t_begin "setup and start" && {
|
6
|
+
unicorn_setup
|
7
|
+
unicorn -D -c $unicorn_config oob_gc.ru
|
8
|
+
unicorn_wait_start
|
9
|
+
}
|
10
|
+
|
11
|
+
t_begin "test default interval (4 requests)" && {
|
12
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
13
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
14
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
15
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
16
|
+
}
|
17
|
+
|
18
|
+
t_begin "GC starting-request returns immediately" && {
|
19
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
20
|
+
}
|
21
|
+
|
22
|
+
t_begin "GC is started after 5 requests" && {
|
23
|
+
test xtrue = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
24
|
+
}
|
25
|
+
|
26
|
+
t_begin "reset GC" && {
|
27
|
+
test xfalse = x$(curl -vsSf -X POST http://$listen/gc_reset 2>> $tmp)
|
28
|
+
}
|
29
|
+
|
30
|
+
t_begin "test default interval again (3 requests)" && {
|
31
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
32
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
33
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
34
|
+
}
|
35
|
+
|
36
|
+
t_begin "GC is started after 5 requests" && {
|
37
|
+
test xtrue = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
38
|
+
}
|
39
|
+
|
40
|
+
t_begin "killing succeeds" && {
|
41
|
+
kill -QUIT $unicorn_pid
|
42
|
+
}
|
43
|
+
|
44
|
+
t_begin "check_stderr" && check_stderr
|
45
|
+
dbgcat r_err
|
46
|
+
|
47
|
+
t_done
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
t_plan 12 "OobGC test with limited path"
|
4
|
+
|
5
|
+
t_begin "setup and start" && {
|
6
|
+
unicorn_setup
|
7
|
+
unicorn -D -c $unicorn_config oob_gc_path.ru
|
8
|
+
unicorn_wait_start
|
9
|
+
}
|
10
|
+
|
11
|
+
t_begin "test default is noop" && {
|
12
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
13
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
14
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
15
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
16
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
17
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
18
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
19
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
20
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
21
|
+
}
|
22
|
+
|
23
|
+
t_begin "4 bad requests to bump counter" && {
|
24
|
+
test xfalse = x$(curl -vsSf http://$listen/BAD 2>> $tmp)
|
25
|
+
test xfalse = x$(curl -vsSf http://$listen/BAD 2>> $tmp)
|
26
|
+
test xfalse = x$(curl -vsSf http://$listen/BAD 2>> $tmp)
|
27
|
+
test xfalse = x$(curl -vsSf http://$listen/BAD 2>> $tmp)
|
28
|
+
}
|
29
|
+
|
30
|
+
t_begin "GC-starting request returns immediately" && {
|
31
|
+
test xfalse = x$(curl -vsSf http://$listen/BAD 2>> $tmp)
|
32
|
+
}
|
33
|
+
|
34
|
+
t_begin "GC was started after 5 requests" && {
|
35
|
+
test xtrue = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
36
|
+
}
|
37
|
+
|
38
|
+
t_begin "reset GC" && {
|
39
|
+
test xfalse = x$(curl -vsSf -X POST http://$listen/gc_reset 2>> $tmp)
|
40
|
+
}
|
41
|
+
|
42
|
+
t_begin "test default is noop" && {
|
43
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
44
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
45
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
46
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
47
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
48
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
49
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
50
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
51
|
+
test xfalse = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
52
|
+
}
|
53
|
+
|
54
|
+
t_begin "4 bad requests to bump counter" && {
|
55
|
+
test xfalse = x$(curl -vsSf http://$listen/BAD 2>> $tmp)
|
56
|
+
test xfalse = x$(curl -vsSf http://$listen/BAD 2>> $tmp)
|
57
|
+
test xfalse = x$(curl -vsSf http://$listen/BAD 2>> $tmp)
|
58
|
+
test xfalse = x$(curl -vsSf http://$listen/BAD 2>> $tmp)
|
59
|
+
}
|
60
|
+
|
61
|
+
t_begin "GC-starting request returns immediately" && {
|
62
|
+
test xfalse = x$(curl -vsSf http://$listen/BAD 2>> $tmp)
|
63
|
+
}
|
64
|
+
|
65
|
+
t_begin "GC was started after 5 requests" && {
|
66
|
+
test xtrue = x$(curl -vsSf http://$listen/ 2>> $tmp)
|
67
|
+
}
|
68
|
+
|
69
|
+
t_begin "killing succeeds" && {
|
70
|
+
kill -QUIT $unicorn_pid
|
71
|
+
}
|
72
|
+
|
73
|
+
t_begin "check_stderr" && check_stderr
|
74
|
+
|
75
|
+
t_done
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'unicorn'
|
3
|
+
|
4
|
+
class TestDroplet < Test::Unit::TestCase
|
5
|
+
def test_create_many_droplets
|
6
|
+
now = Time.now.to_i
|
7
|
+
tmp = (0..1024).map do |i|
|
8
|
+
droplet = Unicorn::Worker.new(i)
|
9
|
+
assert droplet.respond_to?(:tick)
|
10
|
+
assert_equal 0, droplet.tick
|
11
|
+
assert_equal(now, droplet.tick = now)
|
12
|
+
assert_equal now, droplet.tick
|
13
|
+
assert_equal(0, droplet.tick = 0)
|
14
|
+
assert_equal 0, droplet.tick
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_shared_process
|
19
|
+
droplet = Unicorn::Worker.new(0)
|
20
|
+
_, status = Process.waitpid2(fork { droplet.tick += 1; exit!(0) })
|
21
|
+
assert status.success?, status.inspect
|
22
|
+
assert_equal 1, droplet.tick
|
23
|
+
|
24
|
+
_, status = Process.waitpid2(fork { droplet.tick += 1; exit!(0) })
|
25
|
+
assert status.success?, status.inspect
|
26
|
+
assert_equal 2, droplet.tick
|
27
|
+
end
|
28
|
+
end
|
@@ -258,6 +258,20 @@ class HttpParserTest < Test::Unit::TestCase
|
|
258
258
|
assert_equal 'hi y x ASDF', req['HTTP_X_ASDF']
|
259
259
|
end
|
260
260
|
|
261
|
+
def test_continuation_eats_trailing_spaces
|
262
|
+
parser = HttpParser.new
|
263
|
+
header = "GET / HTTP/1.1\r\n" \
|
264
|
+
"X-ASDF: \r\n" \
|
265
|
+
"\t\r\n" \
|
266
|
+
" b \r\n" \
|
267
|
+
" ASDF\r\n\r\n"
|
268
|
+
parser.buf << header
|
269
|
+
req = parser.env
|
270
|
+
assert_equal req, parser.parse
|
271
|
+
assert_equal '', parser.buf
|
272
|
+
assert_equal 'b ASDF', req['HTTP_X_ASDF']
|
273
|
+
end
|
274
|
+
|
261
275
|
def test_continuation_with_absolute_uri_and_ignored_host_header
|
262
276
|
parser = HttpParser.new
|
263
277
|
header = "GET http://example.com/ HTTP/1.1\r\n" \
|
@@ -726,7 +740,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
726
740
|
# then that large header names are caught
|
727
741
|
10.times do |c|
|
728
742
|
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
|
729
|
-
assert_raises
|
743
|
+
assert_raises(Unicorn::HttpParserError,Unicorn::RequestURITooLongError) do
|
730
744
|
parser.buf << get
|
731
745
|
parser.parse
|
732
746
|
parser.clear
|
@@ -736,7 +750,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
736
750
|
# then that large mangled field values are caught
|
737
751
|
10.times do |c|
|
738
752
|
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
739
|
-
assert_raises
|
753
|
+
assert_raises(Unicorn::HttpParserError,Unicorn::RequestURITooLongError) do
|
740
754
|
parser.buf << get
|
741
755
|
parser.parse
|
742
756
|
parser.clear
|
@@ -747,7 +761,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
747
761
|
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
|
748
762
|
get << "X-Test: test\r\n" * (80 * 1024)
|
749
763
|
parser.buf << get
|
750
|
-
assert_raises
|
764
|
+
assert_raises(Unicorn::HttpParserError,Unicorn::RequestURITooLongError) do
|
751
765
|
parser.parse
|
752
766
|
end
|
753
767
|
parser.clear
|
@@ -755,7 +769,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
755
769
|
# finally just that random garbage gets blocked all the time
|
756
770
|
10.times do |c|
|
757
771
|
get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
758
|
-
assert_raises
|
772
|
+
assert_raises(Unicorn::HttpParserError,Unicorn::RequestURITooLongError) do
|
759
773
|
parser.buf << get
|
760
774
|
parser.parse
|
761
775
|
parser.clear
|
@@ -764,6 +778,48 @@ class HttpParserTest < Test::Unit::TestCase
|
|
764
778
|
|
765
779
|
end
|
766
780
|
|
781
|
+
def test_leading_tab
|
782
|
+
parser = HttpParser.new
|
783
|
+
get = "GET / HTTP/1.1\r\nHost:\texample.com\r\n\r\n"
|
784
|
+
assert parser.add_parse(get)
|
785
|
+
assert_equal 'example.com', parser.env['HTTP_HOST']
|
786
|
+
end
|
787
|
+
|
788
|
+
def test_trailing_whitespace
|
789
|
+
parser = HttpParser.new
|
790
|
+
get = "GET / HTTP/1.1\r\nHost: example.com \r\n\r\n"
|
791
|
+
assert parser.add_parse(get)
|
792
|
+
assert_equal 'example.com', parser.env['HTTP_HOST']
|
793
|
+
end
|
794
|
+
|
795
|
+
def test_trailing_tab
|
796
|
+
parser = HttpParser.new
|
797
|
+
get = "GET / HTTP/1.1\r\nHost: example.com\t\r\n\r\n"
|
798
|
+
assert parser.add_parse(get)
|
799
|
+
assert_equal 'example.com', parser.env['HTTP_HOST']
|
800
|
+
end
|
801
|
+
|
802
|
+
def test_trailing_multiple_linear_whitespace
|
803
|
+
parser = HttpParser.new
|
804
|
+
get = "GET / HTTP/1.1\r\nHost: example.com\t \t \t\r\n\r\n"
|
805
|
+
assert parser.add_parse(get)
|
806
|
+
assert_equal 'example.com', parser.env['HTTP_HOST']
|
807
|
+
end
|
808
|
+
|
809
|
+
def test_embedded_linear_whitespace_ok
|
810
|
+
parser = HttpParser.new
|
811
|
+
get = "GET / HTTP/1.1\r\nX-Space: hello\t world\t \r\n\r\n"
|
812
|
+
assert parser.add_parse(get)
|
813
|
+
assert_equal "hello\t world", parser.env["HTTP_X_SPACE"]
|
814
|
+
end
|
815
|
+
|
816
|
+
def test_empty_header
|
817
|
+
parser = HttpParser.new
|
818
|
+
get = "GET / HTTP/1.1\r\nHost: \r\n\r\n"
|
819
|
+
assert parser.add_parse(get)
|
820
|
+
assert_equal '', parser.env['HTTP_HOST']
|
821
|
+
end
|
822
|
+
|
767
823
|
# so we don't care about the portability of this test
|
768
824
|
# if it doesn't leak on Linux, it won't leak anywhere else
|
769
825
|
# unless your C compiler or platform is otherwise broken
|