puma 5.6.5 → 6.4.2

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.

Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +338 -14
  3. data/LICENSE +0 -0
  4. data/README.md +79 -29
  5. data/bin/puma-wild +1 -1
  6. data/docs/architecture.md +0 -0
  7. data/docs/compile_options.md +34 -0
  8. data/docs/deployment.md +0 -0
  9. data/docs/fork_worker.md +1 -3
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +0 -0
  14. data/docs/jungle/rc.d/README.md +0 -0
  15. data/docs/jungle/rc.d/puma.conf +0 -0
  16. data/docs/kubernetes.md +12 -0
  17. data/docs/nginx.md +1 -1
  18. data/docs/plugins.md +0 -0
  19. data/docs/rails_dev_mode.md +0 -0
  20. data/docs/restart.md +1 -0
  21. data/docs/signals.md +0 -0
  22. data/docs/stats.md +0 -0
  23. data/docs/systemd.md +3 -6
  24. data/docs/testing_benchmarks_local_files.md +150 -0
  25. data/docs/testing_test_rackup_ci_files.md +36 -0
  26. data/ext/puma_http11/PumaHttp11Service.java +0 -0
  27. data/ext/puma_http11/ext_help.h +0 -0
  28. data/ext/puma_http11/extconf.rb +16 -9
  29. data/ext/puma_http11/http11_parser.c +1 -1
  30. data/ext/puma_http11/http11_parser.h +1 -1
  31. data/ext/puma_http11/http11_parser.java.rl +2 -2
  32. data/ext/puma_http11/http11_parser.rl +2 -2
  33. data/ext/puma_http11/http11_parser_common.rl +2 -2
  34. data/ext/puma_http11/mini_ssl.c +127 -19
  35. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
  36. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  37. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  38. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +157 -53
  39. data/ext/puma_http11/puma_http11.c +17 -9
  40. data/lib/puma/app/status.rb +4 -4
  41. data/lib/puma/binder.rb +50 -53
  42. data/lib/puma/cli.rb +16 -18
  43. data/lib/puma/client.rb +100 -26
  44. data/lib/puma/cluster/worker.rb +18 -11
  45. data/lib/puma/cluster/worker_handle.rb +4 -1
  46. data/lib/puma/cluster.rb +102 -40
  47. data/lib/puma/commonlogger.rb +21 -14
  48. data/lib/puma/configuration.rb +77 -59
  49. data/lib/puma/const.rb +129 -92
  50. data/lib/puma/control_cli.rb +15 -11
  51. data/lib/puma/detect.rb +7 -4
  52. data/lib/puma/dsl.rb +250 -56
  53. data/lib/puma/error_logger.rb +18 -9
  54. data/lib/puma/events.rb +6 -126
  55. data/lib/puma/io_buffer.rb +39 -4
  56. data/lib/puma/jruby_restart.rb +2 -1
  57. data/lib/puma/json_serialization.rb +0 -0
  58. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  59. data/lib/puma/launcher.rb +102 -175
  60. data/lib/puma/log_writer.rb +147 -0
  61. data/lib/puma/minissl/context_builder.rb +26 -12
  62. data/lib/puma/minissl.rb +104 -11
  63. data/lib/puma/null_io.rb +16 -2
  64. data/lib/puma/plugin/systemd.rb +90 -0
  65. data/lib/puma/plugin/tmp_restart.rb +1 -1
  66. data/lib/puma/plugin.rb +0 -0
  67. data/lib/puma/rack/builder.rb +6 -6
  68. data/lib/puma/rack/urlmap.rb +1 -1
  69. data/lib/puma/rack_default.rb +19 -4
  70. data/lib/puma/reactor.rb +19 -10
  71. data/lib/puma/request.rb +365 -170
  72. data/lib/puma/runner.rb +56 -20
  73. data/lib/puma/sd_notify.rb +149 -0
  74. data/lib/puma/server.rb +137 -89
  75. data/lib/puma/single.rb +13 -11
  76. data/lib/puma/state_file.rb +3 -6
  77. data/lib/puma/thread_pool.rb +57 -19
  78. data/lib/puma/util.rb +0 -11
  79. data/lib/puma.rb +12 -11
  80. data/lib/rack/handler/puma.rb +113 -86
  81. data/tools/Dockerfile +2 -2
  82. data/tools/trickletest.rb +0 -0
  83. metadata +11 -6
  84. data/lib/puma/queue_close.rb +0 -26
  85. data/lib/puma/systemd.rb +0 -46
