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.

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 { |t| t.thread_variable_get(:fork_safe) }
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
- log "! Terminating timed out worker: #{w.pid}"
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
@@ -33,7 +33,7 @@ module Puma
33
33
  Signal.trap "SIGINT", "IGNORE"
34
34
  Signal.trap "SIGCHLD", "DEFAULT"
35
35
 
36
- Thread.new do
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
@@ -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
@@ -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.0".freeze
104
- CODE_NAME = "Fettisdagsbulle".freeze
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
- # Mininum interval to checks worker health
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
@@ -176,7 +176,9 @@ module Puma
176
176
  when 'tcp'
177
177
  TCPSocket.new uri.host, uri.port
178
178
  when 'unix'
179
- UNIXSocket.new "#{uri.host}#{uri.path}"
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
- # at present, MiniSSL::Engine is only defined in extension code, not in minissl.rb
5
- HAS_SSL = const_defined?(:MiniSSL, false) && MiniSSL.const_defined?(:Engine, false)
8
+ # @version 5.2.1
9
+ HAS_FORK = ::Process.respond_to? :fork
6
10
 
7
- def self.ssl?
8
- HAS_SSL
9
- end
11
+ IS_JRUBY = Object.const_defined? :JRUBY_VERSION
10
12
 
11
- IS_JRUBY = defined?(JRUBY_VERSION)
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
- RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?
29
+ IS_MRI
26
30
  end
27
31
 
28
32
  # @version 5.0.0
29
33
  def self.forkable?
30
- ::Process.respond_to?(:fork)
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
@@ -23,7 +23,7 @@ module Puma
23
23
  new $stderr
24
24
  end
25
25
 
26
- # Print occured error details.
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 occured error details only if
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
@@ -133,7 +133,7 @@ module Puma
133
133
 
134
134
  return data_size if need == 0
135
135
 
136
- data = data[wrote..-1]
136
+ data = data.byteslice(wrote..-1)
137
137
  end
138
138
  end
139
139
 
data/lib/puma/null_io.rb CHANGED
@@ -36,6 +36,10 @@ module Puma
36
36
  true
37
37
  end
38
38
 
39
+ def sync
40
+ true
41
+ end
42
+
39
43
  def sync=(v)
40
44
  end
41
45
 
@@ -44,5 +48,9 @@ module Puma
44
48
 
45
49
  def write(*ary)
46
50
  end
51
+
52
+ def flush
53
+ self
54
+ end
47
55
  end
48
56
  end
Binary file
@@ -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
- @closed ||= false
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