puma 6.6.1 → 7.1.0

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.
@@ -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,18 @@ 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
158
+ prune_bundler: false,
156
159
  queue_requests: true,
157
160
  rackup: 'config.ru'.freeze,
158
161
  raise_exception_on_sigterm: true,
@@ -176,24 +179,31 @@ module Puma
176
179
  def initialize(user_options={}, default_options = {}, env = ENV, &block)
177
180
  default_options = self.puma_default_options(env).merge(default_options)
178
181
 
179
- @options = UserFileDefaultOptions.new(user_options, default_options)
182
+ @_options = UserFileDefaultOptions.new(user_options, default_options)
180
183
  @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
184
+ @events = @_options[:events] || Events.new
185
+ @hooks = {}
186
+ @user_dsl = DSL.new(@_options.user_options, self)
187
+ @file_dsl = DSL.new(@_options.file_options, self)
188
+ @default_dsl = DSL.new(@_options.default_options, self)
188
189
 
189
190
  @puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED'
190
191
 
191
192
  if block
192
193
  configure(&block)
193
194
  end
195
+
196
+ @loaded = false
197
+ @clamped = false
194
198
  end
195
199
 
196
- attr_reader :options, :plugins
200
+ attr_reader :plugins, :events, :hooks
201
+
202
+ def options
203
+ raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped
204
+
205
+ @_options
206
+ end
197
207
 
198
208
  def configure
199
209
  yield @user_dsl, @file_dsl, @default_dsl
@@ -206,7 +216,7 @@ module Puma
206
216
  def initialize_copy(other)
207
217
  @conf = nil
208
218
  @cli_options = nil
209
- @options = @options.dup
219
+ @_options = @_options.dup
210
220
  end
211
221
 
212
222
  def flatten
@@ -214,7 +224,7 @@ module Puma
214
224
  end
215
225
 
216
226
  def flatten!
217
- @options = @options.flatten
227
+ @_options = @_options.flatten
218
228
  self
219
229
  end
220
230
 
@@ -227,6 +237,7 @@ module Puma
227
237
  def puma_options_from_env(env = ENV)
228
238
  min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
229
239
  max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
240
+ persistent_timeout = env['PUMA_PERSISTENT_TIMEOUT']
230
241
  workers = if env['WEB_CONCURRENCY'] == 'auto'
231
242
  require_processor_counter
232
243
  ::Concurrent.available_processor_count
@@ -237,24 +248,27 @@ module Puma
237
248
  {
238
249
  min_threads: min && min != "" && Integer(min),
239
250
  max_threads: max && max != "" && Integer(max),
251
+ persistent_timeout: persistent_timeout && persistent_timeout != "" && Integer(persistent_timeout),
240
252
  workers: workers && workers != "" && Integer(workers),
241
253
  environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
242
254
  }
243
255
  end
244
256
 
245
257
  def load
258
+ @loaded = true
246
259
  config_files.each { |config_file| @file_dsl._load_from(config_file) }
247
-
248
- @options
260
+ @_options
249
261
  end
250
262
 
251
263
  def config_files
252
- files = @options.all_of(:config_files)
264
+ raise NotLoadedError, "ensure load is called before accessing config_files" unless @loaded
265
+
266
+ files = @_options.all_of(:config_files)
253
267
 
254
268
  return [] if files == ['-']
255
269
  return files if files.any?
256
270
 