data/lib/puma/cluster.rb CHANGED
@@ -1,12 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/runner'
4
- require 'puma/util'
5
- require 'puma/plugin'
6
- require 'puma/cluster/worker_handle'
7
- require 'puma/cluster/worker'
8
-
9
- require 'time'
3
+ require_relative 'runner'
4
+ require_relative 'util'
5
+ require_relative 'plugin'
6
+ require_relative 'cluster/worker_handle'
7
+ require_relative 'cluster/worker'
10
8
 
11
9
  module Puma
12
10
  # This class is instantiated by the `Puma::Launcher` and used
@@ -17,8 +15,8 @@ module Puma
17
15
  # via the `spawn_workers` method call. Each worker will have it's own
18
16
  # instance of a `Puma::Server`.
19
17
  class Cluster < Runner
20
- def initialize(cli, events)
21
- super cli, events
18
+ def initialize(launcher)
19
+ super(launcher)
22
20
 
23
21
  @phase = 0
24
22
  @workers = []
@@ -27,6 +25,10 @@ module Puma
27
25
  @phased_restart = false
28
26
  end
29
27
 
28
+ # Returns the list of cluster worker handles.
29
+ # @return [Array<Puma::Cluster::WorkerHandle>]
30
+ attr_reader :workers
31
+
30
32
  def stop_workers
31
33
  log "- Gracefully shutting down workers..."
32
34
  @workers.each { |x| x.term }
@@ -83,16 +85,14 @@ module Puma
83
85
  @workers << WorkerHandle.new(idx, pid, @phase, @options)
84
86
  end
85
87
 
86
- if @options[:fork_worker] &&
87
- @workers.all? {|x| x.phase == @phase}
88
-
88
+ if @options[:fork_worker] && all_workers_in_phase?
89
89
  @fork_writer << "0\n"
90
90
  end
91
91
  end
92
92
 
93
93
  # @version 5.0.0
94
94
  def spawn_worker(idx, master)
95
- @launcher.config.run_hooks :before_worker_fork, idx, @launcher.events
95
+ @config.run_hooks(:before_worker_fork, idx, @log_writer)
96
96
 
97
97
  pid = fork { worker(idx, master) }
98
98
  if !pid
@@ -101,7 +101,7 @@ module Puma
101
101
  exit! 1
102
102
  end
103
103
 
104
- @launcher.config.run_hooks :after_worker_fork, idx, @launcher.events
104
+ @config.run_hooks(:after_worker_fork, idx, @log_writer)
105
105
  pid
106
106
  end
107
107
 
@@ -146,10 +146,22 @@ module Puma
146
146
  idx
147
147
  end
148
148
 
149
+ def worker_at(idx)
150
+ @workers.find { |w| w.index == idx }
151
+ end
152
+
149
153
  def all_workers_booted?
150
154
  @workers.count { |w| !w.booted? } == 0
151
155
  end
152
156
 
157
+ def all_workers_in_phase?
158
+ @workers.all? { |w| w.phase == @phase }
159
+ end
160
+
161
+ def all_workers_idle_timed_out?
162
+ (@workers.map(&:pid) - idle_timed_out_worker_pids).empty?
163
+ end
164
+
153
165
  def check_workers
154
166
  return if @next_check >= Time.now
155
167
 
@@ -176,10 +188,10 @@ module Puma
176
188
  end
177
189
  end
178
190
 
