puma 6.6.0 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +309 -5
  3. data/README.md +41 -42
  4. data/docs/5.0-Upgrade.md +98 -0
  5. data/docs/6.0-Upgrade.md +56 -0
  6. data/docs/7.0-Upgrade.md +52 -0
  7. data/docs/8.0-Upgrade.md +100 -0
  8. data/docs/deployment.md +58 -23
  9. data/docs/fork_worker.md +5 -5
  10. data/docs/grpc.md +62 -0
  11. data/docs/images/favicon.svg +1 -0
  12. data/docs/images/running-puma.svg +1 -0
  13. data/docs/images/standard-logo.svg +1 -0
  14. data/docs/jungle/README.md +1 -1
  15. data/docs/kubernetes.md +11 -16
  16. data/docs/plugins.md +2 -2
  17. data/docs/restart.md +2 -2
  18. data/docs/signals.md +21 -21
  19. data/docs/stats.md +4 -3
  20. data/docs/systemd.md +4 -4
  21. data/ext/puma_http11/extconf.rb +2 -17
  22. data/ext/puma_http11/http11_parser.java.rl +51 -65
  23. data/ext/puma_http11/mini_ssl.c +18 -8
  24. data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
  25. data/ext/puma_http11/org/jruby/puma/Http11.java +174 -102
  26. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +71 -85
  27. data/ext/puma_http11/puma_http11.c +122 -118
  28. data/lib/puma/app/status.rb +10 -2
  29. data/lib/puma/binder.rb +12 -10
  30. data/lib/puma/cli.rb +4 -6
  31. data/lib/puma/client.rb +205 -131
  32. data/lib/puma/client_env.rb +171 -0
  33. data/lib/puma/cluster/worker.rb +17 -17
  34. data/lib/puma/cluster/worker_handle.rb +38 -7
  35. data/lib/puma/cluster.rb +44 -30
  36. data/lib/puma/cluster_accept_loop_delay.rb +91 -0
  37. data/lib/puma/commonlogger.rb +3 -3
  38. data/lib/puma/configuration.rb +173 -58
  39. data/lib/puma/const.rb +11 -11
  40. data/lib/puma/control_cli.rb +7 -3
  41. data/lib/puma/detect.rb +13 -0
  42. data/lib/puma/dsl.rb +225 -108
  43. data/lib/puma/error_logger.rb +3 -1
  44. data/lib/puma/events.rb +25 -10
  45. data/lib/puma/io_buffer.rb +8 -4
  46. data/lib/puma/launcher/bundle_pruner.rb +3 -5
  47. data/lib/puma/launcher.rb +57 -53
  48. data/lib/puma/log_writer.rb +8 -2
  49. data/lib/puma/minissl.rb +0 -1
  50. data/lib/puma/plugin/systemd.rb +3 -3
  51. data/lib/puma/rack/urlmap.rb +1 -1
  52. data/lib/puma/reactor.rb +19 -13
  53. data/lib/puma/{request.rb → response.rb} +56 -212
  54. data/lib/puma/runner.rb +9 -18
  55. data/lib/puma/server.rb +182 -97
  56. data/lib/puma/server_plugin_control.rb +32 -0
  57. data/lib/puma/single.rb +7 -4
  58. data/lib/puma/state_file.rb +3 -2
  59. data/lib/puma/thread_pool.rb +170 -99
  60. data/lib/puma/util.rb +0 -7
  61. data/lib/puma.rb +10 -0
  62. data/lib/rack/handler/puma.rb +3 -3
  63. data/tools/Dockerfile +15 -5
  64. metadata +19 -7
  65. data/ext/puma_http11/ext_help.h +0 -15
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'socket'
4
+ require 'uri'
5
+
3
6
  require_relative 'plugin'
4
7
  require_relative 'const'
5
8
  require_relative 'dsl'
9
+ require_relative 'events'
6
10
 
7
11
  module Puma
8
12
  # A class used for storing "leveled" configuration options.
@@ -112,7 +116,7 @@ module Puma
112
116
  # config = Configuration.new({}) do |user_config, file_config, default_config|
113
117
  # user_config.port 3003
114
118
  # end
115
- # config.load
119
+ # config.clamp
116
120
  # puts config.options[:port]
117
121
  # # => 3003
118
122
  #
