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

#_optionsObject (readonly)

Returns the value of attribute _options.



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

def _options
  @_options
end

#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



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

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

  SecureRandom.hex(16)
end

.temp_pathObject



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

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.



313
314
315
316
317
318
319
320
321
322
323
# File 'lib/puma/configuration.rb', line 313

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)


302
303
304
# File 'lib/puma/configuration.rb', line 302

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.



278
279
280
281
282
283
284
285
# File 'lib/puma/configuration.rb', line 278

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

#config_filesObject

Raises:



259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/puma/configuration.rb', line 259

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



326
327
328
# File 'lib/puma/configuration.rb', line 326

def environment
  options[:environment]
end

#final_optionsObject



356
357
358
# File 'lib/puma/configuration.rb', line 356

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



253
254
255
256
257
# File 'lib/puma/configuration.rb', line 253

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

#load_plugin(name) ⇒ Object



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

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
# 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_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



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

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



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

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