179
- @next_check = [
180
- @workers.reject(&:term?).map(&:ping_timeout).min,
181
- @next_check
182
- ].compact.min
191
+ t = @workers.reject(&:term?)
192
+ t.map!(&:ping_timeout)
193
+
194
+ @next_check = [t.min, @next_check].compact.min
183
195
  end
184
196
 
185
197
  def worker(index, master)
@@ -209,8 +221,8 @@ module Puma
209
221
  stop
210
222
  end
211
223
 
212
- def phased_restart
213
- return false if @options[:preload_app]
224
+ def phased_restart(refork = false)
225
+ return false if @options[:preload_app] && !refork
214
226
 
215
227
  @phased_restart = true
216
228
  wakeup!
@@ -226,7 +238,7 @@ module Puma
226
238
  def stop_blocked
227
239
  @status = :stop if @status == :run
228
240
  wakeup!
229
- @control.stop(true) if @control
241
+ @control&.stop true
230
242
  Process.waitall
231
243
  end
232
244
 
@@ -248,24 +260,24 @@ module Puma
248
260
  old_worker_count = @workers.count { |w| w.phase != @phase }
249
261
  worker_status = @workers.map do |w|
250
262
  {
251
- started_at: w.started_at.utc.iso8601,
263
+ started_at: utc_iso8601(w.started_at),
252
264
  pid: w.pid,
253
265
  index: w.index,
254
266
  phase: w.phase,
255
267
  booted: w.booted?,
256
- last_checkin: w.last_checkin.utc.iso8601,
268
+ last_checkin: utc_iso8601(w.last_checkin),
257
269
  last_status: w.last_status,
258
270
  }
259
271
  end
260
272
 
261
273
  {
262
- started_at: @started_at.utc.iso8601,
274
+ started_at: utc_iso8601(@started_at),
263
275
  workers: @workers.size,
264
276
  phase: @phase,
265
277
  booted_workers: worker_status.count { |w| w[:booted] },
266
278
  old_workers: old_worker_count,
267
279
  worker_status: worker_status,
268
- }
280
+ }.merge(super)
269
281
  end
270
282
 
271
283
  def preload?
@@ -274,10 +286,10 @@ module Puma
274
286
 
275
287
  # @version 5.0.0
276
288
  def fork_worker!
277
- if (worker = @workers.find { |w| w.index == 0 })
289
+ if (worker = worker_at 0)
278
290
  worker.phase += 1
279
291
  end
280
- phased_restart
292
+ phased_restart(true)
281
293
  end
282
294
 
283
295
  # We do this in a separate method to keep the lambda scope
@@ -290,7 +302,7 @@ module Puma
290
302
 
291
303
  # Auto-fork after the specified number of requests.
292
304
  if (fork_requests = @options[:fork_worker].to_i) > 0
293
- @launcher.events.register(:ping!) do |w|
305
+ @events.register(:ping!) do |w|
294
306
  fork_worker! if w.index == 0 &&
295
307
  w.phase == 0 &&
296
308
  w.last_status[:requests_count] >= fork_requests
@@ -336,6 +348,8 @@ module Puma
336
348
  def run
337
349
  @status = :run
338
350
 
351
+ @idle_workers = {}
352
+
339
353
  output_header "cluster"
340
354
 
341
355
  # This is aligned with the output from Runner, see Runner#output_header
@@ -372,12 +386,12 @@ module Puma
372
386
  else
373
387
  log "* Restarts: (\u2714) hot (\u2714) phased"
374
388
 
375
- unless @launcher.config.app_configured?
389
+ unless @config.app_configured?
376
390
  error "No application configured, nothing to run"
377
391
  exit 1
378
392
  end
379
393
 
380
- @launcher.binder.parse @options[:binds], self
394
+ @launcher.binder.parse @options[:binds]
381
395
  end
382
396
 
383
397
  read, @wakeup = Puma::Util.pipe
@@ -409,8 +423,9 @@ module Puma
409
423
 
410
424
  @master_read, @worker_write = read, @wakeup
411
425
 
