2019-07-16 18:53:28 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2011-10-04 17:23:51 -04:00
|
|
|
require 'rack/handler'
|
|
|
|
|
|
|
|
module Rack
|
|
|
|
module Handler
|
|
|
|
module Puma
|
2011-11-22 18:06:51 -05:00
|
|
|
DEFAULT_OPTIONS = {
|
2015-12-02 21:08:37 -05:00
|
|
|
:Verbose => false,
|
2016-02-04 17:11:34 -05:00
|
|
|
:Silent => false
|
2011-11-22 18:06:51 -05:00
|
|
|
}
|
2011-10-04 17:23:51 -04:00
|
|
|
|
2017-02-27 16:52:43 -05:00
|
|
|
def self.config(app, options = {})
|
2018-03-21 16:34:54 -04:00
|
|
|
require 'puma'
|
2016-09-06 16:21:08 -04:00
|
|
|
require 'puma/configuration'
|
|
|
|
require 'puma/events'
|
|
|
|
require 'puma/launcher'
|
|
|
|
|
2017-03-03 17:04:56 -05:00
|
|
|
default_options = DEFAULT_OPTIONS.dup
|
|
|
|
|
|
|
|
# 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)
|
2017-11-30 10:52:40 -05:00
|
|
|
(options.keys - user_supplied_options).each do |k|
|
2017-03-03 17:04:56 -05:00
|
|
|
default_options[k] = options.delete(k)
|
|
|
|
end
|
|
|
|
end
|
2011-11-22 18:06:51 -05:00
|
|
|
|
2017-03-03 17:04:56 -05:00
|
|
|
conf = ::Puma::Configuration.new(options, default_options) do |user_config, file_config, default_config|
|
2016-02-06 22:00:29 -05:00
|
|
|
if options.delete(:Verbose)
|
|
|
|
app = Rack::CommonLogger.new(app, STDOUT)
|
|
|
|
end
|
2011-11-22 18:06:51 -05:00
|
|
|
|
2016-02-06 22:00:29 -05:00
|
|
|
if options[:environment]
|
2017-03-03 16:11:59 -05:00
|
|
|
user_config.environment options[:environment]
|
2016-02-06 22:00:29 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
if options[:Threads]
|
|
|
|
min, max = options.delete(:Threads).split(':', 2)
|
2017-03-03 16:11:59 -05:00
|
|
|
user_config.threads min, max
|
2016-02-06 22:00:29 -05:00
|
|
|
end
|
2012-07-05 17:07:45 -04:00
|
|
|
|
2017-05-12 16:28:59 -04:00
|
|
|
if options[:Host] || options[:Port]
|
|
|
|
host = options[:Host] || default_options[:Host]
|
|
|
|
port = options[:Port] || default_options[:Port]
|
|
|
|
self.set_host_port_to_config(host, port, user_config)
|
|
|
|
end
|
2017-05-01 12:54:53 -04:00
|
|
|
|
Rack handler should use provided default host
This issue is somewhat tricky. When Rails is booted via `rails server` there are two types of configuration options passed, ones specified directly by a user like `rails s -p 3001` will always "win".
For any other config that is not explicitly passed in, puma will consider it a "default". For example when you run `rails s` (without -p) then the default port will be 3000.
There is one other way to configure puma though, and that is via a config file:
```
# config/puma.rb
port 3002
```
This is the order of precedence for configuration
1) Anything the user explicitly passes to `rails s`
2) Config specified in `config/puma.rb` file
3) Default values passed in via `rails s`
4) Defaults values stored in puma
This fallback mechanism works well except in the case of calling `port` in a `config/puma.rb` file. To understand look at the [old method definition](https://github.com/puma/puma/blob/2668597ec1dd9546d83db9f2ec5ad092add483e6/lib/puma/dsl.rb#L140-L145):
```
def port(port, host=nil)
host ||= Configuration::DefaultTCPHost
bind "tcp://#{host}:#{port}"
end
```
When the `port` method gets called, even if the user did not specify a `host` the `Configuration::DefaultTCPHost` will be used, which is a problem for local development because it defaults to `0.0.0.0`. [SO about 0.0.0.0 versus localhost](https://stackoverflow.com/questions/20778771/what-is-the-difference-between-0-0-0-0-127-0-0-1-and-localhost).
In this case, while a user did directly specify a port, they did not specify a host, so you would expect the `rails s` defaults passed in to take affect.
To make Puma respect that the host coming from `rails s` has more precedence than it's own default host, we must introduce the ability to set and retrieve a default_host value.
This is then used in the rack handler so when `rails s` passes in `:Host => "localhost"` then it is used instead of reverting to `0.0.0.0`.
The issue with #1699 is the test was wrong, it would have failed if a config file was present with a `port` invocation.
2019-01-04 14:15:31 -05:00
|
|
|
if default_options[:Host]
|
|
|
|
file_config.set_default_host(default_options[:Host])
|
|
|
|
end
|
2017-03-03 17:04:56 -05:00
|
|
|
self.set_host_port_to_config(default_options[:Host], default_options[:Port], default_config)
|
2016-02-06 22:00:29 -05:00
|
|
|
|
2017-03-03 16:11:59 -05:00
|
|
|
user_config.app app
|
2016-02-04 17:11:34 -05:00
|
|
|
end
|
2017-02-27 16:52:43 -05:00
|
|
|
conf
|
|
|
|
end
|
|
|
|
|
2020-03-18 14:19:34 -04:00
|
|
|
def self.run(app, **options)
|
2017-02-27 16:52:43 -05:00
|
|
|
conf = self.config(app, options)
|
2011-10-04 17:23:51 -04:00
|
|
|
|
2016-02-06 22:00:29 -05:00
|
|
|
events = options.delete(:Silent) ? ::Puma::Events.strings : ::Puma::Events.stdio
|
|
|
|
|
|
|
|
launcher = ::Puma::Launcher.new(conf, :events => events)
|
2011-10-04 17:23:51 -04:00
|
|
|
|
2016-02-04 10:25:04 -05:00
|
|
|
yield launcher if block_given?
|
2012-03-25 17:13:11 -04:00
|
|
|
begin
|
2016-02-04 10:25:04 -05:00
|
|
|
launcher.run
|
2012-03-25 17:13:11 -04:00
|
|
|
rescue Interrupt
|
2016-02-04 10:25:04 -05:00
|
|
|
puts "* Gracefully stopping, waiting for requests to finish"
|
|
|
|
launcher.stop
|
|
|
|
puts "* Goodbye!"
|
2012-03-25 17:13:11 -04:00
|
|
|
end
|
2011-10-04 17:23:51 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.valid_options
|
|
|
|
{
|
|
|
|
"Host=HOST" => "Hostname to listen on (default: localhost)",
|
|
|
|
"Port=PORT" => "Port to listen on (default: 8080)",
|
2011-11-22 18:06:51 -05:00
|
|
|
"Threads=MIN:MAX" => "min:max threads to use (default 0:16)",
|
2015-10-20 08:55:42 -04:00
|
|
|
"Verbose" => "Don't report each request (default: false)"
|
2011-10-04 17:23:51 -04:00
|
|
|
}
|
|
|
|
end
|
2019-08-06 11:54:11 -04:00
|
|
|
|
2017-03-03 17:04:56 -05:00
|
|
|
def self.set_host_port_to_config(host, port, config)
|
2017-08-02 15:28:39 -04:00
|
|
|
config.clear_binds! if host || port
|
|
|
|
|
2017-03-03 17:04:56 -05:00
|
|
|
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
|
2011-10-04 17:23:51 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
register :puma, Puma
|
|
|
|
end
|
|
|
|
end
|