puma 4.3.6 → 5.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1346 -518
  3. data/LICENSE +23 -20
  4. data/README.md +74 -31
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +24 -20
  7. data/docs/compile_options.md +19 -0
  8. data/docs/deployment.md +15 -10
  9. data/docs/fork_worker.md +33 -0
  10. data/docs/jungle/README.md +9 -0
  11. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  12. data/{tools → docs}/jungle/rc.d/puma +2 -2
  13. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  14. data/docs/kubernetes.md +66 -0
  15. data/docs/nginx.md +1 -1
  16. data/docs/plugins.md +2 -2
  17. data/docs/rails_dev_mode.md +29 -0
  18. data/docs/restart.md +46 -23
  19. data/docs/signals.md +7 -6
  20. data/docs/stats.md +142 -0
  21. data/docs/systemd.md +27 -67
  22. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  23. data/ext/puma_http11/ext_help.h +1 -1
  24. data/ext/puma_http11/extconf.rb +22 -8
  25. data/ext/puma_http11/http11_parser.c +45 -47
  26. data/ext/puma_http11/http11_parser.h +1 -1
  27. data/ext/puma_http11/http11_parser.java.rl +1 -1
  28. data/ext/puma_http11/http11_parser.rl +1 -1
  29. data/ext/puma_http11/mini_ssl.c +211 -118
  30. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  31. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  32. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +5 -7
  33. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +77 -18
  34. data/ext/puma_http11/puma_http11.c +31 -50
  35. data/lib/puma.rb +46 -0
  36. data/lib/puma/app/status.rb +47 -36
  37. data/lib/puma/binder.rb +177 -103
  38. data/lib/puma/cli.rb +11 -15
  39. data/lib/puma/client.rb +73 -74
  40. data/lib/puma/cluster.rb +184 -198
  41. data/lib/puma/cluster/worker.rb +183 -0
  42. data/lib/puma/cluster/worker_handle.rb +90 -0
  43. data/lib/puma/commonlogger.rb +2 -2
  44. data/lib/puma/configuration.rb +55 -49
  45. data/lib/puma/const.rb +13 -5
  46. data/lib/puma/control_cli.rb +93 -76
  47. data/lib/puma/detect.rb +24 -3
  48. data/lib/puma/dsl.rb +266 -92
  49. data/lib/puma/error_logger.rb +104 -0
  50. data/lib/puma/events.rb +55 -34
  51. data/lib/puma/io_buffer.rb +9 -2
  52. data/lib/puma/jruby_restart.rb +0 -58
  53. data/lib/puma/json.rb +96 -0
  54. data/lib/puma/launcher.rb +113 -45
  55. data/lib/puma/minissl.rb +114 -33
  56. data/lib/puma/minissl/context_builder.rb +6 -3
  57. data/lib/puma/null_io.rb +13 -1
  58. data/lib/puma/plugin.rb +1 -10
  59. data/lib/puma/queue_close.rb +26 -0
  60. data/lib/puma/rack/builder.rb +0 -4
  61. data/lib/puma/reactor.rb +85 -369
  62. data/lib/puma/request.rb +467 -0
  63. data/lib/puma/runner.rb +29 -58
  64. data/lib/puma/server.rb +267 -729
  65. data/lib/puma/single.rb +9 -65
  66. data/lib/puma/state_file.rb +8 -3
  67. data/lib/puma/systemd.rb +46 -0
  68. data/lib/puma/thread_pool.rb +119 -53
  69. data/lib/puma/util.rb +12 -0
  70. data/lib/rack/handler/puma.rb +2 -3
  71. data/tools/{docker/Dockerfile → Dockerfile} +0 -0
  72. metadata +25 -21
  73. data/docs/tcp_mode.md +0 -96
  74. data/ext/puma_http11/io_buffer.c +0 -155
  75. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  76. data/lib/puma/accept_nonblock.rb +0 -29
  77. data/lib/puma/tcp_logger.rb +0 -41
  78. data/tools/jungle/README.md +0 -19
  79. data/tools/jungle/init.d/README.md +0 -61
  80. data/tools/jungle/init.d/puma +0 -421
  81. data/tools/jungle/init.d/run-puma +0 -18
  82. data/tools/jungle/upstart/README.md +0 -61
  83. data/tools/jungle/upstart/puma-manager.conf +0 -31
  84. data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/const.rb CHANGED
@@ -100,8 +100,9 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "4.3.6".freeze
104
- CODE_NAME = "Mysterious Traveller".freeze
103
+ PUMA_VERSION = VERSION = "5.3.2".freeze
104
+ CODE_NAME = "Sweetnighter".freeze
105
+
105
106
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
106
107
 
107
108
  FAST_TRACK_KA_TIMEOUT = 0.2
@@ -175,7 +176,6 @@ module Puma
175
176
  PORT_443 = "443".freeze
176
177
  LOCALHOST = "localhost".freeze
177
178
  LOCALHOST_IP = "127.0.0.1".freeze
178
- LOCALHOST_ADDR = "127.0.0.1:0".freeze
179
179
 
180
180
  SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
181
181
  HTTP_11 = "HTTP/1.1".freeze
@@ -228,7 +228,6 @@ module Puma
228
228
  COLON = ": ".freeze
