unicorn 3.3.1 → 3.4.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/.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