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 CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v3.0.1.GIT
4
+ DEF_VER=v3.1.0.GIT
5
5
 
6
6
  LF='
7
7
  '
@@ -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
- Numeric === seconds or raise ArgumentError,
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
- Integer === nr or raise ArgumentError,
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.1
11
- UNICORN_VERSION = "3.0.1"
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:
@@ -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
@@ -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 < @@client_body_buffer_size ?
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 = @bytes_read
58
+ pos = @tmp.pos
47
59
  consume!
48
60
  @tmp.pos = pos
49
- @len = @bytes_read
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 @bytes_read == 0
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: 5
4
+ hash: 3
5
5
  prerelease: false
6
6
  segments:
7
7
  - 3
8
- - 0
9
8
  - 1
10
- version: 3.0.1
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-03 00:00:00 +00:00
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