Class: Puma::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/puma/configuration.rb

Overview

The main configuration class of Puma.

It can be initialized with a set of “user” options and “default” options. Defaults will be merged with ‘Configuration.puma_default_options`.

This class works together with 2 main other classes the ‘UserFileDefaultOptions` which stores configuration options in order so the precedence is that user set configuration wins over “file” based configuration wins over “default” configuration. These configurations are set via the `DSL` class. This class powers the Puma config file syntax and does double duty as a configuration DSL used by the `Puma::CLI` and Puma rack handler.

It also handles loading plugins.

Note:

‘:port` and `:host` are not valid keys. By the time they make it to the configuration options they are expected to be incorporated into a `:binds` key. Under the hood the DSL maps `port` and `host` calls to `:binds`

config = Configuration.new({}) do |user_config, file_config, default_config|
  user_config.port 3003
end
config.clamp
puts config.options[:port]
# => 3003

It is expected that ‘load` is called on the configuration instance after setting config. This method expands any values in `config_file` and puts them into the correct configuration option hash.

Once all configuration is complete it is expected that ‘clamp` will be called on the instance. This will expand any procs stored under “default” values. This is done because an environment variable may have been modified while loading configuration files.

Defined Under Namespace

Classes: ConfigMiddleware, NotClampedError, NotLoadedError

Constant Summary collapse

DEFAULTS =
{
  auto_trim_time: 30,
  binds: ['tcp://[::]:9292'.freeze],
  debug: false,
  early_hints: nil,
  enable_keep_alives: true,
  environment: 'development'.freeze,
  fiber_per_request: !!ENV.fetch("PUMA_FIBER_PER_REQUEST", false),
  # Number of seconds to wait until we get the first data for the request.
  first_data_timeout: 30,
  force_shutdown_after: -1,
  http_content_length_limit: nil,
  # Number of seconds to wait until the next request before shutting down.
  idle_timeout: nil,
  io_selector_backend: :auto,
  log_requests: false,
  logger: STDOUT,
  # Limits how many requests a keep alive connection can make.
  # The connection will be closed after it reaches `max_keep_alive`
  # requests.
  max_io_threads: 0,
  max_keep_alive: 999,
  max_threads: Puma.mri? ? 5 : 16,
  min_threads: 0,
  mode: :http,
  mutate_stdout_and_stderr_to_sync_on_write: true,
  out_of_band: [],
  # Number of seconds for another request within a persistent session.
  persistent_timeout: 65, # PUMA_PERSISTENT_TIMEOUT
  prune_bundler: false,
  queue_requests: true,
  rackup: 'config.ru'.freeze,
  raise_exception_on_sigterm: true,
  reaping_time: 1,
  remote_address: :socket,
  silence_fork_callback_warning: false,
  silence_single_worker_warning: false,
  tag: File.basename(Dir.getwd),
  tcp_host: '::'.freeze,
  tcp_port: 9292,
  wait_for_less_busy_worker: 0.005,
  worker_boot_timeout: 60,
  worker_check_interval: 5,
  worker_culling_strategy: :youngest,
  worker_shutdown_timeout: 30,
  worker_timeout: 60,
  workers: 0,
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user_options = {}, default_options = {}, env = ENV, &block) ⇒ Configuration

Returns a new instance of Configuration.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/puma/configuration.rb', line 184

def initialize(user_options={}, default_options = {}, env = ENV, &block)
  default_options = self.puma_default_options(env).merge(events: Events.new).merge(default_options)

  @_options    = UserFileDefaultOptions.new(user_options, default_options)
  @plugins     = PluginLoader.new
  @events      = @_options[:events] || Events.new
  @hooks       = {}
  @user_dsl    = DSL.new(@_options.user_options, self)
  @file_dsl    = DSL.new(@_options.file_options, self)
  @default_dsl = DSL.new(@_options.default_options, self)

  @puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED'

  if block
    configure(&block)
  end

  @loaded = false
  @clamped = false
end

Instance Attribute Details

#_optionsObject (readonly)

Returns the value of attribute _options.



205
206
207
# File 'lib/puma/configuration.rb', line 205

def _options
  @_options
end

#eventsObject (readonly)

Returns the value of attribute events.



205
206
207
# File 'lib/puma/configuration.rb', line 205

def events
  @events
end

#hooksObject (readonly)

Returns the value of attribute hooks.



205
206
207
# File 'lib/puma/configuration.rb', line 205

def hooks
  @hooks
end

#pluginsObject (readonly)

Returns the value of attribute plugins.



205
206
207
# File 'lib/puma/configuration.rb', line 205

def plugins
  @plugins
end

Class Method Details

.default_tcp_bind(port = ) ⇒ Object



373
374
375
# File 'lib/puma/configuration.rb', line 373

def self.default_tcp_bind(port = DEFAULTS[:tcp_port])
  URI::Generic.build(scheme: 'tcp', host: default_tcp_host, port: Integer(port)).to_s
end

.default_tcp_hostObject



369
370
371
# File 'lib/puma/configuration.rb', line 369

def self.default_tcp_host
  ipv6_interface_available? ? Const::UNSPECIFIED_IPV6 : Const::UNSPECIFIED_IPV4
end

.ipv6_interface_available?Boolean

Returns:

  • (Boolean)


377
378
379
380
381
382
383
384
# File 'lib/puma/configuration.rb', line 377

def self.ipv6_interface_available?
  Socket.getifaddrs.any? do |ifaddr|
    addr = ifaddr.addr
    addr&.ipv6? && !addr&.ipv6_loopback?
  end
rescue StandardError
  false
end

.random_tokenObject



393
394
395
396
397
# File 'lib/puma/configuration.rb', line 393

def self.random_token
  require 'securerandom' unless defined?(SecureRandom)

  SecureRandom.hex(16)
end

.temp_pathObject



386
387
388
389
390
391
# File 'lib/puma/configuration.rb', line 386

def self.temp_path
  require 'tmpdir'

  t = (Time.now.to_f * 1000).to_i
  "#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
end

Instance Method Details

#appObject

Load the specified rackup file, pull options from the rackup file, and set @app.



322
323
324
325
326
327
328
329
330
331
332
# File 'lib/puma/configuration.rb', line 322

def app
  found = options[:app] || load_rackup

  if options[:log_requests]
    require_relative 'commonlogger'
    logger = options[:custom_logger] ? options[:custom_logger] : options[:logger]
    found = CommonLogger.new(found, logger)
  end

  ConfigMiddleware.new(self, found)
end

#app_configured?Boolean

Indicate if there is a properly configured app

Returns:

  • (Boolean)


311
312
313
# File 'lib/puma/configuration.rb', line 311

def app_configured?
  options[:app] || File.exist?(rackup)
end

#clampObject

Call once all configuration (included from rackup files) is loaded to finalize defaults and lock in the configuration.

This also calls load if it hasn’t been called yet.



285
286
287
288
289
290
291
292
293
294
# File 'lib/puma/configuration.rb', line 285

def clamp
  load unless @loaded
  run_mode_hooks
  set_conditional_default_options
  @_options.finalize_values
  rewrite_unavailable_ipv6_binds!
  @clamped = true
  warn_hooks
  options
end

#config_filesObject

Raises:



266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/puma/configuration.rb', line 266

def config_files
  raise NotLoadedError, "ensure load is called before accessing config_files" unless @loaded

  files = @_options.all_of(:config_files)

  return [] if files == ['-']
  return files if files.any?

  first_default_file = %W(config/puma/#{@_options[:environment]}.rb config/puma.rb).find do |f|
    File.exist?(f)
  end

  [first_default_file]
end

#configureObject



213
214
215
216
217
218
219
# File 'lib/puma/configuration.rb', line 213

def configure
  yield @user_dsl, @file_dsl, @default_dsl
ensure
  @user_dsl._offer_plugins
  @file_dsl._offer_plugins
  @default_dsl._offer_plugins
end

#environmentObject

Return which environment we’re running in



335
336
337
# File 'lib/puma/configuration.rb', line 335

def environment
  options[:environment]
end

#final_optionsObject



365
366
367
# File 'lib/puma/configuration.rb', line 365

def final_options
  options.final_options
end

#flattenObject



227
228
229
# File 'lib/puma/configuration.rb', line 227

def flatten
  dup.flatten!
end

#flatten!Object



231
232
233
234
# File 'lib/puma/configuration.rb', line 231

def flatten!
  @_options = @_options.flatten
  self
end

#initialize_copy(other) ⇒ Object



221
222
223
224
225
# File 'lib/puma/configuration.rb', line 221

def initialize_copy(other)
  @conf        = nil
  @cli_options = nil
  @_options     = @_options.dup
end

#loadObject



260
261
262
263
264
# File 'lib/puma/configuration.rb', line 260

def load
  @loaded = true
  config_files.each { |config_file| @file_dsl._load_from(config_file) }
  @_options
end

#load_plugin(name) ⇒ Object



339
340
341
# File 'lib/puma/configuration.rb', line 339

def load_plugin(name)
  @plugins.create name
end

#optionsObject

Raises:



207
208
209
210
211
# File 'lib/puma/configuration.rb', line 207

def options
  raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped

  @_options
end

#puma_default_options(env = ENV) ⇒ Object



236
237
238
239
240
241
242
# File 'lib/puma/configuration.rb', line 236

def puma_default_options(env = ENV)
  defaults = DEFAULTS.dup
  defaults[:tcp_host] = self.class.default_tcp_host
  defaults[:binds] = [self.class.default_tcp_bind]
  puma_options_from_env(env).each { |k,v| defaults[k] = v if v }
  defaults
end

#puma_options_from_env(env = ENV) ⇒ Object



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/puma/configuration.rb', line 244

def puma_options_from_env(env = ENV)
  min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
  max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
  persistent_timeout = env['PUMA_PERSISTENT_TIMEOUT']
  workers_env = env['WEB_CONCURRENCY']
  workers = workers_env && workers_env.strip != "" ? parse_workers(workers_env.strip) : nil

  {
    min_threads: min && min != "" && Integer(min),
    max_threads: max && max != "" && Integer(max),
    persistent_timeout: persistent_timeout && persistent_timeout != "" && Integer(persistent_timeout),
    workers: workers,
    environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
  }
end

#rackupObject



315
316
317
# File 'lib/puma/configuration.rb', line 315

def rackup
  options[:rackup]
end

#run_hooks(key, arg, log_writer, hook_data = nil) ⇒ Object

Parameters:

  • key (:Symbol)

    hook to run

  • arg (Launcher, Int)

    ‘:before_restart` passes Launcher



346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/puma/configuration.rb', line 346

def run_hooks(key, arg, log_writer, hook_data = nil)
  log_writer.debug { "Running #{key} hooks" }

  options.all_of(key).each do |hook_options|
    begin
      block = hook_options[:block]
      if id = hook_options[:id]
        hook_data[id] ||= Hash.new
        block.call arg, hook_data[id]
      else
        block.call arg
      end
    rescue => e
      log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
      log_writer.debug e.backtrace.join("\n")
    end
  end
end