Class: Puma::Configuration
| Relationships & Source Files | |
| Namespace Children | |
|
Classes:
| |
|
Exceptions:
| |
| Inherits: | Object |
| 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 #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 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.[: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.
Constant Summary
-
DEFAULTS =
# File 'lib/puma/configuration.rb', line 135{ allow_underscore_headers: true, 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, }
Class Attribute Summary
- .ipv6_interface_available? ⇒ Boolean readonly
Class Method Summary
Instance Attribute Summary
- #_options readonly
-
#app_configured? ⇒ Boolean
readonly
Indicate if there is a properly configured app.
- #events readonly
- #hooks readonly
- #plugins readonly
Instance Method Summary
-
#app
Load the specified rackup file, pull options from the rackup file, and set @app.
-
#clamp
Call once all configuration (included from rackup files) is loaded to finalize defaults and lock in the configuration.
- #config_files
- #configure
-
#environment
Return which environment we're running in.
- #final_options
- #flatten
- #flatten!
- #initialize_copy(other)
- #load
- #load_plugin(name)
- #options
- #puma_default_options(env = ENV)
- #puma_options_from_env(env = ENV)
- #rackup
- #run_hooks(key, arg, log_writer, hook_data = nil)
- #load_rackup private
- #parse_workers(value) private
-
#rack_builder
private
Load and use the normal
Rackbuilder if we can, otherwise fallback to our minimal version. - #require_processor_counter private
- #rewrite_unavailable_ipv6_binds! private
- #run_mode_hooks private
- #set_conditional_default_options private
- #warn_hooks private
Constructor Details
.new(user_options = {}, default_options = {}, env = ENV, &block) ⇒ Configuration
# File 'lib/puma/configuration.rb', line 185
def initialize(={}, = {}, env = ENV, &block) = self.(env).merge(events: Events.new).merge() @_options = UserFileDefaultOptions.new(, ) @plugins = PluginLoader.new @events = @_options[:events] || Events.new @hooks = {} @user_dsl = DSL.new(@_options., self) @file_dsl = DSL.new(@_options., self) @default_dsl = DSL.new(@_options., self) @puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED' if block configure(&block) end @loaded = false @clamped = false end
Class Attribute Details
.ipv6_interface_available? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/puma/configuration.rb', line 383
def self.ipv6_interface_available? Socket.getifaddrs.any? do |ifaddr| addr = ifaddr.addr addr&.ipv6? && !addr&.ipv6_loopback? end rescue StandardError false end
Class Method Details
.default_tcp_bind(port = )
[ GitHub ]# File 'lib/puma/configuration.rb', line 379
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_host
[ GitHub ]# File 'lib/puma/configuration.rb', line 375
def self.default_tcp_host ipv6_interface_available? ? Const::UNSPECIFIED_IPV6 : Const::UNSPECIFIED_IPV4 end
.random_token
[ GitHub ]# File 'lib/puma/configuration.rb', line 399
def self.random_token require 'securerandom' unless defined?(SecureRandom) SecureRandom.hex(16) end
.temp_path
[ GitHub ]# File 'lib/puma/configuration.rb', line 392
def self.temp_path require 'tmpdir' t = (Time.now.to_f * 1000).to_i "#{Dir.tmpdir}/puma-status-#{t}-#{$$}" end
Instance Attribute Details
#_options (readonly)
[ GitHub ]
#app_configured? ⇒ Boolean (readonly)
Indicate if there is a properly configured app
#events (readonly)
[ GitHub ]#hooks (readonly)
[ GitHub ]#plugins (readonly)
[ GitHub ]Instance Method Details
#app
Load the specified rackup file, pull options from the rackup file, and set @app.
# File 'lib/puma/configuration.rb', line 328
def app found = [:app] || load_rackup if [:log_requests] require_relative 'commonlogger' logger = [:custom_logger] ? [:custom_logger] : [:logger] found = CommonLogger.new(found, logger) end ConfigMiddleware.new(self, found) end
#clamp
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.
# File 'lib/puma/configuration.rb', line 286
def clamp if @clamped warn "Configuration already clamped, calling clamp multiple times is deprecated and will raise in Puma v9" return end load unless @loaded run_mode_hooks @_options.finalize_values rewrite_unavailable_ipv6_binds! @clamped = true warn_hooks end
#config_files
# File 'lib/puma/configuration.rb', line 267
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
#configure
[ GitHub ]# File 'lib/puma/configuration.rb', line 214
def configure yield @user_dsl, @file_dsl, @default_dsl ensure @user_dsl._offer_plugins @file_dsl._offer_plugins @default_dsl._offer_plugins end
#environment
Return which environment we're running in
# File 'lib/puma/configuration.rb', line 341
def environment [:environment] end
#final_options
[ GitHub ]# File 'lib/puma/configuration.rb', line 371
def . end
#flatten
[ GitHub ]# File 'lib/puma/configuration.rb', line 228
def flatten dup.flatten! end
#flatten!
[ GitHub ]# File 'lib/puma/configuration.rb', line 232
def flatten! @_options = @_options.flatten self end
#initialize_copy(other)
[ GitHub ]# File 'lib/puma/configuration.rb', line 222
def initialize_copy(other) @conf = nil @cli_options = nil @_options = @_options.dup end
#load
[ GitHub ]# File 'lib/puma/configuration.rb', line 261
def load @loaded = true config_files.each { |config_file| @file_dsl._load_from(config_file) } @_options end
#load_plugin(name)
[ GitHub ]# File 'lib/puma/configuration.rb', line 345
def load_plugin(name) @plugins.create name end
#load_rackup (private)
[ GitHub ]# File 'lib/puma/configuration.rb', line 475
def load_rackup raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup) rack_app, = rack_builder.parse_file(rackup) = || {} ..merge!() config_ru_binds = [] .each do |k, v| config_ru_binds << v if k.to_s.start_with?("bind") end .[:binds] = config_ru_binds unless config_ru_binds.empty? rack_app end
#options
# File 'lib/puma/configuration.rb', line 208
def raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped @_options end
#parse_workers(value) (private)
[ GitHub ]# File 'lib/puma/configuration.rb', line 442
def parse_workers(value) if value == :auto || value == 'auto' require_processor_counter Integer(::Concurrent.available_processor_count) else Integer(value) end rescue ArgumentError, TypeError raise ArgumentError, "workers must be an Integer or :auto" end
#puma_default_options(env = ENV)
[ GitHub ]# File 'lib/puma/configuration.rb', line 237
def (env = ENV) defaults = DEFAULTS.dup defaults[:tcp_host] = self.class.default_tcp_host defaults[:binds] = [self.class.default_tcp_bind] (env).each { |k,v| defaults[k] = v if v } defaults end
#puma_options_from_env(env = ENV)
[ GitHub ]# File 'lib/puma/configuration.rb', line 245
def (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
#rack_builder (private)
Load and use the normal Rack builder if we can, otherwise
fallback to our minimal version.
# File 'lib/puma/configuration.rb', line 455
def rack_builder # Load bundler now if we can so that we can pickup rack from # a Gemfile if @puma_bundler_pruned begin require 'bundler/setup' rescue LoadError end end begin require 'rack' require 'rack/builder' ::Rack::Builder rescue LoadError require_relative 'rack/builder' Puma::Rack::Builder end end
#rackup
[ GitHub ]# File 'lib/puma/configuration.rb', line 321
def rackup [:rackup] end
#require_processor_counter (private)
[ GitHub ]# File 'lib/puma/configuration.rb', line 432
def require_processor_counter require 'concurrent/utility/processor_counter' rescue LoadError warn <<~MESSAGE WEB_CONCURRENCY=auto or workers(:auto) requires the "concurrent-ruby" gem to be installed. Please add "concurrent-ruby" to your Gemfile. MESSAGE raise end
#run_hooks(key, arg, log_writer, hook_data = nil)
# File 'lib/puma/configuration.rb', line 352
def run_hooks(key, arg, log_writer, hook_data = nil) log_writer.debug { "Running #{key} hooks" } .all_of(key).each do || begin block = [:block] if id = [: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.}" log_writer.debug e.backtrace.join("\n") end end end
#run_mode_hooks (private)
[ GitHub ]# File 'lib/puma/configuration.rb', line 493
def run_mode_hooks workers_before = @_options[:workers] key = workers_before > 0 ? :cluster : :single @_options.all_of(key).each(&:call) unless @_options[:workers] == workers_before raise "Cannot change the number of workers inside a #{key} configuration hook" end end
#set_conditional_default_options (private)
[ GitHub ]#warn_hooks (private)
[ GitHub ]# File 'lib/puma/configuration.rb', line 509
def warn_hooks return if [:workers] > 0 return if [:silence_fork_callback_warning] log_writer = LogWriter.stdio @hooks.each_key do |hook| .all_of(hook).each do || next unless [:cluster_only] log_writer.log(<<~MSG.tr("\n", " ")) Warning: The code in the `#{hook}` block will not execute in the current Puma configuration. The `#{hook}` block only executes in Puma's cluster mode. To fix this, either remove the `#{hook}` call or increase Puma's worker count above zero. MSG end end end