Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benchmarks/local/puma_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def initialize(argv, stdout=STDOUT, stderr=STDERR)

if @config_file
config = Puma::Configuration.new({ config_files: [@config_file] }, {})
config.load
config.clamp
@state ||= config.options[:state]
@control_url ||= config.options[:control_url]
@control_auth_token ||= config.options[:control_auth_token]
Expand Down
13 changes: 6 additions & 7 deletions lib/puma/binder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

require_relative 'const'
require_relative 'util'
require_relative 'configuration'

module Puma

Expand All @@ -19,9 +18,9 @@ class Binder

RACK_VERSION = [1,6].freeze

def initialize(log_writer, conf = Configuration.new, env: ENV)
def initialize(log_writer, options, env: ENV)
@log_writer = log_writer
@conf = conf
@options = options
@listeners = []
@inherited_fds = {}
@activated_sockets = {}
Expand All @@ -31,10 +30,10 @@ def initialize(log_writer, conf = Configuration.new, env: ENV)
@proto_env = {
"rack.version".freeze => RACK_VERSION,
"rack.errors".freeze => log_writer.stderr,
"rack.multithread".freeze => conf.options[:max_threads] > 1,
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
"rack.multithread".freeze => options[:max_threads] > 1,
"rack.multiprocess".freeze => options[:workers] >= 1,
"rack.run_once".freeze => false,
RACK_URL_SCHEME => conf.options[:rack_url_scheme],
RACK_URL_SCHEME => options[:rack_url_scheme],
"SCRIPT_NAME".freeze => env['SCRIPT_NAME'] || "",

# I'd like to set a default CONTENT_TYPE here but some things
Expand Down Expand Up @@ -246,7 +245,7 @@ def parse(binds, log_writer = nil, log_msg = 'Listening')
cert_key.each do |v|
if params[v]&.start_with?('store:')
index = Integer(params.delete(v).split('store:').last)
params["#{v}_pem"] = @conf.options[:store][index]
params["#{v}_pem"] = @options[:store][index]
end
end
MiniSSL::ContextBuilder.new(params, @log_writer).context
Expand Down
2 changes: 1 addition & 1 deletion lib/puma/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def configure_control_url(command_line_arg)
#

def setup_options(env = ENV)
@conf = Configuration.new({}, {events: @events}, env) do |user_config, file_config|
@conf = Configuration.new({}, { events: @events }, env) do |user_config, file_config|
@parser = OptionParser.new do |o|
o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
user_config.bind arg
Expand Down
108 changes: 75 additions & 33 deletions lib/puma/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require_relative 'plugin'
require_relative 'const'
require_relative 'dsl'
require_relative 'events'

module Puma
# A class used for storing "leveled" configuration options.
Expand Down Expand Up @@ -112,7 +113,7 @@ def final_options
# config = Configuration.new({}) do |user_config, file_config, default_config|
# user_config.port 3003
# end
# config.load
# config.clamp
# puts config.options[:port]
# # => 3003
#
Expand All @@ -125,6 +126,9 @@ def final_options
# is done because an environment variable may have been modified while loading
# configuration files.
class Configuration
class NotLoadedError < StandardError; end
class NotClampedError < StandardError; end

DEFAULTS = {
auto_trim_time: 30,
binds: ['tcp://0.0.0.0:9292'.freeze],
Expand Down Expand Up @@ -174,24 +178,35 @@ class Configuration
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)
@_options = UserFileDefaultOptions.new(user_options, default_options)
@plugins = PluginLoader.new
@user_dsl = DSL.new(@options.user_options, self)
@file_dsl = DSL.new(@options.file_options, self)
@default_dsl = DSL.new(@options.default_options, self)

if !@options[:prune_bundler]
default_options[:preload_app] = (@options[:workers] > 1) && Puma.forkable?
@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)

if !@_options[:prune_bundler]
default_options[:preload_app] = (@_options[:workers] > 1) && Puma.forkable?
end

@puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED'

if block
configure(&block)
end

@loaded = false
@clamped = false
end

attr_reader :options, :plugins
attr_reader :plugins, :events, :hooks

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