229
229
 
230
230
  NEWLINE = "\n".freeze
231
- HTTP_INJECTION_REGEX = /[\r\n]/.freeze
232
231
 
233
232
  HIJACK_P = "rack.hijack?".freeze
234
233
  HIJACK = "rack.hijack".freeze
@@ -236,8 +235,17 @@ module Puma
236
235
 
237
236
  EARLY_HINTS = "rack.early_hints".freeze
238
237
 
239
- # Mininum interval to checks worker health
238
+ # Minimum interval to checks worker health
240
239
  WORKER_CHECK_INTERVAL = 5
241
240
 
241
+ # Illegal character in the key or value of response header
242
+ DQUOTE = "\"".freeze
243
+ HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
244
+ ILLEGAL_HEADER_KEY_REGEX = /[\x00-\x20#{DQUOTE}#{HTTP_HEADER_DELIMITER}]/.freeze
245
+ # header values can contain HTAB?
246
+ ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
247
+
248
+ # Banned keys of response header
249
+ BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
242
250
  end
243
251
  end
@@ -11,7 +11,32 @@ require 'socket'
11
11
  module Puma
12
12
  class ControlCLI
13
13
 
14
- COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory gc gc-stats}
14
+ # values must be string or nil
15
+ # value of `nil` means command cannot be processed via signal
16
+ # @version 5.0.3
17
+ CMD_PATH_SIG_MAP = {
18
+ 'gc' => nil,
19
+ 'gc-stats' => nil,
20
+ 'halt' => 'SIGQUIT',
21
+ 'phased-restart' => 'SIGUSR1',
22
+ 'refork' => 'SIGURG',
23
+ 'reload-worker-directory' => nil,
24
+ 'restart' => 'SIGUSR2',
25
+ 'start' => nil,
26
+ 'stats' => nil,
27
+ 'status' => '',
28
+ 'stop' => 'SIGTERM',
29
+ 'thread-backtraces' => nil
30
+ }.freeze
31
+
32
+ # @deprecated 6.0.0
33
+ COMMANDS = CMD_PATH_SIG_MAP.keys.freeze
34
+
35
+ # commands that cannot be used in a request
36
+ NO_REQ_COMMANDS = %w{refork}.freeze
37
+
38
+ # @version 5.0.0
39
+ PRINTABLE_COMMANDS = %w{gc-stats stats thread-backtraces}.freeze
15
40
 
16
41
  def initialize(argv, stdout=STDOUT, stderr=STDERR)
17
42
  @state = nil
@@ -22,7 +47,7 @@ module Puma
22
47
  @control_auth_token = nil
23
48
  @config_file = nil
24
49
  @command = nil
25
- @environment = ENV['RACK_ENV']
50
+ @environment = ENV['RACK_ENV'] || ENV['RAILS_ENV']
26
51
 
27
52
  @argv = argv.dup
28
53
  @stdout = stdout
@@ -30,7 +55,7 @@ module Puma
30
55
  @cli_options = {}
31
56
 
32
57
  opts = OptionParser.new do |o|
33
- o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{COMMANDS.join("|")})"
58
+ o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{CMD_PATH_SIG_MAP.keys.join("|")})"
34
59
 
35
60
  o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
36
61
  @state = arg
@@ -71,7 +96,7 @@ module Puma
71
96
  end
72
97
 
73
98
  o.on_tail("-V", "--version", "Show version") do
74
- puts Const::PUMA_VERSION
99
+ @stdout.puts Const::PUMA_VERSION
75
100
  exit
76
101
  end
77
102
  end
@@ -81,6 +106,15 @@ module Puma
81
106
 
82
107
  @command = argv.shift
83
108
 
109
+ # check presence of command
110
+ unless @command
111
+ raise "Available commands: #{CMD_PATH_SIG_MAP.keys.join(", ")}"
112
+ end
113
+
114
+ unless CMD_PATH_SIG_MAP.key? @command
115
+ raise "Invalid command: #{@command}"
116
+ end
117
+
84
118
  unless @config_file == '-'
85
119
  environment = @environment || 'development'
86
120
 
@@ -99,16 +133,6 @@ module Puma
99
133
  @pidfile ||= config.options[:pidfile]
100
134
  end
101
135
  end
102
-
103
- # check present of command
104
- unless @command
105
- raise "Available commands: #{COMMANDS.join(", ")}"
106
- end
107
-
108
- unless COMMANDS.include? @command
109
- raise "Invalid command: #{@command}"
110
- end
111
-
112
136
  rescue => e
113
137
  @stdout.puts e.message
114
138
  exit 1
@@ -132,7 +156,7 @@ module Puma
132
156
  @pid = sf.pid
133
157
  elsif @pidfile
134
158
  # get pid from pid_file
135
- File.open(@pidfile) { |f| @pid = f.read.to_i }
159
+ @pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
136
160
  end
137
161
  end
138
162
 
@@ -140,23 +164,29 @@ module Puma
140
164
  uri = URI.parse @control_url
141
165
 
142
166
  # create server object by scheme
