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
  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.



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

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.



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

def events
  @events
end

#hooksObject (readonly)

Returns the value of attribute hooks.



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

def hooks
  @hooks
end

#pluginsObject (readonly)

Returns the value of attribute plugins.



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

def plugins
  @plugins
end

Class Method Details

.random_tokenObject



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

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

  SecureRandom.hex(16)
end

.temp_pathObject



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

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.



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

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)


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

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.



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

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

#config_filesObject

Raises:



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

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



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

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



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

def environment
  options[:environment]
end

#final_optionsObject



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

def final_options
  options.final_options
end

#flattenObject



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

def flatten
  dup.flatten!
end

#flatten!Object



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

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

#initialize_copy(other) ⇒ Object



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

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

#loadObject



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

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

#load_plugin(name) ⇒ Object



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

def load_plugin(name)
  @plugins.create name
end

#optionsObject

Raises:



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

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

  @_options
end

#puma_default_options(env = ENV) ⇒ Object



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

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



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

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



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

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



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

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