@@ -125,43 +129,48 @@ module Puma
125
129
  # is done because an environment variable may have been modified while loading
126
130
  # configuration files.
127
131
  class Configuration
132
+ class NotLoadedError < StandardError; end
133
+ class NotClampedError < StandardError; end
134
+
128
135
  DEFAULTS = {
129
136
  auto_trim_time: 30,
130
- binds: ['tcp://0.0.0.0:9292'.freeze],
131
- clean_thread_locals: false,
137
+ binds: ['tcp://[::]:9292'.freeze],
132
138
  debug: false,
133
- enable_keep_alives: true,
134
139
  early_hints: nil,
140
+ enable_keep_alives: true,
135
141
  environment: 'development'.freeze,
142
+ fiber_per_request: !!ENV.fetch("PUMA_FIBER_PER_REQUEST", false),
136
143
  # Number of seconds to wait until we get the first data for the request.
137
144
  first_data_timeout: 30,
145
+ force_shutdown_after: -1,
146
+ http_content_length_limit: nil,
138
147
  # Number of seconds to wait until the next request before shutting down.
139
148
  idle_timeout: nil,
140
149
  io_selector_backend: :auto,
141
150
  log_requests: false,
142
151
  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,
152
+ # Limits how many requests a keep alive connection can make.
153
+ # The connection will be closed after it reaches `max_keep_alive`
154
+ # requests.
155
+ max_io_threads: 0,
156
+ max_keep_alive: 999,
149
157
  max_threads: Puma.mri? ? 5 : 16,
150
158
  min_threads: 0,
151
159
  mode: :http,
152
160
  mutate_stdout_and_stderr_to_sync_on_write: true,
153
161
  out_of_band: [],
154
162
  # Number of seconds for another request within a persistent session.
155
- persistent_timeout: 20,
163
+ persistent_timeout: 65, # PUMA_PERSISTENT_TIMEOUT
164
+ prune_bundler: false,
156
165
  queue_requests: true,
157
166
  rackup: 'config.ru'.freeze,
158
167
  raise_exception_on_sigterm: true,
159
168
  reaping_time: 1,
160
169
  remote_address: :socket,
161
- silence_single_worker_warning: false,
162
170
  silence_fork_callback_warning: false,
171
+ silence_single_worker_warning: false,
163
172
  tag: File.basename(Dir.getwd),
164
- tcp_host: '0.0.0.0'.freeze,
173
+ tcp_host: '::'.freeze,
165
174
  tcp_port: 9292,
166
175
  wait_for_less_busy_worker: 0.005,
167
176
  worker_boot_timeout: 60,
@@ -170,30 +179,36 @@ module Puma
170
179
  worker_shutdown_timeout: 30,
171
180
  worker_timeout: 60,
172
181
  workers: 0,
173
- http_content_length_limit: nil
174
182
  }
175
183
 
176
184
  def initialize(user_options={}, default_options = {}, env = ENV, &block)
177
- default_options = self.puma_default_options(env).merge(default_options)
185
+ default_options = self.puma_default_options(env).merge(events: Events.new).merge(default_options)
178
186
 
179
- @options = UserFileDefaultOptions.new(user_options, default_options)
187
+ @_options = UserFileDefaultOptions.new(user_options, default_options)
180
188
  @plugins = PluginLoader.new
181
- @user_dsl = DSL.new(@options.user_options, self)
182
- @file_dsl = DSL.new(@options.file_options, self)
183
- @default_dsl = DSL.new(@options.default_options, self)
184
-
185
- if !@options[:prune_bundler]
186
- default_options[:preload_app] = (@options[:workers] > 1) && Puma.forkable?
187
- end
189
+ @events = @_options[:events] || Events.new
190
+ @hooks = {}
191
+ @user_dsl = DSL.new(@_options.user_options, self)
192
+ @file_dsl = DSL.new(@_options.file_options, self)
193
+ @default_dsl = DSL.new(@_options.default_options, self)
188
194
 
189
195
  @puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED'
190
196
 
191
197
  if block
192
198
  configure(&block)
193
199
  end
200
+
201
+ @loaded = false
202
+ @clamped = false
194
203
  end
195
204
 
196
- attr_reader :options, :plugins
205
+ attr_reader :plugins, :events, :hooks, :_options
206
+
207
+ def options
208
+ raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped
209
+
210
+ @_options
211
+ end
197
212
 