143
- server = case uri.scheme
144
- when "ssl"
145
- require 'openssl'
146
- OpenSSL::SSL::SSLSocket.new(
147
- TCPSocket.new(uri.host, uri.port),
148
- OpenSSL::SSL::SSLContext.new
149
- ).tap(&:connect)
150
- when "tcp"
151
- TCPSocket.new uri.host, uri.port
152
- when "unix"
153
- UNIXSocket.new "#{uri.host}#{uri.path}"
154
- else
155
- raise "Invalid scheme: #{uri.scheme}"
156
- end
157
-
158
- if @command == "status"
159
- message "Puma is started"
167
+ server =
168
+ case uri.scheme
169
+ when 'ssl'
170
+ require 'openssl'
171
+ OpenSSL::SSL::SSLSocket.new(
172
+ TCPSocket.new(uri.host, uri.port),
173
+ OpenSSL::SSL::SSLContext.new)
174
+ .tap { |ssl| ssl.sync_close = true } # default is false
175
+ .tap(&:connect)
176
+ when 'tcp'
177
+ TCPSocket.new uri.host, uri.port
178
+ when 'unix'
179
+ # check for abstract UNIXSocket
180
+ UNIXSocket.new(@control_url.start_with?('unix://@') ?
181
+ "\0#{uri.host}#{uri.path}" : "#{uri.host}#{uri.path}")
182
+ else
183
+ raise "Invalid scheme: #{uri.scheme}"
184
+ end
185
+
186
+ if @command == 'status'
187
+ message 'Puma is started'
188
+ elsif NO_REQ_COMMANDS.include? @command
189
+ raise "Invalid request command: #{@command}"
160
190
  else
161
191
  url = "/#{@command}"
162
192
 
@@ -164,10 +194,10 @@ module Puma
164
194
  url = url + "?token=#{@control_auth_token}"
165
195
  end
166
196
 
167
- server << "GET #{url} HTTP/1.0\r\n\r\n"
197
+ server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
168
198
 
169
199
  unless data = server.read
170
- raise "Server closed connection before responding"
200
+ raise 'Server closed connection before responding'
171
201
  end
172
202
 
173
203
  response = data.split("\r\n")
@@ -176,67 +206,55 @@ module Puma
176
206
  raise "Server sent empty response"
177
207
  end
178
208
 
179
- (@http,@code,@message) = response.first.split(" ",3)
209
+ @http, @code, @message = response.first.split(' ',3)
180
210
 
181
- if @code == "403"
182
- raise "Unauthorized access to server (wrong auth token)"
183
- elsif @code == "404"
211
+ if @code == '403'
212
+ raise 'Unauthorized access to server (wrong auth token)'
213
+ elsif @code == '404'
184
214
  raise "Command error: #{response.last}"
185
- elsif @code != "200"
215
+ elsif @code != '200'
186
216
  raise "Bad response from server: #{@code}"
187
217
  end
188
218
 
189
219
  message "Command #{@command} sent success"
190
- message response.last if @command == "stats" || @command == "gc-stats"
220
+ message response.last if PRINTABLE_COMMANDS.include?(@command)
191
221
  end
192
222
  ensure
193
- server.close if server && !server.closed?
223
+ if server
224
+ if uri.scheme == 'ssl'
225
+ server.sysclose
226
+ else
227
+ server.close unless server.closed?
228
+ end
229
+ end
194
230
  end
195
231
 
196
232
  def send_signal
197
233
  unless @pid
198
- raise "Neither pid nor control url available"
234
+ raise 'Neither pid nor control url available'
199
235
  end
200
236
 
201
237
  begin
238
+ sig = CMD_PATH_SIG_MAP[@command]
202
239
 
203
- case @command
204
- when "restart"
205
- Process.kill "SIGUSR2", @pid
206
-
207
- when "halt"
208
- Process.kill "QUIT", @pid
209
-
210
- when "stop"
211
- Process.kill "SIGTERM", @pid
212
-
213
- when "stats"
214
- puts "Stats not available via pid only"
215
- return
216
-
217
- when "reload-worker-directory"
218
- puts "reload-worker-directory not available via pid only"
240
+ if sig.nil?
241
+ @stdout.puts "'#{@command}' not available via pid only"
242
+ @stdout.flush unless @stdout.sync
219
243
  return
220
-
221
- when "phased-restart"
222
- Process.kill "SIGUSR1", @pid
223
-
224
- when "status"
244
+ elsif sig.start_with? 'SIG'
245
+ Process.kill sig, @pid
246
+ elsif @command == 'status'
225
247
  begin
226
248
  Process.kill 0, @pid
227
- puts "Puma is started"
249
+ @stdout.puts 'Puma is started'
250
+ @stdout.flush unless @stdout.sync
228
251
  rescue Errno::ESRCH
229
- raise "Puma is not running"
252
+ raise 'Puma is not running'
230
253
  end
231
-
232
- return
233
-
234
- else
235
254
  return
236
255
  end
237
-
238
256
  rescue SystemCallError
239
- if @command == "restart"
257
+ if @command == 'restart'
240
258
  start
241
259
  else
242
260
  raise "No pid '#{@pid}' found"
@@ -247,14 +265,13 @@ module Puma
247
265
  end
248
266
 
249
267
  def run
250
- return start if @command == "start"
251
-
268
+ return start if @command == 'start'
252
269
  prepare_configuration
253
270
 
