puma 5.5.2 → 6.3.0

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +336 -3
  3. data/README.md +61 -16
  4. data/bin/puma-wild +1 -1
  5. data/docs/architecture.md +4 -4
  6. data/docs/compile_options.md +34 -0
  7. data/docs/fork_worker.md +1 -3
  8. data/docs/nginx.md +1 -1
  9. data/docs/signals.md +1 -0
  10. data/docs/systemd.md +1 -2
  11. data/docs/testing_benchmarks_local_files.md +150 -0
  12. data/docs/testing_test_rackup_ci_files.md +36 -0
  13. data/ext/puma_http11/extconf.rb +28 -14
  14. data/ext/puma_http11/http11_parser.c +1 -1
  15. data/ext/puma_http11/http11_parser.h +1 -1
  16. data/ext/puma_http11/http11_parser.java.rl +2 -2
  17. data/ext/puma_http11/http11_parser.rl +2 -2
  18. data/ext/puma_http11/http11_parser_common.rl +2 -2
  19. data/ext/puma_http11/mini_ssl.c +135 -23
  20. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  21. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  22. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +188 -102
  23. data/ext/puma_http11/puma_http11.c +18 -10
  24. data/lib/puma/app/status.rb +7 -4
  25. data/lib/puma/binder.rb +62 -51
  26. data/lib/puma/cli.rb +19 -20
  27. data/lib/puma/client.rb +108 -26
  28. data/lib/puma/cluster/worker.rb +23 -16
  29. data/lib/puma/cluster/worker_handle.rb +8 -1
  30. data/lib/puma/cluster.rb +62 -41
  31. data/lib/puma/commonlogger.rb +21 -14
  32. data/lib/puma/configuration.rb +76 -55
  33. data/lib/puma/const.rb +133 -97
  34. data/lib/puma/control_cli.rb +21 -18
  35. data/lib/puma/detect.rb +12 -2
  36. data/lib/puma/dsl.rb +270 -55
  37. data/lib/puma/error_logger.rb +18 -9
  38. data/lib/puma/events.rb +6 -126
  39. data/lib/puma/io_buffer.rb +39 -4
  40. data/lib/puma/jruby_restart.rb +2 -1
  41. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  42. data/lib/puma/launcher.rb +114 -175
  43. data/lib/puma/log_writer.rb +147 -0
  44. data/lib/puma/minissl/context_builder.rb +30 -16
  45. data/lib/puma/minissl.rb +126 -17
  46. data/lib/puma/null_io.rb +5 -0
  47. data/lib/puma/plugin/systemd.rb +90 -0
  48. data/lib/puma/plugin/tmp_restart.rb +1 -1
  49. data/lib/puma/plugin.rb +1 -1
  50. data/lib/puma/rack/builder.rb +6 -6
  51. data/lib/puma/rack_default.rb +19 -4
  52. data/lib/puma/reactor.rb +19 -10
  53. data/lib/puma/request.rb +365 -161
  54. data/lib/puma/runner.rb +55 -22
  55. data/lib/puma/sd_notify.rb +149 -0
  56. data/lib/puma/server.rb +91 -94
  57. data/lib/puma/single.rb +13 -11
  58. data/lib/puma/state_file.rb +39 -7
  59. data/lib/puma/thread_pool.rb +25 -21
  60. data/lib/puma/util.rb +12 -14
  61. data/lib/puma.rb +12 -11
  62. data/lib/rack/handler/puma.rb +113 -86
  63. data/tools/Dockerfile +1 -1
  64. metadata +11 -6
  65. data/lib/puma/queue_close.rb +0 -26
  66. data/lib/puma/systemd.rb +0 -46
@@ -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 launcher, launcher.events
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
@@ -33,8 +35,8 @@ module Puma
33
35
  Signal.trap "SIGINT", "IGNORE"
34
36
  Signal.trap "SIGCHLD", "DEFAULT"
35
37
 
36
- Thread.new do
37
- Puma.set_thread_name "worker check pipe"
38
+ Thread.new do
39
+ Puma.set_thread_name "wrkr check"
38
40
  @check_pipe.wait_readable
39
41
  log "! Detected parent died, dying"
40
42
  exit! 1
@@ -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
- @launcher.config.run_hooks :before_worker_boot, index, @launcher.events
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.backtrace[0]
63
+ log e
64
+ log e.backtrace.join("\n ")
62
65
  exit 1
