puma 5.0.4 → 5.5.1
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 +250 -48
- data/README.md +90 -24
- data/docs/architecture.md +57 -20
- data/docs/compile_options.md +21 -0
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +2 -0
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +66 -0
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +7 -7
- data/docs/signals.md +10 -10
- data/docs/stats.md +142 -0
- data/docs/systemd.md +85 -66
- data/ext/puma_http11/extconf.rb +36 -6
- data/ext/puma_http11/http11_parser.c +64 -59
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +177 -84
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +39 -41
- data/ext/puma_http11/puma_http11.c +8 -2
- data/lib/puma/app/status.rb +4 -7
- data/lib/puma/binder.rb +121 -46
- data/lib/puma/cli.rb +9 -0
- data/lib/puma/client.rb +58 -19
- data/lib/puma/cluster/worker.rb +19 -16
- data/lib/puma/cluster/worker_handle.rb +9 -2
- data/lib/puma/cluster.rb +46 -22
- data/lib/puma/configuration.rb +18 -2
- data/lib/puma/const.rb +14 -4
- data/lib/puma/control_cli.rb +76 -71
- data/lib/puma/detect.rb +14 -10
- data/lib/puma/dsl.rb +143 -26
- data/lib/puma/error_logger.rb +12 -5
- data/lib/puma/events.rb +18 -3
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher.rb +54 -6
- data/lib/puma/minissl/context_builder.rb +6 -0
- data/lib/puma/minissl.rb +54 -38
- data/lib/puma/null_io.rb +12 -0
- data/lib/puma/plugin.rb +1 -1
- data/lib/puma/queue_close.rb +7 -7
- data/lib/puma/rack/builder.rb +1 -1
- data/lib/puma/reactor.rb +19 -12
- data/lib/puma/request.rb +45 -16
- data/lib/puma/runner.rb +38 -13
- data/lib/puma/server.rb +62 -123
- data/lib/puma/state_file.rb +5 -3
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +10 -7
- data/lib/puma/util.rb +8 -1
- data/lib/puma.rb +36 -10
- data/lib/rack/handler/puma.rb +1 -0
- metadata +15 -9
data/lib/puma/binder.rb
CHANGED
@@ -13,7 +13,7 @@ module Puma
|
|
13
13
|
require 'puma/minissl'
|
14
14
|
require 'puma/minissl/context_builder'
|
15
15
|
|
16
|
-
# Odd bug in 'pure Ruby' nio4r
|
16
|
+
# Odd bug in 'pure Ruby' nio4r version 2.5.2, which installs with Ruby 2.3.
|
17
17
|
# NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
|
18
18
|
# The bug was that it did not require openssl.
|
19
19
|
# @todo remove when Ruby 2.3 support is dropped
|
@@ -41,6 +41,7 @@ module Puma
|
|
41
41
|
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
42
42
|
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
43
43
|
"rack.run_once".freeze => false,
|
44
|
+
RACK_URL_SCHEME => conf.options[:rack_url_scheme],
|
44
45
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
45
46
|
|
46
47
|
# I'd like to set a default CONTENT_TYPE here but some things
|
@@ -56,6 +57,7 @@ module Puma
|
|
56
57
|
|
57
58
|
@envs = {}
|
58
59
|
@ios = []
|
60
|
+
localhost_authority
|
59
61
|
end
|
60
62
|
|
61
63
|
attr_reader :ios
|
@@ -95,6 +97,7 @@ module Puma
|
|
95
97
|
# @version 5.0.0
|
96
98
|
#
|
97
99
|
def create_activated_fds(env_hash)
|
100
|
+
@events.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
|
98
101
|
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
99
102
|
env_hash['LISTEN_FDS'].to_i.times do |index|
|
100
103
|
sock = TCPServer.for_fd(socket_activation_fd(index))
|
@@ -111,6 +114,43 @@ module Puma
|
|
111
114
|
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
112
115
|
end
|
113
116
|
|
117
|
+
# Synthesize binds from systemd socket activation
|
118
|
+
#
|
119
|
+
# When systemd socket activation is enabled, it can be tedious to keep the
|
120
|
+
# binds in sync. This method can synthesize any binds based on the received
|
121
|
+
# activated sockets. Any existing matching binds will be respected.
|
122
|
+
#
|
123
|
+
# When only_matching is true in, all binds that do not match an activated
|
124
|
+
# socket is removed in place.
|
125
|
+
#
|
126
|
+
# It's a noop if no activated sockets were received.
|
127
|
+
def synthesize_binds_from_activated_fs(binds, only_matching)
|
128
|
+
return binds unless activated_sockets.any?
|
129
|
+
|
130
|
+
activated_binds = []
|
131
|
+
|
132
|
+
activated_sockets.keys.each do |proto, addr, port|
|
133
|
+
if port
|
134
|
+
tcp_url = "#{proto}://#{addr}:#{port}"
|
135
|
+
ssl_url = "ssl://#{addr}:#{port}"
|
136
|
+
ssl_url_prefix = "#{ssl_url}?"
|
137
|
+
|
138
|
+
existing = binds.find { |bind| bind == tcp_url || bind == ssl_url || bind.start_with?(ssl_url_prefix) }
|
139
|
+
|
140
|
+
activated_binds << (existing || tcp_url)
|
141
|
+
else
|
142
|
+
# TODO: can there be a SSL bind without a port?
|
143
|
+
activated_binds << "#{proto}://#{addr}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
if only_matching
|
148
|
+
activated_binds
|
149
|
+
else
|
150
|
+
binds | activated_binds
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
114
154
|
def parse(binds, logger, log_msg = 'Listening')
|
115
155
|
binds.each do |str|
|
116
156
|
uri = URI.parse str
|
@@ -123,21 +163,16 @@ module Puma
|
|
123
163
|
io = inherit_tcp_listener uri.host, uri.port, sock
|
124
164
|
logger.log "* Activated #{str}"
|
125
165
|
else
|
166
|
+
ios_len = @ios.length
|
126
167
|
params = Util.parse_query uri.query
|
127
168
|
|
128
|
-
opt = params.key?('low_latency')
|
169
|
+
opt = params.key?('low_latency') && params['low_latency'] != 'false'
|
129
170
|
bak = params.fetch('backlog', 1024).to_i
|
130
171
|
|
131
172
|
io = add_tcp_listener uri.host, uri.port, opt, bak
|
132
173
|
|
133
|
-
@ios.each do |i|
|
134
|
-
|
135
|
-
addr = if i.local_address.ipv6?
|
136
|
-
"[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
|
137
|
-
else
|
138
|
-
i.local_address.ip_unpack.join(':')
|
139
|
-
end
|
140
|
-
|
174
|
+
@ios[ios_len..-1].each do |i|
|
175
|
+
addr = loc_addr_str i
|
141
176
|
logger.log "* #{log_msg} on http://#{addr}"
|
142
177
|
end
|
143
178
|
end
|
@@ -145,11 +180,20 @@ module Puma
|
|
145
180
|
@listeners << [str, io] if io
|
146
181
|
when "unix"
|
147
182
|
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
183
|
+
abstract = false
|
184
|
+
if str.start_with? 'unix://@'
|
185
|
+
raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket?
|
186
|
+
abstract = true
|
187
|
+
path = "@#{path}"
|
188
|
+
end
|
148
189
|
|
149
190
|
if fd = @inherited_fds.delete(str)
|
191
|
+
@unix_paths << path unless abstract
|
150
192
|
io = inherit_unix_listener path, fd
|
151
193
|
logger.log "* Inherited #{str}"
|
152
|
-
elsif sock = @activated_sockets.delete([ :unix, path ])
|
194
|
+
elsif sock = @activated_sockets.delete([ :unix, path ]) ||
|
195
|
+
@activated_sockets.delete([ :unix, File.realdirpath(path) ])
|
196
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
153
197
|
io = inherit_unix_listener path, sock
|
154
198
|
logger.log "* Activated #{str}"
|
155
199
|
else
|
@@ -173,6 +217,7 @@ module Puma
|
|
173
217
|
end
|
174
218
|
end
|
175
219
|
|
220
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
176
221
|
io = add_unix_listener path, umask, mode, backlog
|
177
222
|
logger.log "* #{log_msg} on #{str}"
|
178
223
|
end
|
@@ -183,7 +228,13 @@ module Puma
|
|
183
228
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
184
229
|
|
185
230
|
params = Util.parse_query uri.query
|
186
|
-
|
231
|
+
|
232
|
+
# If key and certs are not defined and localhost gem is required.
|
233
|
+
# localhost gem will be used for self signed
|
234
|
+
# Load localhost authority if not loaded.
|
235
|
+
ctx = localhost_authority && localhost_authority_context if params.empty?
|
236
|
+
|
237
|
+
ctx ||= MiniSSL::ContextBuilder.new(params, @events).context
|
187
238
|
|
188
239
|
if fd = @inherited_fds.delete(str)
|
189
240
|
logger.log "* Inherited #{str}"
|
@@ -192,8 +243,13 @@ module Puma
|
|
192
243
|
io = inherit_ssl_listener sock, ctx
|
193
244
|
logger.log "* Activated #{str}"
|
194
245
|
else
|
246
|
+
ios_len = @ios.length
|
195
247
|
io = add_ssl_listener uri.host, uri.port, ctx
|
196
|
-
|
248
|
+
|
249
|
+
@ios[ios_len..-1].each do |i|
|
250
|
+
addr = loc_addr_str i
|
251
|
+
logger.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
|
252
|
+
end
|
197
253
|
end
|
198
254
|
|
199
255
|
@listeners << [str, io] if io
|
@@ -221,17 +277,37 @@ module Puma
|
|
221
277
|
end
|
222
278
|
|
223
279
|
# Also close any unused activated sockets
|
224
|
-
@activated_sockets.
|
225
|
-
|
226
|
-
|
227
|
-
sock.
|
228
|
-
|
280
|
+
unless @activated_sockets.empty?
|
281
|
+
fds = @ios.map(&:to_i)
|
282
|
+
@activated_sockets.each do |key, sock|
|
283
|
+
next if fds.include? sock.to_i
|
284
|
+
logger.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
|
285
|
+
begin
|
286
|
+
sock.close
|
287
|
+
rescue SystemCallError
|
288
|
+
end
|
289
|
+
# We have to unlink a unix socket path that's not being used
|
290
|
+
File.unlink key[1] if key.first == :unix
|
229
291
|
end
|
230
|
-
# We have to unlink a unix socket path that's not being used
|
231
|
-
File.unlink key[1] if key[0] == :unix
|
232
292
|
end
|
233
293
|
end
|
234
294
|
|
295
|
+
def localhost_authority
|
296
|
+
@localhost_authority ||= Localhost::Authority.fetch if defined?(Localhost::Authority) && !Puma::IS_JRUBY
|
297
|
+
end
|
298
|
+
|
299
|
+
def localhost_authority_context
|
300
|
+
return unless localhost_authority
|
301
|
+
|
302
|
+
key_path, crt_path = if [:key_path, :certificate_path].all? { |m| localhost_authority.respond_to?(m) }
|
303
|
+
[localhost_authority.key_path, localhost_authority.certificate_path]
|
304
|
+
else
|
305
|
+
local_certificates_path = File.expand_path("~/.localhost")
|
306
|
+
[File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
|
307
|
+
end
|
308
|
+
MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @events).context
|
309
|
+
end
|
310
|
+
|
235
311
|
# Tell the server to listen on host +host+, port +port+.
|
236
312
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
237
313
|
# will be optimized for latency over throughput.
|
@@ -249,6 +325,7 @@ module Puma
|
|
249
325
|
|
250
326
|
host = host[1..-2] if host and host[0..0] == '['
|
251
327
|
tcp_server = TCPServer.new(host, port)
|
328
|
+
|
252
329
|
if optimize_for_latency
|
253
330
|
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
254
331
|
end
|
@@ -260,11 +337,7 @@ module Puma
|
|
260
337
|
end
|
261
338
|
|
262
339
|
def inherit_tcp_listener(host, port, fd)
|
263
|
-
|
264
|
-
s = fd
|
265
|
-
else
|
266
|
-
s = TCPServer.for_fd(fd)
|
267
|
-
end
|
340
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
268
341
|
|
269
342
|
@ios << s
|
270
343
|
s
|
@@ -274,6 +347,8 @@ module Puma
|
|
274
347
|
optimize_for_latency=true, backlog=1024)
|
275
348
|
|
276
349
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
350
|
+
# Puma will try to use local authority context if context is supplied nil
|
351
|
+
ctx ||= localhost_authority_context
|
277
352
|
|
278
353
|
if host == "localhost"
|
279
354
|
loopback_addresses.each do |addr|
|
@@ -301,12 +376,11 @@ module Puma
|
|
301
376
|
|
302
377
|
def inherit_ssl_listener(fd, ctx)
|
303
378
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
379
|
+
# Puma will try to use local authority context if context is supplied nil
|
380
|
+
ctx ||= localhost_authority_context
|
381
|
+
|
382
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
304
383
|
|
305
|
-
if fd.kind_of? TCPServer
|
306
|
-
s = fd
|
307
|
-
else
|
308
|
-
s = TCPServer.for_fd(fd)
|
309
|
-
end
|
310
384
|
ssl = MiniSSL::Server.new(s, ctx)
|
311
385
|
|
312
386
|
env = @proto_env.dup
|
@@ -321,8 +395,6 @@ module Puma
|
|
321
395
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
322
396
|
#
|
323
397
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
324
|
-
@unix_paths << path unless File.exist? path
|
325
|
-
|
326
398
|
# Let anyone connect by default
|
327
399
|
umask ||= 0
|
328
400
|
|
@@ -339,8 +411,7 @@ module Puma
|
|
339
411
|
raise "There is already a server bound to: #{path}"
|
340
412
|
end
|
341
413
|
end
|
342
|
-
|
343
|
-
s = UNIXServer.new(path)
|
414
|
+
s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
|
344
415
|
s.listen backlog
|
345
416
|
@ios << s
|
346
417
|
ensure
|
@@ -359,13 +430,8 @@ module Puma
|
|
359
430
|
end
|
360
431
|
|
361
432
|
def inherit_unix_listener(path, fd)
|
362
|
-
|
433
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
|
363
434
|
|
364
|
-
if fd.kind_of? TCPServer
|
365
|
-
s = fd
|
366
|
-
else
|
367
|
-
s = UNIXServer.for_fd fd
|
368
|
-
end
|
369
435
|
@ios << s
|
370
436
|
|
371
437
|
env = @proto_env.dup
|
@@ -376,24 +442,24 @@ module Puma
|
|
376
442
|
end
|
377
443
|
|
378
444
|
def close_listeners
|
379
|
-
listeners.each do |l, io|
|
380
|
-
io.close unless io.closed?
|
381
|
-
uri = URI.parse
|
445
|
+
@listeners.each do |l, io|
|
446
|
+
io.close unless io.closed?
|
447
|
+
uri = URI.parse l
|
382
448
|
next unless uri.scheme == 'unix'
|
383
449
|
unix_path = "#{uri.host}#{uri.path}"
|
384
|
-
File.unlink unix_path if unix_paths.include? unix_path
|
450
|
+
File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
|
385
451
|
end
|
386
452
|
end
|
387
453
|
|
388
454
|
def redirects_for_restart
|
389
|
-
redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
455
|
+
redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
390
456
|
redirects[:close_others] = true
|
391
457
|
redirects
|
392
458
|
end
|
393
459
|
|
394
460
|
# @version 5.0.0
|
395
461
|
def redirects_for_restart_env
|
396
|
-
listeners.each_with_object({}).with_index do |(listen, memo), i|
|
462
|
+
@listeners.each_with_object({}).with_index do |(listen, memo), i|
|
397
463
|
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
398
464
|
end
|
399
465
|
end
|
@@ -407,6 +473,15 @@ module Puma
|
|
407
473
|
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
408
474
|
end
|
409
475
|
|
476
|
+
def loc_addr_str(io)
|
477
|
+
loc_addr = io.to_io.local_address
|
478
|
+
if loc_addr.ipv6?
|
479
|
+
"[#{loc_addr.ip_unpack[0]}]:#{loc_addr.ip_unpack[1]}"
|
480
|
+
else
|
481
|
+
loc_addr.ip_unpack.join(':')
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
410
485
|
# @version 5.0.0
|
411
486
|
def socket_activation_fd(int)
|
412
487
|
int + 3 # 3 is the magic number you add to follow the SA protocol
|
data/lib/puma/cli.rb
CHANGED
@@ -104,10 +104,19 @@ module Puma
|
|
104
104
|
user_config.bind arg
|
105
105
|
end
|
106
106
|
|
107
|
+
o.on "--bind-to-activated-sockets [only]", "Bind to all activated sockets" do |arg|
|
108
|
+
user_config.bind_to_activated_sockets(arg || true)
|
109
|
+
end
|
110
|
+
|
107
111
|
o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
|
108
112
|
file_config.load arg
|
109
113
|
end
|
110
114
|
|
115
|
+
# Identical to supplying --config "-", but more semantic
|
116
|
+
o.on "--no-config", "Prevent Puma from searching for a config file" do |arg|
|
117
|
+
file_config.load "-"
|
118
|
+
end
|
119
|
+
|
111
120
|
o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
|
112
121
|
configure_control_url(arg)
|
113
122
|
end
|
data/lib/puma/client.rb
CHANGED
@@ -56,6 +56,7 @@ module Puma
|
|
56
56
|
@parser = HttpParser.new
|
57
57
|
@parsed_bytes = 0
|
58
58
|
@read_header = true
|
59
|
+
@read_proxy = false
|
59
60
|
@ready = false
|
60
61
|
|
61
62
|
@body = nil
|
@@ -69,7 +70,9 @@ module Puma
|
|
69
70
|
@hijacked = false
|
70
71
|
|
71
72
|
@peerip = nil
|
73
|
+
@listener = nil
|
72
74
|
@remote_addr_header = nil
|
75
|
+
@expect_proxy_proto = false
|
73
76
|
|
74
77
|
@body_remain = 0
|
75
78
|
|
@@ -81,7 +84,7 @@ module Puma
|
|
81
84
|
|
82
85
|
attr_writer :peerip
|
83
86
|
|
84
|
-
attr_accessor :remote_addr_header
|
87
|
+
attr_accessor :remote_addr_header, :listener
|
85
88
|
|
86
89
|
def_delegators :@io, :closed?
|
87
90
|
|
@@ -105,7 +108,7 @@ module Puma
|
|
105
108
|
|
106
109
|
# @!attribute [r] in_data_phase
|
107
110
|
def in_data_phase
|
108
|
-
|
111
|
+
!(@read_header || @read_proxy)
|
109
112
|
end
|
110
113
|
|
111
114
|
def set_timeout(val)
|
@@ -120,16 +123,19 @@ module Puma
|
|
120
123
|
def reset(fast_check=true)
|
121
124
|
@parser.reset
|
122
125
|
@read_header = true
|
126
|
+
@read_proxy = !!@expect_proxy_proto
|
123
127
|
@env = @proto_env.dup
|
124
128
|
@body = nil
|
125
129
|
@tempfile = nil
|
126
130
|
@parsed_bytes = 0
|
127
131
|
@ready = false
|
128
132
|
@body_remain = 0
|
129
|
-
@peerip = nil
|
133
|
+
@peerip = nil if @remote_addr_header
|
130
134
|
@in_last_chunk = false
|
131
135
|
|
132
136
|
if @buffer
|
137
|
+
return false unless try_to_parse_proxy_protocol
|
138
|
+
|
133
139
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
134
140
|
|
135
141
|
if @parser.finished?
|
@@ -142,8 +148,7 @@ module Puma
|
|
142
148
|
return false
|
143
149
|
else
|
144
150
|
begin
|
145
|
-
if fast_check &&
|
146
|
-
IO.select([@to_io], nil, nil, FAST_TRACK_KA_TIMEOUT)
|
151
|
+
if fast_check && @to_io.wait_readable(FAST_TRACK_KA_TIMEOUT)
|
147
152
|
return try_to_finish
|
148
153
|
end
|
149
154
|
rescue IOError
|
@@ -157,12 +162,36 @@ module Puma
|
|
157
162
|
begin
|
158
163
|
@io.close
|
159
164
|
rescue IOError
|
160
|
-
|
165
|
+
Puma::Util.purge_interrupt_queue
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# If necessary, read the PROXY protocol from the buffer. Returns
|
170
|
+
# false if more data is needed.
|
171
|
+
def try_to_parse_proxy_protocol
|
172
|
+
if @read_proxy
|
173
|
+
if @expect_proxy_proto == :v1
|
174
|
+
if @buffer.include? "\r\n"
|
175
|
+
if md = PROXY_PROTOCOL_V1_REGEX.match(@buffer)
|
176
|
+
if md[1]
|
177
|
+
@peerip = md[1].split(" ")[0]
|
178
|
+
end
|
179
|
+
@buffer = md.post_match
|
180
|
+
end
|
181
|
+
# if the buffer has a \r\n but doesn't have a PROXY protocol
|
182
|
+
# request, this is just HTTP from a non-PROXY client; move on
|
183
|
+
@read_proxy = false
|
184
|
+
return @buffer.size > 0
|
185
|
+
else
|
186
|
+
return false
|
187
|
+
end
|
188
|
+
end
|
161
189
|
end
|
190
|
+
true
|
162
191
|
end
|
163
192
|
|
164
193
|
def try_to_finish
|
165
|
-
return read_body
|
194
|
+
return read_body if in_data_phase
|
166
195
|
|
167
196
|
begin
|
168
197
|
data = @io.read_nonblock(CHUNK_SIZE)
|
@@ -187,6 +216,8 @@ module Puma
|
|
187
216
|
@buffer = data
|
188
217
|
end
|
189
218
|
|
219
|
+
return false unless try_to_parse_proxy_protocol
|
220
|
+
|
190
221
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
191
222
|
|
192
223
|
if @parser.finished?
|
@@ -201,13 +232,13 @@ module Puma
|
|
201
232
|
|
202
233
|
def eagerly_finish
|
203
234
|
return true if @ready
|
204
|
-
return false unless
|
235
|
+
return false unless @to_io.wait_readable(0)
|
205
236
|
try_to_finish
|
206
237
|
end
|
207
238
|
|
208
239
|
def finish(timeout)
|
209
240
|
return if @ready
|
210
|
-
|
241
|
+
@to_io.wait_readable(timeout) || timeout! until try_to_finish
|
211
242
|
end
|
212
243
|
|
213
244
|
def timeout!
|
@@ -239,13 +270,19 @@ module Puma
|
|
239
270
|
# @version 5.0.0
|
240
271
|
#
|
241
272
|
def can_close?
|
242
|
-
# Allow connection to close if
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
273
|
+
# Allow connection to close if we're not in the middle of parsing a request.
|
274
|
+
@parsed_bytes == 0
|
275
|
+
end
|
276
|
+
|
277
|
+
def expect_proxy_proto=(val)
|
278
|
+
if val
|
279
|
+
if @read_header
|
280
|
+
@read_proxy = true
|
281
|
+
end
|
282
|
+
else
|
283
|
+
@read_proxy = false
|
284
|
+
end
|
285
|
+
@expect_proxy_proto = val
|
249
286
|
end
|
250
287
|
|
251
288
|
private
|
@@ -300,6 +337,7 @@ module Puma
|
|
300
337
|
|
301
338
|
if remain > MAX_BODY
|
302
339
|
@body = Tempfile.new(Const::PUMA_TMP_BASE)
|
340
|
+
@body.unlink
|
303
341
|
@body.binmode
|
304
342
|
@tempfile = @body
|
305
343
|
else
|
@@ -312,7 +350,7 @@ module Puma
|
|
312
350
|
|
313
351
|
@body_remain = remain
|
314
352
|
|
315
|
-
|
353
|
+
false
|
316
354
|
end
|
317
355
|
|
318
356
|
def read_body
|
@@ -379,7 +417,7 @@ module Puma
|
|
379
417
|
end
|
380
418
|
|
381
419
|
if decode_chunk(chunk)
|
382
|
-
@env[CONTENT_LENGTH] = @chunked_content_length
|
420
|
+
@env[CONTENT_LENGTH] = @chunked_content_length.to_s
|
383
421
|
return true
|
384
422
|
end
|
385
423
|
end
|
@@ -391,12 +429,13 @@ module Puma
|
|
391
429
|
@prev_chunk = ""
|
392
430
|
|
393
431
|
@body = Tempfile.new(Const::PUMA_TMP_BASE)
|
432
|
+
@body.unlink
|
394
433
|
@body.binmode
|
395
434
|
@tempfile = @body
|
396
435
|
@chunked_content_length = 0
|
397
436
|
|
398
437
|
if decode_chunk(body)
|
399
|
-
@env[CONTENT_LENGTH] = @chunked_content_length
|
438
|
+
@env[CONTENT_LENGTH] = @chunked_content_length.to_s
|
400
439
|
return true
|
401
440
|
end
|
402
441
|
end
|
data/lib/puma/cluster/worker.rb
CHANGED
@@ -33,9 +33,9 @@ module Puma
|
|
33
33
|
Signal.trap "SIGINT", "IGNORE"
|
34
34
|
Signal.trap "SIGCHLD", "DEFAULT"
|
35
35
|
|
36
|
-
|
36
|
+
Thread.new do
|
37
37
|
Puma.set_thread_name "worker check pipe"
|
38
|
-
|
38
|
+
@check_pipe.wait_readable
|
39
39
|
log "! Detected parent died, dying"
|
40
40
|
exit! 1
|
41
41
|
end
|
@@ -54,7 +54,14 @@ module Puma
|
|
54
54
|
# things in shape before booting the app.
|
55
55
|
@launcher.config.run_hooks :before_worker_boot, index, @launcher.events
|
56
56
|
|
57
|
+
begin
|
57
58
|
server = @server ||= start_server
|
59
|
+
rescue Exception => e
|
60
|
+
log "! Unable to start worker"
|
61
|
+
log e.backtrace[0]
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
|
58
65
|
restart_server = Queue.new << true << false
|
59
66
|
|
60
67
|
fork_worker = @options[:fork_worker] && index == 0
|
@@ -99,7 +106,7 @@ module Puma
|
|
99
106
|
begin
|
100
107
|
@worker_write << "b#{Process.pid}:#{index}\n"
|
101
108
|
rescue SystemCallError, IOError
|
102
|
-
|
109
|
+
Puma::Util.purge_interrupt_queue
|
103
110
|
STDERR.puts "Master seems to have exited, exiting."
|
104
111
|
return
|
105
112
|
end
|
@@ -108,13 +115,19 @@ module Puma
|
|
108
115
|
server_thread = server.run
|
109
116
|
stat_thread ||= Thread.new(@worker_write) do |io|
|
110
117
|
Puma.set_thread_name "stat payload"
|
118
|
+
base_payload = "p#{Process.pid}"
|
111
119
|
|
112
120
|
while true
|
113
121
|
begin
|
114
|
-
|
115
|
-
|
122
|
+
b = server.backlog || 0
|
123
|
+
r = server.running || 0
|
124
|
+
t = server.pool_capacity || 0
|
125
|
+
m = server.max_threads || 0
|
126
|
+
rc = server.requests_count || 0
|
127
|
+
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m}, "requests_count": #{rc} }\n!
|
128
|
+
io << payload
|
116
129
|
rescue IOError
|
117
|
-
|
130
|
+
Puma::Util.purge_interrupt_queue
|
118
131
|
break
|
119
132
|
end
|
120
133
|
sleep Const::WORKER_CHECK_INTERVAL
|
@@ -155,16 +168,6 @@ module Puma
|
|
155
168
|
@launcher.config.run_hooks :after_worker_fork, idx, @launcher.events
|
156
169
|
pid
|
157
170
|
end
|
158
|
-
|
159
|
-
def wakeup!
|
160
|
-
return unless @wakeup
|
161
|
-
|
162
|
-
begin
|
163
|
-
@wakeup.write "!" unless @wakeup.closed?
|
164
|
-
rescue SystemCallError, IOError
|
165
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
166
|
-
end
|
167
|
-
end
|
168
171
|
end
|
169
172
|
end
|
170
173
|
end
|
@@ -31,6 +31,10 @@ module Puma
|
|
31
31
|
@stage == :booted
|
32
32
|
end
|
33
33
|
|
34
|
+
def uptime
|
35
|
+
Time.now - started_at
|
36
|
+
end
|
37
|
+
|
34
38
|
def boot!
|
35
39
|
@last_checkin = Time.now
|
36
40
|
@stage = :booted
|
@@ -42,8 +46,11 @@ module Puma
|
|
42
46
|
|
43
47
|
def ping!(status)
|
44
48
|
@last_checkin = Time.now
|
45
|
-
|
46
|
-
@last_status =
|
49
|
+
captures = status.match(/{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads": (?<max_threads>\d*), "requests_count": (?<requests_count>\d*) }/)
|
50
|
+
@last_status = captures.names.inject({}) do |hash, key|
|
51
|
+
hash[key.to_sym] = captures[key].to_i
|
52
|
+
hash
|
53
|
+
end
|
47
54
|
end
|
48
55
|
|
49
56
|
# @see Puma::Cluster#check_workers
|