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://0.0.0.0:9292'.freeze],
  fiber_per_request: !!ENV.fetch("PUMA_FIBER_PER_REQUEST", false),
  debug: false,
  enable_keep_alives: true,
  early_hints: nil,
  environment: 'development'.freeze,
  # Number of seconds to wait until we get the first data for the request.
  first_data_timeout: 30,
  # 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_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_single_worker_warning: false,
  silence_fork_callback_warning: false,
  tag: File.basename(Dir.getwd),
  tcp_host: '0.0.0.0'.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,
  http_content_length_limit: nil
}

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.



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/puma/configuration.rb', line 179

def initialize(user_options={}, default_options = {}, env = ENV, &block)
  default_options = self.puma_default_options(env).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

#eventsObject (readonly)

Returns the value of attribute events.



200
201
202
# File 'lib/puma/configuration.rb', line 200

def events
  @events
end

#hooksObject (readonly)

Returns the value of attribute hooks.



200
201
202
# File 'lib/puma/configuration.rb', line 200

def hooks
  @hooks
end

#pluginsObject (readonly)

Returns the value of attribute plugins.



200
201
202
# File 'lib/puma/configuration.rb', line 200

def plugins
  @plugins
end

Class Method Details

.random_tokenObject



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

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

  SecureRandom.hex(16)
end

.temp_pathObject



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

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.



317
318
319
320
321
322
323
324
325
326
327
# File 'lib/puma/configuration.rb', line 317

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)


306
307
308
# File 'lib/puma/configuration.rb', line 306

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.



282
283
284
285
286
287
288
289
# File 'lib/puma/configuration.rb', line 282

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

#config_filesObject

Raises:



263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/puma/configuration.rb', line 263

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



208
209
210
211
212
213
214
# File 'lib/puma/configuration.rb', line 208

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



330
331
332
# File 'lib/puma/configuration.rb', line 330

def environment
  options[:environment]
end

#final_optionsObject



360
361
362
# File 'lib/puma/configuration.rb', line 360

def final_options
  options.final_options
end

#flattenObject



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

def flatten
  dup.flatten!
end

#flatten!Object



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

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

#initialize_copy(other) ⇒ Object



216
217
218
219
220
# File 'lib/puma/configuration.rb', line 216

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

#loadObject



257
258
259
260
261
# File 'lib/puma/configuration.rb', line 257

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

#load_plugin(name) ⇒ Object



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

def load_plugin(name)
  @plugins.create name
end

#optionsObject

Raises:



202
203
204
205
206
# File 'lib/puma/configuration.rb', line 202

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

  @_options
end

#puma_default_options(env = ENV) ⇒ Object



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

def puma_default_options(env = ENV)
  defaults = DEFAULTS.dup
  puma_options_from_env(env).each { |k,v| defaults[k] = v if v }
  defaults
end

#puma_options_from_env(env = ENV) ⇒ Object



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/puma/configuration.rb', line 237

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 = if env['WEB_CONCURRENCY'] == 'auto'
    require_processor_counter
    ::Concurrent.available_processor_count
  else
    env['WEB_CONCURRENCY']
  end

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

#rackupObject



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

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



341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/puma/configuration.rb', line 341

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