198
213
  def configure
199
214
  yield @user_dsl, @file_dsl, @default_dsl
@@ -206,7 +221,7 @@ module Puma
206
221
  def initialize_copy(other)
207
222
  @conf = nil
208
223
  @cli_options = nil
209
- @options = @options.dup
224
+ @_options = @_options.dup
210
225
  end
211
226
 
212
227
  def flatten
@@ -214,12 +229,14 @@ module Puma
214
229
  end
215
230
 
216
231
  def flatten!
217
- @options = @options.flatten
232
+ @_options = @_options.flatten
218
233
  self
219
234
  end
220
235
 
221
236
  def puma_default_options(env = ENV)
222
237
  defaults = DEFAULTS.dup
238
+ defaults[:tcp_host] = self.class.default_tcp_host
239
+ defaults[:binds] = [self.class.default_tcp_bind]
223
240
  puma_options_from_env(env).each { |k,v| defaults[k] = v if v }
224
241
  defaults
225
242
  end
@@ -227,34 +244,34 @@ module Puma
227
244
  def puma_options_from_env(env = ENV)
228
245
  min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
229
246
  max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
230
- workers = if env['WEB_CONCURRENCY'] == 'auto'
231
- require_processor_counter
232
- ::Concurrent.available_processor_count
233
- else
234
- env['WEB_CONCURRENCY']
235
- end
247
+ persistent_timeout = env['PUMA_PERSISTENT_TIMEOUT']
248
+ workers_env = env['WEB_CONCURRENCY']
249
+ workers = workers_env && workers_env.strip != "" ? parse_workers(workers_env.strip) : nil
236
250
 
237
251
  {
238
252
  min_threads: min && min != "" && Integer(min),
239
253
  max_threads: max && max != "" && Integer(max),
240
- workers: workers && workers != "" && Integer(workers),
254
+ persistent_timeout: persistent_timeout && persistent_timeout != "" && Integer(persistent_timeout),
255
+ workers: workers,
241
256
  environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
242
257
  }
243
258
  end
244
259
 
245
260
  def load
261
+ @loaded = true
246
262
  config_files.each { |config_file| @file_dsl._load_from(config_file) }
247
-
248
- @options
263
+ @_options
249
264
  end
250
265
 
251
266
  def config_files
252
- files = @options.all_of(:config_files)
267
+ raise NotLoadedError, "ensure load is called before accessing config_files" unless @loaded
268
+
269
+ files = @_options.all_of(:config_files)
253
270
 
254
271
  return [] if files == ['-']
255
272
  return files if files.any?
256
273
 
