puma 5.2.0-java → 5.3.2-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 +93 -0
- data/README.md +1 -1
- data/docs/architecture.md +22 -18
- data/docs/deployment.md +1 -1
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +1 -1
- data/docs/plugins.md +1 -1
- data/docs/rails_dev_mode.md +29 -0
- data/docs/restart.md +1 -1
- data/docs/stats.md +1 -1
- data/docs/systemd.md +1 -1
- data/ext/puma_http11/http11_parser.h +1 -1
- data/lib/puma.rb +34 -8
- data/lib/puma/binder.rb +29 -21
- data/lib/puma/client.rb +7 -4
- data/lib/puma/cluster.rb +40 -8
- data/lib/puma/cluster/worker.rb +8 -1
- data/lib/puma/cluster/worker_handle.rb +4 -0
- data/lib/puma/configuration.rb +5 -1
- data/lib/puma/const.rb +3 -3
- data/lib/puma/control_cli.rb +3 -1
- data/lib/puma/detect.rb +14 -10
- data/lib/puma/dsl.rb +23 -1
- data/lib/puma/error_logger.rb +2 -2
- data/lib/puma/minissl.rb +1 -1
- data/lib/puma/null_io.rb +8 -0
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/queue_close.rb +7 -7
- data/lib/puma/request.rb +23 -7
- data/lib/puma/runner.rb +5 -0
- data/lib/puma/server.rb +34 -50
- data/lib/puma/thread_pool.rb +4 -3
- data/lib/rack/handler/puma.rb +1 -0
- metadata +5 -4
data/lib/puma/client.rb
CHANGED
@@ -69,6 +69,7 @@ module Puma
|
|
69
69
|
@hijacked = false
|
70
70
|
|
71
71
|
@peerip = nil
|
72
|
+
@listener = nil
|
72
73
|
@remote_addr_header = nil
|
73
74
|
|
74
75
|
@body_remain = 0
|
@@ -81,7 +82,7 @@ module Puma
|
|
81
82
|
|
82
83
|
attr_writer :peerip
|
83
84
|
|
84
|
-
attr_accessor :remote_addr_header
|
85
|
+
attr_accessor :remote_addr_header, :listener
|
85
86
|
|
86
87
|
def_delegators :@io, :closed?
|
87
88
|
|
@@ -126,7 +127,7 @@ module Puma
|
|
126
127
|
@parsed_bytes = 0
|
127
128
|
@ready = false
|
128
129
|
@body_remain = 0
|
129
|
-
@peerip = nil
|
130
|
+
@peerip = nil if @remote_addr_header
|
130
131
|
@in_last_chunk = false
|
131
132
|
|
132
133
|
if @buffer
|
@@ -295,6 +296,7 @@ module Puma
|
|
295
296
|
|
296
297
|
if remain > MAX_BODY
|
297
298
|
@body = Tempfile.new(Const::PUMA_TMP_BASE)
|
299
|
+
@body.unlink
|
298
300
|
@body.binmode
|
299
301
|
@tempfile = @body
|
300
302
|
else
|
@@ -374,7 +376,7 @@ module Puma
|
|
374
376
|
end
|
375
377
|
|
376
378
|
if decode_chunk(chunk)
|
377
|
-
@env[CONTENT_LENGTH] = @chunked_content_length
|
379
|
+
@env[CONTENT_LENGTH] = @chunked_content_length.to_s
|
378
380
|
return true
|
379
381
|
end
|
380
382
|
end
|
@@ -386,12 +388,13 @@ module Puma
|
|
386
388
|
@prev_chunk = ""
|
387
389
|
|
388
390
|
@body = Tempfile.new(Const::PUMA_TMP_BASE)
|
391
|
+
@body.unlink
|
389
392
|
@body.binmode
|
390
393
|
@tempfile = @body
|
391
394
|
@chunked_content_length = 0
|
392
395
|
|
393
396
|
if decode_chunk(body)
|
394
|
-
@env[CONTENT_LENGTH] = @chunked_content_length
|
397
|
+
@env[CONTENT_LENGTH] = @chunked_content_length.to_s
|
395
398
|
return true
|
396
399
|
end
|
397
400
|
end
|
data/lib/puma/cluster.rb
CHANGED
@@ -43,6 +43,7 @@ module Puma
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def start_phased_restart
|
46
|
+
@events.fire_on_restart!
|
46
47
|
@phase += 1
|
47
48
|
log "- Starting phased worker restart, phase: #{@phase}"
|
48
49
|
|
@@ -317,7 +318,7 @@ module Puma
|
|
317
318
|
|
318
319
|
stop_workers
|
319
320
|
stop
|
320
|
-
|
321
|
+
@events.fire_on_stopped!
|
321
322
|
raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
|
322
323
|
exit 0 # Clean exit, workers were stopped
|
323
324
|
end
|
@@ -332,16 +333,22 @@ module Puma
|
|
332
333
|
# This is aligned with the output from Runner, see Runner#output_header
|
333
334
|
log "* Workers: #{@options[:workers]}"
|
334
335
|
|
335
|
-
# Threads explicitly marked as fork safe will be ignored.
|
336
|
-
# Used in Rails, but may be used by anyone.
|
337
|
-
before = Thread.list.reject { |t| t.thread_variable_get(:fork_safe) }
|
338
|
-
|
339
336
|
if preload?
|
337
|
+
# Threads explicitly marked as fork safe will be ignored. Used in Rails,
|
338
|
+
# but may be used by anyone. Note that we need to explicit
|
339
|
+
# Process::Waiter check here because there's a bug in Ruby 2.6 and below
|
340
|
+
# where calling thread_variable_get on a Process::Waiter will segfault.
|
341
|
+
# We can drop that clause once those versions of Ruby are no longer
|
342
|
+
# supported.
|
343
|
+
fork_safe = ->(t) { !t.is_a?(Process::Waiter) && t.thread_variable_get(:fork_safe) }
|
344
|
+
|
345
|
+
before = Thread.list.reject(&fork_safe)
|
346
|
+
|
340
347
|
log "* Restarts: (\u2714) hot (\u2716) phased"
|
341
348
|
log "* Preloading application"
|
342
349
|
load_and_bind
|
343
350
|
|
344
|
-
after = Thread.list.reject
|
351
|
+
after = Thread.list.reject(&fork_safe)
|
345
352
|
|
346
353
|
if after.size > before.size
|
347
354
|
threads = (after - before)
|
@@ -382,6 +389,8 @@ module Puma
|
|
382
389
|
|
383
390
|
log "Use Ctrl-C to stop"
|
384
391
|
|
392
|
+
single_worker_warning
|
393
|
+
|
385
394
|
redirect_io
|
386
395
|
|
387
396
|
Plugins.fire_background
|
@@ -403,12 +412,16 @@ module Puma
|
|
403
412
|
|
404
413
|
begin
|
405
414
|
booted = false
|
415
|
+
in_phased_restart = false
|
416
|
+
workers_not_booted = @options[:workers]
|
406
417
|
|
407
418
|
while @status == :run
|
408
419
|
begin
|
409
420
|
if @phased_restart
|
410
421
|
start_phased_restart
|
411
422
|
@phased_restart = false
|
423
|
+
in_phased_restart = true
|
424
|
+
workers_not_booted = @options[:workers]
|
412
425
|
end
|
413
426
|
|
414
427
|
check_workers
|
@@ -434,8 +447,9 @@ module Puma
|
|
434
447
|
case req
|
435
448
|
when "b"
|
436
449
|
w.boot!
|
437
|
-
log "- Worker #{w.index} (PID: #{pid}) booted, phase: #{w.phase}"
|
450
|
+
log "- Worker #{w.index} (PID: #{pid}) booted in #{w.uptime.round(2)}s, phase: #{w.phase}"
|
438
451
|
@next_check = Time.now
|
452
|
+
workers_not_booted -= 1
|
439
453
|
when "e"
|
440
454
|
# external term, see worker method, Signal.trap "SIGTERM"
|
441
455
|
w.instance_variable_set :@term, true
|
@@ -453,6 +467,10 @@ module Puma
|
|
453
467
|
log "! Out-of-sync worker list, no #{pid} worker"
|
454
468
|
end
|
455
469
|
end
|
470
|
+
if in_phased_restart && workers_not_booted.zero?
|
471
|
+
@events.fire_on_booted!
|
472
|
+
in_phased_restart = false
|
473
|
+
end
|
456
474
|
|
457
475
|
rescue Interrupt
|
458
476
|
@status = :stop
|
@@ -470,6 +488,15 @@ module Puma
|
|
470
488
|
|
471
489
|
private
|
472
490
|
|
491
|
+
def single_worker_warning
|
492
|
+
return if @options[:workers] != 1 || @options[:silence_single_worker_warning]
|
493
|
+
|
494
|
+
log "! WARNING: Detected running cluster mode with 1 worker."
|
495
|
+
log "! Running Puma in cluster mode with a single worker is often a misconfiguration."
|
496
|
+
log "! Consider running Puma in single-mode (workers = 0) in order to reduce memory overhead."
|
497
|
+
log "! Set the `silence_single_worker_warning` option to silence this warning message."
|
498
|
+
end
|
499
|
+
|
473
500
|
# loops thru @workers, removing workers that exited, and calling
|
474
501
|
# `#term` if needed
|
475
502
|
def wait_workers
|
@@ -499,7 +526,12 @@ module Puma
|
|
499
526
|
def timeout_workers
|
500
527
|
@workers.each do |w|
|
501
528
|
if !w.term? && w.ping_timeout <= Time.now
|
502
|
-
|
529
|
+
details = if w.booted?
|
530
|
+
"(worker failed to check in within #{@options[:worker_timeout]} seconds)"
|
531
|
+
else
|
532
|
+
"(worker failed to boot within #{@options[:worker_boot_timeout]} seconds)"
|
533
|
+
end
|
534
|
+
log "! Terminating timed out worker #{details}: #{w.pid}"
|
503
535
|
w.kill
|
504
536
|
end
|
505
537
|
end
|
data/lib/puma/cluster/worker.rb
CHANGED
@@ -33,7 +33,7 @@ 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
|
IO.select [@check_pipe]
|
39
39
|
log "! Detected parent died, dying"
|
@@ -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
|
data/lib/puma/configuration.rb
CHANGED
@@ -193,6 +193,7 @@ module Puma
|
|
193
193
|
:debug => false,
|
194
194
|
:binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
|
195
195
|
:workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
|
196
|
+
:silence_single_worker_warning => false,
|
196
197
|
:mode => :http,
|
197
198
|
:worker_timeout => DefaultWorkerTimeout,
|
198
199
|
:worker_boot_timeout => DefaultWorkerTimeout,
|
@@ -206,7 +207,8 @@ module Puma
|
|
206
207
|
:first_data_timeout => Const::FIRST_DATA_TIMEOUT,
|
207
208
|
:raise_exception_on_sigterm => true,
|
208
209
|
:max_fast_inline => Const::MAX_FAST_INLINE,
|
209
|
-
:io_selector_backend => :auto
|
210
|
+
:io_selector_backend => :auto,
|
211
|
+
:mutate_stdout_and_stderr_to_sync_on_write => true,
|
210
212
|
}
|
211
213
|
end
|
212
214
|
|
@@ -341,6 +343,8 @@ module Puma
|
|
341
343
|
raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
|
342
344
|
|
343
345
|
rack_app, rack_options = rack_builder.parse_file(rackup)
|
346
|
+
rack_options = rack_options || {}
|
347
|
+
|
344
348
|
@options.file_options.merge!(rack_options)
|
345
349
|
|
346
350
|
config_ru_binds = []
|
data/lib/puma/const.rb
CHANGED
@@ -100,8 +100,8 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "5.2
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "5.3.2".freeze
|
104
|
+
CODE_NAME = "Sweetnighter".freeze
|
105
105
|
|
106
106
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
107
107
|
|
@@ -235,7 +235,7 @@ module Puma
|
|
235
235
|
|
236
236
|
EARLY_HINTS = "rack.early_hints".freeze
|
237
237
|
|
238
|
-
#
|
238
|
+
# Minimum interval to checks worker health
|
239
239
|
WORKER_CHECK_INTERVAL = 5
|
240
240
|
|
241
241
|
# Illegal character in the key or value of response header
|
data/lib/puma/control_cli.rb
CHANGED
@@ -176,7 +176,9 @@ module Puma
|
|
176
176
|
when 'tcp'
|
177
177
|
TCPSocket.new uri.host, uri.port
|
178
178
|
when 'unix'
|
179
|
-
UNIXSocket
|
179
|
+
# check for abstract UNIXSocket
|
180
|
+
UNIXSocket.new(@control_url.start_with?('unix://@') ?
|
181
|
+
"\0#{uri.host}#{uri.path}" : "#{uri.host}#{uri.path}")
|
180
182
|
else
|
181
183
|
raise "Invalid scheme: #{uri.scheme}"
|
182
184
|
end
|
data/lib/puma/detect.rb
CHANGED
@@ -1,32 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This file can be loaded independently of puma.rb, so it cannot have any code
|
4
|
+
# that assumes puma.rb is loaded.
|
5
|
+
|
6
|
+
|
3
7
|
module Puma
|
4
|
-
#
|
5
|
-
|
8
|
+
# @version 5.2.1
|
9
|
+
HAS_FORK = ::Process.respond_to? :fork
|
6
10
|
|
7
|
-
|
8
|
-
HAS_SSL
|
9
|
-
end
|
11
|
+
IS_JRUBY = Object.const_defined? :JRUBY_VERSION
|
10
12
|
|
11
|
-
|
13
|
+
IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/ ||
|
14
|
+
IS_JRUBY && RUBY_DESCRIPTION =~ /mswin/)
|
15
|
+
|
16
|
+
# @version 5.2.0
|
17
|
+
IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
|
12
18
|
|
13
19
|
def self.jruby?
|
14
20
|
IS_JRUBY
|
15
21
|
end
|
16
22
|
|
17
|
-
IS_WINDOWS = RUBY_PLATFORM =~ /mswin|ming|cygwin/
|
18
|
-
|
19
23
|
def self.windows?
|
20
24
|
IS_WINDOWS
|
21
25
|
end
|
22
26
|
|
23
27
|
# @version 5.0.0
|
24
28
|
def self.mri?
|
25
|
-
|
29
|
+
IS_MRI
|
26
30
|
end
|
27
31
|
|
28
32
|
# @version 5.0.0
|
29
33
|
def self.forkable?
|
30
|
-
|
34
|
+
HAS_FORK
|
31
35
|
end
|
32
36
|
end
|
data/lib/puma/dsl.rb
CHANGED
@@ -201,7 +201,7 @@ module Puma
|
|
201
201
|
# * Set the socket backlog depth with +backlog+, default is 1024.
|
202
202
|
# * Set up an SSL certificate with +key+ & +cert+.
|
203
203
|
# * Set whether to optimize for low latency instead of throughput with
|
204
|
-
# +low_latency+, default is to optimize for low latency. This is done
|
204
|
+
# +low_latency+, default is to not optimize for low latency. This is done
|
205
205
|
# via +Socket::TCP_NODELAY+.
|
206
206
|
# * Set socket permissions with +umask+.
|
207
207
|
#
|
@@ -482,6 +482,24 @@ module Puma
|
|
482
482
|
@options[:workers] = count.to_i
|
483
483
|
end
|
484
484
|
|
485
|
+
# Disable warning message when running in cluster mode with a single worker.
|
486
|
+
#
|
487
|
+
# Cluster mode has some overhead of running an additional 'control' process
|
488
|
+
# in order to manage the cluster. If only running a single worker it is
|
489
|
+
# likely not worth paying that overhead vs running in single mode with
|
490
|
+
# additional threads instead.
|
491
|
+
#
|
492
|
+
# There are some scenarios where running cluster mode with a single worker
|
493
|
+
# may still be warranted and valid under certain deployment scenarios, see
|
494
|
+
# https://github.com/puma/puma/issues/2534
|
495
|
+
#
|
496
|
+
# Moving from workers = 1 to workers = 0 will save 10-30% of memory use.
|
497
|
+
#
|
498
|
+
# @note Cluster mode only.
|
499
|
+
def silence_single_worker_warning
|
500
|
+
@options[:silence_single_worker_warning] = true
|
501
|
+
end
|
502
|
+
|
485
503
|
# Code to run immediately before master process
|
486
504
|
# forks workers (once on boot). These hooks can block if necessary
|
487
505
|
# to wait for background operations unknown to Puma to finish before
|
@@ -888,5 +906,9 @@ module Puma
|
|
888
906
|
def io_selector_backend(backend)
|
889
907
|
@options[:io_selector_backend] = backend.to_sym
|
890
908
|
end
|
909
|
+
|
910
|
+
def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
|
911
|
+
@options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
|
912
|
+
end
|
891
913
|
end
|
892
914
|
end
|
data/lib/puma/error_logger.rb
CHANGED
@@ -23,7 +23,7 @@ module Puma
|
|
23
23
|
new $stderr
|
24
24
|
end
|
25
25
|
|
26
|
-
# Print
|
26
|
+
# Print occurred error details.
|
27
27
|
# +options+ hash with additional options:
|
28
28
|
# - +error+ is an exception object
|
29
29
|
# - +req+ the http request
|
@@ -34,7 +34,7 @@ module Puma
|
|
34
34
|
log title(options)
|
35
35
|
end
|
36
36
|
|
37
|
-
# Print
|
37
|
+
# Print occurred error details only if
|
38
38
|
# environment variable PUMA_DEBUG is defined.
|
39
39
|
# +options+ hash with additional options:
|
40
40
|
# - +error+ is an exception object
|
data/lib/puma/minissl.rb
CHANGED
data/lib/puma/null_io.rb
CHANGED
data/lib/puma/puma_http11.jar
CHANGED
Binary file
|
data/lib/puma/queue_close.rb
CHANGED
@@ -5,22 +5,22 @@ module Puma
|
|
5
5
|
# Add a simple implementation for earlier Ruby versions.
|
6
6
|
#
|
7
7
|
module QueueClose
|
8
|
-
def initialize
|
9
|
-
@closed = false
|
10
|
-
super
|
11
|
-
end
|
12
8
|
def close
|
9
|
+
num_waiting.times {push nil}
|
13
10
|
@closed = true
|
14
11
|
end
|
15
12
|
def closed?
|
16
|
-
@closed
|
13
|
+
@closed ||= false
|
17
14
|
end
|
18
15
|
def push(object)
|
19
|
-
|
20
|
-
raise ClosedQueueError if @closed
|
16
|
+
raise ClosedQueueError if closed?
|
21
17
|
super
|
22
18
|
end
|
23
19
|
alias << push
|
20
|
+
def pop(non_block=false)
|
21
|
+
return nil if !non_block && closed? && empty?
|
22
|
+
super
|
23
|
+
end
|
24
24
|
end
|
25
25
|
::Queue.prepend QueueClose
|
26
26
|
end
|