unicorn 3.3.1 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.wrongdoc.yml +2 -2
- data/GIT-VERSION-GEN +1 -1
- data/KNOWN_ISSUES +17 -7
- data/README +2 -2
- data/Rakefile +3 -2
- data/examples/nginx.conf +12 -8
- data/ext/unicorn_http/unicorn_http.rl +12 -1
- data/ext/unicorn_http/unicorn_http_common.rl +1 -1
- data/lib/unicorn/app/exec_cgi.rb +25 -22
- data/lib/unicorn/configurator.rb +17 -11
- data/lib/unicorn/const.rb +2 -2
- data/lib/unicorn/http_response.rb +1 -1
- data/lib/unicorn/http_server.rb +0 -1
- data/lib/unicorn/socket_helper.rb +33 -19
- data/lib/unicorn/tee_input.rb +1 -1
- data/script/isolate_for_tests +1 -1
- data/t/bin/unused_listen +2 -15
- data/test/exec/test_exec.rb +1 -2
- data/test/rails/test_rails.rb +1 -1
- data/test/test_helper.rb +3 -16
- data/test/unit/test_configurator.rb +8 -0
- data/test/unit/test_http_parser.rb +294 -130
- data/test/unit/test_http_parser_ng.rb +147 -126
- data/test/unit/test_request.rb +3 -3
- data/test/unit/test_response.rb +0 -12
- data/test/unit/test_server.rb +4 -7
- data/test/unit/test_tee_input.rb +25 -21
- data/test/unit/test_upload.rb +10 -2
- data/unicorn.gemspec +2 -2
- metadata +12 -12
data/.wrongdoc.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
---
|
2
|
-
cgit_url: http://
|
3
|
-
git_url: git://
|
2
|
+
cgit_url: http://bogomips.org/unicorn.git
|
3
|
+
git_url: git://bogomips.org/unicorn.git
|
4
4
|
rdoc_url: http://unicorn.bogomips.org/
|
5
5
|
changelog_start: v1.1.5
|
6
6
|
merge_html:
|
data/GIT-VERSION-GEN
CHANGED
data/KNOWN_ISSUES
CHANGED
@@ -3,16 +3,17 @@
|
|
3
3
|
Occasionally odd {issues}[link:ISSUES.html] arise without a transparent or
|
4
4
|
acceptable solution. Those issues are documented here.
|
5
5
|
|
6
|
+
* Under some versions of Ruby 1.8, it is necessary to call +srand+ in an
|
7
|
+
after_fork hook to get correct random number generation.
|
8
|
+
|
9
|
+
See http://redmine.ruby-lang.org/issues/show/4338
|
10
|
+
|
6
11
|
* For notes on sandboxing tools such as Bundler or Isolate,
|
7
12
|
see the {Sandbox}[link:Sandbox.html] page.
|
8
13
|
|
9
|
-
*
|
10
|
-
|
11
|
-
|
12
|
-
Until then, it is advisable to call "Kernel.rand" in your after_fork
|
13
|
-
hook to reinitialize the random number generator.
|
14
|
-
|
15
|
-
See http://redmine.ruby-lang.org/issues/show/2962 for more details
|
14
|
+
* nginx with "sendfile on" under FreeBSD 8 is broken when
|
15
|
+
uploads are buffered to disk. Disabling sendfile is required to
|
16
|
+
work around this bug which should be fixed in newer versions of FreeBSD.
|
16
17
|
|
17
18
|
* When using "preload_app true", with apps using background threads
|
18
19
|
need to restart them in the after_fork hook because threads are never
|
@@ -22,6 +23,15 @@ acceptable solution. Those issues are documented here.
|
|
22
23
|
deadlocks. The core Ruby Logger class needlessly uses a MonitorMutex
|
23
24
|
which can be disabled with a {monkey patch}[link:examples/logger_mp_safe.rb]
|
24
25
|
|
26
|
+
== Known Issues (Old)
|
27
|
+
|
28
|
+
* Under Ruby 1.9.1, methods like Array#shuffle and Array#sample will
|
29
|
+
segfault if called after forking. Upgrade to Ruby 1.9.2 or call
|
30
|
+
"Kernel.rand" in your after_fork hook to reinitialize the random
|
31
|
+
number generator.
|
32
|
+
|
33
|
+
See http://redmine.ruby-lang.org/issues/show/2962 for more details
|
34
|
+
|
25
35
|
* Rails 2.3.2 bundles its own version of Rack. This may cause subtle
|
26
36
|
bugs when simultaneously loaded with the system-wide Rack Rubygem
|
27
37
|
which Unicorn depends on. Upgrading to Rails 2.3.4 (or later) is
|
data/README
CHANGED
@@ -85,13 +85,13 @@ You may also install it via RubyGems on Gemcutter:
|
|
85
85
|
You can get the latest source via git from the following locations
|
86
86
|
(these versions may not be stable):
|
87
87
|
|
88
|
-
git://
|
88
|
+
git://bogomips.org/unicorn.git
|
89
89
|
git://repo.or.cz/unicorn.git (mirror)
|
90
90
|
|
91
91
|
You may browse the code from the web and download the latest snapshot
|
92
92
|
tarballs here:
|
93
93
|
|
94
|
-
* http://
|
94
|
+
* http://bogomips.org/unicorn.git (cgit)
|
95
95
|
* http://repo.or.cz/w/unicorn.git (gitweb)
|
96
96
|
|
97
97
|
See the HACKING guide on how to contribute and build prerelease gems
|
data/Rakefile
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
autoload :Gem, 'rubygems'
|
3
|
+
require 'wrongdoc'
|
3
4
|
|
4
|
-
cgit_url =
|
5
|
-
git_url =
|
5
|
+
cgit_url = Wrongdoc.config[:cgit_url]
|
6
|
+
git_url = Wrongdoc.config[:git_url]
|
6
7
|
|
7
8
|
desc "post to RAA"
|
8
9
|
task :raa_update do
|
data/examples/nginx.conf
CHANGED
@@ -83,9 +83,9 @@ http {
|
|
83
83
|
}
|
84
84
|
|
85
85
|
server {
|
86
|
+
# enable one of the following if you're on Linux or FreeBSD
|
86
87
|
# listen 80 default deferred; # for Linux
|
87
88
|
# listen 80 default accept_filter=httpready; # for FreeBSD
|
88
|
-
listen 80 default;
|
89
89
|
|
90
90
|
client_max_body_size 4G;
|
91
91
|
server_name _;
|
@@ -98,7 +98,16 @@ http {
|
|
98
98
|
# path for static files
|
99
99
|
root /path/to/app/current/public;
|
100
100
|
|
101
|
-
|
101
|
+
# Prefer to serve static files directly from nginx to avoid unnecessary
|
102
|
+
# data copies from the application server.
|
103
|
+
#
|
104
|
+
# try_files directive appeared in in nginx 0.7.27 and has stabilized
|
105
|
+
# over time. Older versions of nginx (e.g. 0.6.x) requires
|
106
|
+
# "if (!-f $request_filename)" which was less efficient:
|
107
|
+
# http://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
|
108
|
+
try_files $uri/index.html $uri.html $uri @app;
|
109
|
+
|
110
|
+
location @app {
|
102
111
|
# an HTTP header important enough to have its own Wikipedia entry:
|
103
112
|
# http://en.wikipedia.org/wiki/X-Forwarded-For
|
104
113
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
@@ -122,12 +131,7 @@ http {
|
|
122
131
|
# clients, really.
|
123
132
|
# proxy_buffering off;
|
124
133
|
|
125
|
-
|
126
|
-
# *application* server like Unicorn/Rainbows! serve static files.
|
127
|
-
if (!-f $request_filename) {
|
128
|
-
proxy_pass http://app_server;
|
129
|
-
break;
|
130
|
-
}
|
134
|
+
proxy_pass http://app_server;
|
131
135
|
}
|
132
136
|
|
133
137
|
# Rails error pages
|
@@ -505,7 +505,18 @@ static void set_server_vars(VALUE env, VALUE *server_port)
|
|
505
505
|
if (!NIL_P(host)) {
|
506
506
|
char *host_ptr = RSTRING_PTR(host);
|
507
507
|
long host_len = RSTRING_LEN(host);
|
508
|
-
char *colon
|
508
|
+
char *colon;
|
509
|
+
|
510
|
+
if (*host_ptr == '[') { /* ipv6 address format */
|
511
|
+
char *rbracket = memchr(host_ptr + 1, ']', host_len - 1);
|
512
|
+
|
513
|
+
if (rbracket)
|
514
|
+
colon = (rbracket[1] == ':') ? rbracket + 1 : NULL;
|
515
|
+
else
|
516
|
+
colon = memchr(host_ptr + 1, ':', host_len - 1);
|
517
|
+
} else {
|
518
|
+
colon = memchr(host_ptr, ':', host_len);
|
519
|
+
}
|
509
520
|
|
510
521
|
if (colon) {
|
511
522
|
long port_start = colon - host_ptr + 1;
|
@@ -26,7 +26,7 @@
|
|
26
26
|
|
27
27
|
# URI schemes and absolute paths
|
28
28
|
scheme = ( "http"i ("s"i)? ) $downcase_char >mark %scheme;
|
29
|
-
hostname = (alnum | "-" | "." | "_")
|
29
|
+
hostname = ((alnum | "-" | "." | "_")+ | ("[" (":" | xdigit)+ "]"));
|
30
30
|
host_with_port = (hostname (":" digit*)?) >mark %host;
|
31
31
|
userinfo = ((unreserved | escape | ";" | ":" | "&" | "=" | "+")+ "@")*;
|
32
32
|
|
data/lib/unicorn/app/exec_cgi.rb
CHANGED
@@ -28,6 +28,24 @@ module Unicorn::App
|
|
28
28
|
SERVER_SOFTWARE
|
29
29
|
).map { |x| x.freeze } # frozen strings are faster for Hash assignments
|
30
30
|
|
31
|
+
class Body < Unicorn::TmpIO
|
32
|
+
def body_offset=(n)
|
33
|
+
sysseek(@body_offset = n)
|
34
|
+
end
|
35
|
+
|
36
|
+
def each(&block)
|
37
|
+
sysseek @body_offset
|
38
|
+
# don't use a preallocated buffer for sysread since we can't
|
39
|
+
# guarantee an actual socket is consuming the yielded string
|
40
|
+
# (or if somebody is pushing to an array for eventual concatenation
|
41
|
+
begin
|
42
|
+
yield sysread(CHUNK_SIZE)
|
43
|
+
rescue EOFError
|
44
|
+
break
|
45
|
+
end while true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
31
49
|
# Intializes the app, example of usage in a config.ru
|
32
50
|
# map "/cgit" do
|
33
51
|
# run Unicorn::App::ExecCgi.new("/path/to/cgit.cgi")
|
@@ -43,7 +61,7 @@ module Unicorn::App
|
|
43
61
|
|
44
62
|
# Calls the app
|
45
63
|
def call(env)
|
46
|
-
out, err =
|
64
|
+
out, err = Body.new, Unicorn::TmpIO.new
|
47
65
|
inp = force_file_input(env)
|
48
66
|
pid = fork { run_child(inp, out, err, env) }
|
49
67
|
inp.close
|
@@ -67,9 +85,9 @@ module Unicorn::App
|
|
67
85
|
ENV['GATEWAY_INTERFACE'] = 'CGI/1.1'
|
68
86
|
env.keys.grep(/^HTTP_/) { |key| ENV[key] = env[key] }
|
69
87
|
|
70
|
-
|
71
|
-
|
72
|
-
|
88
|
+
$stdin.reopen(inp)
|
89
|
+
$stdout.reopen(out)
|
90
|
+
$stderr.reopen(err)
|
73
91
|
exec(*args)
|
74
92
|
end
|
75
93
|
|
@@ -87,23 +105,7 @@ module Unicorn::App
|
|
87
105
|
offset = 4
|
88
106
|
end
|
89
107
|
offset += head.length
|
90
|
-
|
91
|
-
# Allows +out+ to be used as a Rack body.
|
92
|
-
out.instance_eval { class << self; self; end }.instance_eval {
|
93
|
-
define_method(:each) { |&blk|
|
94
|
-
sysseek(offset)
|
95
|
-
|
96
|
-
# don't use a preallocated buffer for sysread since we can't
|
97
|
-
# guarantee an actual socket is consuming the yielded string
|
98
|
-
# (or if somebody is pushing to an array for eventual concatenation
|
99
|
-
begin
|
100
|
-
blk.call(sysread(CHUNK_SIZE))
|
101
|
-
rescue EOFError
|
102
|
-
break
|
103
|
-
end while true
|
104
|
-
}
|
105
|
-
}
|
106
|
-
|
108
|
+
out.body_offset = offset
|
107
109
|
size -= offset
|
108
110
|
prev = nil
|
109
111
|
headers = Rack::Utils::HeaderHash.new
|
@@ -113,8 +115,9 @@ module Unicorn::App
|
|
113
115
|
when /^[ \t]/ then headers[prev] << "\n#{line}" if prev
|
114
116
|
end
|
115
117
|
end
|
118
|
+
status = headers.delete("Status") || 200
|
116
119
|
headers['Content-Length'] = size.to_s
|
117
|
-
[
|
120
|
+
[ status, headers, out ]
|
118
121
|
end
|
119
122
|
|
120
123
|
# ensures rack.input is a file handle that we can redirect stdin to
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -247,13 +247,13 @@ class Unicorn::Configurator
|
|
247
247
|
#
|
248
248
|
# Default: operating system defaults (usually Nagle's algorithm enabled)
|
249
249
|
#
|
250
|
-
# +:tcp_nopush+: enables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
|
250
|
+
# +:tcp_nopush+: enables/disables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
|
251
251
|
#
|
252
|
-
# This
|
253
|
-
#
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
252
|
+
# This is enabled by default as of Unicorn 3.4. This prevents partial
|
253
|
+
# TCP frames from being sent out and reduces wakeups in nginx if it is
|
254
|
+
# on a different machine. Since Unicorn is only designed for applications
|
255
|
+
# that send the response body quickly without keepalive, sockets will
|
256
|
+
# always be flushed on close to prevent delays.
|
257
257
|
#
|
258
258
|
# This has no effect on UNIX sockets.
|
259
259
|
#
|
@@ -455,7 +455,7 @@ class Unicorn::Configurator
|
|
455
455
|
# Rainbows!/Zbatery installations facing untrusted clients directly
|
456
456
|
# should set this to +false+. This is +true+ by default as Unicorn
|
457
457
|
# is designed to only sit behind trusted nginx proxies.
|
458
|
-
def trust_x_forwarded(bool)
|
458
|
+
def trust_x_forwarded(bool) # :nodoc:
|
459
459
|
set_bool(:trust_x_forwarded, bool)
|
460
460
|
end
|
461
461
|
|
@@ -473,10 +473,10 @@ class Unicorn::Configurator
|
|
473
473
|
File.expand_path(address)
|
474
474
|
when %r{\A(?:\*:)?(\d+)\z}
|
475
475
|
"0.0.0.0:#$1"
|
476
|
-
when %r{\A(
|
477
|
-
|
478
|
-
|
479
|
-
|
476
|
+
when %r{\A\[([a-fA-F0-9:]+)\]\z}, %r/\A((?:\d+\.){3}\d+)\z/
|
477
|
+
canonicalize_tcp($1, 80)
|
478
|
+
when %r{\A\[([a-fA-F0-9:]+)\]:(\d+)\z}, %r{\A(.*):(\d+)\z}
|
479
|
+
canonicalize_tcp($1, $2.to_i)
|
480
480
|
else
|
481
481
|
address
|
482
482
|
end
|
@@ -489,6 +489,12 @@ private
|
|
489
489
|
set[var] = n
|
490
490
|
end
|
491
491
|
|
492
|
+
def canonicalize_tcp(addr, port)
|
493
|
+
packed = Socket.pack_sockaddr_in(port, addr)
|
494
|
+
port, addr = Socket.unpack_sockaddr_in(packed)
|
495
|
+
/:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
|
496
|
+
end
|
497
|
+
|
492
498
|
def set_path(var, path) #:nodoc:
|
493
499
|
case path
|
494
500
|
when NilClass, String
|
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.
|
11
|
-
UNICORN_VERSION = "3.
|
10
|
+
# The current version of Unicorn, currently 3.4.0
|
11
|
+
UNICORN_VERSION = "3.4.0"
|
12
12
|
|
13
13
|
# default TCP listen host address (0.0.0.0, all interfaces)
|
14
14
|
DEFAULT_HOST = "0.0.0.0"
|
@@ -26,7 +26,7 @@ module Unicorn::HttpResponse
|
|
26
26
|
"Status: #{status}\r\n" \
|
27
27
|
"Connection: close\r\n"
|
28
28
|
headers.each do |key, value|
|
29
|
-
next if %r{\A(?:Date\z|
|
29
|
+
next if %r{\A(?:Date\z|Connection\z)}i =~ key
|
30
30
|
if value =~ /\n/
|
31
31
|
# avoiding blank, key-only cookies with /\n+/
|
32
32
|
buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -23,6 +23,10 @@ module Unicorn
|
|
23
23
|
|
24
24
|
# same default value as Mongrel
|
25
25
|
:backlog => 1024,
|
26
|
+
|
27
|
+
# since we don't do keepalive, we'll always flush-on-close and
|
28
|
+
# this saves packets for everyone.
|
29
|
+
:tcp_nopush => true,
|
26
30
|
}
|
27
31
|
#:startdoc:
|
28
32
|
|
@@ -45,17 +49,19 @@ module Unicorn
|
|
45
49
|
|
46
50
|
def set_tcp_sockopt(sock, opt)
|
47
51
|
# highly portable, but off by default because we don't do keepalive
|
48
|
-
if defined?(TCP_NODELAY)
|
52
|
+
if defined?(TCP_NODELAY)
|
53
|
+
val = opt[:tcp_nodelay]
|
54
|
+
val = DEFAULTS[:tcp_nodelay] if nil == val
|
49
55
|
sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
|
50
56
|
end
|
51
57
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
58
|
+
val = opt[:tcp_nopush]
|
59
|
+
val = DEFAULTS[:tcp_nopush] if nil == val
|
60
|
+
val = val ? 1 : 0
|
61
|
+
if defined?(TCP_CORK) # Linux
|
62
|
+
sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
|
63
|
+
elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is untested (FreeBSD)
|
64
|
+
sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
|
59
65
|
end
|
60
66
|
|
61
67
|
# No good reason to ever have deferred accepts off
|
@@ -64,17 +70,17 @@ module Unicorn
|
|
64
70
|
# this differs from nginx, since nginx doesn't allow us to
|
65
71
|
# configure the the timeout...
|
66
72
|
seconds = opt[:tcp_defer_accept]
|
67
|
-
seconds = DEFAULTS[:tcp_defer_accept] if seconds
|
73
|
+
seconds = DEFAULTS[:tcp_defer_accept] if [true,nil].include?(seconds)
|
68
74
|
seconds = 0 unless seconds # nil/false means disable this
|
69
75
|
sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, seconds)
|
70
76
|
elsif respond_to?(:accf_arg)
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
77
|
+
name = opt[:accept_filter]
|
78
|
+
name = DEFAULTS[:accept_filter] if nil == name
|
79
|
+
begin
|
80
|
+
sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
|
81
|
+
rescue => e
|
82
|
+
logger.error("#{sock_name(sock)} " \
|
83
|
+
"failed to set accept_filter=#{name} (#{e.inspect})")
|
78
84
|
end
|
79
85
|
end
|
80
86
|
end
|
@@ -130,7 +136,8 @@ module Unicorn
|
|
130
136
|
ensure
|
131
137
|
File.umask(old_umask)
|
132
138
|
end
|
133
|
-
elsif
|
139
|
+
elsif /\A(\d+\.\d+\.\d+\.\d+):(\d+)\z/ =~ address ||
|
140
|
+
/\A\[([a-fA-F0-9:]+)\]:(\d+)\z/ =~ address
|
134
141
|
Kgio::TCPServer.new($1, $2.to_i)
|
135
142
|
else
|
136
143
|
raise ArgumentError, "Don't know how to bind: #{address}"
|
@@ -139,6 +146,13 @@ module Unicorn
|
|
139
146
|
sock
|
140
147
|
end
|
141
148
|
|
149
|
+
# returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6
|
150
|
+
def tcp_name(sock)
|
151
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
152
|
+
/:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
|
153
|
+
end
|
154
|
+
module_function :tcp_name
|
155
|
+
|
142
156
|
# Returns the configuration name of a socket as a string. sock may
|
143
157
|
# be a string value, in which case it is returned as-is
|
144
158
|
# Warning: TCP sockets may not always return the name given to it.
|
@@ -148,10 +162,10 @@ module Unicorn
|
|
148
162
|
when UNIXServer
|
149
163
|
Socket.unpack_sockaddr_un(sock.getsockname)
|
150
164
|
when TCPServer
|
151
|
-
|
165
|
+
tcp_name(sock)
|
152
166
|
when Socket
|
153
167
|
begin
|
154
|
-
|
168
|
+
tcp_name(sock)
|
155
169
|
rescue ArgumentError
|
156
170
|
Socket.unpack_sockaddr_un(sock.getsockname)
|
157
171
|
end
|
data/lib/unicorn/tee_input.rb
CHANGED