254
- if Puma.windows?
271
+ if Puma.windows? || @control_url
255
272
  send_request
256
273
  else
257
- @control_url ? send_request : send_signal
274
+ send_signal
258
275
  end
259
276
 
260
277
  rescue => e
@@ -262,7 +279,7 @@ module Puma
262
279
  exit 1
263
280
  end
264
281
 
265
- private
282
+ private
266
283
  def start
267
284
  require 'puma/cli'
268
285
 
data/lib/puma/detect.rb CHANGED
@@ -1,15 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # This file can be loaded independently of puma.rb, so it cannot have any code
4
+ # that assumes puma.rb is loaded.
5
+
6
+
3
7
  module Puma
4
- IS_JRUBY = defined?(JRUBY_VERSION)
8
+ # @version 5.2.1
9
+ HAS_FORK = ::Process.respond_to? :fork
10
+
11
+ IS_JRUBY = Object.const_defined? :JRUBY_VERSION
12
+
13
+ IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/ ||
14
+ IS_JRUBY && RUBY_DESCRIPTION =~ /mswin/)
15
+
16
+ # @version 5.2.0
17
+ IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
5
18
 
6
19
  def self.jruby?
7
20
  IS_JRUBY
8
21
  end
9
22
 
10
- IS_WINDOWS = RUBY_PLATFORM =~ /mswin|ming|cygwin/
11
-
12
23
  def self.windows?
13
24
  IS_WINDOWS
14
25
  end
26
+
27
+ # @version 5.0.0
28
+ def self.mri?
29
+ IS_MRI
30
+ end
31
+
32
+ # @version 5.0.0
33
+ def self.forkable?
34
+ HAS_FORK
35
+ end
15
36
  end
data/lib/puma/dsl.rb CHANGED
@@ -14,25 +14,60 @@ module Puma
14
14
  # end
15
15
  # config.load
16
16
  #
17
- # puts config.options[:binds]
18
- # "tcp://127.0.0.1:3001"
17
+ # puts config.options[:binds] # => "tcp://127.0.0.1:3001"
19
18
  #
20
19
  # Used to load file:
21
20
  #
22
21
  # $ cat puma_config.rb
23
- # port 3002
22
+ # port 3002
23
+ #
24
+ # Resulting configuration:
24
25
  #
25
26
  # config = Configuration.new(config_file: "puma_config.rb")
26
27
  # config.load
27
28
  #
28
- # puts config.options[:binds]
29
- # # => "tcp://127.0.0.1:3002"
29
+ # puts config.options[:binds] # => "tcp://127.0.0.1:3002"
30
30
  #
31
31
  # You can also find many examples being used by the test suite in
32
32
  # +test/config+.
33
+ #
33
34
  class DSL
34
35
  include ConfigDefault
35
36
 
37
+ # convenience method so logic can be used in CI
38
+ # @see ssl_bind
39
+ #
40
+ def self.ssl_bind_str(host, port, opts)
41
+ verify = opts.fetch(:verify_mode, 'none').to_s
42
+
43
+ tls_str =
44
+ if opts[:no_tlsv1_1] then '&no_tlsv1_1=true'
45
+ elsif opts[:no_tlsv1] then '&no_tlsv1=true'
46
+ else ''
47
+ end
48
+
49
+ ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
50
+
51
+ if defined?(JRUBY_VERSION)
52
+ ssl_cipher_list = opts[:ssl_cipher_list] ?
53
+ "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil
54
+
55
+ keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
56
+
57
+ "ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
58
+ "&verify_mode=#{verify}#{tls_str}#{ca_additions}"
59
+ else
60
+ ssl_cipher_filter = opts[:ssl_cipher_filter] ?
61
+ "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
62
+
63
+ v_flags = (ary = opts[:verification_flags]) ?
64
+ "&verification_flags=#{Array(ary).join ','}" : nil
65
+
66
+ "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
67
+ "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}"
68
+ end
69
+ end
70
+
36
71
  def initialize(options, config)
37
72
  @config = config
38
73
  @options = options
@@ -98,6 +133,9 @@ module Puma
98
133
  # [body]
99
134
  # ]
100
135
  # end
136
+ #
137
+ # @see Puma::Configuration#app
138
+ #
101
139
  def app(obj=nil, &block)
102
140
  obj ||= block
103
141
 
@@ -160,12 +198,12 @@ module Puma
160
198
  #
161
199
  # You can use query parameters within the url to specify options:
162
200
  #
163
- # - Set the socket backlog depth with +backlog+, default is 1024.
164
- # - Set up an SSL certificate with +key+ & +cert+.
165
- # - Set whether to optimize for low latency instead of throughput with
166
- # +low_latency+, default is to optimize for low latency. This is done
167
- # via +Socket::TCP_NODELAY+.
168
- # - Set socket permissions with +umask+.
201
+ # * Set the socket backlog depth with +backlog+, default is 1024.
202
+ # * Set up an SSL certificate with +key+ & +cert+.
203
+ # * Set whether to optimize for low latency instead of throughput with
204
+ # +low_latency+, default is to not optimize for low latency. This is done
205
+ # via +Socket::TCP_NODELAY+.
206
+ # * Set socket permissions with +umask+.
169
207
  #
170
208
  # @example Backlog depth