63
66
  end
64
67
 
@@ -76,15 +79,14 @@ module Puma
76
79
  end
77
80
 
78
81
  Thread.new do
79
- Puma.set_thread_name "worker fork pipe"
82
+ Puma.set_thread_name "wrkr fork"
80
83
  while (idx = @fork_pipe.gets)
81
84
  idx = idx.to_i
82
85
  if idx == -1 # stop server
83
86
  if restart_server.length > 0
84
87
  restart_server.clear
85
88
  server.begin_restart(true)
86
- @launcher.config.run_hooks :before_refork, nil, @launcher.events
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
@@ -113,8 +115,13 @@ module Puma
113
115
 
114
116
  while restart_server.pop
115
117
  server_thread = server.run
118
+
119
+ if @log_writer.debug? && index == 0
120
+ debug_loaded_extensions "Loaded Extensions - worker 0:"
121
+ end
122
+
116
123
  stat_thread ||= Thread.new(@worker_write) do |io|
117
- Puma.set_thread_name "stat payload"
124
+ Puma.set_thread_name "stat pld"
118
125
  base_payload = "p#{Process.pid}"
119
126
 
120
127
  while true
@@ -130,7 +137,7 @@ module Puma
130
137
  Puma::Util.purge_interrupt_queue
131
138
  break
132
139
  end
133
- sleep Const::WORKER_CHECK_INTERVAL
140
+ sleep @options[:worker_check_interval]
134
141
  end
135
142
  end
136
143
  server_thread.join
@@ -138,7 +145,7 @@ module Puma
138
145
 
139
146
  # Invoke any worker shutdown hooks so they can prevent the worker
140
147
  # exiting until any background operations are completed
141
- @launcher.config.run_hooks :before_worker_shutdown, index, @launcher.events
148
+ @config.run_hooks(:before_worker_shutdown, index, @log_writer, @hook_data)
142
149
  ensure
143
150
  @worker_write << "t#{Process.pid}\n" rescue nil
144
151
  @worker_write.close
@@ -147,7 +154,7 @@ module Puma
147
154
  private
148
155
 
149
156
  def spawn_worker(idx)
150
- @launcher.config.run_hooks :before_worker_fork, idx, @launcher.events
157
+ @config.run_hooks(:before_worker_fork, idx, @log_writer, @hook_data)
151
158
 
152
159
  pid = fork do
153
160
  new_worker = Worker.new index: idx,
@@ -165,7 +172,7 @@ module Puma
165
172
  exit! 1
166
173
  end
167
174
 
168
- @launcher.config.run_hooks :after_worker_fork, idx, @launcher.events
175
+ @config.run_hooks(:after_worker_fork, idx, @log_writer, @hook_data)
169
176
  pid
170
177
  end
171
178
  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
@@ -40,6 +43,10 @@ module Puma
40
43
  @stage = :booted
41
44
  end
42
45
 
46
+ def term!
47
+ @term = true
48
+ end
49
+
43
50
  def term?
44
51
  @term
45
52
  end
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 }
@@ -92,7 +94,7 @@ module Puma
92
94
 
93
95
  # @version 5.0.0
94
96
  def spawn_worker(idx, master)
95
- @launcher.config.run_hooks :before_worker_fork, idx, @launcher.events
97
+ @config.run_hooks(:before_worker_fork, idx, @log_writer)
96
98
 
97
99
  pid = fork { worker(idx, master) }
98
100
  if !pid
@@ -101,31 +103,49 @@ module Puma
101
103
  exit! 1
102
104
  end
103
105
 
104
- @launcher.config.run_hooks :after_worker_fork, idx, @launcher.events
106
+ @config.run_hooks(:after_worker_fork, idx, @log_writer)
105
107
  pid
106
108
  end
107
109
 
108
110
  def cull_workers
109
111
  diff = @workers.size - @options[:workers]
110
112
  return if diff < 1
113
+ debug "Culling #{diff} workers"
111
114
 
112
- debug "Culling #{diff.inspect} workers"
113
-
114
- workers_to_cull = @workers[-diff,diff]
115
- debug "Workers to cull: #{workers_to_cull.inspect}"
115
+ workers = workers_to_cull(diff)
116
+ debug "Workers to cull: #{workers.inspect}"
116
117
 
117
- workers_to_cull.each do |worker|
118
+ workers.each do |worker|
118
119
  log "- Worker #{worker.index} (PID: #{worker.pid}) terminating"
