puma 6.6.1 → 7.0.3

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.
@@ -110,7 +110,6 @@ module Puma
110
110
  begin
111
111
  @worker_write << "#{PIPE_BOOT}#{Process.pid}:#{index}\n"
112
112
  rescue SystemCallError, IOError
113
- Puma::Util.purge_interrupt_queue
114
113
  STDERR.puts "Master seems to have exited, exiting."
115
114
  return
116
115
  end
@@ -128,16 +127,16 @@ module Puma
128
127
 
129
128
  while true
130
129
  begin
131
- b = server.backlog || 0
132
- r = server.running || 0
133
- t = server.pool_capacity || 0
134
- m = server.max_threads || 0
135
- rc = server.requests_count || 0
136
- bt = server.busy_threads || 0
137
- payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads":#{m}, "requests_count":#{rc}, "busy_threads":#{bt} }\n!
138
- io << payload
130
+ payload = base_payload.dup
131
+
132
+ hsh = server.stats
133
+ hsh.each do |k, v|
134
+ payload << %Q! "#{k}":#{v || 0},!
135
+ end
136
+ # sub call properly adds 'closing' string
137
+ io << payload.sub(/,\z/, " }\n")
138
+ server.reset_max
139
139
  rescue IOError
140
- Puma::Util.purge_interrupt_queue
141
140
  break
142
141
  end
143
142
  sleep @options[:worker_check_interval]
@@ -4,13 +4,15 @@ module Puma
4
4
  class Cluster < Runner
5
5
  #—————————————————————— DO NOT USE — this class is for internal use only ———
6
6
 
7
-
8
7
  # This class represents a worker process from the perspective of the puma
9
8
  # master process. It contains information about the process and its health
10
9
  # and it exposes methods to control the process via IPC. It does not
11
10
  # include the actual logic executed by the worker process itself. For that,
12
11
  # see Puma::Cluster::Worker.
13
12
  class WorkerHandle # :nodoc:
13
+ # array of stat 'max' keys
14
+ WORKER_MAX_KEYS = [:backlog_max, :reactor_max]
15
+
14
16
  def initialize(idx, pid, phase, options)
15
17
  @index = idx
16
18
  @pid = pid
@@ -23,6 +25,7 @@ module Puma
23
25
  @last_checkin = Time.now
24
26
  @last_status = {}
25
27
  @term = false
28
+ @worker_max = Array.new WORKER_MAX_KEYS.length, 0
26
29
  end
27
30
 
28
31
  attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
@@ -51,12 +54,40 @@ module Puma
51
54
  @term
52
55
  end
53
56
 