257
- first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
274
+ first_default_file = %W(config/puma/#{@_options[:environment]}.rb config/puma.rb).find do |f|
258
275
  File.exist?(f)
259
276
  end
260
277
 
@@ -262,9 +279,18 @@ module Puma
262
279
  end
263
280
 
264
281
  # Call once all configuration (included from rackup files)
265
- # is loaded to flesh out any defaults
282
+ # is loaded to finalize defaults and lock in the configuration.
283
+ #
284
+ # This also calls load if it hasn't been called yet.
266
285
  def clamp
267
- @options.finalize_values
286
+ load unless @loaded
287
+ run_mode_hooks
288
+ set_conditional_default_options
289
+ @_options.finalize_values
290
+ rewrite_unavailable_ipv6_binds!
291
+ @clamped = true
292
+ warn_hooks
293
+ options
268
294
  end
269
295
 
270
296
  # Injects the Configuration object into the env
@@ -283,11 +309,11 @@ module Puma
283
309
  # Indicate if there is a properly configured app
284
310
  #
285
311
  def app_configured?
286
- @options[:app] || File.exist?(rackup)
312
+ options[:app] || File.exist?(rackup)
287
313
  end
288
314
 
289
315
  def rackup
290
- @options[:rackup]
316
+ options[:rackup]
291
317
  end
292
318
 
293
319
  # Load the specified rackup file, pull options from
@@ -296,9 +322,9 @@ module Puma
296
322
  def app
297
323
  found = options[:app] || load_rackup
298
324
 
299
- if @options[:log_requests]
325
+ if options[:log_requests]
300
326
  require_relative 'commonlogger'
301
- logger = @options[:logger]
327
+ logger = options[:custom_logger] ? options[:custom_logger] : options[:logger]
302
328
  found = CommonLogger.new(found, logger)
303
329
  end
304
330
 
@@ -307,7 +333,7 @@ module Puma
307
333
 
308
334
  # Return which environment we're running in
309
335
  def environment
310
- @options[:environment]
336
+ options[:environment]
311
337
  end
312
338
 
313
339
  def load_plugin(name)
@@ -315,18 +341,19 @@ module Puma
315
341
  end
316
342
 
317
343
  # @param key [:Symbol] hook to run
318
- # @param arg [Launcher, Int] `:on_restart` passes Launcher
344
+ # @param arg [Launcher, Int] `:before_restart` passes Launcher
319
345
  #
320
346
  def run_hooks(key, arg, log_writer, hook_data = nil)
321
- log_writer.debug "Running #{key} hooks"
347
+ log_writer.debug { "Running #{key} hooks" }
322
348
 
323
- @options.all_of(key).each do |b|
349
+ options.all_of(key).each do |hook_options|
324
350
  begin
325
- if Array === b
326
- hook_data[b[1]] ||= Hash.new
327
- b[0].call arg, hook_data[b[1]]
351
+ block = hook_options[:block]
352
+ if id = hook_options[:id]
353
+ hook_data[id] ||= Hash.new
354
+ block.call arg, hook_data[id]
328
355
  else
329
- b.call arg
356
+ block.call arg
330
357
  end
331
358
  rescue => e
332
359
  log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
@@ -336,7 +363,24 @@ module Puma
336
363
  end
337
364
 
338
365
  def final_options
339
- @options.final_options
366
+ options.final_options
367
+ end
368
+
369
+ def self.default_tcp_host
370
+ ipv6_interface_available? ? Const::UNSPECIFIED_IPV6 : Const::UNSPECIFIED_IPV4
371
+ end
372
+
373
+ def self.default_tcp_bind(port = DEFAULTS[:tcp_port])
374
+ URI::Generic.build(scheme: 'tcp', host: default_tcp_host, port: Integer(port)).to_s
375
+ end
376
+
377
+ def self.ipv6_interface_available?
378
+ Socket.getifaddrs.any? do |ifaddr|
379
+ addr = ifaddr.addr
380
+ addr&.ipv6? && !addr&.ipv6_loopback?
381
+ end
382
+ rescue StandardError
383
+ false
340
384
  end
341
385
 
342
386
  def self.temp_path
@@ -346,18 +390,60 @@ module Puma
346
390
  "#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
347
391
  end
348
392
 
393
+ def self.random_token
394
+ require 'securerandom' unless defined?(SecureRandom)
395
+
396
+ SecureRandom.hex(16)
397
+ end
398
+
349
399
  private
350
400
 
401
+ def rewrite_unavailable_ipv6_binds!
402
+ return if self.class.ipv6_interface_available?
403
+
404
+ tried_ipv6_bind = false
405
+ bind_schemes = ['tcp', 'ssl']
406
+
407
+ @_options[:binds] = Array(@_options[:binds]).map do |bind|
408
+ uri = URI.parse(bind)
409
+ next bind unless bind_schemes.include?(uri.scheme)
410
+
411
+ host = uri.host&.delete_prefix('[')&.delete_suffix(']')
412
+ next bind unless host&.include?(':')
413
+
414
+ tried_ipv6_bind = true
415
+ next bind unless host == Const::UNSPECIFIED_IPV6
416
+
417
+ uri.host = Const::UNSPECIFIED_IPV4
418
+ uri.to_s
419
+ rescue URI::InvalidURIError
420
+ bind
421
+ end
422
+
423
+ warn "WARNING: IPv6 bind requested but no non-loopback IPv6 interface was detected" if tried_ipv6_bind
424
+ end
425
+
351
426
  def require_processor_counter
352
427
  require 'concurrent/utility/processor_counter'
353
428
  rescue LoadError
354
429
  warn <<~MESSAGE
355
- WEB_CONCURRENCY=auto requires the "concurrent-ruby" gem to be installed.
430
+ WEB_CONCURRENCY=auto or workers(:auto) requires the "concurrent-ruby" gem to be installed.
356
431
  Please add "concurrent-ruby" to your Gemfile.
357
432
  MESSAGE
358
433
  raise
359
434
  end
360
435
 
436
+ def parse_workers(value)
437
+ if value == :auto || value == 'auto'
438
+ require_processor_counter
439
+ Integer(::Concurrent.available_processor_count)
440
+ else
441
+ Integer(value)
442
+ end
443
+ rescue ArgumentError, TypeError
444
+ raise ArgumentError, "workers must be an Integer or :auto"
445
+ end
446
+
361
447
  # Load and use the normal Rack builder if we can, otherwise
362
448
  # fallback to our minimal version.
363
449
  def rack_builder
@@ -386,22 +472,51 @@ module Puma
386
472
  rack_app, rack_options = rack_builder.parse_file(rackup)
387
473
  rack_options = rack_options || {}
388
474
 
389
- @options.file_options.merge!(rack_options)
475
+ options.file_options.merge!(rack_options)
390
476
 
391
477
  config_ru_binds = []
392
478
  rack_options.each do |k, v|
393
479
  config_ru_binds << v if k.to_s.start_with?("bind")
394
480
  end
395
481
 
396
- @options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
482
+ options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
397
483
 
398
484
  rack_app
399
485
  end
400
486
 
401
- def self.random_token
402
- require 'securerandom' unless defined?(SecureRandom)
487
+ def run_mode_hooks
488
+ workers_before = @_options[:workers]
489
+ key = workers_before > 0 ? :cluster : :single
403
490
 
404
- SecureRandom.hex(16)
491
+ @_options.all_of(key).each(&:call)
492
+
493
+ unless @_options[:workers] == workers_before
494
+ raise "cannot change the number of workers inside a #{key} configuration hook"
495
+ end
496
+ end
497
+
498
+ def set_conditional_default_options
499
+ @_options.default_options[:preload_app] = !@_options[:prune_bundler] &&
500
+ (@_options[:workers] > 1) && Puma.forkable?
501
+ end
502
+
503
+ def warn_hooks
504
+ return if options[:workers] > 0
505
+ return if options[:silence_fork_callback_warning]
506
+
507
+ log_writer = LogWriter.stdio
508
+ @hooks.each_key do |hook|
509
+ options.all_of(hook).each do |hook_options|
510
+ next unless hook_options[:cluster_only]
511
+
512
+ log_writer.log(<<~MSG.tr("\n", " "))
513
+ Warning: The code in the `#{hook}` block will not execute
514
+ in the current Puma configuration. The `#{hook}` block only
515
+ executes in Puma's cluster mode. To fix this, either remove the
516
+ `#{hook}` call or increase Puma's worker count above zero.
517
+ MSG
518
+ end
519
+ end
405
520
  end
406
521
  end
407
522
  end
data/lib/puma/const.rb CHANGED
@@ -100,13 +100,11 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "6.6.0"
104
- CODE_NAME = "Return to Forever"
103
+ PUMA_VERSION = VERSION = "8.0.2"
104
+ CODE_NAME = "Into the Arena"
105
105
 
106
106
  PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
107
107
 
108
- FAST_TRACK_KA_TIMEOUT = 0.2
109
-
110
108
  # How long to wait when getting some write blocking on the socket when
111
109
  # sending data back
112
110
  WRITE_TIMEOUT = 10
@@ -125,9 +123,9 @@ module Puma
125
123
  # Indicate that we couldn't parse the request
126
124
  400 => "HTTP/1.1 400 Bad Request\r\n\r\n",
127
125
  # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
128
- 404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n",
126
+ 404 => "HTTP/1.1 404 Not Found\r\nconnection: close\r\n\r\n",
129
127
  # The standard empty 408 response for requests that timed out.
130
- 408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n",
128
+ 408 => "HTTP/1.1 408 Request Timeout\r\nconnection: close\r\n\r\n",
131
129
  # Indicate that there was an internal error, obviously.
132
130
  500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n",
133
131
  # Incorrect or invalid header value
@@ -230,6 +228,7 @@ module Puma
230
228
  RACK_INPUT = "rack.input"
231
229
  RACK_URL_SCHEME = "rack.url_scheme"
232
230
  RACK_AFTER_REPLY = "rack.after_reply"
231
+ RACK_RESPONSE_FINISHED = "rack.response_finished"
233
232
  PUMA_SOCKET = "puma.socket"
234
233
  PUMA_CONFIG = "puma.config"
235
234
  PUMA_PEERCERT = "puma.peercert"
@@ -252,14 +251,14 @@ module Puma
252
251
  KEEP_ALIVE = "keep-alive"
253
252
 
254
253
  CONTENT_LENGTH2 = "content-length"
255
- CONTENT_LENGTH_S = "Content-Length: "
254
+ CONTENT_LENGTH_S = "content-length: "
256
255
  TRANSFER_ENCODING = "transfer-encoding"
257
256
  TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING"
258
257
 
259
- CONNECTION_CLOSE = "Connection: close\r\n"
260
- CONNECTION_KEEP_ALIVE = "Connection: Keep-Alive\r\n"
258
+ CONNECTION_CLOSE = "connection: close\r\n"
259
+ CONNECTION_KEEP_ALIVE = "connection: keep-alive\r\n"
261
260
 
262
- TRANSFER_ENCODING_CHUNKED = "Transfer-Encoding: chunked\r\n"
261
+ TRANSFER_ENCODING_CHUNKED = "transfer-encoding: chunked\r\n"
263
262
  CLOSE_CHUNKED = "0\r\n\r\n"
264
263
 
265
264
  CHUNKED = "chunked"
@@ -292,7 +291,8 @@ module Puma
292
291
  # Banned keys of response header
293
292
  BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
294
293
 
295
- PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
294
+ PROXY_PROTOCOL_V1_REGEX = /\APROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
295
+ PROXY_PROTOCOL_V1_MAX_LENGTH = 107
296
296
 
297
297
  # All constants are prefixed with `PIPE_` to avoid name collisions.
298
298
  module PipeRequest
@@ -16,7 +16,7 @@ module Puma
16
16
  'gc' => nil,
17
17
  'gc-stats' => nil,
18
18
  'halt' => 'SIGQUIT',
19
- 'info' => 'SIGINFO',
19
+ 'info' => Puma.backtrace_signal,
20
20
  'phased-restart' => 'SIGUSR1',
21
21
  'refork' => 'SIGURG',
22
22
  'reload-worker-directory' => nil,
@@ -124,11 +124,15 @@ module Puma
124
124
  end
125
125
 
126
126
  if @config_file
127
+ # needed because neither `Puma::CLI` or `Puma::Server` are loaded
128
+ require_relative '../puma'
129
+
127
130
  require_relative 'configuration'
128
131
  require_relative 'log_writer'
129
132
 
130
133
  config = Puma::Configuration.new({ config_files: [@config_file] }, {} , env)
131
- config.load
134
+ config.clamp
135
+
132
136
  @state ||= config.options[:state]
133
137
  @control_url ||= config.options[:control_url]
134
138
  @control_auth_token ||= config.options[:control_auth_token]
@@ -248,7 +252,7 @@ module Puma
248
252
  @stdout.flush unless @stdout.sync
249
253
  return
250
254
  elsif sig.start_with? 'SIG'
251
- if Signal.list.key? sig.sub(/\ASIG/, '')
255
+ if Signal.list.key? sig.delete_prefix('SIG')
252
256
  Process.kill sig, @pid
253
257
  else
254
258
  raise "Signal '#{sig}' not available'"
data/lib/puma/detect.rb CHANGED
@@ -18,6 +18,8 @@ module Puma
18
18
 
19
19
  IS_LINUX = !(IS_OSX || IS_WINDOWS)
20
20
 
21
+ IS_ARM = RUBY_PLATFORM.include? 'aarch64'
22
+
21
23
  # @version 5.2.0
22
24
  IS_MRI = RUBY_ENGINE == 'ruby'
23
25
 
@@ -33,6 +35,13 @@ module Puma
33
35
  IS_WINDOWS
34
36
  end
35
37
 
38
+ BACKTRACE_SIGNAL =
39
+ if Signal.list.key?("INFO")
40
+ "SIGINFO"
41
+ elsif Signal.list.key?("PWR")
42
+ "SIGPWR"
43
+ end
44
+
36
45
  # @version 5.0.0
37
46
  def self.mri?
38
47
  IS_MRI
@@ -42,4 +51,8 @@ module Puma
42
51
  def self.forkable?
43
52
  HAS_FORK
44
53
  end
54
+
55
+ def self.backtrace_signal
56
+ BACKTRACE_SIGNAL
57
+ end
45
58
  end