257
- first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
271
+ first_default_file = %W(config/puma/#{@_options[:environment]}.rb config/puma.rb).find do |f|
258
272
  File.exist?(f)
259
273
  end
260
274
 
@@ -262,9 +276,16 @@ module Puma
262
276
  end
263
277
 
264
278
  # Call once all configuration (included from rackup files)
265
- # is loaded to flesh out any defaults
279
+ # is loaded to finalize defaults and lock in the configuration.
280
+ #
281
+ # This also calls load if it hasn't been called yet.
266
282
  def clamp
267
- @options.finalize_values
283
+ load unless @loaded
284
+ set_conditional_default_options
285
+ @_options.finalize_values
286
+ @clamped = true
287
+ warn_hooks
288
+ options
268
289
  end
269
290
 
270
291
  # Injects the Configuration object into the env
@@ -283,11 +304,11 @@ module Puma
283
304
  # Indicate if there is a properly configured app
284
305
  #
285
306
  def app_configured?
286
- @options[:app] || File.exist?(rackup)
307
+ options[:app] || File.exist?(rackup)
287
308
  end
288
309
 
289
310
  def rackup
290
- @options[:rackup]
311
+ options[:rackup]
291
312
  end
292
313
 
293
314
  # Load the specified rackup file, pull options from
@@ -296,9 +317,9 @@ module Puma
296
317
  def app
297
318
  found = options[:app] || load_rackup
298
319
 
299
- if @options[:log_requests]
320
+ if options[:log_requests]
300
321
  require_relative 'commonlogger'
301
- logger = @options[:logger]
322
+ logger = options[:custom_logger] ? options[:custom_logger] : options[:logger]
302
323
  found = CommonLogger.new(found, logger)
303
324
  end
304
325
 
@@ -307,7 +328,7 @@ module Puma
307
328
 
308
329
  # Return which environment we're running in
309
330
  def environment
310
- @options[:environment]
331
+ options[:environment]
311
332
  end
312
333
 
313
334
  def load_plugin(name)
@@ -315,18 +336,19 @@ module Puma
315
336
  end
316
337
 
317
338
  # @param key [:Symbol] hook to run
318
- # @param arg [Launcher, Int] `:on_restart` passes Launcher
339
+ # @param arg [Launcher, Int] `:before_restart` passes Launcher
319
340
  #
320
341
  def run_hooks(key, arg, log_writer, hook_data = nil)
321
342
  log_writer.debug "Running #{key} hooks"
322
343
 
323
- @options.all_of(key).each do |b|
344
+ options.all_of(key).each do |hook_options|
324
345
  begin
325
- if Array === b
326
- hook_data[b[1]] ||= Hash.new
327
- b[0].call arg, hook_data[b[1]]
346
+ block = hook_options[:block]
347
+ if id = hook_options[:id]
348
+ hook_data[id] ||= Hash.new
349
+ block.call arg, hook_data[id]
328
350
  else
329
- b.call arg
351
+ block.call arg
330
352
  end
331
353
  rescue => e
332
354
  log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
@@ -336,7 +358,7 @@ module Puma
336
358
  end
337
359
 
338
360
  def final_options
339
- @options.final_options
361
+ options.final_options
340
362
  end
341
363
 
342
364
  def self.temp_path
@@ -346,6 +368,12 @@ module Puma
346
368
  "#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
347
369
  end
348
370
 
371
+ def self.random_token
372
+ require 'securerandom' unless defined?(SecureRandom)
373
+
374
+ SecureRandom.hex(16)
375
+ end
376
+
349
377
  private
350
378
 
351
379
  def require_processor_counter
@@ -386,22 +414,40 @@ module Puma
386
414
  rack_app, rack_options = rack_builder.parse_file(rackup)
387
415
  rack_options = rack_options || {}
388
416
 
389
- @options.file_options.merge!(rack_options)
417
+ options.file_options.merge!(rack_options)
390
418
 
391
419
  config_ru_binds = []
392
420
  rack_options.each do |k, v|
393
421
  config_ru_binds << v if k.to_s.start_with?("bind")
394
422
  end
395
423
 
396
- @options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
424
+ options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
397
425
 
398
426
  rack_app
399
427
  end
400
428
 
401
- def self.random_token
402
- require 'securerandom' unless defined?(SecureRandom)
429
+ def set_conditional_default_options
430
+ @_options.default_options[:preload_app] = !@_options[:prune_bundler] &&
431
+ (@_options[:workers] > 1) && Puma.forkable?
432
+ end
403
433
 
404
- SecureRandom.hex(16)
434
+ def warn_hooks
435
+ return if options[:workers] > 0
436
+ return if options[:silence_fork_callback_warning]
437
+
438
+ log_writer = LogWriter.stdio
439
+ @hooks.each_key do |hook|
440
+ options.all_of(hook).each do |hook_options|
441
+ next unless hook_options[:cluster_only]
442
+
443
+ log_writer.log(<<~MSG.tr("\n", " "))
444
+ Warning: The code in the `#{hook}` block will not execute
445
+ in the current Puma configuration. The `#{hook}` block only
446
+ executes in Puma's cluster mode. To fix this, either remove the
447
+ `#{hook}` call or increase Puma's worker count above zero.
448
+ MSG
449
+ end
450
+ end
405
451
  end
406
452
  end
407
453
  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.1.0"
104
+ CODE_NAME = "Neon Witch"
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