54
- STATUS_PATTERN = /{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads":(?<max_threads>\d*), "requests_count":(?<requests_count>\d*), "busy_threads":(?<busy_threads>\d*) }/
55
- private_constant :STATUS_PATTERN
56
-
57
57
  def ping!(status)
58
+ hsh = {}
59
+ k, v = nil, nil
60
+ status.tr('}{"', '').strip.split(", ") do |kv|
61
+ cntr = 0
62
+ kv.split(':') do |t|
63
+ if cntr == 0
64
+ k = t
65
+ cntr = 1
66
+ else
67
+ v = t
68
+ end
69
+ end
70
+ hsh[k.to_sym] = v.to_i
71
+ end
72
+
73
+ # check stat max values, we can't signal workers to reset the max values,
74
+ # so we do so here
75
+ WORKER_MAX_KEYS.each_with_index do |key, idx|
76
+ next unless hsh[key]
77
+
78
+ if hsh[key] < @worker_max[idx]
79
+ hsh[key] = @worker_max[idx]
80
+ else
81
+ @worker_max[idx] = hsh[key]
82
+ end
83
+ end
58
84
  @last_checkin = Time.now
59
- @last_status = status.match(STATUS_PATTERN).named_captures.map { |c_name, c| [c_name.to_sym, c.to_i] }.to_h
85
+ @last_status = hsh
86
+ end
87
+
88
+ # Resets max values to zero. Called whenever `Cluster#stats` is called
89
+ def reset_max
90
+ WORKER_MAX_KEYS.length.times { |idx| @worker_max[idx] = 0 }
60
91
  end
61
92
 
62
93
  # @see Puma::Cluster#check_workers
data/lib/puma/cluster.rb CHANGED
@@ -22,6 +22,7 @@ module Puma
22
22
  @workers = []
23
23
  @next_check = Time.now
24
24
 
25
+ @worker_max = [] # keeps track of 'max' stat values
25
26
  @phased_restart = false
26
27
  end
27
28
 
@@ -45,8 +46,7 @@ module Puma
45
46
  end
46
47
 
47
48
  def start_phased_restart(refork = false)
48
- @events.fire_on_restart!
49
-
49
+ @events.fire_before_restart!
50
50
  @phase += 1
51
51
  if refork
52
52
  log "- Starting worker refork, phase: #{@phase}"
@@ -268,11 +268,14 @@ module Puma
268
268
  end
269
269
 
270
270
  # Inside of a child process, this will return all zeroes, as @workers is only populated in
271
- # the master process.
271
+ # the master process. Calling this also resets stat 'max' values to zero.
272
272
  # @!attribute [r] stats
273
+ # @return [Hash]
274
+
273
275
  def stats
274
276
  old_worker_count = @workers.count { |w| w.phase != @phase }
275
277
  worker_status = @workers.map do |w|
278
+ w.reset_max
276
279
  {
277
280
  started_at: utc_iso8601(w.started_at),
278
281
  pid: w.pid,
@@ -283,7 +286,6 @@ module Puma
283
286
  last_status: w.last_status,
284
287
  }
285
288
  end
286
-
287
289
  {
288
290
  started_at: utc_iso8601(@started_at),
289
291
  workers: @workers.size,
@@ -352,7 +354,7 @@ module Puma
352
354
 
353
355
  stop_workers
354
356
  stop
355
- @events.fire_on_stopped!
357
+ @events.fire_after_stopped!
356
358
  raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
357
359
  exit 0 # Clean exit, workers were stopped
358
360
  end
@@ -369,12 +371,8 @@ module Puma
369
371
 
370
372
  if preload?
371
373
  # Threads explicitly marked as fork safe will be ignored. Used in Rails,
372
- # but may be used by anyone. Note that we need to explicit
373
- # Process::Waiter check here because there's a bug in Ruby 2.6 and below
374
- # where calling thread_variable_get on a Process::Waiter will segfault.
375
- # We can drop that clause once those versions of Ruby are no longer
376
- # supported.
377
- fork_safe = ->(t) { !t.is_a?(Process::Waiter) && t.thread_variable_get(:fork_safe) }
374
+ # but may be used by anyone.
375
+ fork_safe = ->(t) { t.thread_variable_get(:fork_safe) }
378
376
 
379
377
  before = Thread.list.reject(&fork_safe)
380
378
 
@@ -423,6 +421,7 @@ module Puma
423
421
 
424
422
  log "Use Ctrl-C to stop"
425
423
 
424
+ warn_ruby_mn_threads
426
425
  single_worker_warning
427
426
 
428
427
  redirect_io
@@ -511,7 +510,7 @@ module Puma
511
510
  end
512
511
 
513
512
  if !booted && @workers.none? {|worker| worker.last_status.empty?}
514
- @events.fire_on_booted!
513
+ @events.fire_after_booted!
515
514
  debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
516
515
  booted = true
517
516
  end
@@ -528,7 +527,7 @@ module Puma
528
527
  end
529
528
 
530
529
  if in_phased_restart && workers_not_booted.zero?
531
- @events.fire_on_booted!
530
+ @events.fire_after_booted!
532
531
  debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
533
532
  in_phased_restart = false
534
533
  end
@@ -29,13 +29,13 @@ module Puma
29
29
 
30
30
  CONTENT_LENGTH = 'Content-Length' # should be lower case from app,
31
31
  # Util::HeaderHash allows mixed
32
- HTTP_VERSION = Const::HTTP_VERSION
33
32
  HTTP_X_FORWARDED_FOR = Const::HTTP_X_FORWARDED_FOR
34
33
  PATH_INFO = Const::PATH_INFO
35
34
  QUERY_STRING = Const::QUERY_STRING
36
35
  REMOTE_ADDR = Const::REMOTE_ADDR
37
36
  REMOTE_USER = 'REMOTE_USER'
38
37
  REQUEST_METHOD = Const::REQUEST_METHOD
38
+ SERVER_PROTOCOL = Const::SERVER_PROTOCOL
39
39
 
40
40
  def initialize(app, logger=nil)
41
41
  @app = app
@@ -70,7 +70,7 @@ module Puma
70
70
  env[REQUEST_METHOD],
71
71
  env[PATH_INFO],
72
72
  env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
73
- env[HTTP_VERSION],
73
+ env[SERVER_PROTOCOL],
74
74
  now - began_at ]
75
75
 
76
76
  write(msg)
@@ -87,7 +87,7 @@ module Puma
87
87
  env[REQUEST_METHOD],
88
88
  env[PATH_INFO],
89
89
  env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
90
- env[HTTP_VERSION],
90
+ env[SERVER_PROTOCOL],
91
91
  status.to_s[0..3],
92
92
  length,
93
93
  now - began_at ]