@_options
end

def configure
yield @user_dsl, @file_dsl, @default_dsl
Expand All @@ -204,15 +219,15 @@ def configure
def initialize_copy(other)
@conf = nil
@cli_options = nil
@options = @options.dup
@_options = @_options.dup
end

def flatten
dup.flatten!
end

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

Expand Down Expand Up @@ -241,28 +256,36 @@ def puma_options_from_env(env = ENV)
end

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

@options
@_options
end

def config_files
files = @options.all_of(: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|
first_default_file = %W(config/puma/#{@_options[:environment]}.rb config/puma.rb).find do |f|
File.exist?(f)
end

[first_default_file]
end

# Call once all configuration (included from rackup files)
# is loaded to flesh out any defaults
# is loaded to finalize defaults and lock in the configuration.
#
# This also calls load if it hasn't been called yet.
def clamp
@options.finalize_values
load unless @loaded
@clamped = true
options.finalize_values
warn_hooks
options
end

# Injects the Configuration object into the env
Expand All @@ -281,11 +304,11 @@ def call(env)
# Indicate if there is a properly configured app
#
def app_configured?
@options[:app] || File.exist?(rackup)
options[:app] || File.exist?(rackup)
end

def rackup
@options[:rackup]
options[:rackup]
end

# Load the specified rackup file, pull options from
Expand All @@ -294,9 +317,9 @@ def rackup
def app
found = options[:app] || load_rackup

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

Expand All @@ -305,7 +328,7 @@ def app

# Return which environment we're running in
def environment
@options[:environment]
options[:environment]
end

def load_plugin(name)
Expand All @@ -318,13 +341,14 @@ def load_plugin(name)
def run_hooks(key, arg, log_writer, hook_data = nil)
log_writer.debug "Running #{key} hooks"

@options.all_of(key).each do |b|
options.all_of(key).each do |hook_options|
begin
if Array === b
hook_data[b[1]] ||= Hash.new
b[0].call arg, hook_data[b[1]]
block = hook_options[:block]
if id = hook_options[:id]
hook_data[id] ||= Hash.new
block.call arg, hook_data[id]
else
b.call arg
block.call arg
end
rescue => e
log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
Expand All @@ -334,7 +358,7 @@ def run_hooks(key, arg, log_writer, hook_data = nil)
end

def final_options
@options.final_options
options.final_options
end

def self.temp_path
Expand All @@ -344,6 +368,12 @@ def self.temp_path
"#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
end

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

SecureRandom.hex(16)
end

private

def require_processor_counter
Expand Down Expand Up @@ -384,22 +414,34 @@ def load_rackup
rack_app, rack_options = rack_builder.parse_file(rackup)
rack_options = rack_options || {}

@options.file_options.merge!(rack_options)
options.file_options.merge!(rack_options)

config_ru_binds = []
rack_options.each do |k, v|
config_ru_binds << v if k.to_s.start_with?("bind")
end

@options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?

rack_app
end

def self.random_token
require 'securerandom' unless defined?(SecureRandom)
def warn_hooks
return if options[:workers] > 0
return if options[:silence_fork_callback_warning]

SecureRandom.hex(16)
@hooks.each do |key, method|
options.all_of(key).each do |hook_options|
next unless hook_options[:cluster_only]

LogWriter.stdio.log(<<~MSG.tr("\n", " "))
Warning: The code in the `#{method}` block will not execute
in the current Puma configuration. The `#{method}` block only
executes in Puma's cluster mode. To fix this, either remove the
`#{method}` call or increase Puma's worker count above zero.
MSG
end
end
end
end
end
3 changes: 2 additions & 1 deletion lib/puma/control_cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ def initialize(argv, stdout=STDOUT, stderr=STDERR, env: ENV)
require_relative 'log_writer'

config = Puma::Configuration.new({ config_files: [@config_file] }, {} , env)
config.load
config.clamp

@state ||= config.options[:state]
@control_url ||= config.options[:control_url]
@control_auth_token ||= config.options[:control_auth_token]
Expand Down
Loading