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 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