mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Implement user_supplied_options behavior.
When options are passed to the Puma rack handler it is unknown if the options were set via a framework as a default or via a user. Puma currently has 3 different sources of configuration, the user via command line, the config files, and defaults. Rails 5.1+ will record the values actually specified by the user versus the values specified by the frameworks. It passes these values to the Rack handler and now it's up to Puma to do something with that information. When only framework defaults are passed it will set ``` options[:user_supplied_options] = [] ``` When one or more options are specified by the user such as `:Port` then those keys will be in the array. In that example it will look like this ``` options[:user_supplied_options] = [:Port] ``` This change is 100% backwards compatible. If the framework is older and does not pass this information then the `user_supplied_options` will not be set, in that case we assume all values are user supplied. Internally we accomplish this separation by replacing `LeveledOptions` which was a generic way of specifying options with different priorities with a more explicit `UserFileDefaultOptions` this assumes only 3 levels of options and it will use them in the order supplied (user config wins over file based config wins over defaults). Now instead of using 1 dsl to set all values, we use 3. A user dsl, a file dsl and a Configuration.new` will return all 3 DSLs to the block. It's up to the person using the block to use the correct dsl corresponding to the source of data they are getting.
This commit is contained in:
parent
24f12579bf
commit
85dfe8edcf
4 changed files with 138 additions and 45 deletions
|
@ -77,9 +77,10 @@ module Puma
|
|||
return cfg
|
||||
end
|
||||
|
||||
def initialize(options={}, &blk)
|
||||
def initialize(options={}, default_options = {}, &blk)
|
||||
default_options = self.puma_default_options.merge(default_options)
|
||||
|
||||
@options = UserFileDefaultOptions.new(options, self.default_options)
|
||||
@options = UserFileDefaultOptions.new(options, default_options)
|
||||
@plugins = PluginLoader.new
|
||||
@user_dsl = DSL.new(@options.user_options, self)
|
||||
@file_dsl = DSL.new(@options.file_options, self)
|
||||
|
@ -115,7 +116,7 @@ module Puma
|
|||
self
|
||||
end
|
||||
|
||||
def default_options
|
||||
def puma_default_options
|
||||
{
|
||||
:min_threads => 0,
|
||||
:max_threads => 16,
|
||||
|
|
|
@ -13,9 +13,20 @@ module Rack
|
|||
require 'puma/events'
|
||||
require 'puma/launcher'
|
||||
|
||||
options = DEFAULT_OPTIONS.merge(options)
|
||||
default_options = DEFAULT_OPTIONS.dup
|
||||
|
||||
conf = ::Puma::Configuration.new(options) do |user_config, file_config, default_config|
|
||||
# Libraries pass in values such as :Port and there is no way to determine
|
||||
# if it is a default provided by the library or a special value provided
|
||||
# by the user. A special key `user_supplied_options` can be passed. This
|
||||
# contains an array of all explicitly defined user options. We then
|
||||
# know that all other values are defaults
|
||||
if user_supplied_options = options.delete(:user_supplied_options)
|
||||
(options.keys - user_supplied_options).each do |k, v|
|
||||
default_options[k] = options.delete(k)
|
||||
end
|
||||
end
|
||||
|
||||
conf = ::Puma::Configuration.new(options, default_options) do |user_config, file_config, default_config|
|
||||
user_config.quiet
|
||||
|
||||
if options.delete(:Verbose)
|
||||
|
@ -31,30 +42,16 @@ module Rack
|
|||
user_config.threads min, max
|
||||
end
|
||||
|
||||
host = options[:Host]
|
||||
|
||||
if host && (host[0,1] == '.' || host[0,1] == '/')
|
||||
user_config.bind "unix://#{host}"
|
||||
elsif host && host =~ /^ssl:\/\//
|
||||
uri = URI.parse(host)
|
||||
uri.port ||= options[:Port] || ::Puma::Configuration::DefaultTCPPort
|
||||
user_config.bind uri.to_s
|
||||
else
|
||||
if host
|
||||
options[:Port] ||= ::Puma::Configuration::DefaultTCPPort
|
||||
end
|
||||
|
||||
if port = options[:Port]
|
||||
host ||= ::Puma::Configuration::DefaultTCPHost
|
||||
user_config.port port, host
|
||||
end
|
||||
end
|
||||
self.set_host_port_to_config(options[:Host], options[:Port], user_config)
|
||||
self.set_host_port_to_config(default_options[:Host], default_options[:Port], default_config)
|
||||
|
||||
user_config.app app
|
||||
end
|
||||
conf
|
||||
end
|
||||
|
||||
|
||||
|
||||
def self.run(app, options = {})
|
||||
conf = self.config(app, options)
|
||||
|
||||
|
@ -80,6 +77,26 @@ module Rack
|
|||
"Verbose" => "Don't report each request (default: false)"
|
||||
}
|
||||
end
|
||||
private
|
||||
def self.set_host_port_to_config(host, port, config)
|
||||
if host && (host[0,1] == '.' || host[0,1] == '/')
|
||||
config.bind "unix://#{host}"
|
||||
elsif host && host =~ /^ssl:\/\//
|
||||
uri = URI.parse(host)
|
||||
uri.port ||= port || ::Puma::Configuration::DefaultTCPPort
|
||||
config.bind uri.to_s
|
||||
else
|
||||
|
||||
if host
|
||||
port ||= ::Puma::Configuration::DefaultTCPPort
|
||||
end
|
||||
|
||||
if port
|
||||
host ||= ::Puma::Configuration::DefaultTCPHost
|
||||
config.port port, host
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
register :puma, Puma
|
||||
|
|
|
@ -13,6 +13,8 @@ require "minitest/pride"
|
|||
require "puma"
|
||||
require "puma/detect"
|
||||
|
||||
$LOAD_PATH << File.expand_path("../../lib", __FILE__)
|
||||
|
||||
# Either takes a string to do a get request against, or a tuple of [URI, HTTP] where
|
||||
# HTTP is some kind of Net::HTTP request object (POST, HEAD, etc.)
|
||||
def hit(uris)
|
||||
|
|
|
@ -53,37 +53,50 @@ class TestPathHandler < Minitest::Test
|
|||
assert_equal("/test", @input["PATH_INFO"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# def test_user_supplied_port_wins_over_config_file
|
||||
# user_port = 5001
|
||||
# file_port = 6001
|
||||
# options = {}
|
||||
class TestUserSuppliedOptionsPortIsSet < Minitest::Test
|
||||
def setup
|
||||
@options = {}
|
||||
@options[:user_supplied_options] = [:Port]
|
||||
end
|
||||
|
||||
# Tempfile.open("puma.rb") do |f|
|
||||
# f.puts "port #{file_port}"
|
||||
# f.close
|
||||
|
||||
# options[:config_files] = [f.path]
|
||||
# options[:user_supplied_options] = [:Port]
|
||||
# options[:Port] = user_port
|
||||
|
||||
# conf = Rack::Handler::Puma.config(app, options)
|
||||
# conf.load
|
||||
# assert_equal ["tcp://0.0.0.0:#{user_port}"], conf.options[:binds]
|
||||
# end
|
||||
# end
|
||||
|
||||
def test_default_port_loses_to_config_file
|
||||
def test_port_wins_over_config
|
||||
user_port = 5001
|
||||
file_port = 6001
|
||||
options = {}
|
||||
|
||||
Dir.mktmpdir do |d|
|
||||
Dir.chdir(d) do
|
||||
FileUtils.mkdir("config")
|
||||
File.open("config/puma.rb", "w") { |f| f << "port #{6001}" }
|
||||
File.open("config/puma.rb", "w") { |f| f << "port #{file_port}" }
|
||||
|
||||
conf = Rack::Handler::Puma.config(app, options)
|
||||
@options[:Port] = user_port
|
||||
conf = Rack::Handler::Puma.config(->{}, @options)
|
||||
conf.load
|
||||
|
||||
assert_equal ["tcp://0.0.0.0:#{user_port}"], conf.options[:binds]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestUserSuppliedOptionsIsEmpty < Minitest::Test
|
||||
def setup
|
||||
@options = {}
|
||||
@options[:user_supplied_options] = []
|
||||
end
|
||||
|
||||
def test_config_file_wins_over_port
|
||||
user_port = 5001
|
||||
file_port = 6001
|
||||
|
||||
Dir.mktmpdir do |d|
|
||||
Dir.chdir(d) do
|
||||
FileUtils.mkdir("config")
|
||||
File.open("config/puma.rb", "w") { |f| f << "port #{file_port}" }
|
||||
|
||||
@options[:Port] = user_port
|
||||
conf = Rack::Handler::Puma.config(->{}, @options)
|
||||
conf.load
|
||||
|
||||
assert_equal ["tcp://0.0.0.0:#{file_port}"], conf.options[:binds]
|
||||
|
@ -91,3 +104,63 @@ class TestPathHandler < Minitest::Test
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestUserSuppliedOptionsIsNotPresent < Minitest::Test
|
||||
def setup
|
||||
@options = {}
|
||||
end
|
||||
|
||||
def test_default_port_when_no_config_file
|
||||
options = {}
|
||||
conf = Rack::Handler::Puma.config(->{}, options)
|
||||
conf.load
|
||||
|
||||
assert_equal ["tcp://0.0.0.0:9292"], conf.options[:binds]
|
||||
end
|
||||
|
||||
def test_config_wins_over_default
|
||||
file_port = 6001
|
||||
@options = {}
|
||||
|
||||
Dir.mktmpdir do |d|
|
||||
Dir.chdir(d) do
|
||||
FileUtils.mkdir("config")
|
||||
File.open("config/puma.rb", "w") { |f| f << "port #{file_port}" }
|
||||
|
||||
conf = Rack::Handler::Puma.config(-> {}, @options)
|
||||
conf.load
|
||||
|
||||
assert_equal ["tcp://0.0.0.0:#{file_port}"], conf.options[:binds]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_user_port_wins_over_default
|
||||
user_port = 5001
|
||||
@options[:Port] = user_port
|
||||
conf = Rack::Handler::Puma.config(->{}, @options)
|
||||
conf.load
|
||||
|
||||
assert_equal ["tcp://0.0.0.0:#{user_port}"], conf.options[:binds]
|
||||
end
|
||||
|
||||
def test_user_port_wins_over_config
|
||||
user_port = 5001
|
||||
file_port = 6001
|
||||
@options = {}
|
||||
|
||||
Dir.mktmpdir do |d|
|
||||
Dir.chdir(d) do
|
||||
FileUtils.mkdir("config")
|
||||
File.open("config/puma.rb", "w") { |f| f << "port #{file_port}" }
|
||||
|
||||
@options[:Port] = user_port
|
||||
conf = Rack::Handler::Puma.config(->{}, @options)
|
||||
conf.load
|
||||
|
||||
assert_equal ["tcp://0.0.0.0:#{user_port}"], conf.options[:binds]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue