From 0584a3345e9d7ecd84494abc6ba7971309b5a338 Mon Sep 17 00:00:00 2001 From: schneems Date: Wed, 2 Aug 2017 14:28:39 -0500 Subject: [PATCH] [close #1327] Fix double port bind in Rails The "default" configuration puma level is initialized with a "binds" of `tcp://0.0.0.0:9292`. Puma is designed to be able to bind to multiple ports. When a `:port` is sent from Rails along with an empty `user_supplied_options` then the port is treated as a "default". This is merged in with the system defaults, and then later converted into a "binds" via calling `config.port` in the `set_host_port_to_config` method. The bug comes due to the "level" of the configuration. Since both are being set on the same "level" the `port` call does not over-write the existing binds but instead prepends to the array. We can fix by ensuring that any binds in a given "level" are empty before setting it. --- lib/puma/dsl.rb | 4 ++++ lib/rack/handler/puma.rb | 2 ++ test/test_rack_handler.rb | 10 ++++++++++ 3 files changed, 16 insertions(+) diff --git a/lib/puma/dsl.rb b/lib/puma/dsl.rb index eca8fe71f8..e2e142246c 100644 --- a/lib/puma/dsl.rb +++ b/lib/puma/dsl.rb @@ -118,6 +118,10 @@ def bind(url) @options[:binds] << url end + def clear_binds! + @options[:binds] = [] + end + # Define the TCP port to bind to. Use +bind+ for more advanced options. # def port(port, host=nil) diff --git a/lib/rack/handler/puma.rb b/lib/rack/handler/puma.rb index ef1a2a7f6a..83b757c24a 100644 --- a/lib/rack/handler/puma.rb +++ b/lib/rack/handler/puma.rb @@ -84,6 +84,8 @@ def self.valid_options end private def self.set_host_port_to_config(host, port, config) + config.clear_binds! if host || port + if host && (host[0,1] == '.' || host[0,1] == '/') config.bind "unix://#{host}" elsif host && host =~ /^ssl:\/\// diff --git a/test/test_rack_handler.rb b/test/test_rack_handler.rb index 73809cb12d..fa9e75a638 100644 --- a/test/test_rack_handler.rb +++ b/test/test_rack_handler.rb @@ -153,6 +153,16 @@ def test_config_wins_over_default end end + def test_user_port_wins_over_default_when_user_supplied_is_blank + user_port = 5001 + @options[:user_supplied_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 + def test_user_port_wins_over_default user_port = 5001 @options[:Port] = user_port