412
- @launcher.config.run_hooks :before_fork, nil, @launcher.events
413
- Puma::Util.nakayoshi_gc @events if @options[:nakayoshi_fork]
426
+ @options[:worker_write] = @worker_write
427
+
428
+ @config.run_hooks(:before_fork, nil, @log_writer)
414
429
 
415
430
  spawn_workers
416
431
 
@@ -425,6 +440,11 @@ module Puma
425
440
 
426
441
  while @status == :run
427
442
  begin
443
+ if all_workers_idle_timed_out?
444
+ log "- All workers reached idle timeout"
445
+ break
446
+ end
447
+
428
448
  if @phased_restart
429
449
  start_phased_restart
430
450
  @phased_restart = false
@@ -445,7 +465,7 @@ module Puma
445
465
 
446
466
  if req == "b" || req == "f"
447
467
  pid, idx = result.split(':').map(&:to_i)
448
- w = @workers.find {|x| x.index == idx}
468
+ w = worker_at idx
449
469
  w.pid = pid if w.pid.nil?
450
470
  end
451
471
 
@@ -462,22 +482,37 @@ module Puma
462
482
  when "t"
463
483
  w.term unless w.term?
464
484
  when "p"
465
- w.ping!(result.sub(/^\d+/,'').chomp)
466
- @launcher.events.fire(:ping!, w)
485
+ status = result.sub(/^\d+/,'').chomp
486
+ w.ping!(status)
487
+ @events.fire(:ping!, w)
488
+
489
+ if in_phased_restart && workers_not_booted.positive? && w0 = worker_at(0)
490
+ w0.ping!(status)
491
+ @events.fire(:ping!, w0)
492
+ end
493
+
467
494
  if !booted && @workers.none? {|worker| worker.last_status.empty?}
468
- @launcher.events.fire_on_booted!
495
+ @events.fire_on_booted!
496
+ debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
469
497
  booted = true
470
498
  end
499
+ when "i"
500
+ if @idle_workers[pid]
501
+ @idle_workers.delete pid
502
+ else
503
+ @idle_workers[pid] = true
504
+ end
471
505
  end
472
506
  else
473
507
  log "! Out-of-sync worker list, no #{pid} worker"
474
508
  end
475
509
  end
510
+
476
511
  if in_phased_restart && workers_not_booted.zero?
477
512
  @events.fire_on_booted!
513
+ debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
478
514
  in_phased_restart = false
479
515
  end
480
-
481
516
  rescue Interrupt
482
517
  @status = :stop
483
518
  end
@@ -506,10 +541,28 @@ module Puma
506
541
  # loops thru @workers, removing workers that exited, and calling
507
542
  # `#term` if needed
508
543
  def wait_workers
544
+ # Reap all children, known workers or otherwise.
545
+ # If puma has PID 1, as it's common in containerized environments,
546
+ # then it's responsible for reaping orphaned processes, so we must reap
547
+ # all our dead children, regardless of whether they are workers we spawned
548
+ # or some reattached processes.
549
+ reaped_children = {}
550
+ loop do
551
+ begin
552
+ pid, status = Process.wait2(-1, Process::WNOHANG)
553
+ break unless pid
554
+ reaped_children[pid] = status
555
+ rescue Errno::ECHILD
556
+ break
557
+ end
558
+ end
559
+
509
560
  @workers.reject! do |w|
510
561
  next false if w.pid.nil?
511
562
  begin
512
- if Process.wait(w.pid, Process::WNOHANG)
563
+ # When `fork_worker` is enabled, some worker may not be direct children, but grand children.
564
+ # Because of this they won't be reaped by `Process.wait2(-1)`, so we need to check them individually)
565
+ if reaped_children.delete(w.pid) || (@options[:fork_worker] && Process.wait(w.pid, Process::WNOHANG))
513
566
  true
514
567
  else
515
568
  w.term if w.term?
@@ -526,6 +579,11 @@ module Puma
526
579
  end
527
580
  end
528
581
  end
582
+
583
+ # Log unknown children
584
+ reaped_children.each do |pid, status|
585
+ log "! reaped unknown child process pid=#{pid} status=#{status}"
586
+ end
529
587
  end
