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