unicorn-camilo 4.8.2.5.19 → 5.0.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.
- checksums.yaml +4 -4
- data/.document +0 -1
- data/.gitignore +2 -2
- data/{.wrongdoc.yml → .olddoc.yml} +7 -2
- data/Documentation/unicorn.1.txt +9 -2
- data/Documentation/unicorn_rails.1.txt +2 -2
- data/FAQ +9 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +31 -46
- data/HACKING +13 -27
- data/ISSUES +80 -16
- data/KNOWN_ISSUES +10 -10
- data/Links +10 -7
- data/PHILOSOPHY +1 -1
- data/README +8 -13
- data/Rakefile +0 -44
- data/Sandbox +1 -1
- data/TUNING +6 -3
- data/archive/.gitignore +3 -0
- data/archive/slrnpull.conf +4 -0
- data/bin/unicorn +1 -1
- data/bin/unicorn_rails +1 -1
- data/examples/unicorn.conf.rb +11 -0
- data/ext/unicorn_http/httpdate.c +1 -1
- data/ext/unicorn_http/unicorn_http.rl +48 -150
- data/lib/unicorn.rb +9 -15
- data/lib/unicorn/configurator.rb +3 -20
- data/lib/unicorn/const.rb +2 -25
- data/lib/unicorn/http_request.rb +4 -1
- data/lib/unicorn/http_response.rb +1 -3
- data/lib/unicorn/http_server.rb +85 -86
- data/lib/unicorn/socket_helper.rb +33 -67
- data/lib/unicorn/tee_input.rb +8 -1
- data/lib/unicorn/tmpio.rb +2 -4
- data/lib/unicorn/util.rb +1 -0
- data/lib/unicorn/worker.rb +1 -13
- data/t/GNUmakefile +1 -5
- data/t/README +1 -1
- data/t/t0002-parser-error.sh +3 -3
- data/test/exec/test_exec.rb +1 -1
- data/test/test_helper.rb +2 -2
- data/test/unit/test_http_parser.rb +3 -3
- data/test/unit/test_http_parser_ng.rb +8 -117
- data/test/unit/test_request.rb +1 -1
- data/test/unit/test_response.rb +3 -9
- data/test/unit/test_server.rb +3 -3
- data/test/unit/test_signals.rb +1 -1
- data/test/unit/test_socket_helper.rb +5 -5
- data/test/unit/test_tee_input.rb +10 -0
- data/test/unit/test_upload.rb +1 -1
- data/test/unit/test_util.rb +1 -1
- data/unicorn.gemspec +7 -10
- metadata +15 -33
- data/examples/git.ru +0 -13
- data/lib/unicorn/app/exec_cgi.rb +0 -154
- data/lib/unicorn/app/inetd.rb +0 -109
- data/lib/unicorn/ssl_client.rb +0 -11
- data/lib/unicorn/ssl_configurator.rb +0 -104
- data/lib/unicorn/ssl_server.rb +0 -42
- data/local.mk.sample +0 -59
- data/script/isolate_for_tests +0 -31
- data/t/t0016-trust-x-forwarded-false.sh +0 -30
- data/t/t0017-trust-x-forwarded-true.sh +0 -30
- data/test/unit/test_http_parser_xftrust.rb +0 -38
- data/test/unit/test_sni_hostnames.rb +0 -47
data/lib/unicorn.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
require 'fcntl'
|
3
2
|
require 'etc'
|
4
3
|
require 'stringio'
|
5
4
|
require 'rack'
|
@@ -22,8 +21,7 @@ module Unicorn
|
|
22
21
|
# since there is nothing in the application stack that is responsible
|
23
22
|
# for client shutdowns/disconnects. This exception is visible to Rack
|
24
23
|
# applications unless PrereadInput middleware is loaded.
|
25
|
-
|
26
|
-
end
|
24
|
+
ClientShutdown = Class.new(EOFError)
|
27
25
|
|
28
26
|
# :stopdoc:
|
29
27
|
|
@@ -67,6 +65,7 @@ module Unicorn
|
|
67
65
|
use Rack::CommonLogger, $stderr
|
68
66
|
use Rack::ShowExceptions
|
69
67
|
use Rack::Lint
|
68
|
+
use Rack::TempfileReaper if Rack.const_defined?(:TempfileReaper)
|
70
69
|
run inner_app
|
71
70
|
end.to_app
|
72
71
|
when "deployment"
|
@@ -74,6 +73,7 @@ module Unicorn
|
|
74
73
|
use Rack::ContentLength
|
75
74
|
use Rack::Chunked
|
76
75
|
use Rack::CommonLogger, $stderr
|
76
|
+
use Rack::TempfileReaper if Rack.const_defined?(:TempfileReaper)
|
77
77
|
run inner_app
|
78
78
|
end.to_app
|
79
79
|
else
|
@@ -100,19 +100,13 @@ module Unicorn
|
|
100
100
|
|
101
101
|
# remove this when we only support Ruby >= 2.0
|
102
102
|
def self.pipe # :nodoc:
|
103
|
-
Kgio::Pipe.new.each { |io| io.
|
103
|
+
Kgio::Pipe.new.each { |io| io.close_on_exec = true }
|
104
104
|
end
|
105
105
|
# :startdoc:
|
106
106
|
end
|
107
107
|
# :enddoc:
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
require 'unicorn/configurator'
|
114
|
-
require 'unicorn/tmpio'
|
115
|
-
require 'unicorn/util'
|
116
|
-
require 'unicorn/http_response'
|
117
|
-
require 'unicorn/worker'
|
118
|
-
require 'unicorn/http_server'
|
108
|
+
|
109
|
+
%w(const socket_helper stream_input tee_input http_request configurator
|
110
|
+
tmpio util http_response worker http_server).each do |s|
|
111
|
+
require_relative "unicorn/#{s}"
|
112
|
+
end
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
require 'logger'
|
3
|
-
require 'unicorn/ssl_configurator'
|
4
3
|
|
5
4
|
# Implements a simple DSL for configuring a \Unicorn server.
|
6
5
|
#
|
@@ -13,7 +12,6 @@ require 'unicorn/ssl_configurator'
|
|
13
12
|
# See the link:/TUNING.html document for more information on tuning unicorn.
|
14
13
|
class Unicorn::Configurator
|
15
14
|
include Unicorn
|
16
|
-
include Unicorn::SSLConfigurator
|
17
15
|
|
18
16
|
# :stopdoc:
|
19
17
|
attr_accessor :set, :config_file, :after_reload
|
@@ -49,7 +47,6 @@ class Unicorn::Configurator
|
|
49
47
|
:check_client_connection => false,
|
50
48
|
:rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
|
51
49
|
:client_body_buffer_size => Unicorn::Const::MAX_BODY,
|
52
|
-
:trust_x_forwarded => true,
|
53
50
|
}
|
54
51
|
#:startdoc:
|
55
52
|
|
@@ -172,7 +169,7 @@ class Unicorn::Configurator
|
|
172
169
|
set_hook(:before_exec, block_given? ? block : args[0], 1)
|
173
170
|
end
|
174
171
|
|
175
|
-
# Sets the before_murder hook to a
|
172
|
+
# Sets the before_murder hook to a given Proc object. This Proc object
|
176
173
|
# will be called by the master process before killing a lazy worker
|
177
174
|
# with SIGKILL, the point of this callback is NOT to prevent killing
|
178
175
|
# but to provide an instrumentation hook
|
@@ -333,8 +330,6 @@ class Unicorn::Configurator
|
|
333
330
|
# to receive IPv4 queries on dual-stack systems. A separate IPv4-only
|
334
331
|
# listener is required if this is true.
|
335
332
|
#
|
336
|
-
# This option is only available for Ruby 1.9.2 and later.
|
337
|
-
#
|
338
333
|
# Enabling this option for the IPv6-only listener and having a
|
339
334
|
# separate IPv4 listener is recommended if you wish to support IPv6
|
340
335
|
# on the same TCP port. Otherwise, the value of \env[\"REMOTE_ADDR\"]
|
@@ -582,18 +577,6 @@ class Unicorn::Configurator
|
|
582
577
|
set[:user] = [ user, group ]
|
583
578
|
end
|
584
579
|
|
585
|
-
# Sets whether or not the parser will trust X-Forwarded-Proto and
|
586
|
-
# X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly.
|
587
|
-
# Rainbows!/Zbatery installations facing untrusted clients directly
|
588
|
-
# should set this to +false+. This is +true+ by default as Unicorn
|
589
|
-
# is designed to only sit behind trusted nginx proxies.
|
590
|
-
#
|
591
|
-
# This has never been publically documented and is subject to removal
|
592
|
-
# in future releases.
|
593
|
-
def trust_x_forwarded(bool) # :nodoc:
|
594
|
-
set_bool(:trust_x_forwarded, bool)
|
595
|
-
end
|
596
|
-
|
597
580
|
# expands "unix:path/to/foo" to a socket relative to the current path
|
598
581
|
# expands pathnames of sockets if relative to "~" or "~username"
|
599
582
|
# expands "*:port and ":port" to "0.0.0.0:port"
|
@@ -627,7 +610,7 @@ private
|
|
627
610
|
def canonicalize_tcp(addr, port)
|
628
611
|
packed = Socket.pack_sockaddr_in(port, addr)
|
629
612
|
port, addr = Socket.unpack_sockaddr_in(packed)
|
630
|
-
|
613
|
+
addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
|
631
614
|
end
|
632
615
|
|
633
616
|
def set_path(var, path) #:nodoc:
|
@@ -683,7 +666,7 @@ private
|
|
683
666
|
raise ArgumentError, "rackup file (#{ru}) not readable"
|
684
667
|
|
685
668
|
# it could be a .rb file, too, we don't parse those manually
|
686
|
-
ru
|
669
|
+
ru.end_with?('.ru') or return
|
687
670
|
|
688
671
|
/^#\\(.*)/ =~ File.read(ru) or return
|
689
672
|
RACKUP[:optparse].parse!($1.split(/\s+/))
|
data/lib/unicorn/const.rb
CHANGED
@@ -1,12 +1,6 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
|
3
|
-
# :
|
4
|
-
# Frequently used constants when constructing requests or responses.
|
5
|
-
# Many times the constant just refers to a string with the same
|
6
|
-
# contents. Using these constants gave about a 3% to 10% performance
|
7
|
-
# improvement over using the strings directly. Symbols did not really
|
8
|
-
# improve things much compared to constants.
|
9
|
-
module Unicorn::Const
|
3
|
+
module Unicorn::Const # :nodoc:
|
10
4
|
# default TCP listen host address (0.0.0.0, all interfaces)
|
11
5
|
DEFAULT_HOST = "0.0.0.0"
|
12
6
|
|
@@ -23,22 +17,5 @@ module Unicorn::Const
|
|
23
17
|
# temporary file for reading (112 kilobytes). This is the default
|
24
18
|
# value of client_body_buffer_size.
|
25
19
|
MAX_BODY = 1024 * 112
|
26
|
-
|
27
|
-
# :stopdoc:
|
28
|
-
# common errors we'll send back
|
29
|
-
# (N.B. these are not used by unicorn, but we won't drop them until
|
30
|
-
# unicorn 5.x to avoid breaking Rainbows!).
|
31
|
-
ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
|
32
|
-
ERROR_414_RESPONSE = "HTTP/1.1 414 Request-URI Too Long\r\n\r\n"
|
33
|
-
ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"
|
34
|
-
ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
|
35
|
-
|
36
|
-
EXPECT_100_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
|
37
|
-
EXPECT_100_RESPONSE_SUFFIXED = "100 Continue\r\n\r\nHTTP/1.1 "
|
38
|
-
|
39
|
-
HTTP_RESPONSE_START = ['HTTP', '/1.1 ']
|
40
|
-
HTTP_EXPECT = "HTTP_EXPECT"
|
41
|
-
|
42
|
-
# :startdoc:
|
43
20
|
end
|
44
|
-
|
21
|
+
require_relative 'version'
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -26,8 +26,11 @@ class Unicorn::HttpParser
|
|
26
26
|
|
27
27
|
# :stopdoc:
|
28
28
|
# A frozen format for this is about 15% faster
|
29
|
+
# Drop these frozen strings when Ruby 2.2 becomes more prevalent,
|
30
|
+
# 2.2+ optimizes hash assignments when used with literal string keys
|
29
31
|
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
30
32
|
RACK_INPUT = 'rack.input'.freeze
|
33
|
+
HTTP_RESPONSE_START = [ 'HTTP', '/1.1 ']
|
31
34
|
@@input_class = Unicorn::TeeInput
|
32
35
|
@@check_client_connection = false
|
33
36
|
|
@@ -86,7 +89,7 @@ class Unicorn::HttpParser
|
|
86
89
|
# detect if the socket is valid by writing a partial response:
|
87
90
|
if @@check_client_connection && headers?
|
88
91
|
@response_start_sent = true
|
89
|
-
|
92
|
+
HTTP_RESPONSE_START.each { |c| socket.write(c) }
|
90
93
|
end
|
91
94
|
|
92
95
|
e[RACK_INPUT] = 0 == content_length ?
|
@@ -24,14 +24,12 @@ module Unicorn::HttpResponse
|
|
24
24
|
# writes the rack_response to socket as an HTTP response
|
25
25
|
def http_response_write(socket, status, headers, body,
|
26
26
|
response_start_sent=false)
|
27
|
-
status = CODES[status.to_i] || status
|
28
27
|
hijack = nil
|
29
28
|
|
30
29
|
http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
|
31
30
|
if headers
|
32
|
-
buf = "#{http_response_start}#{status}\r\n" \
|
31
|
+
buf = "#{http_response_start}#{CODES[status.to_i] || status}\r\n" \
|
33
32
|
"Date: #{httpdate}\r\n" \
|
34
|
-
"Status: #{status}\r\n" \
|
35
33
|
"Connection: close\r\n"
|
36
34
|
headers.each do |key, value|
|
37
35
|
case key
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
require "unicorn/ssl_server"
|
3
2
|
|
4
3
|
# This is the process manager of Unicorn. This manages worker
|
5
4
|
# processes which in turn handle the I/O and application process.
|
@@ -22,10 +21,6 @@ class Unicorn::HttpServer
|
|
22
21
|
attr_reader :pid, :logger
|
23
22
|
include Unicorn::SocketHelper
|
24
23
|
include Unicorn::HttpResponse
|
25
|
-
include Unicorn::SSLServer
|
26
|
-
|
27
|
-
# backwards compatibility with 1.x
|
28
|
-
Worker = Unicorn::Worker
|
29
24
|
|
30
25
|
# all bound listener sockets
|
31
26
|
LISTENERS = []
|
@@ -33,23 +28,6 @@ class Unicorn::HttpServer
|
|
33
28
|
# listeners we have yet to bind
|
34
29
|
NEW_LISTENERS = []
|
35
30
|
|
36
|
-
# This hash maps PIDs to Workers
|
37
|
-
WORKERS = {}
|
38
|
-
|
39
|
-
# We use SELF_PIPE differently in the master and worker processes:
|
40
|
-
#
|
41
|
-
# * The master process never closes or reinitializes this once
|
42
|
-
# initialized. Signal handlers in the master process will write to
|
43
|
-
# it to wake up the master from IO.select in exactly the same manner
|
44
|
-
# djb describes in http://cr.yp.to/docs/selfpipe.html
|
45
|
-
#
|
46
|
-
# * The workers immediately close the pipe they inherit. See the
|
47
|
-
# Unicorn::Worker class for the pipe workers use.
|
48
|
-
SELF_PIPE = []
|
49
|
-
|
50
|
-
# signal queue used for self-piping
|
51
|
-
SIG_QUEUE = []
|
52
|
-
|
53
31
|
# list of signals we care about and trap in master.
|
54
32
|
QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
|
55
33
|
|
@@ -70,9 +48,9 @@ class Unicorn::HttpServer
|
|
70
48
|
# you can set the following in your Unicorn config file, HUP and then
|
71
49
|
# continue with the traditional USR2 + QUIT upgrade steps:
|
72
50
|
#
|
73
|
-
# Unicorn::HttpServer::START_CTX[0] = "/home/bofh/
|
51
|
+
# Unicorn::HttpServer::START_CTX[0] = "/home/bofh/2.2.0/bin/unicorn"
|
74
52
|
START_CTX = {
|
75
|
-
:argv => ARGV.map
|
53
|
+
:argv => ARGV.map(&:dup),
|
76
54
|
0 => $0.dup,
|
77
55
|
}
|
78
56
|
# We favor ENV['PWD'] since it is (usually) symlink aware for Capistrano
|
@@ -101,6 +79,19 @@ class Unicorn::HttpServer
|
|
101
79
|
self.config = Unicorn::Configurator.new(options)
|
102
80
|
self.listener_opts = {}
|
103
81
|
|
82
|
+
# We use @self_pipe differently in the master and worker processes:
|
83
|
+
#
|
84
|
+
# * The master process never closes or reinitializes this once
|
85
|
+
# initialized. Signal handlers in the master process will write to
|
86
|
+
# it to wake up the master from IO.select in exactly the same manner
|
87
|
+
# djb describes in http://cr.yp.to/docs/selfpipe.html
|
88
|
+
#
|
89
|
+
# * The workers immediately close the pipe they inherit. See the
|
90
|
+
# Unicorn::Worker class for the pipe workers use.
|
91
|
+
@self_pipe = []
|
92
|
+
@workers = {} # hash maps PIDs to Workers
|
93
|
+
@sig_queue = [] # signal queue used for self-piping
|
94
|
+
|
104
95
|
# we try inheriting listeners first, so we bind them later.
|
105
96
|
# we don't write the pid file until we've bound listeners in case
|
106
97
|
# unicorn was started twice by mistake. Even though our #pid= method
|
@@ -120,13 +111,13 @@ class Unicorn::HttpServer
|
|
120
111
|
inherit_listeners!
|
121
112
|
# this pipe is used to wake us up from select(2) in #join when signals
|
122
113
|
# are trapped. See trap_deferred.
|
123
|
-
|
114
|
+
@self_pipe.replace(Unicorn.pipe)
|
124
115
|
@master_pid = $$
|
125
116
|
|
126
117
|
# setup signal handlers before writing pid file in case people get
|
127
118
|
# trigger happy and send signals as soon as the pid file exists.
|
128
119
|
# Note that signals don't actually get handled until the #join method
|
129
|
-
QUEUE_SIGS.each { |sig| trap(sig) {
|
120
|
+
QUEUE_SIGS.each { |sig| trap(sig) { @sig_queue << sig; awaken_master } }
|
130
121
|
trap(:CHLD) { awaken_master }
|
131
122
|
|
132
123
|
# write pid early for Mongrel compatibility if we're not inheriting sockets
|
@@ -159,9 +150,6 @@ class Unicorn::HttpServer
|
|
159
150
|
|
160
151
|
LISTENERS.delete_if do |io|
|
161
152
|
if dead_names.include?(sock_name(io))
|
162
|
-
IO_PURGATORY.delete_if do |pio|
|
163
|
-
pio.fileno == io.fileno && (pio.close rescue nil).nil? # true
|
164
|
-
end
|
165
153
|
(io.close rescue nil).nil? # true
|
166
154
|
else
|
167
155
|
set_server_sockopt(io, listener_opts[sock_name(io)])
|
@@ -199,7 +187,7 @@ class Unicorn::HttpServer
|
|
199
187
|
if path
|
200
188
|
if x = valid_pid?(path)
|
201
189
|
return path if pid && path == pid && x == $$
|
202
|
-
if x == reexec_pid && pid
|
190
|
+
if x == reexec_pid && pid.end_with?('.oldbin')
|
203
191
|
logger.warn("will not set pid=#{path} while reexec-ed "\
|
204
192
|
"child is running PID:#{x}")
|
205
193
|
return
|
@@ -242,7 +230,7 @@ class Unicorn::HttpServer
|
|
242
230
|
begin
|
243
231
|
io = bind_listen(address, opt)
|
244
232
|
unless Kgio::TCPServer === io || Kgio::UNIXServer === io
|
245
|
-
|
233
|
+
io.autoclose = false
|
246
234
|
io = server_cast(io)
|
247
235
|
end
|
248
236
|
logger.info "listening on addr=#{sock_name(io)} fd=#{io.fileno}"
|
@@ -268,21 +256,25 @@ class Unicorn::HttpServer
|
|
268
256
|
# is signalling us too often.
|
269
257
|
def join
|
270
258
|
respawn = true
|
271
|
-
last_check =
|
259
|
+
last_check = time_now
|
272
260
|
|
273
261
|
proc_name 'master'
|
274
262
|
logger.info "master process ready" # test_exec.rb relies on this message
|
275
263
|
if @ready_pipe
|
276
|
-
|
264
|
+
begin
|
265
|
+
@ready_pipe.syswrite($$.to_s)
|
266
|
+
rescue => e
|
267
|
+
logger.warn("grandparent died too soon?: #{e.message} (#{e.class})")
|
268
|
+
end
|
277
269
|
@ready_pipe = @ready_pipe.close rescue nil
|
278
270
|
end
|
279
271
|
begin
|
280
272
|
reap_all_workers
|
281
|
-
case
|
273
|
+
case @sig_queue.shift
|
282
274
|
when nil
|
283
275
|
# avoid murdering workers after our master process (or the
|
284
276
|
# machine) comes out of suspend/hibernation
|
285
|
-
if (last_check + @timeout) >= (last_check =
|
277
|
+
if (last_check + @timeout) >= (last_check = time_now)
|
286
278
|
sleep_time = murder_lazy_workers
|
287
279
|
else
|
288
280
|
sleep_time = @timeout/2.0 + 1
|
@@ -336,8 +328,8 @@ class Unicorn::HttpServer
|
|
336
328
|
# Terminates all workers, but does not exit master process
|
337
329
|
def stop(graceful = true)
|
338
330
|
self.listeners = []
|
339
|
-
limit =
|
340
|
-
until
|
331
|
+
limit = time_now + timeout
|
332
|
+
until @workers.empty? || time_now > limit
|
341
333
|
if graceful
|
342
334
|
soft_kill_each_worker(:QUIT)
|
343
335
|
else
|
@@ -366,14 +358,6 @@ class Unicorn::HttpServer
|
|
366
358
|
Unicorn::TeeInput.client_body_buffer_size = bytes
|
367
359
|
end
|
368
360
|
|
369
|
-
def trust_x_forwarded
|
370
|
-
Unicorn::HttpParser.trust_x_forwarded?
|
371
|
-
end
|
372
|
-
|
373
|
-
def trust_x_forwarded=(bool)
|
374
|
-
Unicorn::HttpParser.trust_x_forwarded = bool
|
375
|
-
end
|
376
|
-
|
377
361
|
def check_client_connection
|
378
362
|
Unicorn::HttpRequest.check_client_connection
|
379
363
|
end
|
@@ -386,13 +370,17 @@ class Unicorn::HttpServer
|
|
386
370
|
|
387
371
|
# wait for a signal hander to wake us up and then consume the pipe
|
388
372
|
def master_sleep(sec)
|
389
|
-
|
390
|
-
|
373
|
+
@self_pipe[0].kgio_wait_readable(sec) or return
|
374
|
+
# 11 bytes is the maximum string length which can be embedded within
|
375
|
+
# the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
|
376
|
+
# Most reads are only one byte here and uncommon, so it's not worth a
|
377
|
+
# persistent buffer, either:
|
378
|
+
@self_pipe[0].kgio_tryread(11)
|
391
379
|
end
|
392
380
|
|
393
381
|
def awaken_master
|
394
382
|
return if $$ != @master_pid
|
395
|
-
|
383
|
+
@self_pipe[1].kgio_trywrite('.') # wakeup master process from select
|
396
384
|
end
|
397
385
|
|
398
386
|
# reaps all unreaped workers
|
@@ -406,7 +394,7 @@ class Unicorn::HttpServer
|
|
406
394
|
self.pid = pid.chomp('.oldbin') if pid
|
407
395
|
proc_name 'master'
|
408
396
|
else
|
409
|
-
worker =
|
397
|
+
worker = @workers.delete(wpid) and worker.close rescue nil
|
410
398
|
m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
|
411
399
|
status.success? ? logger.info(m) : logger.error(m)
|
412
400
|
end
|
@@ -444,10 +432,7 @@ class Unicorn::HttpServer
|
|
444
432
|
self.reexec_pid = fork do
|
445
433
|
listener_fds = {}
|
446
434
|
LISTENERS.each do |sock|
|
447
|
-
|
448
|
-
# Ruby that sets FD_CLOEXEC by default on new file descriptors
|
449
|
-
# ref: http://redmine.ruby-lang.org/issues/5041
|
450
|
-
sock.close_on_exec = false if sock.respond_to?(:close_on_exec=)
|
435
|
+
sock.close_on_exec = false
|
451
436
|
listener_fds[sock.fileno] = sock
|
452
437
|
end
|
453
438
|
ENV['UNICORN_FD'] = listener_fds.keys.join(',')
|
@@ -460,13 +445,13 @@ class Unicorn::HttpServer
|
|
460
445
|
(3..1024).each do |io|
|
461
446
|
next if listener_fds.include?(io)
|
462
447
|
io = IO.for_fd(io) rescue next
|
463
|
-
|
464
|
-
io.
|
448
|
+
io.autoclose = false
|
449
|
+
io.close_on_exec = true
|
465
450
|
end
|
466
451
|
|
467
452
|
# exec(command, hash) works in at least 1.9.1+, but will only be
|
468
453
|
# required in 1.9.4/2.0.0 at earliest.
|
469
|
-
cmd << listener_fds
|
454
|
+
cmd << listener_fds
|
470
455
|
logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
|
471
456
|
before_exec.call(self)
|
472
457
|
exec(*cmd)
|
@@ -477,8 +462,8 @@ class Unicorn::HttpServer
|
|
477
462
|
# forcibly terminate all workers that haven't checked in in timeout seconds. The timeout is implemented using an unlinked File
|
478
463
|
def murder_lazy_workers
|
479
464
|
next_sleep = @timeout - 1
|
480
|
-
now =
|
481
|
-
|
465
|
+
now = time_now.to_i
|
466
|
+
@workers.dup.each_pair do |wpid, worker|
|
482
467
|
tick = worker.tick
|
483
468
|
0 == tick and next # skip workers that haven't processed any clients
|
484
469
|
diff = now - tick
|
@@ -497,7 +482,7 @@ class Unicorn::HttpServer
|
|
497
482
|
end
|
498
483
|
|
499
484
|
def after_fork_internal
|
500
|
-
|
485
|
+
@self_pipe.each(&:close).clear # this is master-only, now
|
501
486
|
@ready_pipe.close if @ready_pipe
|
502
487
|
Unicorn::Configurator::RACKUP.clear
|
503
488
|
@ready_pipe = @init_listeners = @before_exec = @before_fork = nil
|
@@ -512,11 +497,11 @@ class Unicorn::HttpServer
|
|
512
497
|
def spawn_missing_workers
|
513
498
|
worker_nr = -1
|
514
499
|
until (worker_nr += 1) == @worker_processes
|
515
|
-
|
516
|
-
worker = Worker.new(worker_nr)
|
500
|
+
@workers.value?(worker_nr) and next
|
501
|
+
worker = Unicorn::Worker.new(worker_nr)
|
517
502
|
before_fork.call(self, worker)
|
518
503
|
if pid = fork
|
519
|
-
|
504
|
+
@workers[pid] = worker
|
520
505
|
worker.atfork_parent
|
521
506
|
else
|
522
507
|
after_fork_internal
|
@@ -530,9 +515,9 @@ class Unicorn::HttpServer
|
|
530
515
|
end
|
531
516
|
|
532
517
|
def maintain_worker_count
|
533
|
-
(off =
|
518
|
+
(off = @workers.size - worker_processes) == 0 and return
|
534
519
|
off < 0 and return spawn_missing_workers
|
535
|
-
|
520
|
+
@workers.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
|
536
521
|
end
|
537
522
|
|
538
523
|
# if we get any error, try to write something back to the client
|
@@ -560,12 +545,15 @@ class Unicorn::HttpServer
|
|
560
545
|
rescue
|
561
546
|
end
|
562
547
|
|
563
|
-
def
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
548
|
+
def e100_response_write(client, env)
|
549
|
+
# We use String#freeze to avoid allocations under Ruby 2.1+
|
550
|
+
# Not many users hit this code path, so it's better to reduce the
|
551
|
+
# constant table sizes even for 1.9.3-2.0 users who'll hit extra
|
552
|
+
# allocations here.
|
553
|
+
client.write(@request.response_start_sent ?
|
554
|
+
"100 Continue\r\n\r\nHTTP/1.1 ".freeze :
|
555
|
+
"HTTP/1.1 100 Continue\r\n\r\n".freeze)
|
556
|
+
env.delete('HTTP_EXPECT'.freeze)
|
569
557
|
end
|
570
558
|
|
571
559
|
# once a client is accepted, it is processed in its entirety here
|
@@ -575,8 +563,7 @@ class Unicorn::HttpServer
|
|
575
563
|
return if @request.hijacked?
|
576
564
|
|
577
565
|
if 100 == status.to_i
|
578
|
-
client
|
579
|
-
env.delete(Unicorn::Const::HTTP_EXPECT)
|
566
|
+
e100_response_write(client, env)
|
580
567
|
status, headers, body = @app.call(env)
|
581
568
|
return if @request.hijacked?
|
582
569
|
end
|
@@ -609,22 +596,21 @@ class Unicorn::HttpServer
|
|
609
596
|
worker.atfork_child
|
610
597
|
# we'll re-trap :QUIT later for graceful shutdown iff we accept clients
|
611
598
|
EXIT_SIGS.each { |sig| trap(sig) { exit!(0) } }
|
612
|
-
exit!(0) if (
|
599
|
+
exit!(0) if (@sig_queue & EXIT_SIGS)[0]
|
613
600
|
WORKER_QUEUE_SIGS.each { |sig| trap(sig, nil) }
|
614
601
|
trap(:CHLD, 'DEFAULT')
|
615
|
-
|
602
|
+
@sig_queue.clear
|
616
603
|
proc_name "worker[#{worker.nr}]"
|
617
604
|
START_CTX.clear
|
618
|
-
|
605
|
+
@workers.clear
|
619
606
|
|
620
607
|
after_fork.call(self, worker) # can drop perms and create listeners
|
621
|
-
LISTENERS.each { |sock| sock.
|
608
|
+
LISTENERS.each { |sock| sock.close_on_exec = true }
|
622
609
|
|
623
610
|
worker.user(*user) if user.kind_of?(Array) && ! worker.switched
|
624
611
|
self.timeout /= 2.0 # halve it for select()
|
625
612
|
@config = nil
|
626
613
|
build_app! unless preload_app
|
627
|
-
ssl_enable!
|
628
614
|
@after_fork = @listener_opts = @orig_app = nil
|
629
615
|
readers = LISTENERS.dup
|
630
616
|
readers << worker
|
@@ -659,7 +645,7 @@ class Unicorn::HttpServer
|
|
659
645
|
begin
|
660
646
|
nr < 0 and reopen_worker_logs(worker.nr)
|
661
647
|
nr = 0
|
662
|
-
worker.tick =
|
648
|
+
worker.tick = time_now.to_i
|
663
649
|
tmp = ready.dup
|
664
650
|
while sock = tmp.shift
|
665
651
|
# Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
|
@@ -667,7 +653,7 @@ class Unicorn::HttpServer
|
|
667
653
|
if client = sock.kgio_tryaccept
|
668
654
|
process_client(client)
|
669
655
|
nr += 1
|
670
|
-
worker.tick =
|
656
|
+
worker.tick = time_now.to_i
|
671
657
|
end
|
672
658
|
break if nr < 0
|
673
659
|
end
|
@@ -684,7 +670,7 @@ class Unicorn::HttpServer
|
|
684
670
|
ppid == Process.ppid or return
|
685
671
|
|
686
672
|
# timeout used so we can detect parent death:
|
687
|
-
worker.tick =
|
673
|
+
worker.tick = time_now.to_i
|
688
674
|
ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
|
689
675
|
rescue => e
|
690
676
|
redo if nr < 0 && readers[0]
|
@@ -696,17 +682,17 @@ class Unicorn::HttpServer
|
|
696
682
|
# is no longer running.
|
697
683
|
def kill_worker(signal, wpid)
|
698
684
|
Process.kill(signal, wpid)
|
699
|
-
|
700
|
-
|
685
|
+
rescue Errno::ESRCH
|
686
|
+
worker = @workers.delete(wpid) and worker.close rescue nil
|
701
687
|
end
|
702
688
|
|
703
689
|
# delivers a signal to each worker
|
704
690
|
def kill_each_worker(signal)
|
705
|
-
|
691
|
+
@workers.keys.each { |wpid| kill_worker(signal, wpid) }
|
706
692
|
end
|
707
693
|
|
708
694
|
def soft_kill_each_worker(signal)
|
709
|
-
|
695
|
+
@workers.each_value { |worker| worker.soft_kill(signal) }
|
710
696
|
end
|
711
697
|
|
712
698
|
# unlinks a PID file at given +path+ if it contains the current PID
|
@@ -776,10 +762,10 @@ class Unicorn::HttpServer
|
|
776
762
|
def inherit_listeners!
|
777
763
|
# inherit sockets from parents, they need to be plain Socket objects
|
778
764
|
# before they become Kgio::UNIXServer or Kgio::TCPServer
|
779
|
-
inherited = ENV['UNICORN_FD'].to_s.split(
|
765
|
+
inherited = ENV['UNICORN_FD'].to_s.split(',').map do |fd|
|
780
766
|
io = Socket.for_fd(fd.to_i)
|
781
767
|
set_server_sockopt(io, listener_opts[sock_name(io)])
|
782
|
-
|
768
|
+
io.autoclose = false
|
783
769
|
logger.info "inherited addr=#{sock_name(io)} fd=#{fd}"
|
784
770
|
server_cast(io)
|
785
771
|
end
|
@@ -808,4 +794,17 @@ class Unicorn::HttpServer
|
|
808
794
|
raise ArgumentError, "no listeners" if LISTENERS.empty?
|
809
795
|
NEW_LISTENERS.clear
|
810
796
|
end
|
797
|
+
|
798
|
+
# try to use the monotonic clock in Ruby >= 2.1, it is immune to clock
|
799
|
+
# offset adjustments and generates less garbage (Float vs Time object)
|
800
|
+
begin
|
801
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
802
|
+
def time_now
|
803
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
804
|
+
end
|
805
|
+
rescue NameError, NoMethodError
|
806
|
+
def time_now # Ruby <= 2.0
|
807
|
+
Time.now
|
808
|
+
end
|
809
|
+
end
|
811
810
|
end
|