119
120
  worker.term
120
121
  end
121
122
  end
122
123
 
124
+ def workers_to_cull(diff)
125
+ workers = @workers.sort_by(&:started_at)
126
+
127
+ # In fork_worker mode, worker 0 acts as our master process.
128
+ # We should avoid culling it to preserve copy-on-write memory gains.
129
+ workers.reject! { |w| w.index == 0 } if @options[:fork_worker]
130
+
131
+ workers[cull_start_index(diff), diff]
132
+ end
133
+
134
+ def cull_start_index(diff)
135
+ case @options[:worker_culling_strategy]
136
+ when :oldest
137
+ 0
138
+ else # :youngest
139
+ -diff
140
+ end
141
+ end
142
+
123
143
  # @!attribute [r] next_worker_index
124
144
  def next_worker_index
125
- all_positions = 0...@options[:workers]
126
- occupied_positions = @workers.map { |w| w.index }
127
- available_positions = all_positions.to_a - occupied_positions
128
- available_positions.first
145
+ occupied_positions = @workers.map(&:index)
146
+ idx = 0
147
+ idx += 1 until !occupied_positions.include?(idx)
148
+ idx
129
149
  end
130
150
 
131
151
  def all_workers_booted?
@@ -135,7 +155,7 @@ module Puma
135
155
  def check_workers
136
156
  return if @next_check >= Time.now
137
157
 
138
- @next_check = Time.now + Const::WORKER_CHECK_INTERVAL
158
+ @next_check = Time.now + @options[:worker_check_interval]
139
159
 
140
160
  timeout_workers
141
161
  wait_workers
@@ -158,10 +178,10 @@ module Puma
158
178
  end
159
179
  end
160
180
 
161
- @next_check = [
162
- @workers.reject(&:term?).map(&:ping_timeout).min,
163
- @next_check
164
- ].compact.min
181
+ t = @workers.reject(&:term?)
182
+ t.map!(&:ping_timeout)
183
+
184
+ @next_check = [t.min, @next_check].compact.min
165
185
  end
166
186
 
167
187
  def worker(index, master)
@@ -191,8 +211,8 @@ module Puma
191
211
  stop
192
212
  end
193
213
 
194
- def phased_restart
195
- return false if @options[:preload_app]
214
+ def phased_restart(refork = false)
215
+ return false if @options[:preload_app] && !refork
196
216
 
197
217
  @phased_restart = true
198
218
  wakeup!
@@ -208,7 +228,7 @@ module Puma
208
228
  def stop_blocked
209
229
  @status = :stop if @status == :run
210
230
  wakeup!
211
- @control.stop(true) if @control
231
+ @control&.stop true
212
232
  Process.waitall
213
233
  end
214
234
 
@@ -230,24 +250,24 @@ module Puma
230
250
  old_worker_count = @workers.count { |w| w.phase != @phase }
231
251
  worker_status = @workers.map do |w|
232
252
  {
233
- started_at: w.started_at.utc.iso8601,
253
+ started_at: utc_iso8601(w.started_at),
234
254
  pid: w.pid,
235
255
  index: w.index,
236
256
  phase: w.phase,
237
257
  booted: w.booted?,
238
- last_checkin: w.last_checkin.utc.iso8601,
258
+ last_checkin: utc_iso8601(w.last_checkin),
239
259
  last_status: w.last_status,
240
260
  }
241
261
  end
242
262
 
243
263
  {
244
- started_at: @started_at.utc.iso8601,
264
+ started_at: utc_iso8601(@started_at),
245
265
  workers: @workers.size,
246
266
  phase: @phase,
247
267
  booted_workers: worker_status.count { |w| w[:booted] },
248
268
  old_workers: old_worker_count,
249
269
  worker_status: worker_status,
250
- }
270
+ }.merge(super)
251
271
  end
252
272
 
253
273
  def preload?
@@ -259,7 +279,7 @@ module Puma
259
279
  if (worker = @workers.find { |w| w.index == 0 })
260
280
  worker.phase += 1
261
281
  end
262
- phased_restart
282
+ phased_restart(true)
263
283
  end
264
284
 
265
285
  # We do this in a separate method to keep the lambda scope
@@ -272,7 +292,7 @@ module Puma
272
292
 
273
293
  # Auto-fork after the specified number of requests.
274
294
  if (fork_requests = @options[:fork_worker].to_i) > 0