530
588
 
531
589
  # @version 5.0.0
@@ -533,14 +591,18 @@ module Puma
533
591
  @workers.each do |w|
534
592
  if !w.term? && w.ping_timeout <= Time.now
535
593
  details = if w.booted?
536
- "(worker failed to check in within #{@options[:worker_timeout]} seconds)"
594
+ "(Worker #{w.index} failed to check in within #{@options[:worker_timeout]} seconds)"
537
595
  else
538
- "(worker failed to boot within #{@options[:worker_boot_timeout]} seconds)"
596
+ "(Worker #{w.index} failed to boot within #{@options[:worker_boot_timeout]} seconds)"
539
597
  end
540
598
  log "! Terminating timed out worker #{details}: #{w.pid}"
541
599
  w.kill
542
600
  end
543
601
  end
544
602
  end
603
+
604
+ def idle_timed_out_worker_pids
605
+ @idle_workers.keys
606
+ end
545
607
  end
546
608
  end
@@ -3,7 +3,7 @@
3
3
  module Puma
4
4
  # Rack::CommonLogger forwards every request to the given +app+, and
5
5
  # logs a line in the
6
- # {Apache common log format}[https://httpd.apache.org/docs/1.3/logs.html#common]
6
+ # {Apache common log format}[https://httpd.apache.org/docs/2.4/logs.html#common]
7
7
  # to the +logger+.
8
8
  #
9
9
  # If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
@@ -16,7 +16,7 @@ module Puma
16
16
  # (which is called without arguments in order to make the error appear for
17
17
  # sure)
18
18
  class CommonLogger
19
- # Common Log Format: https://httpd.apache.org/docs/1.3/logs.html#common
19
+ # Common Log Format: https://httpd.apache.org/docs/2.4/logs.html#common
20
20
  #
21
21
  # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
22
22
  #
@@ -25,10 +25,17 @@ module Puma
25
25
 
26
26
  HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
27
27
 
28
- CONTENT_LENGTH = 'Content-Length'.freeze
29
- PATH_INFO = 'PATH_INFO'.freeze
30
- QUERY_STRING = 'QUERY_STRING'.freeze
31
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
28
+ LOG_TIME_FORMAT = '%d/%b/%Y:%H:%M:%S %z'
29
+
30
+ CONTENT_LENGTH = 'Content-Length' # should be lower case from app,
31
+ # Util::HeaderHash allows mixed
32
+ HTTP_VERSION = Const::HTTP_VERSION
33
+ HTTP_X_FORWARDED_FOR = Const::HTTP_X_FORWARDED_FOR
34
+ PATH_INFO = Const::PATH_INFO
35
+ QUERY_STRING = Const::QUERY_STRING
36
+ REMOTE_ADDR = Const::REMOTE_ADDR
37
+ REMOTE_USER = 'REMOTE_USER'
38
+ REQUEST_METHOD = Const::REQUEST_METHOD
32
39
 
33
40
  def initialize(app, logger=nil)
34
41
  @app = app
@@ -57,13 +64,13 @@ module Puma
57
64
  now = Time.now
58
65
 
59
66
  msg = HIJACK_FORMAT % [
60
- env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
61
- env["REMOTE_USER"] || "-",
62
- now.strftime("%d/%b/%Y %H:%M:%S"),
67
+ env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
68
+ env[REMOTE_USER] || "-",
69
+ now.strftime(LOG_TIME_FORMAT),
63
70
  env[REQUEST_METHOD],
64
71
  env[PATH_INFO],
65
72
  env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
66
- env["HTTP_VERSION"],
73
+ env[HTTP_VERSION],
67
74
  now - began_at ]
68
75
 
69
76
  write(msg)
@@ -74,13 +81,13 @@ module Puma
74
81
  length = extract_content_length(header)
75
82
 