@@ -3,6 +3,7 @@
3
3
  require_relative 'plugin'
4
4
  require_relative 'const'
5
5
  require_relative 'dsl'
6
+ require_relative 'events'
6
7
 
7
8
  module Puma
8
9
  # A class used for storing "leveled" configuration options.
@@ -112,7 +113,7 @@ module Puma
112
113
  # config = Configuration.new({}) do |user_config, file_config, default_config|
113
114
  # user_config.port 3003
114
115
  # end
115
- # config.load
116
+ # config.clamp
116
117
  # puts config.options[:port]
117
118
  # # => 3003
118
119
  #
@@ -125,10 +126,13 @@ module Puma
125
126
  # is done because an environment variable may have been modified while loading
126
127
  # configuration files.
127
128
  class Configuration
129
+ class NotLoadedError < StandardError; end
130
+ class NotClampedError < StandardError; end
131
+
128
132
  DEFAULTS = {
129
133
  auto_trim_time: 30,
130
134
  binds: ['tcp://0.0.0.0:9292'.freeze],
131
- clean_thread_locals: false,
135
+ fiber_per_request: !!ENV.fetch("PUMA_FIBER_PER_REQUEST", false),
132
136
  debug: false,
133
137
  enable_keep_alives: true,
134
138
  early_hints: nil,
@@ -140,19 +144,17 @@ module Puma
140
144
  io_selector_backend: :auto,
141
145
  log_requests: false,
142
146
  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,
147
+ # Limits how many requests a keep alive connection can make.
148
+ # The connection will be closed after it reaches `max_keep_alive`
149
+ # requests.
150
+ max_keep_alive: 999,
149
151
  max_threads: Puma.mri? ? 5 : 16,
150
152
  min_threads: 0,
151
153
  mode: :http,
152
154
  mutate_stdout_and_stderr_to_sync_on_write: true,
153
155
  out_of_band: [],
154
156
  # Number of seconds for another request within a persistent session.
155
- persistent_timeout: 20,
157
+ persistent_timeout: 65, # PUMA_PERSISTENT_TIMEOUT
156
158
  queue_requests: true,
157
159
  rackup: 'config.ru'.freeze,
158
160
  raise_exception_on_sigterm: true,
@@ -176,24 +178,31 @@ module Puma
176
178
  def initialize(user_options={}, default_options = {}, env = ENV, &block)
177
179
  default_options = self.puma_default_options(env).merge(default_options)
178
180
 
179
- @options = UserFileDefaultOptions.new(user_options, default_options)
181
+ @_options = UserFileDefaultOptions.new(user_options, default_options)
180
182
  @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
183
+ @events = @_options[:events] || Events.new
184
+ @hooks = {}
185
+ @user_dsl = DSL.new(@_options.user_options, self)
186
+ @file_dsl = DSL.new(@_options.file_options, self)
187
+ @default_dsl = DSL.new(@_options.default_options, self)
188
188
 
189
189
  @puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED'
190
190
 
191
191
  if block
192
192
  configure(&block)
193
193
  end
194
+
195
+ @loaded = false
196
+ @clamped = false
194
197
  end
195
198
 
196
- attr_reader :options, :plugins
199
+ attr_reader :plugins, :events, :hooks
200
+
201
+ def options
202
+ raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped
203
+
204
+ @_options
205
+ end
197
206
 
198
207
  def configure
199
208
  yield @user_dsl, @file_dsl, @default_dsl
@@ -206,7 +215,7 @@ module Puma
206
215
  def initialize_copy(other)
207
216
  @conf = nil
208
217
  @cli_options = nil
209
- @options = @options.dup
218
+ @_options = @_options.dup
210
219
  end
211
220
 
212
221
  def flatten
@@ -214,7 +223,7 @@ module Puma
214
223
  end
215
224
 
216
225
  def flatten!
217
- @options = @options.flatten
226
+ @_options = @_options.flatten
218
227
  self
219
228
  end
220
229
 
@@ -227,6 +236,7 @@ module Puma
227
236
  def puma_options_from_env(env = ENV)
228
237
  min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
229
238
  max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
239
+ persistent_timeout = env['PUMA_PERSISTENT_TIMEOUT']
230
240
  workers = if env['WEB_CONCURRENCY'] == 'auto'
231
241
  require_processor_counter
232
242
  ::Concurrent.available_processor_count
@@ -237,24 +247,27 @@ module Puma
237
247
  {
238
248
  min_threads: min && min != "" && Integer(min),
239
249
  max_threads: max && max != "" && Integer(max),
250
+ persistent_timeout: persistent_timeout && persistent_timeout != "" && Integer(persistent_timeout),
240
251
  workers: workers && workers != "" && Integer(workers),
241
252
  environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
242
253
  }
243
254
  end
244
255
 
245
256
  def load
257
+ @loaded = true
246
258
  config_files.each { |config_file| @file_dsl._load_from(config_file) }
247
-
248
- @options
259
+ @_options
249
260
  end
250
261
 
251
262
  def config_files
252
- files = @options.all_of(:config_files)
263
+ raise NotLoadedError, "ensure load is called before accessing config_files" unless @loaded
264
+
265
+ files = @_options.all_of(:config_files)
253
266
 
254
267
  return [] if files == ['-']
255
268
  return files if files.any?
256
269
 
257
- first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
270
+ first_default_file = %W(config/puma/#{@_options[:environment]}.rb config/puma.rb).find do |f|
258
271
  File.exist?(f)
259
272
  end
260
273
 
@@ -262,9 +275,16 @@ module Puma
262
275
  end
263
276
 
264
277
  # Call once all configuration (included from rackup files)
265
- # is loaded to flesh out any defaults
278
+ # is loaded to finalize defaults and lock in the configuration.
279
+ #
280
+ # This also calls load if it hasn't been called yet.
266
281
  def clamp
267
- @options.finalize_values
282
+ load unless @loaded
283
+ set_conditional_default_options
284
+ @_options.finalize_values
285
+ @clamped = true
286
+ warn_hooks
287
+ options
268
288
  end
269
289
 
270
290
  # Injects the Configuration object into the env
@@ -283,11 +303,11 @@ module Puma
283
303
  # Indicate if there is a properly configured app
284
304
  #
285
305
  def app_configured?
286
- @options[:app] || File.exist?(rackup)
306
+ options[:app] || File.exist?(rackup)
287
307
  end
288
308
 
289
309
  def rackup
290
- @options[:rackup]
310
+ options[:rackup]
291
311
  end
292
312
 
293
313
  # Load the specified rackup file, pull options from
@@ -296,9 +316,9 @@ module Puma
296
316
  def app
297
317
  found = options[:app] || load_rackup
298
318
 
299
- if @options[:log_requests]
319
+ if options[:log_requests]
300
320
  require_relative 'commonlogger'
301
- logger = @options[:logger]
321
+ logger = options[:custom_logger] ? options[:custom_logger] : options[:logger]
302
322
  found = CommonLogger.new(found, logger)
303
323
  end
304
324
 
@@ -307,7 +327,7 @@ module Puma
307
327
 
308
328
  # Return which environment we're running in
309
329
  def environment
310
- @options[:environment]
330
+ options[:environment]
311
331
  end
312
332
 
313
333
  def load_plugin(name)
@@ -315,18 +335,19 @@ module Puma
315
335
  end
316
336
 
317
337
  # @param key [:Symbol] hook to run
318
- # @param arg [Launcher, Int] `:on_restart` passes Launcher
338
+ # @param arg [Launcher, Int] `:before_restart` passes Launcher
319
339
  #
320
340
  def run_hooks(key, arg, log_writer, hook_data = nil)
321
341
  log_writer.debug "Running #{key} hooks"
322
342
 
323
- @options.all_of(key).each do |b|
343
+ options.all_of(key).each do |hook_options|
324
344
  begin
325
- if Array === b
326
- hook_data[b[1]] ||= Hash.new
327
- b[0].call arg, hook_data[b[1]]
345
+ block = hook_options[:block]
346
+ if id = hook_options[:id]
347
+ hook_data[id] ||= Hash.new
348
+ block.call arg, hook_data[id]
328
349
  else
329
- b.call arg
350
+ block.call arg
330
351
  end
331
352
  rescue => e
332
353
  log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
@@ -336,7 +357,7 @@ module Puma
336
357
  end
337
358
 
338
359
  def final_options
339
- @options.final_options
360
+ options.final_options
340
361
  end
341
362
 
342
363
  def self.temp_path
@@ -346,6 +367,12 @@ module Puma
346
367
  "#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
347
368
  end
348
369
 
370
+ def self.random_token
371
+ require 'securerandom' unless defined?(SecureRandom)
372
+
373
+ SecureRandom.hex(16)
374
+ end
375
+
349
376
  private
350
377
 
351
378
  def require_processor_counter
@@ -386,22 +413,40 @@ module Puma
386
413
  rack_app, rack_options = rack_builder.parse_file(rackup)
387
414
  rack_options = rack_options || {}
388
415
 
389
- @options.file_options.merge!(rack_options)
416
+ options.file_options.merge!(rack_options)
390
417
 
391
418
  config_ru_binds = []
392
419
  rack_options.each do |k, v|
393
420
  config_ru_binds << v if k.to_s.start_with?("bind")
394
421
  end
395
422
 
396
- @options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
423
+ options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
397
424
 
398
425
  rack_app
399
426
  end
400
427
 
401
- def self.random_token
402
- require 'securerandom' unless defined?(SecureRandom)
428
+ def set_conditional_default_options
429
+ @_options.default_options[:preload_app] = !@_options[:prune_bundler] &&
430
+ (@_options[:workers] > 1) && Puma.forkable?
431
+ end
403
432
 
404
- SecureRandom.hex(16)
433
+ def warn_hooks
434
+ return if options[:workers] > 0
435
+ return if options[:silence_fork_callback_warning]
436
+
437
+ log_writer = LogWriter.stdio
438
+ @hooks.each_key do |hook|
439
+ options.all_of(hook).each do |hook_options|
440
+ next unless hook_options[:cluster_only]
441
+
442
+ log_writer.log(<<~MSG.tr("\n", " "))
443
+ Warning: The code in the `#{hook}` block will not execute
444
+ in the current Puma configuration. The `#{hook}` block only
445
+ executes in Puma's cluster mode. To fix this, either remove the
446
+ `#{hook}` call or increase Puma's worker count above zero.
447
+ MSG
448
+ end
449
+ end
405
450
  end
406
451
  end
407
452
  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.1"
104
- CODE_NAME = "Return to Forever"
103
+ PUMA_VERSION = VERSION = "7.0.3"
104
+ CODE_NAME = "Romantic Warrior"
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"
@@ -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