275
- @launcher.events.register(:ping!) do |w|
295
+ @events.register(:ping!) do |w|
276
296
  fork_worker! if w.index == 0 &&
277
297
  w.phase == 0 &&
278
298
  w.last_status[:requests_count] >= fork_requests
@@ -354,12 +374,12 @@ module Puma
354
374
  else
355
375
  log "* Restarts: (\u2714) hot (\u2714) phased"
356
376
 
357
- unless @launcher.config.app_configured?
377
+ unless @config.app_configured?
358
378
  error "No application configured, nothing to run"
359
379
  exit 1
360
380
  end
361
381
 
362
- @launcher.binder.parse @options[:binds], self
382
+ @launcher.binder.parse @options[:binds]
363
383
  end
364
384
 
365
385
  read, @wakeup = Puma::Util.pipe
@@ -391,8 +411,7 @@ module Puma
391
411
 
392
412
  @master_read, @worker_write = read, @wakeup
393
413
 
394
- @launcher.config.run_hooks :before_fork, nil, @launcher.events
395
- Puma::Util.nakayoshi_gc @events if @options[:nakayoshi_fork]
414
+ @config.run_hooks(:before_fork, nil, @log_writer)
396
415
 
397
416
  spawn_workers
398
417
 
@@ -440,14 +459,15 @@ module Puma
440
459
  workers_not_booted -= 1
441
460
  when "e"
442
461
  # external term, see worker method, Signal.trap "SIGTERM"
443
- w.instance_variable_set :@term, true
462
+ w.term!
444
463
  when "t"
445
464
  w.term unless w.term?
446
465
  when "p"
447
466
  w.ping!(result.sub(/^\d+/,'').chomp)
448
- @launcher.events.fire(:ping!, w)
467
+ @events.fire(:ping!, w)
449
468
  if !booted && @workers.none? {|worker| worker.last_status.empty?}
450
- @launcher.events.fire_on_booted!
469
+ @events.fire_on_booted!
470
+ debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
451
471
  booted = true
452
472
  end
453
473
  end
@@ -457,6 +477,7 @@ module Puma
457
477
  end
458
478
  if in_phased_restart && workers_not_booted.zero?
459
479
  @events.fire_on_booted!
480
+ debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
460
481
  in_phased_restart = false
461
482
  end
462
483
 
@@ -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,20 +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
+ # note that dsl is loaded at end of file, requires ConfigDefault constants
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
- DefaultWorkerTimeout = 60
15
- DefaultWorkerShutdownTimeout = 30
16
- end
17
-
18
9
  # A class used for storing "leveled" configuration options.
19
10
  #
20
11
  # In this class any "user" specified options take precedence over any
@@ -135,7 +126,50 @@ module Puma
135
126
  # is done because an environment variable may have been modified while loading
136
127
  # configuration files.
137
128
  class Configuration
138
- 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
+ io_selector_backend: :auto,
139
+ log_requests: false,
140
+ logger: STDOUT,
141
+ # How many requests to attempt inline before sending a client back to
142
+ # the reactor to be subject to normal ordering. The idea here is that
143
+ # we amortize the cost of going back to the reactor for a well behaved
144
+ # but very "greedy" client across 10 requests. This prevents a not
145
+ # well behaved client from monopolizing the thread forever.
146
+ max_fast_inline: 10,
147
+ max_threads: Puma.mri? ? 5 : 16,
148
+ min_threads: 0,
149
+ mode: :http,
150
+ mutate_stdout_and_stderr_to_sync_on_write: true,
151
+ out_of_band: [],
152
+ # Number of seconds for another request within a persistent session.
153
+ persistent_timeout: 20,
154
+ queue_requests: true,
155
+ rackup: 'config.ru'.freeze,
156
+ raise_exception_on_sigterm: true,
157
+ reaping_time: 1,
158
+ remote_address: :socket,
159
+ silence_single_worker_warning: false,
160
+ silence_fork_callback_warning: false,
161
+ tag: File.basename(Dir.getwd),
162
+ tcp_host: '0.0.0.0'.freeze,
163
+ tcp_port: 9292,
164
+ wait_for_less_busy_worker: 0.005,
165
+ worker_boot_timeout: 60,
166
+ worker_check_interval: 5,
167
+ worker_culling_strategy: :youngest,
168
+ worker_shutdown_timeout: 30,
169
+ worker_timeout: 60,
170
+ workers: 0,
171
+ http_content_length_limit: nil
172
+ }
139
173
 
