unicorn 4.9.0 → 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/Application_Timeouts +3 -3
- data/DESIGN +2 -4
- data/Documentation/unicorn.1.txt +8 -5
- data/Documentation/unicorn_rails.1.txt +2 -2
- data/FAQ +17 -8
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +6 -1
- data/ISSUES +20 -28
- data/KNOWN_ISSUES +9 -9
- data/Links +14 -17
- data/PHILOSOPHY +0 -6
- data/README +22 -17
- data/SIGNALS +1 -1
- data/Sandbox +4 -4
- data/TUNING +11 -8
- data/bin/unicorn +1 -1
- data/bin/unicorn_rails +1 -1
- data/examples/nginx.conf +10 -11
- data/examples/unicorn.conf.rb +1 -4
- data/ext/unicorn_http/extconf.rb +1 -0
- data/ext/unicorn_http/httpdate.c +1 -1
- data/ext/unicorn_http/unicorn_http.rl +89 -156
- data/lib/unicorn.rb +10 -18
- data/lib/unicorn/configurator.rb +17 -31
- data/lib/unicorn/const.rb +2 -25
- data/lib/unicorn/http_request.rb +22 -33
- data/lib/unicorn/http_response.rb +14 -32
- data/lib/unicorn/http_server.rb +129 -122
- data/lib/unicorn/socket_helper.rb +36 -72
- data/lib/unicorn/stream_input.rb +3 -3
- data/lib/unicorn/tmpio.rb +0 -5
- data/lib/unicorn/util.rb +2 -1
- data/lib/unicorn/worker.rb +3 -15
- data/t/hijack.ru +2 -1
- data/t/t0200-rack-hijack.sh +5 -2
- data/test/exec/test_exec.rb +52 -0
- data/test/test_helper.rb +3 -2
- data/test/unit/test_http_parser_ng.rb +16 -114
- data/test/unit/test_response.rb +19 -16
- data/test/unit/test_socket_helper.rb +1 -1
- data/unicorn.gemspec +10 -1
- metadata +10 -23
- 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/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'
|
@@ -11,10 +10,10 @@ require 'kgio'
|
|
11
10
|
# enough functionality to service web application requests fast as possible.
|
12
11
|
# :startdoc:
|
13
12
|
|
14
|
-
#
|
15
|
-
# internals are subject to change.
|
13
|
+
# unicorn exposes very little of an user-visible API and most of its
|
14
|
+
# internals are subject to change. unicorn is designed to host Rack
|
16
15
|
# applications, so applications should be written against the Rack SPEC
|
17
|
-
# and not
|
16
|
+
# and not unicorn internals.
|
18
17
|
module Unicorn
|
19
18
|
|
20
19
|
# Raised inside TeeInput when a client closes the socket inside the
|
@@ -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
|
|
@@ -102,19 +100,13 @@ module Unicorn
|
|
102
100
|
|
103
101
|
# remove this when we only support Ruby >= 2.0
|
104
102
|
def self.pipe # :nodoc:
|
105
|
-
Kgio::Pipe.new.each { |io| io.
|
103
|
+
Kgio::Pipe.new.each { |io| io.close_on_exec = true }
|
106
104
|
end
|
107
105
|
# :startdoc:
|
108
106
|
end
|
109
107
|
# :enddoc:
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
require 'unicorn/configurator'
|
116
|
-
require 'unicorn/tmpio'
|
117
|
-
require 'unicorn/util'
|
118
|
-
require 'unicorn/http_response'
|
119
|
-
require 'unicorn/worker'
|
120
|
-
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,8 +1,7 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
require 'logger'
|
3
|
-
require 'unicorn/ssl_configurator'
|
4
3
|
|
5
|
-
# Implements a simple DSL for configuring a
|
4
|
+
# Implements a simple DSL for configuring a unicorn server.
|
6
5
|
#
|
7
6
|
# See http://unicorn.bogomips.org/examples/unicorn.conf.rb and
|
8
7
|
# http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb
|
@@ -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
|
@@ -48,7 +46,6 @@ class Unicorn::Configurator
|
|
48
46
|
:check_client_connection => false,
|
49
47
|
:rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
|
50
48
|
:client_body_buffer_size => Unicorn::Const::MAX_BODY,
|
51
|
-
:trust_x_forwarded => true,
|
52
49
|
}
|
53
50
|
#:startdoc:
|
54
51
|
|
@@ -257,6 +254,11 @@ class Unicorn::Configurator
|
|
257
254
|
#
|
258
255
|
# Default: 1024
|
259
256
|
#
|
257
|
+
# Note: with the Linux kernel, the net.core.somaxconn sysctl defaults
|
258
|
+
# to 128, capping this value to 128. Raising the sysctl allows a
|
259
|
+
# larger backlog (which may not be desirable with multiple,
|
260
|
+
# load-balanced machines).
|
261
|
+
#
|
260
262
|
# [:rcvbuf => bytes, :sndbuf => bytes]
|
261
263
|
#
|
262
264
|
# Maximum receive and send buffer sizes (in bytes) of sockets.
|
@@ -280,20 +282,19 @@ class Unicorn::Configurator
|
|
280
282
|
# Setting this to +true+ can make streaming responses in Rails 3.1
|
281
283
|
# appear more quickly at the cost of slightly higher bandwidth usage.
|
282
284
|
# The effect of this option is most visible if nginx is not used,
|
283
|
-
# but nginx remains highly recommended with
|
285
|
+
# but nginx remains highly recommended with unicorn.
|
284
286
|
#
|
285
287
|
# This has no effect on UNIX sockets.
|
286
288
|
#
|
287
|
-
# Default: +true+ (Nagle's algorithm disabled) in
|
288
|
-
#
|
289
|
-
# 3.x
|
289
|
+
# Default: +true+ (Nagle's algorithm disabled) in unicorn
|
290
|
+
# This defaulted to +false+ in unicorn 3.x
|
290
291
|
#
|
291
292
|
# [:tcp_nopush => true or false]
|
292
293
|
#
|
293
294
|
# Enables/disables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
|
294
295
|
#
|
295
296
|
# This prevents partial TCP frames from being sent out and reduces
|
296
|
-
# wakeups in nginx if it is on a different machine. Since
|
297
|
+
# wakeups in nginx if it is on a different machine. Since unicorn
|
297
298
|
# is only designed for applications that send the response body
|
298
299
|
# quickly without keepalive, sockets will always be flushed on close
|
299
300
|
# to prevent delays.
|
@@ -301,7 +302,7 @@ class Unicorn::Configurator
|
|
301
302
|
# This has no effect on UNIX sockets.
|
302
303
|
#
|
303
304
|
# Default: +false+
|
304
|
-
# This defaulted to +true+ in
|
305
|
+
# This defaulted to +true+ in unicorn 3.4 - 3.7
|
305
306
|
#
|
306
307
|
# [:ipv6only => true or false]
|
307
308
|
#
|
@@ -385,12 +386,10 @@ class Unicorn::Configurator
|
|
385
386
|
# and +false+ or +nil+ is synonymous for a value of zero.
|
386
387
|
#
|
387
388
|
# A value of +1+ is a good optimization for local networks
|
388
|
-
# and trusted clients.
|
389
|
-
#
|
390
|
-
# denial-of-service attacks. There is no good reason to ever
|
391
|
-
# disable this with a +zero+ value when serving HTTP.
|
389
|
+
# and trusted clients. There is no good reason to ever
|
390
|
+
# disable this with a +zero+ value with unicorn.
|
392
391
|
#
|
393
|
-
# Default: 1
|
392
|
+
# Default: 1
|
394
393
|
#
|
395
394
|
# [:accept_filter => String]
|
396
395
|
#
|
@@ -399,8 +398,7 @@ class Unicorn::Configurator
|
|
399
398
|
# This enables either the "dataready" or (default) "httpready"
|
400
399
|
# accept() filter under FreeBSD. This is intended as an
|
401
400
|
# optimization to reduce context switches with common GET/HEAD
|
402
|
-
# requests.
|
403
|
-
# some protection against certain denial-of-service attacks, too.
|
401
|
+
# requests.
|
404
402
|
#
|
405
403
|
# There is no good reason to change from the default.
|
406
404
|
#
|
@@ -556,18 +554,6 @@ class Unicorn::Configurator
|
|
556
554
|
set[:user] = [ user, group ]
|
557
555
|
end
|
558
556
|
|
559
|
-
# Sets whether or not the parser will trust X-Forwarded-Proto and
|
560
|
-
# X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly.
|
561
|
-
# Rainbows!/Zbatery installations facing untrusted clients directly
|
562
|
-
# should set this to +false+. This is +true+ by default as Unicorn
|
563
|
-
# is designed to only sit behind trusted nginx proxies.
|
564
|
-
#
|
565
|
-
# This has never been publically documented and is subject to removal
|
566
|
-
# in future releases.
|
567
|
-
def trust_x_forwarded(bool) # :nodoc:
|
568
|
-
set_bool(:trust_x_forwarded, bool)
|
569
|
-
end
|
570
|
-
|
571
557
|
# expands "unix:path/to/foo" to a socket relative to the current path
|
572
558
|
# expands pathnames of sockets if relative to "~" or "~username"
|
573
559
|
# expands "*:port and ":port" to "0.0.0.0:port"
|
@@ -601,7 +587,7 @@ private
|
|
601
587
|
def canonicalize_tcp(addr, port)
|
602
588
|
packed = Socket.pack_sockaddr_in(port, addr)
|
603
589
|
port, addr = Socket.unpack_sockaddr_in(packed)
|
604
|
-
|
590
|
+
addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
|
605
591
|
end
|
606
592
|
|
607
593
|
def set_path(var, path) #:nodoc:
|
@@ -657,7 +643,7 @@ private
|
|
657
643
|
raise ArgumentError, "rackup file (#{ru}) not readable"
|
658
644
|
|
659
645
|
# it could be a .rb file, too, we don't parse those manually
|
660
|
-
ru
|
646
|
+
ru.end_with?('.ru') or return
|
661
647
|
|
662
648
|
/^#\\(.*)/ =~ File.read(ru) or return
|
663
649
|
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
@@ -13,7 +13,8 @@ class Unicorn::HttpParser
|
|
13
13
|
"rack.multiprocess" => true,
|
14
14
|
"rack.multithread" => false,
|
15
15
|
"rack.run_once" => false,
|
16
|
-
"rack.version" => [1,
|
16
|
+
"rack.version" => [1, 2],
|
17
|
+
"rack.hijack?" => true,
|
17
18
|
"SCRIPT_NAME" => "",
|
18
19
|
|
19
20
|
# this is not in the Rack spec, but some apps may rely on it
|
@@ -22,12 +23,11 @@ class Unicorn::HttpParser
|
|
22
23
|
|
23
24
|
NULL_IO = StringIO.new("")
|
24
25
|
|
25
|
-
attr_accessor :response_start_sent
|
26
|
-
|
27
26
|
# :stopdoc:
|
28
27
|
# A frozen format for this is about 15% faster
|
29
|
-
|
30
|
-
|
28
|
+
# Drop these frozen strings when Ruby 2.2 becomes more prevalent,
|
29
|
+
# 2.2+ optimizes hash assignments when used with literal string keys
|
30
|
+
HTTP_RESPONSE_START = [ 'HTTP', '/1.1 ']
|
31
31
|
@@input_class = Unicorn::TeeInput
|
32
32
|
@@check_client_connection = false
|
33
33
|
|
@@ -73,7 +73,7 @@ class Unicorn::HttpParser
|
|
73
73
|
# identify the client for the immediate request to the server;
|
74
74
|
# that client may be a proxy, gateway, or other intermediary
|
75
75
|
# acting on behalf of the actual source client."
|
76
|
-
e[REMOTE_ADDR] = socket.kgio_addr
|
76
|
+
e['REMOTE_ADDR'] = socket.kgio_addr
|
77
77
|
|
78
78
|
# short circuit the common case with small GET requests first
|
79
79
|
socket.kgio_read!(16384, buf)
|
@@ -85,38 +85,27 @@ class Unicorn::HttpParser
|
|
85
85
|
|
86
86
|
# detect if the socket is valid by writing a partial response:
|
87
87
|
if @@check_client_connection && headers?
|
88
|
-
|
89
|
-
|
88
|
+
self.response_start_sent = true
|
89
|
+
HTTP_RESPONSE_START.each { |c| socket.write(c) }
|
90
90
|
end
|
91
91
|
|
92
|
-
e[
|
93
|
-
|
94
|
-
hijack_setup(e, socket)
|
95
|
-
e.merge!(DEFAULTS)
|
96
|
-
end
|
97
|
-
|
98
|
-
# Rack 1.5.0 (protocol version 1.2) adds hijack request support
|
99
|
-
if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
|
100
|
-
DEFAULTS["rack.hijack?"] = true
|
101
|
-
DEFAULTS["rack.version"] = [1, 2]
|
92
|
+
e['rack.input'] = 0 == content_length ?
|
93
|
+
NULL_IO : @@input_class.new(socket, self)
|
102
94
|
|
103
|
-
|
104
|
-
|
95
|
+
# for Rack hijacking in Rack 1.5 and later
|
96
|
+
e['unicorn.socket'] = socket
|
97
|
+
e['rack.hijack'] = self
|
105
98
|
|
106
|
-
|
107
|
-
|
108
|
-
end
|
99
|
+
e.merge!(DEFAULTS)
|
100
|
+
end
|
109
101
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
def hijack_setup(e, _)
|
116
|
-
end
|
102
|
+
# for rack.hijack, we respond to this method so no extra allocation
|
103
|
+
# of a proc object
|
104
|
+
def call
|
105
|
+
env['rack.hijack_io'] = env['unicorn.socket']
|
106
|
+
end
|
117
107
|
|
118
|
-
|
119
|
-
|
120
|
-
end
|
108
|
+
def hijacked?
|
109
|
+
env.include?('rack.hijack_io'.freeze)
|
121
110
|
end
|
122
111
|
end
|
@@ -10,66 +10,48 @@
|
|
10
10
|
# is the job of Rack, with the exception of the "Date" and "Status" header.
|
11
11
|
module Unicorn::HttpResponse
|
12
12
|
|
13
|
-
#
|
14
|
-
CODES = Rack::Utils::HTTP_STATUS_CODES.inject({}) { |hash,(code,msg)|
|
15
|
-
hash[code] = "#{code} #{msg}"
|
16
|
-
hash
|
17
|
-
}
|
18
|
-
CRLF = "\r\n"
|
19
|
-
|
13
|
+
# internal API, code will always be common-enough-for-even-old-Rack
|
20
14
|
def err_response(code, response_start_sent)
|
21
|
-
"#{response_start_sent ? '' : 'HTTP/1.1 '}
|
15
|
+
"#{response_start_sent ? '' : 'HTTP/1.1 '}" \
|
16
|
+
"#{code} #{Rack::Utils::HTTP_STATUS_CODES[code]}\r\n\r\n"
|
22
17
|
end
|
23
18
|
|
24
19
|
# writes the rack_response to socket as an HTTP response
|
25
20
|
def http_response_write(socket, status, headers, body,
|
26
21
|
response_start_sent=false)
|
27
|
-
status = CODES[status.to_i] || status
|
28
22
|
hijack = nil
|
29
23
|
|
30
|
-
http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
|
31
24
|
if headers
|
32
|
-
|
25
|
+
code = status.to_i
|
26
|
+
msg = Rack::Utils::HTTP_STATUS_CODES[code]
|
27
|
+
start = response_start_sent ? ''.freeze : 'HTTP/1.1 '.freeze
|
28
|
+
buf = "#{start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \
|
33
29
|
"Date: #{httpdate}\r\n" \
|
34
|
-
"Status: #{status}\r\n" \
|
35
30
|
"Connection: close\r\n"
|
36
31
|
headers.each do |key, value|
|
37
32
|
case key
|
38
|
-
when %r{\A(?:Date
|
33
|
+
when %r{\A(?:Date|Connection)\z}i
|
39
34
|
next
|
40
35
|
when "rack.hijack"
|
41
|
-
#
|
42
|
-
#
|
43
|
-
hijack =
|
36
|
+
# This should only be hit under Rack >= 1.5, as this was an illegal
|
37
|
+
# key in Rack < 1.5
|
38
|
+
hijack = value
|
44
39
|
else
|
45
|
-
if value
|
40
|
+
if value.include?("\n".freeze)
|
46
41
|
# avoiding blank, key-only cookies with /\n+/
|
47
|
-
|
42
|
+
value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
|
48
43
|
else
|
49
44
|
buf << "#{key}: #{value}\r\n"
|
50
45
|
end
|
51
46
|
end
|
52
47
|
end
|
53
|
-
socket.write(buf <<
|
48
|
+
socket.write(buf << "\r\n".freeze)
|
54
49
|
end
|
55
50
|
|
56
51
|
if hijack
|
57
|
-
body = nil # ensure we do not close body
|
58
52
|
hijack.call(socket)
|
59
53
|
else
|
60
54
|
body.each { |chunk| socket.write(chunk) }
|
61
55
|
end
|
62
|
-
ensure
|
63
|
-
body.respond_to?(:close) and body.close
|
64
|
-
end
|
65
|
-
|
66
|
-
# Rack 1.5.0 (protocol version 1.2) adds response hijacking support
|
67
|
-
if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
|
68
|
-
def hijack_prepare(value)
|
69
|
-
value
|
70
|
-
end
|
71
|
-
else
|
72
|
-
def hijack_prepare(_)
|
73
|
-
end
|
74
56
|
end
|
75
57
|
end
|
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.
|
@@ -8,50 +7,27 @@ require "unicorn/ssl_server"
|
|
8
7
|
#
|
9
8
|
# Users do not need to know the internals of this class, but reading the
|
10
9
|
# {source}[http://bogomips.org/unicorn.git/tree/lib/unicorn/http_server.rb]
|
11
|
-
# is education for programmers wishing to learn how
|
12
|
-
# See Unicorn::Configurator for information on how to configure
|
10
|
+
# is education for programmers wishing to learn how unicorn works.
|
11
|
+
# See Unicorn::Configurator for information on how to configure unicorn.
|
13
12
|
class Unicorn::HttpServer
|
14
13
|
# :stopdoc:
|
15
|
-
attr_accessor :app, :
|
14
|
+
attr_accessor :app, :timeout, :worker_processes,
|
16
15
|
:before_fork, :after_fork, :before_exec,
|
17
16
|
:listener_opts, :preload_app,
|
18
|
-
:
|
19
|
-
:master_pid, :config, :ready_pipe, :user
|
17
|
+
:orig_app, :config, :ready_pipe, :user
|
20
18
|
|
21
19
|
attr_reader :pid, :logger
|
22
20
|
include Unicorn::SocketHelper
|
23
21
|
include Unicorn::HttpResponse
|
24
|
-
include Unicorn::SSLServer
|
25
|
-
|
26
|
-
# backwards compatibility with 1.x
|
27
|
-
Worker = Unicorn::Worker
|
28
22
|
|
29
23
|
# all bound listener sockets
|
24
|
+
# note: this is public used by raindrops, but not recommended for use
|
25
|
+
# in new projects
|
30
26
|
LISTENERS = []
|
31
27
|
|
32
28
|
# listeners we have yet to bind
|
33
29
|
NEW_LISTENERS = []
|
34
30
|
|
35
|
-
# This hash maps PIDs to Workers
|
36
|
-
WORKERS = {}
|
37
|
-
|
38
|
-
# We use SELF_PIPE differently in the master and worker processes:
|
39
|
-
#
|
40
|
-
# * The master process never closes or reinitializes this once
|
41
|
-
# initialized. Signal handlers in the master process will write to
|
42
|
-
# it to wake up the master from IO.select in exactly the same manner
|
43
|
-
# djb describes in http://cr.yp.to/docs/selfpipe.html
|
44
|
-
#
|
45
|
-
# * The workers immediately close the pipe they inherit. See the
|
46
|
-
# Unicorn::Worker class for the pipe workers use.
|
47
|
-
SELF_PIPE = []
|
48
|
-
|
49
|
-
# signal queue used for self-piping
|
50
|
-
SIG_QUEUE = []
|
51
|
-
|
52
|
-
# list of signals we care about and trap in master.
|
53
|
-
QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
|
54
|
-
|
55
31
|
# :startdoc:
|
56
32
|
# We populate this at startup so we can figure out how to reexecute
|
57
33
|
# and upgrade the currently running instance of Unicorn
|
@@ -71,7 +47,7 @@ class Unicorn::HttpServer
|
|
71
47
|
#
|
72
48
|
# Unicorn::HttpServer::START_CTX[0] = "/home/bofh/2.2.0/bin/unicorn"
|
73
49
|
START_CTX = {
|
74
|
-
:argv => ARGV.map
|
50
|
+
:argv => ARGV.map(&:dup),
|
75
51
|
0 => $0.dup,
|
76
52
|
}
|
77
53
|
# We favor ENV['PWD'] since it is (usually) symlink aware for Capistrano
|
@@ -92,7 +68,7 @@ class Unicorn::HttpServer
|
|
92
68
|
def initialize(app, options = {})
|
93
69
|
@app = app
|
94
70
|
@request = Unicorn::HttpRequest.new
|
95
|
-
|
71
|
+
@reexec_pid = 0
|
96
72
|
options = options.dup
|
97
73
|
@ready_pipe = options.delete(:ready_pipe)
|
98
74
|
@init_listeners = options[:listeners] ? options[:listeners].dup : []
|
@@ -100,6 +76,19 @@ class Unicorn::HttpServer
|
|
100
76
|
self.config = Unicorn::Configurator.new(options)
|
101
77
|
self.listener_opts = {}
|
102
78
|
|
79
|
+
# We use @self_pipe differently in the master and worker processes:
|
80
|
+
#
|
81
|
+
# * The master process never closes or reinitializes this once
|
82
|
+
# initialized. Signal handlers in the master process will write to
|
83
|
+
# it to wake up the master from IO.select in exactly the same manner
|
84
|
+
# djb describes in http://cr.yp.to/docs/selfpipe.html
|
85
|
+
#
|
86
|
+
# * The workers immediately close the pipe they inherit. See the
|
87
|
+
# Unicorn::Worker class for the pipe workers use.
|
88
|
+
@self_pipe = []
|
89
|
+
@workers = {} # hash maps PIDs to Workers
|
90
|
+
@sig_queue = [] # signal queue used for self-piping
|
91
|
+
|
103
92
|
# we try inheriting listeners first, so we bind them later.
|
104
93
|
# we don't write the pid file until we've bound listeners in case
|
105
94
|
# unicorn was started twice by mistake. Even though our #pid= method
|
@@ -111,7 +100,10 @@ class Unicorn::HttpServer
|
|
111
100
|
# monitoring tools may also rely on pid files existing before we
|
112
101
|
# attempt to connect to the listener(s)
|
113
102
|
config.commit!(self, :skip => [:listeners, :pid])
|
114
|
-
|
103
|
+
@orig_app = app
|
104
|
+
# list of signals we care about and trap in master.
|
105
|
+
@queue_sigs = [
|
106
|
+
:WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
|
115
107
|
end
|
116
108
|
|
117
109
|
# Runs the thing. Returns self so you can run join on it
|
@@ -119,13 +111,13 @@ class Unicorn::HttpServer
|
|
119
111
|
inherit_listeners!
|
120
112
|
# this pipe is used to wake us up from select(2) in #join when signals
|
121
113
|
# are trapped. See trap_deferred.
|
122
|
-
|
114
|
+
@self_pipe.replace(Unicorn.pipe)
|
123
115
|
@master_pid = $$
|
124
116
|
|
125
117
|
# setup signal handlers before writing pid file in case people get
|
126
118
|
# trigger happy and send signals as soon as the pid file exists.
|
127
119
|
# Note that signals don't actually get handled until the #join method
|
128
|
-
|
120
|
+
@queue_sigs.each { |sig| trap(sig) { @sig_queue << sig; awaken_master } }
|
129
121
|
trap(:CHLD) { awaken_master }
|
130
122
|
|
131
123
|
# write pid early for Mongrel compatibility if we're not inheriting sockets
|
@@ -158,9 +150,6 @@ class Unicorn::HttpServer
|
|
158
150
|
|
159
151
|
LISTENERS.delete_if do |io|
|
160
152
|
if dead_names.include?(sock_name(io))
|
161
|
-
IO_PURGATORY.delete_if do |pio|
|
162
|
-
pio.fileno == io.fileno && (pio.close rescue nil).nil? # true
|
163
|
-
end
|
164
153
|
(io.close rescue nil).nil? # true
|
165
154
|
else
|
166
155
|
set_server_sockopt(io, listener_opts[sock_name(io)])
|
@@ -198,7 +187,7 @@ class Unicorn::HttpServer
|
|
198
187
|
if path
|
199
188
|
if x = valid_pid?(path)
|
200
189
|
return path if pid && path == pid && x == $$
|
201
|
-
if x == reexec_pid && pid
|
190
|
+
if x == @reexec_pid && pid.end_with?('.oldbin')
|
202
191
|
logger.warn("will not set pid=#{path} while reexec-ed "\
|
203
192
|
"child is running PID:#{x}")
|
204
193
|
return
|
@@ -241,7 +230,7 @@ class Unicorn::HttpServer
|
|
241
230
|
begin
|
242
231
|
io = bind_listen(address, opt)
|
243
232
|
unless Kgio::TCPServer === io || Kgio::UNIXServer === io
|
244
|
-
|
233
|
+
io.autoclose = false
|
245
234
|
io = server_cast(io)
|
246
235
|
end
|
247
236
|
logger.info "listening on addr=#{sock_name(io)} fd=#{io.fileno}"
|
@@ -267,7 +256,7 @@ class Unicorn::HttpServer
|
|
267
256
|
# is signalling us too often.
|
268
257
|
def join
|
269
258
|
respawn = true
|
270
|
-
last_check =
|
259
|
+
last_check = time_now
|
271
260
|
|
272
261
|
proc_name 'master'
|
273
262
|
logger.info "master process ready" # test_exec.rb relies on this message
|
@@ -281,11 +270,11 @@ class Unicorn::HttpServer
|
|
281
270
|
end
|
282
271
|
begin
|
283
272
|
reap_all_workers
|
284
|
-
case
|
273
|
+
case @sig_queue.shift
|
285
274
|
when nil
|
286
275
|
# avoid murdering workers after our master process (or the
|
287
276
|
# machine) comes out of suspend/hibernation
|
288
|
-
if (last_check + @timeout) >= (last_check =
|
277
|
+
if (last_check + @timeout) >= (last_check = time_now)
|
289
278
|
sleep_time = murder_lazy_workers
|
290
279
|
else
|
291
280
|
sleep_time = @timeout/2.0 + 1
|
@@ -306,13 +295,13 @@ class Unicorn::HttpServer
|
|
306
295
|
when :USR2 # exec binary, stay alive in case something went wrong
|
307
296
|
reexec
|
308
297
|
when :WINCH
|
309
|
-
if
|
298
|
+
if $stdin.tty?
|
299
|
+
logger.info "SIGWINCH ignored because we're not daemonized"
|
300
|
+
else
|
310
301
|
respawn = false
|
311
302
|
logger.info "gracefully stopping all workers"
|
312
303
|
soft_kill_each_worker(:QUIT)
|
313
304
|
self.worker_processes = 0
|
314
|
-
else
|
315
|
-
logger.info "SIGWINCH ignored because we're not daemonized"
|
316
305
|
end
|
317
306
|
when :TTIN
|
318
307
|
respawn = true
|
@@ -339,8 +328,8 @@ class Unicorn::HttpServer
|
|
339
328
|
# Terminates all workers, but does not exit master process
|
340
329
|
def stop(graceful = true)
|
341
330
|
self.listeners = []
|
342
|
-
limit =
|
343
|
-
until
|
331
|
+
limit = time_now + timeout
|
332
|
+
until @workers.empty? || time_now > limit
|
344
333
|
if graceful
|
345
334
|
soft_kill_each_worker(:QUIT)
|
346
335
|
else
|
@@ -369,14 +358,6 @@ class Unicorn::HttpServer
|
|
369
358
|
Unicorn::TeeInput.client_body_buffer_size = bytes
|
370
359
|
end
|
371
360
|
|
372
|
-
def trust_x_forwarded
|
373
|
-
Unicorn::HttpParser.trust_x_forwarded?
|
374
|
-
end
|
375
|
-
|
376
|
-
def trust_x_forwarded=(bool)
|
377
|
-
Unicorn::HttpParser.trust_x_forwarded = bool
|
378
|
-
end
|
379
|
-
|
380
361
|
def check_client_connection
|
381
362
|
Unicorn::HttpRequest.check_client_connection
|
382
363
|
end
|
@@ -389,17 +370,17 @@ class Unicorn::HttpServer
|
|
389
370
|
|
390
371
|
# wait for a signal hander to wake us up and then consume the pipe
|
391
372
|
def master_sleep(sec)
|
373
|
+
@self_pipe[0].kgio_wait_readable(sec) or return
|
392
374
|
# 11 bytes is the maximum string length which can be embedded within
|
393
375
|
# the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
|
394
376
|
# Most reads are only one byte here and uncommon, so it's not worth a
|
395
377
|
# persistent buffer, either:
|
396
|
-
|
397
|
-
SELF_PIPE[0].kgio_tryread(11)
|
378
|
+
@self_pipe[0].kgio_tryread(11)
|
398
379
|
end
|
399
380
|
|
400
381
|
def awaken_master
|
401
382
|
return if $$ != @master_pid
|
402
|
-
|
383
|
+
@self_pipe[1].kgio_trywrite('.') # wakeup master process from select
|
403
384
|
end
|
404
385
|
|
405
386
|
# reaps all unreaped workers
|
@@ -407,13 +388,13 @@ class Unicorn::HttpServer
|
|
407
388
|
begin
|
408
389
|
wpid, status = Process.waitpid2(-1, Process::WNOHANG)
|
409
390
|
wpid or return
|
410
|
-
if reexec_pid == wpid
|
391
|
+
if @reexec_pid == wpid
|
411
392
|
logger.error "reaped #{status.inspect} exec()-ed"
|
412
|
-
|
393
|
+
@reexec_pid = 0
|
413
394
|
self.pid = pid.chomp('.oldbin') if pid
|
414
395
|
proc_name 'master'
|
415
396
|
else
|
416
|
-
worker =
|
397
|
+
worker = @workers.delete(wpid) and worker.close rescue nil
|
417
398
|
m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
|
418
399
|
status.success? ? logger.info(m) : logger.error(m)
|
419
400
|
end
|
@@ -424,13 +405,13 @@ class Unicorn::HttpServer
|
|
424
405
|
|
425
406
|
# reexecutes the START_CTX with a new binary
|
426
407
|
def reexec
|
427
|
-
if reexec_pid > 0
|
408
|
+
if @reexec_pid > 0
|
428
409
|
begin
|
429
|
-
Process.kill(0, reexec_pid)
|
430
|
-
logger.error "reexec-ed child already running PID
|
410
|
+
Process.kill(0, @reexec_pid)
|
411
|
+
logger.error "reexec-ed child already running PID:#@reexec_pid"
|
431
412
|
return
|
432
413
|
rescue Errno::ESRCH
|
433
|
-
|
414
|
+
@reexec_pid = 0
|
434
415
|
end
|
435
416
|
end
|
436
417
|
|
@@ -448,13 +429,10 @@ class Unicorn::HttpServer
|
|
448
429
|
end
|
449
430
|
end
|
450
431
|
|
451
|
-
|
432
|
+
@reexec_pid = fork do
|
452
433
|
listener_fds = {}
|
453
434
|
LISTENERS.each do |sock|
|
454
|
-
|
455
|
-
# Ruby that sets FD_CLOEXEC by default on new file descriptors
|
456
|
-
# ref: http://redmine.ruby-lang.org/issues/5041
|
457
|
-
sock.close_on_exec = false if sock.respond_to?(:close_on_exec=)
|
435
|
+
sock.close_on_exec = false
|
458
436
|
listener_fds[sock.fileno] = sock
|
459
437
|
end
|
460
438
|
ENV['UNICORN_FD'] = listener_fds.keys.join(',')
|
@@ -467,13 +445,13 @@ class Unicorn::HttpServer
|
|
467
445
|
(3..1024).each do |io|
|
468
446
|
next if listener_fds.include?(io)
|
469
447
|
io = IO.for_fd(io) rescue next
|
470
|
-
|
471
|
-
io.
|
448
|
+
io.autoclose = false
|
449
|
+
io.close_on_exec = true
|
472
450
|
end
|
473
451
|
|
474
452
|
# exec(command, hash) works in at least 1.9.1+, but will only be
|
475
453
|
# required in 1.9.4/2.0.0 at earliest.
|
476
|
-
cmd << listener_fds
|
454
|
+
cmd << listener_fds
|
477
455
|
logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
|
478
456
|
before_exec.call(self)
|
479
457
|
exec(*cmd)
|
@@ -484,8 +462,8 @@ class Unicorn::HttpServer
|
|
484
462
|
# forcibly terminate all workers that haven't checked in in timeout seconds. The timeout is implemented using an unlinked File
|
485
463
|
def murder_lazy_workers
|
486
464
|
next_sleep = @timeout - 1
|
487
|
-
now =
|
488
|
-
|
465
|
+
now = time_now.to_i
|
466
|
+
@workers.dup.each_pair do |wpid, worker|
|
489
467
|
tick = worker.tick
|
490
468
|
0 == tick and next # skip workers that haven't processed any clients
|
491
469
|
diff = now - tick
|
@@ -503,12 +481,13 @@ class Unicorn::HttpServer
|
|
503
481
|
end
|
504
482
|
|
505
483
|
def after_fork_internal
|
506
|
-
|
484
|
+
@self_pipe.each(&:close).clear # this is master-only, now
|
507
485
|
@ready_pipe.close if @ready_pipe
|
508
486
|
Unicorn::Configurator::RACKUP.clear
|
509
487
|
@ready_pipe = @init_listeners = @before_exec = @before_fork = nil
|
510
488
|
|
511
|
-
|
489
|
+
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/36450
|
490
|
+
srand # remove in unicorn 6
|
512
491
|
|
513
492
|
# The OpenSSL PRNG is seeded with only the pid, and apps with frequently
|
514
493
|
# dying workers can recycle pids
|
@@ -518,11 +497,11 @@ class Unicorn::HttpServer
|
|
518
497
|
def spawn_missing_workers
|
519
498
|
worker_nr = -1
|
520
499
|
until (worker_nr += 1) == @worker_processes
|
521
|
-
|
522
|
-
worker = Worker.new(worker_nr)
|
500
|
+
@workers.value?(worker_nr) and next
|
501
|
+
worker = Unicorn::Worker.new(worker_nr)
|
523
502
|
before_fork.call(self, worker)
|
524
503
|
if pid = fork
|
525
|
-
|
504
|
+
@workers[pid] = worker
|
526
505
|
worker.atfork_parent
|
527
506
|
else
|
528
507
|
after_fork_internal
|
@@ -536,9 +515,9 @@ class Unicorn::HttpServer
|
|
536
515
|
end
|
537
516
|
|
538
517
|
def maintain_worker_count
|
539
|
-
(off =
|
518
|
+
(off = @workers.size - worker_processes) == 0 and return
|
540
519
|
off < 0 and return spawn_missing_workers
|
541
|
-
|
520
|
+
@workers.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
|
542
521
|
end
|
543
522
|
|
544
523
|
# if we get any error, try to write something back to the client
|
@@ -566,29 +545,37 @@ class Unicorn::HttpServer
|
|
566
545
|
rescue
|
567
546
|
end
|
568
547
|
|
569
|
-
def
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
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)
|
575
557
|
end
|
576
558
|
|
577
559
|
# once a client is accepted, it is processed in its entirety here
|
578
560
|
# in 3 easy steps: read request, call app, write app response
|
579
561
|
def process_client(client)
|
580
562
|
status, headers, body = @app.call(env = @request.read(client))
|
581
|
-
return if @request.hijacked?
|
582
563
|
|
583
|
-
|
584
|
-
client.write(expect_100_response)
|
585
|
-
env.delete(Unicorn::Const::HTTP_EXPECT)
|
586
|
-
status, headers, body = @app.call(env)
|
564
|
+
begin
|
587
565
|
return if @request.hijacked?
|
566
|
+
|
567
|
+
if 100 == status.to_i
|
568
|
+
e100_response_write(client, env)
|
569
|
+
status, headers, body = @app.call(env)
|
570
|
+
return if @request.hijacked?
|
571
|
+
end
|
572
|
+
@request.headers? or headers = nil
|
573
|
+
http_response_write(client, status, headers, body,
|
574
|
+
@request.response_start_sent)
|
575
|
+
ensure
|
576
|
+
body.respond_to?(:close) and body.close
|
588
577
|
end
|
589
|
-
|
590
|
-
http_response_write(client, status, headers, body,
|
591
|
-
@request.response_start_sent)
|
578
|
+
|
592
579
|
unless client.closed? # rack.hijack may've close this for us
|
593
580
|
client.shutdown # in case of fork() in Rack app
|
594
581
|
client.close # flush and uncork socket immediately, no keepalive
|
@@ -597,9 +584,6 @@ class Unicorn::HttpServer
|
|
597
584
|
handle_error(client, e)
|
598
585
|
end
|
599
586
|
|
600
|
-
EXIT_SIGS = [ :QUIT, :TERM, :INT ]
|
601
|
-
WORKER_QUEUE_SIGS = QUEUE_SIGS - EXIT_SIGS
|
602
|
-
|
603
587
|
def nuke_listeners!(readers)
|
604
588
|
# only called from the worker, ordering is important here
|
605
589
|
tmp = readers.dup
|
@@ -614,23 +598,23 @@ class Unicorn::HttpServer
|
|
614
598
|
def init_worker_process(worker)
|
615
599
|
worker.atfork_child
|
616
600
|
# we'll re-trap :QUIT later for graceful shutdown iff we accept clients
|
617
|
-
|
618
|
-
exit!(0)
|
619
|
-
|
601
|
+
exit_sigs = [ :QUIT, :TERM, :INT ]
|
602
|
+
exit_sigs.each { |sig| trap(sig) { exit!(0) } }
|
603
|
+
exit!(0) if (@sig_queue & exit_sigs)[0]
|
604
|
+
(@queue_sigs - exit_sigs).each { |sig| trap(sig, nil) }
|
620
605
|
trap(:CHLD, 'DEFAULT')
|
621
|
-
|
606
|
+
@sig_queue.clear
|
622
607
|
proc_name "worker[#{worker.nr}]"
|
623
608
|
START_CTX.clear
|
624
|
-
|
609
|
+
@workers.clear
|
625
610
|
|
626
611
|
after_fork.call(self, worker) # can drop perms and create listeners
|
627
|
-
LISTENERS.each { |sock| sock.
|
612
|
+
LISTENERS.each { |sock| sock.close_on_exec = true }
|
628
613
|
|
629
614
|
worker.user(*user) if user.kind_of?(Array) && ! worker.switched
|
630
615
|
self.timeout /= 2.0 # halve it for select()
|
631
616
|
@config = nil
|
632
617
|
build_app! unless preload_app
|
633
|
-
ssl_enable!
|
634
618
|
@after_fork = @listener_opts = @orig_app = nil
|
635
619
|
readers = LISTENERS.dup
|
636
620
|
readers << worker
|
@@ -651,7 +635,7 @@ class Unicorn::HttpServer
|
|
651
635
|
# for connections and doesn't die until the parent dies (or is
|
652
636
|
# given a INT, QUIT, or TERM signal)
|
653
637
|
def worker_loop(worker)
|
654
|
-
ppid = master_pid
|
638
|
+
ppid = @master_pid
|
655
639
|
readers = init_worker_process(worker)
|
656
640
|
nr = 0 # this becomes negative if we need to reopen logs
|
657
641
|
|
@@ -665,7 +649,7 @@ class Unicorn::HttpServer
|
|
665
649
|
begin
|
666
650
|
nr < 0 and reopen_worker_logs(worker.nr)
|
667
651
|
nr = 0
|
668
|
-
worker.tick =
|
652
|
+
worker.tick = time_now.to_i
|
669
653
|
tmp = ready.dup
|
670
654
|
while sock = tmp.shift
|
671
655
|
# Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
|
@@ -673,7 +657,7 @@ class Unicorn::HttpServer
|
|
673
657
|
if client = sock.kgio_tryaccept
|
674
658
|
process_client(client)
|
675
659
|
nr += 1
|
676
|
-
worker.tick =
|
660
|
+
worker.tick = time_now.to_i
|
677
661
|
end
|
678
662
|
break if nr < 0
|
679
663
|
end
|
@@ -690,7 +674,7 @@ class Unicorn::HttpServer
|
|
690
674
|
ppid == Process.ppid or return
|
691
675
|
|
692
676
|
# timeout used so we can detect parent death:
|
693
|
-
worker.tick =
|
677
|
+
worker.tick = time_now.to_i
|
694
678
|
ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
|
695
679
|
rescue => e
|
696
680
|
redo if nr < 0 && readers[0]
|
@@ -702,17 +686,17 @@ class Unicorn::HttpServer
|
|
702
686
|
# is no longer running.
|
703
687
|
def kill_worker(signal, wpid)
|
704
688
|
Process.kill(signal, wpid)
|
705
|
-
|
706
|
-
|
689
|
+
rescue Errno::ESRCH
|
690
|
+
worker = @workers.delete(wpid) and worker.close rescue nil
|
707
691
|
end
|
708
692
|
|
709
693
|
# delivers a signal to each worker
|
710
694
|
def kill_each_worker(signal)
|
711
|
-
|
695
|
+
@workers.keys.each { |wpid| kill_worker(signal, wpid) }
|
712
696
|
end
|
713
697
|
|
714
698
|
def soft_kill_each_worker(signal)
|
715
|
-
|
699
|
+
@workers.each_value { |worker| worker.soft_kill(signal) }
|
716
700
|
end
|
717
701
|
|
718
702
|
# unlinks a PID file at given +path+ if it contains the current PID
|
@@ -745,7 +729,7 @@ class Unicorn::HttpServer
|
|
745
729
|
config.commit!(self)
|
746
730
|
soft_kill_each_worker(:QUIT)
|
747
731
|
Unicorn::Util.reopen_logs
|
748
|
-
self.app = orig_app
|
732
|
+
self.app = @orig_app
|
749
733
|
build_app! if preload_app
|
750
734
|
logger.info "done reloading config_file=#{config.config_file}"
|
751
735
|
rescue StandardError, LoadError, SyntaxError => e
|
@@ -782,12 +766,23 @@ class Unicorn::HttpServer
|
|
782
766
|
def inherit_listeners!
|
783
767
|
# inherit sockets from parents, they need to be plain Socket objects
|
784
768
|
# before they become Kgio::UNIXServer or Kgio::TCPServer
|
785
|
-
inherited = ENV['UNICORN_FD'].to_s.split(
|
769
|
+
inherited = ENV['UNICORN_FD'].to_s.split(',')
|
770
|
+
|
771
|
+
# emulate sd_listen_fds() for systemd
|
772
|
+
sd_pid, sd_fds = ENV.values_at('LISTEN_PID', 'LISTEN_FDS')
|
773
|
+
if sd_pid.to_i == $$ # n.b. $$ can never be zero
|
774
|
+
# 3 = SD_LISTEN_FDS_START
|
775
|
+
inherited.concat((3...(3 + sd_fds.to_i)).to_a)
|
776
|
+
end
|
777
|
+
# to ease debugging, we will not unset LISTEN_PID and LISTEN_FDS
|
778
|
+
|
779
|
+
inherited.map! do |fd|
|
786
780
|
io = Socket.for_fd(fd.to_i)
|
781
|
+
io.autoclose = false
|
782
|
+
io = server_cast(io)
|
787
783
|
set_server_sockopt(io, listener_opts[sock_name(io)])
|
788
|
-
|
789
|
-
|
790
|
-
server_cast(io)
|
784
|
+
logger.info "inherited addr=#{sock_name(io)} fd=#{io.fileno}"
|
785
|
+
io
|
791
786
|
end
|
792
787
|
|
793
788
|
config_listeners = config[:listeners].dup
|
@@ -810,8 +805,20 @@ class Unicorn::HttpServer
|
|
810
805
|
# call only after calling inherit_listeners!
|
811
806
|
# This binds any listeners we did NOT inherit from the parent
|
812
807
|
def bind_new_listeners!
|
813
|
-
NEW_LISTENERS.each { |addr| listen(addr) }
|
808
|
+
NEW_LISTENERS.each { |addr| listen(addr) }.clear
|
814
809
|
raise ArgumentError, "no listeners" if LISTENERS.empty?
|
815
|
-
|
810
|
+
end
|
811
|
+
|
812
|
+
# try to use the monotonic clock in Ruby >= 2.1, it is immune to clock
|
813
|
+
# offset adjustments and generates less garbage (Float vs Time object)
|
814
|
+
begin
|
815
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
816
|
+
def time_now
|
817
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
818
|
+
end
|
819
|
+
rescue NameError, NoMethodError
|
820
|
+
def time_now # Ruby <= 2.0
|
821
|
+
Time.now
|
822
|
+
end
|
816
823
|
end
|
817
824
|
end
|