76
83
  msg = FORMAT % [
77
- env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
78
- env["REMOTE_USER"] || "-",
79
- now.strftime("%d/%b/%Y:%H:%M:%S %z"),
84
+ env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
85
+ env[REMOTE_USER] || "-",
86
+ now.strftime(LOG_TIME_FORMAT),
80
87
  env[REQUEST_METHOD],
81
88
  env[PATH_INFO],
82
89
  env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
83
- env["HTTP_VERSION"],
90
+ env[HTTP_VERSION],
84
91
  status.to_s[0..3],
85
92
  length,
86
93
  now - began_at ]
@@ -1,21 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/rack/builder'
4
- require 'puma/plugin'
5
- require 'puma/const'
3
+ require_relative 'rack/builder'
4
+ require_relative 'plugin'
5
+ require_relative 'const'
6
+ require_relative 'dsl'
6
7
 
7
8
  module Puma
8
-
9
- module ConfigDefault
10
- DefaultRackup = "config.ru"
11
-
12
- DefaultTCPHost = "0.0.0.0"
13
- DefaultTCPPort = 9292
14
- DefaultWorkerCheckInterval = 5
15
- DefaultWorkerTimeout = 60
16
- DefaultWorkerShutdownTimeout = 30
17
- end
18
-
19
9
  # A class used for storing "leveled" configuration options.
20
10
  #
21
11
  # In this class any "user" specified options take precedence over any
@@ -136,7 +126,52 @@ module Puma
136
126
  # is done because an environment variable may have been modified while loading
137
127
  # configuration files.
138
128
  class Configuration
139
- include ConfigDefault
129
+ DEFAULTS = {
130
+ auto_trim_time: 30,
131
+ binds: ['tcp://0.0.0.0:9292'.freeze],
132
+ clean_thread_locals: false,
133
+ debug: false,
134
+ early_hints: nil,
135
+ environment: 'development'.freeze,
136
+ # Number of seconds to wait until we get the first data for the request.
137
+ first_data_timeout: 30,
138
+ # Number of seconds to wait until the next request before shutting down.
139
+ idle_timeout: nil,
140
+ io_selector_backend: :auto,
141
+ log_requests: false,
142
+ logger: STDOUT,
143
+ # How many requests to attempt inline before sending a client back to
144
+ # the reactor to be subject to normal ordering. The idea here is that
145
+ # we amortize the cost of going back to the reactor for a well behaved
146
+ # but very "greedy" client across 10 requests. This prevents a not
147
+ # well behaved client from monopolizing the thread forever.
148
+ max_fast_inline: 10,
149
+ max_threads: Puma.mri? ? 5 : 16,
150
+ min_threads: 0,
151
+ mode: :http,
152
+ mutate_stdout_and_stderr_to_sync_on_write: true,
153
+ out_of_band: [],
154
+ # Number of seconds for another request within a persistent session.
155
+ persistent_timeout: 20,
156
+ queue_requests: true,
157
+ rackup: 'config.ru'.freeze,
158
+ raise_exception_on_sigterm: true,
159
+ reaping_time: 1,
160
+ remote_address: :socket,
161
+ silence_single_worker_warning: false,
162
+ silence_fork_callback_warning: false,
163
+ tag: File.basename(Dir.getwd),
164
+ tcp_host: '0.0.0.0'.freeze,
165
+ tcp_port: 9292,
166
+ wait_for_less_busy_worker: 0.005,
167
+ worker_boot_timeout: 60,
168
+ worker_check_interval: 5,
169
+ worker_culling_strategy: :youngest,
170
+ worker_shutdown_timeout: 30,
171
+ worker_timeout: 60,
172
+ workers: 0,
173
+ http_content_length_limit: nil
174
+ }
140
175
 
141
176
  def initialize(user_options={}, default_options = {}, &block)
142
177
  default_options = self.puma_default_options.merge(default_options)
@@ -181,37 +216,22 @@ module Puma
181
216
  self
182
217
  end
183
218
 
184
- # @version 5.0.0
185
- def default_max_threads
186
- Puma.mri? ? 5 : 16
219
+ def puma_default_options
220
+ defaults = DEFAULTS.dup
221
+ puma_options_from_env.each { |k,v| defaults[k] = v if v }
222
+ defaults
187
223
  end