171
209
  # bind 'unix:///var/run/puma.sock?backlog=512'
@@ -175,6 +213,9 @@ module Puma
175
213
  # bind 'tcp://0.0.0.0:9292?low_latency=false'
176
214
  # @example Socket permissions
177
215
  # bind 'unix:///var/run/puma.sock?umask=0111'
216
+ # @see Puma::Runner#load_and_bind
217
+ # @see Puma::Cluster#run
218
+ #
178
219
  def bind(url)
179
220
  @options[:binds] ||= []
180
221
  @options[:binds] << url
@@ -184,22 +225,49 @@ module Puma
184
225
  @options[:binds] = []
185
226
  end
186
227
 
228
+ # Bind to (systemd) activated sockets, regardless of configured binds.
229
+ #
230
+ # Systemd can present sockets as file descriptors that are already opened.
231
+ # By default Puma will use these but only if it was explicitly told to bind
232
+ # to the socket. If not, it will close the activated sockets. This means
233
+ # all configuration is duplicated.
234
+ #
235
+ # Binds can contain additional configuration, but only SSL config is really
236
+ # relevant since the unix and TCP socket options are ignored.
237
+ #
238
+ # This means there is a lot of duplicated configuration for no additional
239
+ # value in most setups. This method tells the launcher to bind to all
240
+ # activated sockets, regardless of existing bind.
241
+ #
242
+ # To clear configured binds, the value only can be passed. This will clear
243
+ # out any binds that may have been configured.
244
+ #
245
+ # @example Use any systemd activated sockets as well as configured binds
246
+ # bind_to_activated_sockets
247
+ #
248
+ # @example Only bind to systemd activated sockets, ignoring other binds
249
+ # bind_to_activated_sockets 'only'
250
+ def bind_to_activated_sockets(bind=true)
251
+ @options[:bind_to_activated_sockets] = bind
252
+ end
253
+
187
254
  # Define the TCP port to bind to. Use +bind+ for more advanced options.
188
255
  #
189
256
  # @example
190
257
  # port 9292
191
258
  def port(port, host=nil)
192
259
  host ||= default_host
193
- bind "tcp://#{host}:#{port}"
260
+ bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s
194
261
  end
195
262
 
196
- # Define how long persistent connections can be idle before Puma closes
197
- # them.
263
+ # Define how long persistent connections can be idle before Puma closes them.
264
+ # @see Puma::Server.new
198
265
  def persistent_timeout(seconds)
199
266
  @options[:persistent_timeout] = Integer(seconds)
200
267
  end
201
268
 
202
269
  # Define how long the tcp socket stays open, if no data has been received.
270
+ # @see Puma::Server.new
203
271
  def first_data_timeout(seconds)
204
272
  @options[:first_data_timeout] = Integer(seconds)
205
273
  end
@@ -210,24 +278,11 @@ module Puma
210
278
  @options[:clean_thread_locals] = which
211
279
  end
212
280
 
213
- # Daemonize the server into the background. It's highly recommended to
214
- # use this in combination with +pidfile+ and +stdout_redirect+.
215
- #
216
- # The default is "false".
217
- #
218
- # @example
219
- # daemonize
281
+ # When shutting down, drain the accept socket of pending connections and
282
+ # process them. This loops over the accept socket until there are no more
283
+ # read events and then stops looking and waits for the requests to finish.
284
+ # @see Puma::Server#graceful_shutdown
220
285
  #
221
- # @example
222
- # daemonize false
223
- def daemonize(which=true)
224
- @options[:daemon] = which
225
- end
226
-
227
- # When shutting down, drain the accept socket of pending
228
- # connections and process them. This loops over the accept
229
- # socket until there are no more read events and then stops
230
- # looking and waits for the requests to finish.
231
286
  def drain_on_shutdown(which=true)
232
287
  @options[:drain_on_shutdown] = which
233
288
  end
@@ -250,6 +305,7 @@ module Puma
250
305
  #
251
306
  # Puma always waits a few seconds after killing a thread for it to try
252
307
  # to finish up it's work, even in :immediately mode.
308
+ # @see Puma::Server#graceful_shutdown
253
309
  def force_shutdown_after(val=:forever)
254
310
  i = case val
255
311
  when :forever
@@ -257,7 +313,7 @@ module Puma
257
313
  when :immediately
258
314
  0
259
315
  else
260
- Integer(val)
316
+ Float(val)
261
317
  end
262
318
 
263
319
  @options[:force_shutdown_after] = i
@@ -322,20 +378,14 @@ module Puma
322
378
  # @example
323
379
  # rackup '/u/apps/lolcat/config.ru'
324
380
  def rackup(path)
325
- @options[:rackup] = path.to_s
326
- end
327
-
328
- # Run Puma in TCP mode
329
- #
330
- def tcp_mode!
331
- @options[:mode] = :tcp
381
+ @options[:rackup] ||= path.to_s
332
382
  end
333
383
 
334
384
  def early_hints(answer=true)
335
385
  @options[:early_hints] = answer
336
386
  end
337
387
 
338
- # Redirect STDOUT and STDERR to files specified. The +append+ parameter
388
+ # Redirect +STDOUT+ and +STDERR+ to files specified. The +append+ parameter
339
389
  # specifies whether the output is appended, the default is +false+.
