unicorn 3.0.1 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/GIT-VERSION-GEN +1 -1
- data/lib/unicorn/configurator.rb +17 -10
- data/lib/unicorn/const.rb +4 -3
- data/lib/unicorn/http_server.rb +8 -0
- data/lib/unicorn/tee_input.rb +16 -4
- data/t/rack-input-tests.ru +21 -0
- data/t/t0015-configurator-internals.sh +25 -0
- data/t/t0100-rack-input-tests.sh +124 -0
- data/t/t0116-client_body_buffer_size.sh +80 -0
- data/t/t0116.ru +16 -0
- metadata +9 -4
data/GIT-VERSION-GEN
CHANGED
data/lib/unicorn/configurator.rb
CHANGED
@@ -9,6 +9,7 @@ require 'logger'
|
|
9
9
|
# nginx is also available at
|
10
10
|
# http://unicorn.bogomips.org/examples/nginx.conf
|
11
11
|
class Unicorn::Configurator
|
12
|
+
include Unicorn
|
12
13
|
attr_accessor :set, :config_file, :after_reload
|
13
14
|
|
14
15
|
# :stopdoc:
|
@@ -40,6 +41,7 @@ class Unicorn::Configurator
|
|
40
41
|
:pid => nil,
|
41
42
|
:preload_app => false,
|
42
43
|
:rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
|
44
|
+
:client_body_buffer_size => Unicorn::Const::MAX_BODY,
|
43
45
|
}
|
44
46
|
#:startdoc:
|
45
47
|
|
@@ -180,11 +182,7 @@ class Unicorn::Configurator
|
|
180
182
|
# server 192.168.0.9:8080 fail_timeout=0;
|
181
183
|
# }
|
182
184
|
def timeout(seconds)
|
183
|
-
|
184
|
-
"not numeric: timeout=#{seconds.inspect}"
|
185
|
-
seconds >= 3 or raise ArgumentError,
|
186
|
-
"too low: timeout=#{seconds.inspect}"
|
187
|
-
set[:timeout] = seconds
|
185
|
+
set_int(:timeout, seconds, 3)
|
188
186
|
end
|
189
187
|
|
190
188
|
# sets the current number of worker_processes to +nr+. Each worker
|
@@ -194,11 +192,7 @@ class Unicorn::Configurator
|
|
194
192
|
# the rest of your Unicorn configuration. See the SIGNALS document
|
195
193
|
# for more information.
|
196
194
|
def worker_processes(nr)
|
197
|
-
|
198
|
-
"not an integer: worker_processes=#{nr.inspect}"
|
199
|
-
nr >= 0 or raise ArgumentError,
|
200
|
-
"not non-negative: worker_processes=#{nr.inspect}"
|
201
|
-
set[:worker_processes] = nr
|
195
|
+
set_int(:worker_processes, nr, 1)
|
202
196
|
end
|
203
197
|
|
204
198
|
# sets listeners to the given +addresses+, replacing or augmenting the
|
@@ -392,6 +386,14 @@ class Unicorn::Configurator
|
|
392
386
|
set_bool(:rewindable_input, bool)
|
393
387
|
end
|
394
388
|
|
389
|
+
# The maximum size (in +bytes+) to buffer in memory before
|
390
|
+
# resorting to a temporary file. Default is 112 kilobytes.
|
391
|
+
# This option has no effect if "rewindable_input" is set to
|
392
|
+
# +false+.
|
393
|
+
def client_body_buffer_size(bytes)
|
394
|
+
set_int(:client_body_buffer_size, bytes, 0)
|
395
|
+
end
|
396
|
+
|
395
397
|
# Allow redirecting $stderr to a given path. Unlike doing this from
|
396
398
|
# the shell, this allows the unicorn process to know the path its
|
397
399
|
# writing to and rotate the file if it is used for logging. The
|
@@ -470,6 +472,11 @@ class Unicorn::Configurator
|
|
470
472
|
end
|
471
473
|
|
472
474
|
private
|
475
|
+
def set_int(var, n, min)
|
476
|
+
Integer === n or raise ArgumentError, "not an integer: #{var}=#{n.inspect}"
|
477
|
+
n >= min or raise ArgumentError, "too low (< #{min}): #{var}=#{n.inspect}"
|
478
|
+
set[var] = n
|
479
|
+
end
|
473
480
|
|
474
481
|
def set_path(var, path) #:nodoc:
|
475
482
|
case path
|
data/lib/unicorn/const.rb
CHANGED
@@ -7,8 +7,8 @@
|
|
7
7
|
# improve things much compared to constants.
|
8
8
|
module Unicorn::Const
|
9
9
|
|
10
|
-
# The current version of Unicorn, currently 3.0
|
11
|
-
UNICORN_VERSION = "3.0
|
10
|
+
# The current version of Unicorn, currently 3.1.0
|
11
|
+
UNICORN_VERSION = "3.1.0"
|
12
12
|
|
13
13
|
# default TCP listen host address (0.0.0.0, all interfaces)
|
14
14
|
DEFAULT_HOST = "0.0.0.0"
|
@@ -23,7 +23,8 @@ module Unicorn::Const
|
|
23
23
|
CHUNK_SIZE = 16 * 1024
|
24
24
|
|
25
25
|
# Maximum request body size before it is moved out of memory and into a
|
26
|
-
# temporary file for reading (112 kilobytes).
|
26
|
+
# temporary file for reading (112 kilobytes). This is the default
|
27
|
+
# value of of client_body_buffer_size.
|
27
28
|
MAX_BODY = 1024 * 112
|
28
29
|
|
29
30
|
# :stopdoc:
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -364,6 +364,14 @@ class Unicorn::HttpServer
|
|
364
364
|
Unicorn::TeeInput : Unicorn::StreamInput
|
365
365
|
end
|
366
366
|
|
367
|
+
def client_body_buffer_size
|
368
|
+
Unicorn::TeeInput.client_body_buffer_size
|
369
|
+
end
|
370
|
+
|
371
|
+
def client_body_buffer_size=(bytes)
|
372
|
+
Unicorn::TeeInput.client_body_buffer_size = bytes
|
373
|
+
end
|
374
|
+
|
367
375
|
private
|
368
376
|
|
369
377
|
# wait for a signal hander to wake us up and then consume the pipe
|
data/lib/unicorn/tee_input.rb
CHANGED
@@ -16,12 +16,24 @@ class Unicorn::TeeInput < Unicorn::StreamInput
|
|
16
16
|
# resorting to a temporary file. Default is 112 kilobytes.
|
17
17
|
@@client_body_buffer_size = Unicorn::Const::MAX_BODY
|
18
18
|
|
19
|
+
# sets the maximum size of request bodies to buffer in memory,
|
20
|
+
# amounts larger than this are buffered to the filesystem
|
21
|
+
def self.client_body_buffer_size=(bytes)
|
22
|
+
@@client_body_buffer_size = bytes
|
23
|
+
end
|
24
|
+
|
25
|
+
# returns the maximum size of request bodies to buffer in memory,
|
26
|
+
# amounts larger than this are buffered to the filesystem
|
27
|
+
def self.client_body_buffer_size
|
28
|
+
@@client_body_buffer_size
|
29
|
+
end
|
30
|
+
|
19
31
|
# Initializes a new TeeInput object. You normally do not have to call
|
20
32
|
# this unless you are writing an HTTP server.
|
21
33
|
def initialize(socket, request)
|
22
34
|
@len = request.content_length
|
23
35
|
super
|
24
|
-
@tmp = @len && @len
|
36
|
+
@tmp = @len && @len <= @@client_body_buffer_size ?
|
25
37
|
StringIO.new("") : Unicorn::TmpIO.new
|
26
38
|
end
|
27
39
|
|
@@ -43,10 +55,10 @@ class Unicorn::TeeInput < Unicorn::StreamInput
|
|
43
55
|
# specified +length+ in a loop until it returns +nil+.
|
44
56
|
def size
|
45
57
|
@len and return @len
|
46
|
-
pos = @
|
58
|
+
pos = @tmp.pos
|
47
59
|
consume!
|
48
60
|
@tmp.pos = pos
|
49
|
-
@len = @
|
61
|
+
@len = @tmp.size
|
50
62
|
end
|
51
63
|
|
52
64
|
# :call-seq:
|
@@ -92,7 +104,7 @@ class Unicorn::TeeInput < Unicorn::StreamInput
|
|
92
104
|
# the offset (zero) of the +ios+ pointer. Subsequent reads will
|
93
105
|
# start from the beginning of the previously-buffered input.
|
94
106
|
def rewind
|
95
|
-
return 0 if
|
107
|
+
return 0 if 0 == @tmp.size
|
96
108
|
consume! if @socket
|
97
109
|
@tmp.rewind # Rack does not specify what the return value is here
|
98
110
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# SHA1 checksum generator
|
2
|
+
require 'digest/sha1'
|
3
|
+
use Rack::ContentLength
|
4
|
+
cap = 16384
|
5
|
+
app = lambda do |env|
|
6
|
+
/\A100-continue\z/i =~ env['HTTP_EXPECT'] and
|
7
|
+
return [ 100, {}, [] ]
|
8
|
+
digest = Digest::SHA1.new
|
9
|
+
input = env['rack.input']
|
10
|
+
input.size if env["PATH_INFO"] == "/size_first"
|
11
|
+
input.rewind if env["PATH_INFO"] == "/rewind_first"
|
12
|
+
if buf = input.read(rand(cap))
|
13
|
+
begin
|
14
|
+
raise "#{buf.size} > #{cap}" if buf.size > cap
|
15
|
+
digest.update(buf)
|
16
|
+
end while input.read(rand(cap), buf)
|
17
|
+
end
|
18
|
+
|
19
|
+
[ 200, {'Content-Type' => 'text/plain'}, [ digest.hexdigest << "\n" ] ]
|
20
|
+
end
|
21
|
+
run app
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
t_plan 4 "configurator internals tests (from FAQ)"
|
4
|
+
|
5
|
+
t_begin "setup and start" && {
|
6
|
+
unicorn_setup
|
7
|
+
cat >> $unicorn_config <<EOF
|
8
|
+
HttpRequest::DEFAULTS["rack.url_scheme"] = "https"
|
9
|
+
Configurator::DEFAULTS[:logger].formatter = Logger::Formatter.new
|
10
|
+
EOF
|
11
|
+
unicorn -D -c $unicorn_config env.ru
|
12
|
+
unicorn_wait_start
|
13
|
+
}
|
14
|
+
|
15
|
+
t_begin "single request" && {
|
16
|
+
curl -sSfv http://$listen/ | grep '"rack.url_scheme"=>"https"'
|
17
|
+
}
|
18
|
+
|
19
|
+
t_begin "killing succeeds" && {
|
20
|
+
kill $unicorn_pid
|
21
|
+
}
|
22
|
+
|
23
|
+
t_begin "no errors" && check_stderr
|
24
|
+
|
25
|
+
t_done
|
@@ -0,0 +1,124 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
test -r random_blob || die "random_blob required, run with 'make $0'"
|
4
|
+
|
5
|
+
t_plan 10 "rack.input read tests"
|
6
|
+
|
7
|
+
t_begin "setup and startup" && {
|
8
|
+
rtmpfiles curl_out curl_err
|
9
|
+
unicorn_setup
|
10
|
+
unicorn -D rack-input-tests.ru -c $unicorn_config
|
11
|
+
blob_sha1=$(rsha1 < random_blob)
|
12
|
+
blob_size=$(wc -c < random_blob)
|
13
|
+
t_info "blob_sha1=$blob_sha1"
|
14
|
+
unicorn_wait_start
|
15
|
+
}
|
16
|
+
|
17
|
+
t_begin "corked identity request" && {
|
18
|
+
rm -f $tmp
|
19
|
+
(
|
20
|
+
cat $fifo > $tmp &
|
21
|
+
printf 'PUT / HTTP/1.0\r\n'
|
22
|
+
printf 'Content-Length: %d\r\n\r\n' $blob_size
|
23
|
+
cat random_blob
|
24
|
+
wait
|
25
|
+
echo ok > $ok
|
26
|
+
) | ( sleep 1 && socat - TCP4:$listen > $fifo )
|
27
|
+
test 1 -eq $(grep $blob_sha1 $tmp |wc -l)
|
28
|
+
test x"$(cat $ok)" = xok
|
29
|
+
}
|
30
|
+
|
31
|
+
t_begin "corked chunked request" && {
|
32
|
+
rm -f $tmp
|
33
|
+
(
|
34
|
+
cat $fifo > $tmp &
|
35
|
+
content-md5-put < random_blob
|
36
|
+
wait
|
37
|
+
echo ok > $ok
|
38
|
+
) | ( sleep 1 && socat - TCP4:$listen > $fifo )
|
39
|
+
test 1 -eq $(grep $blob_sha1 $tmp |wc -l)
|
40
|
+
test x"$(cat $ok)" = xok
|
41
|
+
}
|
42
|
+
|
43
|
+
t_begin "corked identity request (input#size first)" && {
|
44
|
+
rm -f $tmp
|
45
|
+
(
|
46
|
+
cat $fifo > $tmp &
|
47
|
+
printf 'PUT /size_first HTTP/1.0\r\n'
|
48
|
+
printf 'Content-Length: %d\r\n\r\n' $blob_size
|
49
|
+
cat random_blob
|
50
|
+
wait
|
51
|
+
echo ok > $ok
|
52
|
+
) | ( sleep 1 && socat - TCP4:$listen > $fifo )
|
53
|
+
test 1 -eq $(grep $blob_sha1 $tmp |wc -l)
|
54
|
+
test x"$(cat $ok)" = xok
|
55
|
+
}
|
56
|
+
|
57
|
+
t_begin "corked identity request (input#rewind first)" && {
|
58
|
+
rm -f $tmp
|
59
|
+
(
|
60
|
+
cat $fifo > $tmp &
|
61
|
+
printf 'PUT /rewind_first HTTP/1.0\r\n'
|
62
|
+
printf 'Content-Length: %d\r\n\r\n' $blob_size
|
63
|
+
cat random_blob
|
64
|
+
wait
|
65
|
+
echo ok > $ok
|
66
|
+
) | ( sleep 1 && socat - TCP4:$listen > $fifo )
|
67
|
+
test 1 -eq $(grep $blob_sha1 $tmp |wc -l)
|
68
|
+
test x"$(cat $ok)" = xok
|
69
|
+
}
|
70
|
+
|
71
|
+
t_begin "corked chunked request (input#size first)" && {
|
72
|
+
rm -f $tmp
|
73
|
+
(
|
74
|
+
cat $fifo > $tmp &
|
75
|
+
printf 'PUT /size_first HTTP/1.1\r\n'
|
76
|
+
printf 'Host: example.com\r\n'
|
77
|
+
printf 'Transfer-Encoding: chunked\r\n'
|
78
|
+
printf 'Trailer: Content-MD5\r\n'
|
79
|
+
printf '\r\n'
|
80
|
+
content-md5-put --no-headers < random_blob
|
81
|
+
wait
|
82
|
+
echo ok > $ok
|
83
|
+
) | ( sleep 1 && socat - TCP4:$listen > $fifo )
|
84
|
+
test 1 -eq $(grep $blob_sha1 $tmp |wc -l)
|
85
|
+
test 1 -eq $(grep $blob_sha1 $tmp |wc -l)
|
86
|
+
test x"$(cat $ok)" = xok
|
87
|
+
}
|
88
|
+
|
89
|
+
t_begin "corked chunked request (input#rewind first)" && {
|
90
|
+
rm -f $tmp
|
91
|
+
(
|
92
|
+
cat $fifo > $tmp &
|
93
|
+
printf 'PUT /rewind_first HTTP/1.1\r\n'
|
94
|
+
printf 'Host: example.com\r\n'
|
95
|
+
printf 'Transfer-Encoding: chunked\r\n'
|
96
|
+
printf 'Trailer: Content-MD5\r\n'
|
97
|
+
printf '\r\n'
|
98
|
+
content-md5-put --no-headers < random_blob
|
99
|
+
wait
|
100
|
+
echo ok > $ok
|
101
|
+
) | ( sleep 1 && socat - TCP4:$listen > $fifo )
|
102
|
+
test 1 -eq $(grep $blob_sha1 $tmp |wc -l)
|
103
|
+
test x"$(cat $ok)" = xok
|
104
|
+
}
|
105
|
+
|
106
|
+
t_begin "regular request" && {
|
107
|
+
curl -sSf -T random_blob http://$listen/ > $curl_out 2> $curl_err
|
108
|
+
test x$blob_sha1 = x$(cat $curl_out)
|
109
|
+
test ! -s $curl_err
|
110
|
+
}
|
111
|
+
|
112
|
+
t_begin "chunked request" && {
|
113
|
+
curl -sSf -T- < random_blob http://$listen/ > $curl_out 2> $curl_err
|
114
|
+
test x$blob_sha1 = x$(cat $curl_out)
|
115
|
+
test ! -s $curl_err
|
116
|
+
}
|
117
|
+
|
118
|
+
dbgcat r_err
|
119
|
+
|
120
|
+
t_begin "shutdown" && {
|
121
|
+
kill $unicorn_pid
|
122
|
+
}
|
123
|
+
|
124
|
+
t_done
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
t_plan 12 "client_body_buffer_size settings"
|
4
|
+
|
5
|
+
t_begin "setup and start" && {
|
6
|
+
unicorn_setup
|
7
|
+
rtmpfiles unicorn_config_tmp one_meg
|
8
|
+
dd if=/dev/zero bs=1M count=1 of=$one_meg
|
9
|
+
cat >> $unicorn_config <<EOF
|
10
|
+
after_fork do |server, worker|
|
11
|
+
File.open("$fifo", "wb") { |fp| fp.syswrite "START" }
|
12
|
+
end
|
13
|
+
EOF
|
14
|
+
cat $unicorn_config > $unicorn_config_tmp
|
15
|
+
echo client_body_buffer_size 0 >> $unicorn_config
|
16
|
+
unicorn -D -c $unicorn_config t0116.ru
|
17
|
+
unicorn_wait_start
|
18
|
+
fs_class=Unicorn::TmpIO
|
19
|
+
mem_class=StringIO
|
20
|
+
|
21
|
+
test x"$(cat $fifo)" = xSTART
|
22
|
+
}
|
23
|
+
|
24
|
+
t_begin "class for a zero-byte file should be StringIO" && {
|
25
|
+
> $tmp
|
26
|
+
test xStringIO = x"$(curl -T $tmp -sSf http://$listen/input_class)"
|
27
|
+
}
|
28
|
+
|
29
|
+
t_begin "class for a 1 byte file should be filesystem-backed" && {
|
30
|
+
echo > $tmp
|
31
|
+
test x$fs_class = x"$(curl -T $tmp -sSf http://$listen/tmp_class)"
|
32
|
+
}
|
33
|
+
|
34
|
+
t_begin "reload with default client_body_buffer_size" && {
|
35
|
+
mv $unicorn_config_tmp $unicorn_config
|
36
|
+
kill -HUP $unicorn_pid
|
37
|
+
test x"$(cat $fifo)" = xSTART
|
38
|
+
}
|
39
|
+
|
40
|
+
t_begin "class for a 1 byte file should be memory-backed" && {
|
41
|
+
echo > $tmp
|
42
|
+
test x$mem_class = x"$(curl -T $tmp -sSf http://$listen/tmp_class)"
|
43
|
+
}
|
44
|
+
|
45
|
+
t_begin "class for a random blob file should be filesystem-backed" && {
|
46
|
+
resp="$(curl -T random_blob -sSf http://$listen/tmp_class)"
|
47
|
+
test x$fs_class = x"$resp"
|
48
|
+
}
|
49
|
+
|
50
|
+
t_begin "one megabyte file should be filesystem-backed" && {
|
51
|
+
resp="$(curl -T $one_meg -sSf http://$listen/tmp_class)"
|
52
|
+
test x$fs_class = x"$resp"
|
53
|
+
}
|
54
|
+
|
55
|
+
t_begin "reload with a big client_body_buffer_size" && {
|
56
|
+
echo "client_body_buffer_size(1024 * 1024)" >> $unicorn_config
|
57
|
+
kill -HUP $unicorn_pid
|
58
|
+
test x"$(cat $fifo)" = xSTART
|
59
|
+
}
|
60
|
+
|
61
|
+
t_begin "one megabyte file should be memory-backed" && {
|
62
|
+
resp="$(curl -T $one_meg -sSf http://$listen/tmp_class)"
|
63
|
+
test x$mem_class = x"$resp"
|
64
|
+
}
|
65
|
+
|
66
|
+
t_begin "one megabyte + 1 byte file should be filesystem-backed" && {
|
67
|
+
echo >> $one_meg
|
68
|
+
resp="$(curl -T $one_meg -sSf http://$listen/tmp_class)"
|
69
|
+
test x$fs_class = x"$resp"
|
70
|
+
}
|
71
|
+
|
72
|
+
t_begin "killing succeeds" && {
|
73
|
+
kill $unicorn_pid
|
74
|
+
}
|
75
|
+
|
76
|
+
t_begin "check stderr" && {
|
77
|
+
check_stderr
|
78
|
+
}
|
79
|
+
|
80
|
+
t_done
|
data/t/t0116.ru
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#\ -E none
|
2
|
+
use Rack::ContentLength
|
3
|
+
use Rack::ContentType, 'text/plain'
|
4
|
+
app = lambda do |env|
|
5
|
+
input = env['rack.input']
|
6
|
+
case env["PATH_INFO"]
|
7
|
+
when "/tmp_class"
|
8
|
+
body = input.instance_variable_get(:@tmp).class.name
|
9
|
+
when "/input_class"
|
10
|
+
body = input.class.name
|
11
|
+
else
|
12
|
+
return [ 500, {}, [] ]
|
13
|
+
end
|
14
|
+
[ 200, {}, [ body ] ]
|
15
|
+
end
|
16
|
+
run app
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 3
|
8
|
-
- 0
|
9
8
|
- 1
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 3.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Unicorn hackers
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-12-
|
18
|
+
date: 2010-12-09 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -198,6 +198,7 @@ files:
|
|
198
198
|
- t/my-tap-lib.sh
|
199
199
|
- t/pid.ru
|
200
200
|
- t/preread_input.ru
|
201
|
+
- t/rack-input-tests.ru
|
201
202
|
- t/rails3-app/.gitignore
|
202
203
|
- t/rails3-app/Gemfile
|
203
204
|
- t/rails3-app/Rakefile
|
@@ -248,6 +249,10 @@ files:
|
|
248
249
|
- t/t0013.ru
|
249
250
|
- t/t0014-rewindable-input-true.sh
|
250
251
|
- t/t0014.ru
|
252
|
+
- t/t0015-configurator-internals.sh
|
253
|
+
- t/t0100-rack-input-tests.sh
|
254
|
+
- t/t0116-client_body_buffer_size.sh
|
255
|
+
- t/t0116.ru
|
251
256
|
- t/t0300-rails3-basic.sh
|
252
257
|
- t/t0301-rails3-missing-config-ru.sh
|
253
258
|
- t/t0302-rails3-alt-working_directory.sh
|