188
224
 
189
- def puma_default_options
225
+ def puma_options_from_env
226
+ min = ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS']
227
+ max = ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS']
228
+ workers = ENV['WEB_CONCURRENCY']
229
+
190
230
  {
191
- :min_threads => Integer(ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS'] || 0),
192
- :max_threads => Integer(ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS'] || default_max_threads),
193
- :log_requests => false,
194
- :debug => false,
195
- :binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
196
- :workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
197
- :silence_single_worker_warning => false,
198
- :mode => :http,
199
- :worker_check_interval => DefaultWorkerCheckInterval,
200
- :worker_timeout => DefaultWorkerTimeout,
201
- :worker_boot_timeout => DefaultWorkerTimeout,
202
- :worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
203
- :worker_culling_strategy => :youngest,
204
- :remote_address => :socket,
205
- :tag => method(:infer_tag),
206
- :environment => -> { ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' },
207
- :rackup => DefaultRackup,
208
- :logger => STDOUT,
209
- :persistent_timeout => Const::PERSISTENT_TIMEOUT,
210
- :first_data_timeout => Const::FIRST_DATA_TIMEOUT,
211
- :raise_exception_on_sigterm => true,
212
- :max_fast_inline => Const::MAX_FAST_INLINE,
213
- :io_selector_backend => :auto,
214
- :mutate_stdout_and_stderr_to_sync_on_write => true,
231
+ min_threads: min && Integer(min),
232
+ max_threads: max && Integer(max),
233
+ workers: workers && Integer(workers),
234
+ environment: ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'],
215
235
  }
216
236
  end
217
237
 
@@ -227,7 +247,7 @@ module Puma
227
247
  return [] if files == ['-']
228
248
  return files if files.any?
229
249
 
230
- first_default_file = %W(config/puma/#{environment_str}.rb config/puma.rb).find do |f|
250
+ first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
231
251
  File.exist?(f)
232
252
  end
233
253
 
@@ -270,7 +290,7 @@ module Puma
270
290
  found = options[:app] || load_rackup
271
291
 
272
292
  if @options[:log_requests]
273
- require 'puma/commonlogger'
293
+ require_relative 'commonlogger'
274
294
  logger = @options[:logger]
275
295
  found = CommonLogger.new(found, logger)
276
296
  end
@@ -283,21 +303,25 @@ module Puma
283
303
  @options[:environment]
284
304
  end
285
305
 
286
- def environment_str
287
- environment.respond_to?(:call) ? environment.call : environment
288
- end
289
-
290
306
  def load_plugin(name)
291
307
  @plugins.create name
292
308
  end
293
309
 
294
- def run_hooks(key, arg, events)
310
+ # @param key [:Symbol] hook to run
311
+ # @param arg [Launcher, Int] `:on_restart` passes Launcher
312
+ #
313
+ def run_hooks(key, arg, log_writer, hook_data = nil)
295
314
  @options.all_of(key).each do |b|
296
315
  begin
297
- b.call arg
316
+ if Array === b
317
+ hook_data[b[1]] ||= Hash.new
318
+ b[0].call arg, hook_data[b[1]]
319
+ else
320
+ b.call arg
321
+ end
298
322
  rescue => e
299
- events.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
300
- events.debug e.backtrace.join("\n")
323
+ log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
324
+ log_writer.debug e.backtrace.join("\n")
301
325
  end
302
326
  end
303
327
  end
@@ -315,10 +339,6 @@ module Puma
315
339
 
316
340
  private
317
341
 
318
- def infer_tag
319
- File.basename(Dir.getwd)
320
- end
321
-
322
342
  # Load and use the normal Rack builder if we can, otherwise
323
343
  # fallback to our minimal version.
324
344
  def rack_builder
@@ -367,5 +387,3 @@ module Puma
367
387
  end
368
388
  end
369
389
  end
370
-
371
- require 'puma/dsl'