340
390
  #
341
391
  # @example
@@ -355,7 +405,10 @@ module Puma
355
405
  # Configure +min+ to be the minimum number of threads to use to answer
356
406
  # requests and +max+ the maximum.
357
407
  #
358
- # The default is "0, 16".
408
+ # The default is the environment variables +PUMA_MIN_THREADS+ / +PUMA_MAX_THREADS+
409
+ # (or +MIN_THREADS+ / +MAX_THREADS+ if the +PUMA_+ variables aren't set).
410
+ #
411
+ # If these environment variables aren't set, the default is "0, 5" in MRI or "0, 16" for other interpreters.
359
412
  #
360
413
  # @example
361
414
  # threads 0, 16
@@ -376,8 +429,8 @@ module Puma
376
429
  @options[:max_threads] = max
377
430
  end
378
431
 
379
- # Instead of "bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'" you
380
- # can also use the "ssl_bind" option.
432
+ # Instead of `bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'` you
433
+ # can also use the this method.
381
434
  #
382
435
  # @example
383
436
  # ssl_bind '127.0.0.1', '9292', {
@@ -385,29 +438,17 @@ module Puma
385
438
  # key: path_to_key,
386
439
  # ssl_cipher_filter: cipher_filter, # optional
387
440
  # verify_mode: verify_mode, # default 'none'
441
+ # verification_flags: flags, # optional, not supported by JRuby
388
442
  # }
389
- # @example For JRuby additional keys are required: keystore & keystore_pass.
443
+ # @example For JRuby, two keys are required: keystore & keystore_pass.
390
444
  # ssl_bind '127.0.0.1', '9292', {
391
- # cert: path_to_cert,
392
- # key: path_to_key,
393
- # ssl_cipher_filter: cipher_filter, # optional
394
- # verify_mode: verify_mode, # default 'none'
395
445
  # keystore: path_to_keystore,
396
- # keystore_pass: password
446
+ # keystore_pass: password,
447
+ # ssl_cipher_list: cipher_list, # optional
448
+ # verify_mode: verify_mode # default 'none'
397
449
  # }
398
450
  def ssl_bind(host, port, opts)
399
- verify = opts.fetch(:verify_mode, 'none').to_s
400
- no_tlsv1 = opts.fetch(:no_tlsv1, 'false')
401
- no_tlsv1_1 = opts.fetch(:no_tlsv1_1, 'false')
402
- ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
403
-
404
- if defined?(JRUBY_VERSION)
405
- keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
406
- bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}"
407
- else
408
- ssl_cipher_filter = "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" if opts[:ssl_cipher_filter]
409
- bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}#{ssl_cipher_filter}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}"
410
- end
451
+ bind self.class.ssl_bind_str(host, port, opts)
411
452
  end
412
453
 
413
454
  # Use +path+ as the file to store the server info state. This is
@@ -419,16 +460,46 @@ module Puma
419
460
  @options[:state] = path.to_s
420
461
  end
421
462
 
463
+ # Use +permission+ to restrict permissions for the state file.
464
+ #
465
+ # @example
466
+ # state_permission 0600
467
+ # @version 5.0.0
468
+ #
469
+ def state_permission(permission)
470
+ @options[:state_permission] = permission
471
+ end
472
+
422
473
  # How many worker processes to run. Typically this is set to
423
- # to the number of available cores.
474
+ # the number of available cores.
424
475
  #
425
- # The default is 0.
476
+ # The default is the value of the environment variable +WEB_CONCURRENCY+ if
477
+ # set, otherwise 0.
426
478
  #
427
479
  # @note Cluster mode only.
480
+ # @see Puma::Cluster
428
481
  def workers(count)
429
482
  @options[:workers] = count.to_i
430
483
  end
431
484
 
485
+ # Disable warning message when running in cluster mode with a single worker.
486
+ #
487
+ # Cluster mode has some overhead of running an additional 'control' process
488
+ # in order to manage the cluster. If only running a single worker it is
489
+ # likely not worth paying that overhead vs running in single mode with
490
+ # additional threads instead.
491
+ #
492
+ # There are some scenarios where running cluster mode with a single worker
493
+ # may still be warranted and valid under certain deployment scenarios, see
494
+ # https://github.com/puma/puma/issues/2534
495
+ #
496
+ # Moving from workers = 1 to workers = 0 will save 10-30% of memory use.
497
+ #
498
+ # @note Cluster mode only.
499
+ def silence_single_worker_warning
500
+ @options[:silence_single_worker_warning] = true
501
+ end
502
+
432
503
  # Code to run immediately before master process
433
504
  # forks workers (once on boot). These hooks can block if necessary
434
505
  # to wait for background operations unknown to Puma to finish before
@@ -455,8 +526,8 @@ module Puma
455
526
  #
456
527
  # @note Cluster mode only.
457
528
  # @example
458
- # on_worker_fork do
459
- # puts 'Before worker fork...'
529
+ # on_worker_boot do
530
+ # puts 'Before worker boot...'
460
531
  # end
461
532
  def on_worker_boot(&block)
462
533
  @options[:before_worker_boot] ||= []
@@ -512,6 +583,29 @@ module Puma
512
583
 
