unicorn 3.0.1 → 3.1.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/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
|