puma 5.6.5-java → 6.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +102 -11
- data/README.md +21 -17
- data/bin/puma-wild +1 -1
- data/docs/compile_options.md +34 -0
- data/docs/fork_worker.md +1 -3
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/extconf.rb +11 -8
- data/ext/puma_http11/http11_parser.c +1 -1
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +2 -2
- data/ext/puma_http11/http11_parser.rl +2 -2
- data/ext/puma_http11/http11_parser_common.rl +2 -2
- data/ext/puma_http11/mini_ssl.c +36 -15
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +156 -53
- data/ext/puma_http11/puma_http11.c +17 -9
- data/lib/puma/app/status.rb +3 -3
- data/lib/puma/binder.rb +36 -42
- data/lib/puma/cli.rb +11 -17
- data/lib/puma/client.rb +22 -12
- data/lib/puma/cluster/worker.rb +13 -11
- data/lib/puma/cluster/worker_handle.rb +4 -1
- data/lib/puma/cluster.rb +28 -25
- data/lib/puma/configuration.rb +74 -58
- data/lib/puma/const.rb +14 -18
- data/lib/puma/control_cli.rb +3 -6
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +93 -52
- data/lib/puma/error_logger.rb +17 -9
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +29 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +96 -156
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +23 -12
- data/lib/puma/minissl.rb +82 -11
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/rack/builder.rb +4 -4
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +3 -3
- data/lib/puma/request.rb +292 -161
- data/lib/puma/runner.rb +41 -20
- data/lib/puma/server.rb +53 -66
- data/lib/puma/single.rb +10 -10
- data/lib/puma/state_file.rb +1 -4
- data/lib/puma/systemd.rb +3 -2
- data/lib/puma/thread_pool.rb +16 -13
- data/lib/puma/util.rb +0 -11
- data/lib/puma.rb +11 -8
- data/lib/rack/handler/puma.rb +9 -9
- metadata +7 -3
- data/lib/puma/queue_close.rb +0 -26
data/lib/puma/binder.rb
CHANGED
@@ -3,24 +3,15 @@
|
|
3
3
|
require 'uri'
|
4
4
|
require 'socket'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
require_relative 'const'
|
7
|
+
require_relative 'util'
|
8
|
+
require_relative 'configuration'
|
9
9
|
|
10
10
|
module Puma
|
11
11
|
|
12
12
|
if HAS_SSL
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# Odd bug in 'pure Ruby' nio4r version 2.5.2, which installs with Ruby 2.3.
|
17
|
-
# NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
|
18
|
-
# The bug was that it did not require openssl.
|
19
|
-
# @todo remove when Ruby 2.3 support is dropped
|
20
|
-
#
|
21
|
-
if windows? && RbConfig::CONFIG['ruby_version'] == '2.3.0'
|
22
|
-
require 'openssl'
|
23
|
-
end
|
13
|
+
require_relative 'minissl'
|
14
|
+
require_relative 'minissl/context_builder'
|
24
15
|
end
|
25
16
|
|
26
17
|
class Binder
|
@@ -28,8 +19,8 @@ module Puma
|
|
28
19
|
|
29
20
|
RACK_VERSION = [1,6].freeze
|
30
21
|
|
31
|
-
def initialize(
|
32
|
-
@
|
22
|
+
def initialize(log_writer, conf = Configuration.new)
|
23
|
+
@log_writer = log_writer
|
33
24
|
@conf = conf
|
34
25
|
@listeners = []
|
35
26
|
@inherited_fds = {}
|
@@ -38,7 +29,7 @@ module Puma
|
|
38
29
|
|
39
30
|
@proto_env = {
|
40
31
|
"rack.version".freeze => RACK_VERSION,
|
41
|
-
"rack.errors".freeze =>
|
32
|
+
"rack.errors".freeze => log_writer.stderr,
|
42
33
|
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
43
34
|
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
44
35
|
"rack.run_once".freeze => false,
|
@@ -51,7 +42,6 @@ module Puma
|
|
51
42
|
# infer properly.
|
52
43
|
|
53
44
|
"QUERY_STRING".freeze => "",
|
54
|
-
SERVER_PROTOCOL => HTTP_11,
|
55
45
|
SERVER_SOFTWARE => PUMA_SERVER_STRING,
|
56
46
|
GATEWAY_INTERFACE => CGI_VER
|
57
47
|
}
|
@@ -80,7 +70,7 @@ module Puma
|
|
80
70
|
# @!attribute [r] connected_ports
|
81
71
|
# @version 5.0.0
|
82
72
|
def connected_ports
|
83
|
-
ios.map { |io| io.addr[1] }.uniq
|
73
|
+
t = ios.map { |io| io.addr[1] }; t.uniq!; t
|
84
74
|
end
|
85
75
|
|
86
76
|
# @version 5.0.0
|
@@ -98,7 +88,7 @@ module Puma
|
|
98
88
|
# @version 5.0.0
|
99
89
|
#
|
100
90
|
def create_activated_fds(env_hash)
|
101
|
-
@
|
91
|
+
@log_writer.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
|
102
92
|
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
103
93
|
env_hash['LISTEN_FDS'].to_i.times do |index|
|
104
94
|
sock = TCPServer.for_fd(socket_activation_fd(index))
|
@@ -106,11 +96,11 @@ module Puma
|
|
106
96
|
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
107
97
|
rescue ArgumentError # Try to parse as a port/ip
|
108
98
|
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
109
|
-
addr = "[#{addr}]" if addr
|
99
|
+
addr = "[#{addr}]" if addr&.include? ':'
|
110
100
|
[:tcp, addr, port]
|
111
101
|
end
|
112
102
|
@activated_sockets[key] = sock
|
113
|
-
@
|
103
|
+
@log_writer.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
114
104
|
end
|
115
105
|
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
116
106
|
end
|
@@ -152,17 +142,18 @@ module Puma
|
|
152
142
|
end
|
153
143
|
end
|
154
144
|
|
155
|
-
def parse(binds,
|
145
|
+
def parse(binds, log_writer = nil, log_msg = 'Listening')
|
146
|
+
log_writer ||= @log_writer
|
156
147
|
binds.each do |str|
|
157
148
|
uri = URI.parse str
|
158
149
|
case uri.scheme
|
159
150
|
when "tcp"
|
160
151
|
if fd = @inherited_fds.delete(str)
|
161
152
|
io = inherit_tcp_listener uri.host, uri.port, fd
|
162
|
-
|
153
|
+
log_writer.log "* Inherited #{str}"
|
163
154
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
164
155
|
io = inherit_tcp_listener uri.host, uri.port, sock
|
165
|
-
|
156
|
+
log_writer.log "* Activated #{str}"
|
166
157
|
else
|
167
158
|
ios_len = @ios.length
|
168
159
|
params = Util.parse_query uri.query
|
@@ -174,7 +165,7 @@ module Puma
|
|
174
165
|
|
175
166
|
@ios[ios_len..-1].each do |i|
|
176
167
|
addr = loc_addr_str i
|
177
|
-
|
168
|
+
log_writer.log "* #{log_msg} on http://#{addr}"
|
178
169
|
end
|
179
170
|
end
|
180
171
|
|
@@ -191,12 +182,12 @@ module Puma
|
|
191
182
|
if fd = @inherited_fds.delete(str)
|
192
183
|
@unix_paths << path unless abstract || File.exist?(path)
|
193
184
|
io = inherit_unix_listener path, fd
|
194
|
-
|
185
|
+
log_writer.log "* Inherited #{str}"
|
195
186
|
elsif sock = @activated_sockets.delete([ :unix, path ]) ||
|
196
187
|
@activated_sockets.delete([ :unix, File.realdirpath(path) ])
|
197
188
|
@unix_paths << path unless abstract || File.exist?(path)
|
198
189
|
io = inherit_unix_listener path, sock
|
199
|
-
|
190
|
+
log_writer.log "* Activated #{str}"
|
200
191
|
else
|
201
192
|
umask = nil
|
202
193
|
mode = nil
|
@@ -220,11 +211,12 @@ module Puma
|
|
220
211
|
|
221
212
|
@unix_paths << path unless abstract || File.exist?(path)
|
222
213
|
io = add_unix_listener path, umask, mode, backlog
|
223
|
-
|
214
|
+
log_writer.log "* #{log_msg} on #{str}"
|
224
215
|
end
|
225
216
|
|
226
217
|
@listeners << [str, io]
|
227
218
|
when "ssl"
|
219
|
+
cert_key = %w[cert key]
|
228
220
|
|
229
221
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
230
222
|
|
@@ -233,28 +225,29 @@ module Puma
|
|
233
225
|
# If key and certs are not defined and localhost gem is required.
|
234
226
|
# localhost gem will be used for self signed
|
235
227
|
# Load localhost authority if not loaded.
|
236
|
-
|
228
|
+
# Ruby 3 `values_at` accepts an array, earlier do not
|
229
|
+
if params.values_at(*cert_key).all? { |v| v.to_s.empty? }
|
237
230
|
ctx = localhost_authority && localhost_authority_context
|
238
231
|
end
|
239
232
|
|
240
233
|
ctx ||=
|
241
234
|
begin
|
242
235
|
# Extract cert_pem and key_pem from options[:store] if present
|
243
|
-
|
244
|
-
if params[v]
|
236
|
+
cert_key.each do |v|
|
237
|
+
if params[v]&.start_with?('store:')
|
245
238
|
index = Integer(params.delete(v).split('store:').last)
|
246
239
|
params["#{v}_pem"] = @conf.options[:store][index]
|
247
240
|
end
|
248
241
|
end
|
249
|
-
MiniSSL::ContextBuilder.new(params, @
|
242
|
+
MiniSSL::ContextBuilder.new(params, @log_writer).context
|
250
243
|
end
|
251
244
|
|
252
245
|
if fd = @inherited_fds.delete(str)
|
253
|
-
|
246
|
+
log_writer.log "* Inherited #{str}"
|
254
247
|
io = inherit_ssl_listener fd, ctx
|
255
248
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
256
249
|
io = inherit_ssl_listener sock, ctx
|
257
|
-
|
250
|
+
log_writer.log "* Activated #{str}"
|
258
251
|
else
|
259
252
|
ios_len = @ios.length
|
260
253
|
backlog = params.fetch('backlog', 1024).to_i
|
@@ -262,20 +255,20 @@ module Puma
|
|
262
255
|
|
263
256
|
@ios[ios_len..-1].each do |i|
|
264
257
|
addr = loc_addr_str i
|
265
|
-
|
258
|
+
log_writer.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
|
266
259
|
end
|
267
260
|
end
|
268
261
|
|
269
262
|
@listeners << [str, io] if io
|
270
263
|
else
|
271
|
-
|
264
|
+
log_writer.error "Invalid URI: #{str}"
|
272
265
|
end
|
273
266
|
end
|
274
267
|
|
275
268
|
# If we inherited fds but didn't use them (because of a
|
276
269
|
# configuration change), then be sure to close them.
|
277
270
|
@inherited_fds.each do |str, fd|
|
278
|
-
|
271
|
+
log_writer.log "* Closing unused inherited connection: #{str}"
|
279
272
|
|
280
273
|
begin
|
281
274
|
IO.for_fd(fd).close
|
@@ -295,7 +288,7 @@ module Puma
|
|
295
288
|
fds = @ios.map(&:to_i)
|
296
289
|
@activated_sockets.each do |key, sock|
|
297
290
|
next if fds.include? sock.to_i
|
298
|
-
|
291
|
+
log_writer.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
|
299
292
|
begin
|
300
293
|
sock.close
|
301
294
|
rescue SystemCallError
|
@@ -319,7 +312,7 @@ module Puma
|
|
319
312
|
local_certificates_path = File.expand_path("~/.localhost")
|
320
313
|
[File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
|
321
314
|
end
|
322
|
-
MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @
|
315
|
+
MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @log_writer).context
|
323
316
|
end
|
324
317
|
|
325
318
|
# Tell the server to listen on host +host+, port +port+.
|
@@ -482,9 +475,10 @@ module Puma
|
|
482
475
|
|
483
476
|
# @!attribute [r] loopback_addresses
|
484
477
|
def loopback_addresses
|
485
|
-
Socket.ip_address_list.select do |addrinfo|
|
478
|
+
t = Socket.ip_address_list.select do |addrinfo|
|
486
479
|
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
487
|
-
end
|
480
|
+
end
|
481
|
+
t.map! { |addrinfo| addrinfo.ip_address }; t.uniq!; t
|
488
482
|
end
|
489
483
|
|
490
484
|
def loc_addr_str(io)
|
data/lib/puma/cli.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
require 'optparse'
|
4
4
|
require 'uri'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
require_relative '../puma'
|
7
|
+
require_relative 'configuration'
|
8
|
+
require_relative 'launcher'
|
9
|
+
require_relative 'const'
|
10
|
+
require_relative 'log_writer'
|
11
11
|
|
12
12
|
module Puma
|
13
13
|
class << self
|
@@ -21,19 +21,13 @@ module Puma
|
|
21
21
|
# Handles invoke a Puma::Server in a command line style.
|
22
22
|
#
|
23
23
|
class CLI
|
24
|
-
# @deprecated 6.0.0
|
25
|
-
KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
|
26
|
-
|
27
24
|
# Create a new CLI object using +argv+ as the command line
|
28
25
|
# arguments.
|
29
26
|
#
|
30
|
-
|
31
|
-
# this object will report status on.
|
32
|
-
#
|
33
|
-
def initialize(argv, events=Events.stdio)
|
27
|
+
def initialize(argv, log_writer = LogWriter.stdio, events = Events.new)
|
34
28
|
@debug = false
|
35
29
|
@argv = argv.dup
|
36
|
-
|
30
|
+
@log_writer = log_writer
|
37
31
|
@events = events
|
38
32
|
|
39
33
|
@conf = nil
|
@@ -69,7 +63,7 @@ module Puma
|
|
69
63
|
end
|
70
64
|
end
|
71
65
|
|
72
|
-
@launcher = Puma::Launcher.new(@conf, :events => @events, :argv => argv)
|
66
|
+
@launcher = Puma::Launcher.new(@conf, :log_writer => @log_writer, :events => @events, :argv => argv)
|
73
67
|
end
|
74
68
|
|
75
69
|
attr_reader :launcher
|
@@ -83,7 +77,7 @@ module Puma
|
|
83
77
|
|
84
78
|
private
|
85
79
|
def unsupported(str)
|
86
|
-
@
|
80
|
+
@log_writer.error(str)
|
87
81
|
raise UnsupportedOption
|
88
82
|
end
|
89
83
|
|
@@ -152,7 +146,7 @@ module Puma
|
|
152
146
|
|
153
147
|
o.on "-p", "--port PORT", "Define the TCP port to bind to",
|
154
148
|
"Use -b for more advanced options" do |arg|
|
155
|
-
user_config.bind "tcp://#{Configuration::
|
149
|
+
user_config.bind "tcp://#{Configuration::DEFAULTS[:tcp_host]}:#{arg}"
|
156
150
|
end
|
157
151
|
|
158
152
|
o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
|
@@ -186,7 +180,7 @@ module Puma
|
|
186
180
|
end
|
187
181
|
|
188
182
|
o.on "-s", "--silent", "Do not log prompt messages other than errors" do
|
189
|
-
@
|
183
|
+
@log_writer = LogWriter.new(NullIO.new, $stderr)
|
190
184
|
end
|
191
185
|
|
192
186
|
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
data/lib/puma/client.rb
CHANGED
@@ -8,7 +8,7 @@ class IO
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
require_relative 'detect'
|
12
12
|
require 'tempfile'
|
13
13
|
require 'forwardable'
|
14
14
|
|
@@ -25,6 +25,9 @@ module Puma
|
|
25
25
|
|
26
26
|
class HttpParserError501 < IOError; end
|
27
27
|
|
28
|
+
#———————————————————————— DO NOT USE — this class is for internal use only ———
|
29
|
+
|
30
|
+
|
28
31
|
# An instance of this class represents a unique request from a client.
|
29
32
|
# For example, this could be a web request from a browser or from CURL.
|
30
33
|
#
|
@@ -38,7 +41,7 @@ module Puma
|
|
38
41
|
# the header and body are fully buffered via the `try_to_finish` method.
|
39
42
|
# They can be used to "time out" a response via the `timeout_at` reader.
|
40
43
|
#
|
41
|
-
class Client
|
44
|
+
class Client # :nodoc:
|
42
45
|
|
43
46
|
# this tests all values but the last, which must be chunked
|
44
47
|
ALLOWED_TRANSFER_ENCODING = %w[compress deflate gzip].freeze
|
@@ -63,11 +66,7 @@ module Puma
|
|
63
66
|
@io = io
|
64
67
|
@to_io = io.to_io
|
65
68
|
@proto_env = env
|
66
|
-
|
67
|
-
@env = nil
|
68
|
-
else
|
69
|
-
@env = env.dup
|
70
|
-
end
|
69
|
+
@env = env ? env.dup : nil
|
71
70
|
|
72
71
|
@parser = HttpParser.new
|
73
72
|
@parsed_bytes = 0
|
@@ -86,6 +85,7 @@ module Puma
|
|
86
85
|
@hijacked = false
|
87
86
|
|
88
87
|
@peerip = nil
|
88
|
+
@peer_family = nil
|
89
89
|
@listener = nil
|
90
90
|
@remote_addr_header = nil
|
91
91
|
@expect_proxy_proto = false
|
@@ -273,7 +273,7 @@ module Puma
|
|
273
273
|
return @peerip if @peerip
|
274
274
|
|
275
275
|
if @remote_addr_header
|
276
|
-
hdr = (@env[@remote_addr_header] ||
|
276
|
+
hdr = (@env[@remote_addr_header] || @io.peeraddr.last).split(/[\s,]/).first
|
277
277
|
@peerip = hdr
|
278
278
|
return hdr
|
279
279
|
end
|
@@ -281,6 +281,16 @@ module Puma
|
|
281
281
|
@peerip ||= @io.peeraddr.last
|
282
282
|
end
|
283
283
|
|
284
|
+
def peer_family
|
285
|
+
return @peer_family if @peer_family
|
286
|
+
|
287
|
+
@peer_family ||= begin
|
288
|
+
@io.local_address.afamily
|
289
|
+
rescue
|
290
|
+
Socket::AF_INET
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
284
294
|
# Returns true if the persistent connection can be closed immediately
|
285
295
|
# without waiting for the configured idle/shutdown timeout.
|
286
296
|
# @version 5.0.0
|
@@ -304,7 +314,7 @@ module Puma
|
|
304
314
|
private
|
305
315
|
|
306
316
|
def setup_body
|
307
|
-
@body_read_start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :
|
317
|
+
@body_read_start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
|
308
318
|
|
309
319
|
if @env[HTTP_EXPECT] == CONTINUE
|
310
320
|
# TODO allow a hook here to check the headers before
|
@@ -348,7 +358,7 @@ module Puma
|
|
348
358
|
|
349
359
|
if cl
|
350
360
|
# cannot contain characters that are not \d
|
351
|
-
if cl
|
361
|
+
if CONTENT_LENGTH_VALUE_INVALID.match? cl
|
352
362
|
raise HttpParserError, "Invalid Content-Length: #{cl.inspect}"
|
353
363
|
end
|
354
364
|
else
|
@@ -513,7 +523,7 @@ module Puma
|
|
513
523
|
# Puma doesn't process chunk extensions, but should parse if they're
|
514
524
|
# present, which is the reason for the semicolon regex
|
515
525
|
chunk_hex = line.strip[/\A[^;]+/]
|
516
|
-
if chunk_hex
|
526
|
+
if CHUNK_SIZE_INVALID.match? chunk_hex
|
517
527
|
raise HttpParserError, "Invalid chunk size: '#{chunk_hex}'"
|
518
528
|
end
|
519
529
|
len = chunk_hex.to_i(16)
|
@@ -576,7 +586,7 @@ module Puma
|
|
576
586
|
|
577
587
|
def set_ready
|
578
588
|
if @body_read_start
|
579
|
-
@env['puma.request_body_wait'] = Process.clock_gettime(Process::CLOCK_MONOTONIC, :
|
589
|
+
@env['puma.request_body_wait'] = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - @body_read_start
|
580
590
|
end
|
581
591
|
@requests_served += 1
|
582
592
|
@ready = true
|
data/lib/puma/cluster/worker.rb
CHANGED
@@ -2,27 +2,29 @@
|
|
2
2
|
|
3
3
|
module Puma
|
4
4
|
class Cluster < Puma::Runner
|
5
|
+
#—————————————————————— DO NOT USE — this class is for internal use only ———
|
6
|
+
|
7
|
+
|
5
8
|
# This class is instantiated by the `Puma::Cluster` and represents a single
|
6
9
|
# worker process.
|
7
10
|
#
|
8
11
|
# At the core of this class is running an instance of `Puma::Server` which
|
9
12
|
# gets created via the `start_server` method from the `Puma::Runner` class
|
10
13
|
# that this inherits from.
|
11
|
-
class Worker < Puma::Runner
|
14
|
+
class Worker < Puma::Runner # :nodoc:
|
12
15
|
attr_reader :index, :master
|
13
16
|
|
14
17
|
def initialize(index:, master:, launcher:, pipes:, server: nil)
|
15
|
-
super
|
18
|
+
super(launcher)
|
16
19
|
|
17
20
|
@index = index
|
18
21
|
@master = master
|
19
|
-
@launcher = launcher
|
20
|
-
@options = launcher.options
|
21
22
|
@check_pipe = pipes[:check_pipe]
|
22
23
|
@worker_write = pipes[:worker_write]
|
23
24
|
@fork_pipe = pipes[:fork_pipe]
|
24
25
|
@wakeup = pipes[:wakeup]
|
25
26
|
@server = server
|
27
|
+
@hook_data = {}
|
26
28
|
end
|
27
29
|
|
28
30
|
def run
|
@@ -52,13 +54,14 @@ module Puma
|
|
52
54
|
|
53
55
|
# Invoke any worker boot hooks so they can get
|
54
56
|
# things in shape before booting the app.
|
55
|
-
@
|
57
|
+
@config.run_hooks(:before_worker_boot, index, @log_writer, @hook_data)
|
56
58
|
|
57
59
|
begin
|
58
60
|
server = @server ||= start_server
|
59
61
|
rescue Exception => e
|
60
62
|
log "! Unable to start worker"
|
61
|
-
log e
|
63
|
+
log e
|
64
|
+
log e.backtrace.join("\n ")
|
62
65
|
exit 1
|
63
66
|
end
|
64
67
|
|
@@ -83,8 +86,7 @@ module Puma
|
|
83
86
|
if restart_server.length > 0
|
84
87
|
restart_server.clear
|
85
88
|
server.begin_restart(true)
|
86
|
-
@
|
87
|
-
Puma::Util.nakayoshi_gc @events if @options[:nakayoshi_fork]
|
89
|
+
@config.run_hooks(:before_refork, nil, @log_writer, @hook_data)
|
88
90
|
end
|
89
91
|
elsif idx == 0 # restart server
|
90
92
|
restart_server << true << false
|
@@ -138,7 +140,7 @@ module Puma
|
|
138
140
|
|
139
141
|
# Invoke any worker shutdown hooks so they can prevent the worker
|
140
142
|
# exiting until any background operations are completed
|
141
|
-
@
|
143
|
+
@config.run_hooks(:before_worker_shutdown, index, @log_writer, @hook_data)
|
142
144
|
ensure
|
143
145
|
@worker_write << "t#{Process.pid}\n" rescue nil
|
144
146
|
@worker_write.close
|
@@ -147,7 +149,7 @@ module Puma
|
|
147
149
|
private
|
148
150
|
|
149
151
|
def spawn_worker(idx)
|
150
|
-
@
|
152
|
+
@config.run_hooks(:before_worker_fork, idx, @log_writer, @hook_data)
|
151
153
|
|
152
154
|
pid = fork do
|
153
155
|
new_worker = Worker.new index: idx,
|
@@ -165,7 +167,7 @@ module Puma
|
|
165
167
|
exit! 1
|
166
168
|
end
|
167
169
|
|
168
|
-
@
|
170
|
+
@config.run_hooks(:after_worker_fork, idx, @log_writer, @hook_data)
|
169
171
|
pid
|
170
172
|
end
|
171
173
|
end
|
@@ -2,12 +2,15 @@
|
|
2
2
|
|
3
3
|
module Puma
|
4
4
|
class Cluster < Runner
|
5
|
+
#—————————————————————— DO NOT USE — this class is for internal use only ———
|
6
|
+
|
7
|
+
|
5
8
|
# This class represents a worker process from the perspective of the puma
|
6
9
|
# master process. It contains information about the process and its health
|
7
10
|
# and it exposes methods to control the process via IPC. It does not
|
8
11
|
# include the actual logic executed by the worker process itself. For that,
|
9
12
|
# see Puma::Cluster::Worker.
|
10
|
-
class WorkerHandle
|
13
|
+
class WorkerHandle # :nodoc:
|
11
14
|
def initialize(idx, pid, phase, options)
|
12
15
|
@index = idx
|
13
16
|
@pid = pid
|
data/lib/puma/cluster.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
require_relative 'runner'
|
4
|
+
require_relative 'util'
|
5
|
+
require_relative 'plugin'
|
6
|
+
require_relative 'cluster/worker_handle'
|
7
|
+
require_relative 'cluster/worker'
|
8
8
|
|
9
9
|
require 'time'
|
10
10
|
|
@@ -17,8 +17,8 @@ module Puma
|
|
17
17
|
# via the `spawn_workers` method call. Each worker will have it's own
|
18
18
|
# instance of a `Puma::Server`.
|
19
19
|
class Cluster < Runner
|
20
|
-
def initialize(
|
21
|
-
super
|
20
|
+
def initialize(launcher)
|
21
|
+
super(launcher)
|
22
22
|
|
23
23
|
@phase = 0
|
24
24
|
@workers = []
|
@@ -27,6 +27,10 @@ module Puma
|
|
27
27
|
@phased_restart = false
|
28
28
|
end
|
29
29
|
|
30
|
+
# Returns the list of cluster worker handles.
|
31
|
+
# @return [Array<Puma::Cluster::WorkerHandle>]
|
32
|
+
attr_reader :workers
|
33
|
+
|
30
34
|
def stop_workers
|
31
35
|
log "- Gracefully shutting down workers..."
|
32
36
|
@workers.each { |x| x.term }
|
@@ -92,7 +96,7 @@ module Puma
|
|
92
96
|
|
93
97
|
# @version 5.0.0
|
94
98
|
def spawn_worker(idx, master)
|
95
|
-
@
|
99
|
+
@config.run_hooks(:before_worker_fork, idx, @log_writer)
|
96
100
|
|
97
101
|
pid = fork { worker(idx, master) }
|
98
102
|
if !pid
|
@@ -101,7 +105,7 @@ module Puma
|
|
101
105
|
exit! 1
|
102
106
|
end
|
103
107
|
|
104
|
-
@
|
108
|
+
@config.run_hooks(:after_worker_fork, idx, @log_writer)
|
105
109
|
pid
|
106
110
|
end
|
107
111
|
|
@@ -176,10 +180,10 @@ module Puma
|
|
176
180
|
end
|
177
181
|
end
|
178
182
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
].compact.min
|
183
|
+
t = @workers.reject(&:term?)
|
184
|
+
t.map!(&:ping_timeout)
|
185
|
+
|
186
|
+
@next_check = [t.min, @next_check].compact.min
|
183
187
|
end
|
184
188
|
|
185
189
|
def worker(index, master)
|
@@ -209,8 +213,8 @@ module Puma
|
|
209
213
|
stop
|
210
214
|
end
|
211
215
|
|
212
|
-
def phased_restart
|
213
|
-
return false if @options[:preload_app]
|
216
|
+
def phased_restart(refork = false)
|
217
|
+
return false if @options[:preload_app] && !refork
|
214
218
|
|
215
219
|
@phased_restart = true
|
216
220
|
wakeup!
|
@@ -226,7 +230,7 @@ module Puma
|
|
226
230
|
def stop_blocked
|
227
231
|
@status = :stop if @status == :run
|
228
232
|
wakeup!
|
229
|
-
@control
|
233
|
+
@control&.stop true
|
230
234
|
Process.waitall
|
231
235
|
end
|
232
236
|
|
@@ -265,7 +269,7 @@ module Puma
|
|
265
269
|
booted_workers: worker_status.count { |w| w[:booted] },
|
266
270
|
old_workers: old_worker_count,
|
267
271
|
worker_status: worker_status,
|
268
|
-
}
|
272
|
+
}.merge(super)
|
269
273
|
end
|
270
274
|
|
271
275
|
def preload?
|
@@ -277,7 +281,7 @@ module Puma
|
|
277
281
|
if (worker = @workers.find { |w| w.index == 0 })
|
278
282
|
worker.phase += 1
|
279
283
|
end
|
280
|
-
phased_restart
|
284
|
+
phased_restart(true)
|
281
285
|
end
|
282
286
|
|
283
287
|
# We do this in a separate method to keep the lambda scope
|
@@ -290,7 +294,7 @@ module Puma
|
|
290
294
|
|
291
295
|
# Auto-fork after the specified number of requests.
|
292
296
|
if (fork_requests = @options[:fork_worker].to_i) > 0
|
293
|
-
@
|
297
|
+
@events.register(:ping!) do |w|
|
294
298
|
fork_worker! if w.index == 0 &&
|
295
299
|
w.phase == 0 &&
|
296
300
|
w.last_status[:requests_count] >= fork_requests
|
@@ -372,12 +376,12 @@ module Puma
|
|
372
376
|
else
|
373
377
|
log "* Restarts: (\u2714) hot (\u2714) phased"
|
374
378
|
|
375
|
-
unless @
|
379
|
+
unless @config.app_configured?
|
376
380
|
error "No application configured, nothing to run"
|
377
381
|
exit 1
|
378
382
|
end
|
379
383
|
|
380
|
-
@launcher.binder.parse @options[:binds]
|
384
|
+
@launcher.binder.parse @options[:binds]
|
381
385
|
end
|
382
386
|
|
383
387
|
read, @wakeup = Puma::Util.pipe
|
@@ -409,8 +413,7 @@ module Puma
|
|
409
413
|
|
410
414
|
@master_read, @worker_write = read, @wakeup
|
411
415
|
|
412
|
-
@
|
413
|
-
Puma::Util.nakayoshi_gc @events if @options[:nakayoshi_fork]
|
416
|
+
@config.run_hooks(:before_fork, nil, @log_writer)
|
414
417
|
|
415
418
|
spawn_workers
|
416
419
|
|
@@ -463,9 +466,9 @@ module Puma
|
|
463
466
|
w.term unless w.term?
|
464
467
|
when "p"
|
465
468
|
w.ping!(result.sub(/^\d+/,'').chomp)
|
466
|
-
@
|
469
|
+
@events.fire(:ping!, w)
|
467
470
|
if !booted && @workers.none? {|worker| worker.last_status.empty?}
|
468
|
-
@
|
471
|
+
@events.fire_on_booted!
|
469
472
|
booted = true
|
470
473
|
end
|
471
474
|
end
|