513
584
  alias_method :after_worker_boot, :after_worker_fork
514
585
 
586
+ # When `fork_worker` is enabled, code to run in Worker 0
587
+ # before all other workers are re-forked from this process,
588
+ # after the server has temporarily stopped serving requests
589
+ # (once per complete refork cycle).
590
+ #
591
+ # This can be used to trigger extra garbage-collection to maximize
592
+ # copy-on-write efficiency, or close any connections to remote servers
593
+ # (database, Redis, ...) that were opened while the server was running.
594
+ #
595
+ # This can be called multiple times to add several hooks.
596
+ #
597
+ # @note Cluster mode with `fork_worker` enabled only.
598
+ # @example
599
+ # on_refork do
600
+ # 3.times {GC.start}
601
+ # end
602
+ # @version 5.0.0
603
+ #
604
+ def on_refork(&block)
605
+ @options[:before_refork] ||= []
606
+ @options[:before_refork] << block
607
+ end
608
+
515
609
  # Code to run out-of-band when the worker is idle.
516
610
  # These hooks run immediately after a request has finished
517
611
  # processing and there are no busy threads on the worker.
@@ -536,19 +630,8 @@ module Puma
536
630
  @options[:directory] = dir.to_s
537
631
  end
538
632
 
539
- # DEPRECATED: The directory to operate out of.
540
- def worker_directory(dir)
541
- $stderr.puts "worker_directory is deprecated. Please use `directory`"
542
- directory dir
543
- end
544
-
545
- # Run the app as a raw TCP app instead of an HTTP rack app.
546
- def tcp_mode
547
- @options[:mode] = :tcp
548
- end
549
-
550
633
  # Preload the application before starting the workers; this conflicts with
551
- # phased restart feature. This is off by default.
634
+ # phased restart feature. On by default if your app uses more than 1 worker.
552
635
  #
553
636
  # @note Cluster mode only.
554
637
  # @example
@@ -583,7 +666,7 @@ module Puma
583
666
  # new Bundler context and thus can float around as the release
584
667
  # dictates.
585
668
  #
586
- # See also: extra_runtime_dependencies
669
+ # @see extra_runtime_dependencies
587
670
  #
588
671
  # @note This is incompatible with +preload_app!+.
589
672
  # @note This is only supported for RubyGems 2.2+
@@ -600,6 +683,9 @@ module Puma
600
683
  #
601
684
  # @example
602
685
  # raise_exception_on_sigterm false
686
+ # @see Puma::Launcher#setup_signals
687
+ # @see Puma::Cluster#setup_signals
688
+ #
603
689
  def raise_exception_on_sigterm(answer=true)
604
690
  @options[:raise_exception_on_sigterm] = answer
605
691
  end
@@ -615,6 +701,8 @@ module Puma
615
701
  # extra_runtime_dependencies ['gem_name_1', 'gem_name_2']
616
702
  # @example
617
703
  # extra_runtime_dependencies ['puma_worker_killer', 'puma-heroku']
704
+ # @see Puma::Launcher#extra_runtime_deps_directories
705
+ #
618
706
  def extra_runtime_dependencies(answer = [])
619
707
  @options[:extra_runtime_dependencies] = Array(answer)
620
708
  end
@@ -642,6 +730,8 @@ module Puma
642
730
  # @note Cluster mode only.
643
731
  # @example
644
732
  # worker_timeout 60
733
+ # @see Puma::Cluster::Worker#ping_timeout
734
+ #
645
735
  def worker_timeout(timeout)
646
736
  timeout = Integer(timeout)
647
737
  min = Const::WORKER_CHECK_INTERVAL
@@ -658,15 +748,20 @@ module Puma
658
748
  # If unspecified, this defaults to the value of worker_timeout.
659
749
  #
660
750
  # @note Cluster mode only.
661
- # @example:
751
+ #
752
+ # @example
662
753
  # worker_boot_timeout 60
754
+ # @see Puma::Cluster::Worker#ping_timeout
755
+ #
663
756
  def worker_boot_timeout(timeout)
664
757
  @options[:worker_boot_timeout] = Integer(timeout)
665
758
  end
666
759
 
667
- # Set the timeout for worker shutdown
760
+ # Set the timeout for worker shutdown.
668
761
  #
669
762
  # @note Cluster mode only.
763
+ # @see Puma::Cluster::Worker#term
764
+ #
670
765
  def worker_shutdown_timeout(timeout)
671
766
  @options[:worker_shutdown_timeout] = Integer(timeout)
672
767
  end
@@ -684,6 +779,7 @@ module Puma
684
779
  # slow clients will occupy a handler thread while the request
685
780
  # is being sent. A reverse proxy, such as nginx, can handle
686
781
  # slow clients and queue requests before they reach Puma.
782
+ # @see Puma::Server
687
783
  def queue_requests(answer=true)
688
784
  @options[:queue_requests] = answer
689
785
  end
@@ -691,10 +787,25 @@ module Puma
691
787
  # When a shutdown is requested, the backtraces of all the
692
788
  # threads will be written to $stdout. This can help figure
693
789
  # out why shutdown is hanging.
790
+ #
694
791
  def shutdown_debug(val=true)
695
792
  @options[:shutdown_debug] = val