140
174
  def initialize(user_options={}, default_options = {}, &block)
141
175
  default_options = self.puma_default_options.merge(default_options)
@@ -180,35 +214,22 @@ module Puma
180
214
  self
181
215
  end
182
216
 
183
- # @version 5.0.0
184
- def default_max_threads
185
- Puma.mri? ? 5 : 16
217
+ def puma_default_options
218
+ defaults = DEFAULTS.dup
219
+ puma_options_from_env.each { |k,v| defaults[k] = v if v }
220
+ defaults
186
221
  end
187
222
 
188
- def puma_default_options
223
+ def puma_options_from_env
224
+ min = ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS']
225
+ max = ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS']
226
+ workers = ENV['WEB_CONCURRENCY']
227
+
189
228
  {
190
- :min_threads => Integer(ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS'] || 0),
191
- :max_threads => Integer(ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS'] || default_max_threads),
192
- :log_requests => false,
193
- :debug => false,
194
- :binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
195
- :workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
196
- :silence_single_worker_warning => false,
197
- :mode => :http,
198
- :worker_timeout => DefaultWorkerTimeout,
199
- :worker_boot_timeout => DefaultWorkerTimeout,
200
- :worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
201
- :remote_address => :socket,
202
- :tag => method(:infer_tag),
203
- :environment => -> { ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' },
204
- :rackup => DefaultRackup,
205
- :logger => STDOUT,
206
- :persistent_timeout => Const::PERSISTENT_TIMEOUT,
207
- :first_data_timeout => Const::FIRST_DATA_TIMEOUT,
208
- :raise_exception_on_sigterm => true,
209
- :max_fast_inline => Const::MAX_FAST_INLINE,
210
- :io_selector_backend => :auto,
211
- :mutate_stdout_and_stderr_to_sync_on_write => true,
229
+ min_threads: min && Integer(min),
230
+ max_threads: max && Integer(max),
231
+ workers: workers && Integer(workers),
232
+ environment: ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'],
212
233
  }
213
234
  end
214
235
 
@@ -224,7 +245,7 @@ module Puma
224
245
  return [] if files == ['-']
225
246
  return files if files.any?
226
247
 
227
- first_default_file = %W(config/puma/#{environment_str}.rb config/puma.rb).find do |f|
248
+ first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
228
249
  File.exist?(f)
229
250
  end
230
251
 
@@ -267,7 +288,7 @@ module Puma
267
288
  found = options[:app] || load_rackup
268
289
 
269
290
  if @options[:log_requests]
270
- require 'puma/commonlogger'
291
+ require_relative 'commonlogger'
271
292
  logger = @options[:logger]
272
293
  found = CommonLogger.new(found, logger)
273
294
  end
@@ -280,21 +301,25 @@ module Puma
280
301
  @options[:environment]
281
302
  end
282
303
 
283
- def environment_str
284
- environment.respond_to?(:call) ? environment.call : environment
285
- end
286
-
287
304
  def load_plugin(name)
288
305
  @plugins.create name
289
306
  end
290
307
 
291
- def run_hooks(key, arg, events)
308
+ # @param key [:Symbol] hook to run
309
+ # @param arg [Launcher, Int] `:on_restart` passes Launcher
310
+ #
311
+ def run_hooks(key, arg, log_writer, hook_data = nil)
292
312
  @options.all_of(key).each do |b|
293
313
  begin
294
- b.call arg
314
+ if Array === b
315
+ hook_data[b[1]] ||= Hash.new
316
+ b[0].call arg, hook_data[b[1]]
317
+ else
318
+ b.call arg
319
+ end
295
320
  rescue => e
296
- events.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
297
- events.debug e.backtrace.join("\n")
321
+ log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
322
+ log_writer.debug e.backtrace.join("\n")
298
323
  end
299
324
  end
300
325
  end
@@ -312,10 +337,6 @@ module Puma
312
337
 
313
338
  private
314
339
 
315
- def infer_tag
316
- File.basename(Dir.getwd)
317
- end
318
-
319
340
  # Load and use the normal Rack builder if we can, otherwise
320
341
  # fallback to our minimal version.
321
342
  def rack_builder
@@ -365,4 +386,4 @@ module Puma
365
386
  end
366
387
  end
367
388
 
368
- require 'puma/dsl'
389
+ require_relative 'dsl'