696
793
  end
697
794
 
795
+
796
+ # Attempts to route traffic to less-busy workers by causing them to delay
797
+ # listening on the socket, allowing workers which are not processing any
798
+ # requests to pick up new requests first.
799
+ #
800
+ # Only works on MRI. For all other interpreters, this setting does nothing.
801
+ # @see Puma::Server#handle_servers
802
+ # @see Puma::ThreadPool#wait_for_less_busy_worker
803
+ # @version 5.0.0
804
+ #
805
+ def wait_for_less_busy_worker(val=0.005)
806
+ @options[:wait_for_less_busy_worker] = val.to_f
807
+ end
808
+
698
809
  # Control how the remote address of the connection is set. This
699
810
  # is configurable because to calculate the true socket peer address
700
811
  # a kernel syscall is required which for very fast rack handlers
@@ -702,18 +813,18 @@ module Puma
702
813
  #
703
814
  # There are 4 possible values:
704
815
  #
705
- # * :socket (the default) - read the peername from the socket using the
706
- # syscall. This is the normal behavior.
707
- # * :localhost - set the remote address to "127.0.0.1"
708
- # * header: http_header - set the remote address to the value of the
709
- # provided http header. For instance:
710
- # `set_remote_address header: "X-Real-IP"`.
711
- # Only the first word (as separated by spaces or comma)
712
- # is used, allowing headers such as X-Forwarded-For
713
- # to be used as well.
714
- # * Any string - this allows you to hardcode remote address to any value
715
- # you wish. Because Puma never uses this field anyway, it's
716
- # format is entirely in your hands.
816
+ # 1. **:socket** (the default) - read the peername from the socket using the
817
+ # syscall. This is the normal behavior.
818
+ # 2. **:localhost** - set the remote address to "127.0.0.1"
819
+ # 3. **header: <http_header>**- set the remote address to the value of the
820
+ # provided http header. For instance:
821
+ # `set_remote_address header: "X-Real-IP"`.
822
+ # Only the first word (as separated by spaces or comma) is used, allowing
823
+ # headers such as X-Forwarded-For to be used as well.
824
+ # 4. **\<Any string\>** - this allows you to hardcode remote address to any value
825
+ # you wish. Because Puma never uses this field anyway, it's format is
826
+ # entirely in your hands.
827
+ #
717
828
  def set_remote_address(val=:socket)
718
829
  case val
719
830
  when :socket
@@ -736,5 +847,68 @@ module Puma
736
847
  end
737
848
  end
738
849
 
850
+ # When enabled, workers will be forked from worker 0 instead of from the master process.
851
+ # This option is similar to `preload_app` because the app is preloaded before forking,
852
+ # but it is compatible with phased restart.
853
+ #
854
+ # This option also enables the `refork` command (SIGURG), which optimizes copy-on-write performance
855
+ # in a running app.
856
+ #
857
+ # A refork will automatically trigger once after the specified number of requests
858
+ # (default 1000), or pass 0 to disable auto refork.
859
+ #
860
+ # @note Cluster mode only.
861
+ # @version 5.0.0
862
+ #
863
+ def fork_worker(after_requests=1000)
864
+ @options[:fork_worker] = Integer(after_requests)
865
+ end
866
+
867
+ # When enabled, Puma will GC 4 times before forking workers.
868
+ # If available (Ruby 2.7+), we will also call GC.compact.
869
+ # Not recommended for non-MRI Rubies.
870
+ #
871
+ # Based on the work of Koichi Sasada and Aaron Patterson, this option may
872
+ # decrease memory utilization of preload-enabled cluster-mode Pumas. It will
873
+ # also increase time to boot and fork. See your logs for details on how much
874
+ # time this adds to your boot process. For most apps, it will be less than one
875
+ # second.
876
+ #
877
+ # @see Puma::Cluster#nakayoshi_gc
878
+ # @version 5.0.0
879
+ #
880
+ def nakayoshi_fork(enabled=true)
881
+ @options[:nakayoshi_fork] = enabled
882
+ end
883
+
884
+ # The number of requests to attempt inline before sending a client back to
885
+ # the reactor to be subject to normal ordering.
886
+ #
887
+ def max_fast_inline(num_of_requests)
888
+ @options[:max_fast_inline] = Float(num_of_requests)
889
+ end
890
+
891
+ # Specify the backend for the IO selector.
892
+ #
893
+ # Provided values will be passed directly to +NIO::Selector.new+, with the
894
+ # exception of +:auto+ which will let nio4r choose the backend.
895
+ #
896
+ # Check the documentation of +NIO::Selector.backends+ for the list of valid
897
+ # options. Note that the available options on your system will depend on the
898
+ # operating system. If you want to use the pure Ruby backend (not
899
+ # recommended due to its comparatively low performance), set environment
900
+ # variable +NIO4R_PURE+ to +true+.
901
+ #
902
+ # The default is +:auto+.
903
+ #
904
+ # @see https://github.com/socketry/nio4r/blob/master/lib/nio/selector.rb
905
+ #
906
+ def io_selector_backend(backend)
907
+ @options[:io_selector_backend] = backend.to_sym
908
+ end
909
+
910
+ def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
911
+ @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
912
+ end
739
913
  end
740
914
  end