puma 4.3.12 → 6.0.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1618 -521
  3. data/LICENSE +23 -20
  4. data/README.md +130 -42
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +63 -26
  7. data/docs/compile_options.md +55 -0
  8. data/docs/deployment.md +60 -69
  9. data/docs/fork_worker.md +31 -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 +2 -2
  16. data/docs/plugins.md +15 -15
  17. data/docs/rails_dev_mode.md +28 -0
  18. data/docs/restart.md +46 -23
  19. data/docs/signals.md +13 -11
  20. data/docs/stats.md +142 -0
  21. data/docs/systemd.md +85 -128
  22. data/docs/testing_benchmarks_local_files.md +150 -0
  23. data/docs/testing_test_rackup_ci_files.md +36 -0
  24. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  25. data/ext/puma_http11/ext_help.h +1 -1
  26. data/ext/puma_http11/extconf.rb +49 -12
  27. data/ext/puma_http11/http11_parser.c +46 -48
  28. data/ext/puma_http11/http11_parser.h +2 -2
  29. data/ext/puma_http11/http11_parser.java.rl +3 -3
  30. data/ext/puma_http11/http11_parser.rl +3 -3
  31. data/ext/puma_http11/http11_parser_common.rl +2 -2
  32. data/ext/puma_http11/mini_ssl.c +250 -93
  33. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  34. data/ext/puma_http11/org/jruby/puma/Http11.java +6 -6
  35. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +4 -6
  36. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
  37. data/ext/puma_http11/puma_http11.c +46 -57
  38. data/lib/puma/app/status.rb +52 -38
  39. data/lib/puma/binder.rb +232 -119
  40. data/lib/puma/cli.rb +33 -33
  41. data/lib/puma/client.rb +129 -88
  42. data/lib/puma/cluster/worker.rb +175 -0
  43. data/lib/puma/cluster/worker_handle.rb +97 -0
  44. data/lib/puma/cluster.rb +224 -231
  45. data/lib/puma/commonlogger.rb +2 -2
  46. data/lib/puma/configuration.rb +112 -87
  47. data/lib/puma/const.rb +86 -91
  48. data/lib/puma/control_cli.rb +99 -79
  49. data/lib/puma/detect.rb +31 -2
  50. data/lib/puma/dsl.rb +426 -110
  51. data/lib/puma/error_logger.rb +112 -0
  52. data/lib/puma/events.rb +16 -115
  53. data/lib/puma/io_buffer.rb +44 -2
  54. data/lib/puma/jruby_restart.rb +2 -59
  55. data/lib/puma/json_serialization.rb +96 -0
  56. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  57. data/lib/puma/launcher.rb +170 -148
  58. data/lib/puma/log_writer.rb +137 -0
  59. data/lib/puma/minissl/context_builder.rb +35 -19
  60. data/lib/puma/minissl.rb +213 -55
  61. data/lib/puma/null_io.rb +18 -1
  62. data/lib/puma/plugin/tmp_restart.rb +1 -1
  63. data/lib/puma/plugin.rb +3 -12
  64. data/lib/puma/rack/builder.rb +5 -9
  65. data/lib/puma/rack_default.rb +1 -1
  66. data/lib/puma/reactor.rb +85 -369
  67. data/lib/puma/request.rb +644 -0
  68. data/lib/puma/runner.rb +86 -76
  69. data/lib/puma/server.rb +306 -793
  70. data/lib/puma/single.rb +18 -74
  71. data/lib/puma/state_file.rb +45 -8
  72. data/lib/puma/systemd.rb +47 -0
  73. data/lib/puma/thread_pool.rb +136 -68
  74. data/lib/puma/util.rb +21 -4
  75. data/lib/puma.rb +54 -7
  76. data/lib/rack/handler/puma.rb +11 -12
  77. data/tools/{docker/Dockerfile → Dockerfile} +1 -1
  78. metadata +31 -23
  79. data/docs/tcp_mode.md +0 -96
  80. data/ext/puma_http11/io_buffer.c +0 -155
  81. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  82. data/lib/puma/accept_nonblock.rb +0 -29
  83. data/lib/puma/tcp_logger.rb +0 -41
  84. data/tools/jungle/README.md +0 -19
  85. data/tools/jungle/init.d/README.md +0 -61
  86. data/tools/jungle/init.d/puma +0 -421
  87. data/tools/jungle/init.d/run-puma +0 -18
  88. data/tools/jungle/upstart/README.md +0 -61
  89. data/tools/jungle/upstart/puma-manager.conf +0 -31
  90. data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/dsl.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/const'
3
+ require_relative 'const'
4
+ require_relative 'util'
4
5
 
5
6
  module Puma
6
7
  # The methods that are available for use inside the configuration file.
@@ -14,24 +15,108 @@ module Puma
14
15
  # end
15
16
  # config.load
16
17
  #
17
- # puts config.options[:binds]
18
- # "tcp://127.0.0.1:3001"
18
+ # puts config.options[:binds] # => "tcp://127.0.0.1:3001"
19
19
  #
20
20
  # Used to load file:
21
21
  #
22
22
  # $ cat puma_config.rb
23
- # port 3002
23
+ # port 3002
24
+ #
25
+ # Resulting configuration:
24
26
  #
25
27
  # config = Configuration.new(config_file: "puma_config.rb")
26
28
  # config.load
27
29
  #
28
- # puts config.options[:binds]
29
- # # => "tcp://127.0.0.1:3002"
30
+ # puts config.options[:binds] # => "tcp://127.0.0.1:3002"
30
31
  #
31
32
  # You can also find many examples being used by the test suite in
32
33
  # +test/config+.
34
+ #
35
+ # Puma v6 adds the option to specify a key name (String or Symbol) to the
36
+ # hooks that run inside the forked workers. All the hooks run inside the
37
+ # {Puma::Cluster::Worker#run} method.
38
+ #
39
+ # Previously, the worker index and the LogWriter instance were passed to the
40
+ # hook blocks/procs. If a key name is specified, a hash is passed as the last
41
+ # parameter. This allows storage of data, typically objects that are created
42
+ # before the worker that need to be passed to the hook when the worker is shutdown.
43
+ #
44
+ # The following hooks have been updated:
45
+ #
46
+ # | DSL Method | Options Key | Fork Block Location |
47
+ # | on_worker_boot | :before_worker_boot | inside, before |
48
+ # | on_worker_shutdown | :before_worker_shutdown | inside, after |
49
+ # | on_refork | :before_refork | inside |
50
+ #
33
51
  class DSL
34
- include ConfigDefault
52
+ ON_WORKER_KEY = [String, Symbol].freeze
53
+
54
+ # convenience method so logic can be used in CI
55
+ # @see ssl_bind
56
+ #
57
+ def self.ssl_bind_str(host, port, opts)
58
+ verify = opts.fetch(:verify_mode, 'none').to_s
59
+
60
+ tls_str =
61
+ if opts[:no_tlsv1_1] then '&no_tlsv1_1=true'
62
+ elsif opts[:no_tlsv1] then '&no_tlsv1=true'
63
+ else ''
64
+ end
65
+
66
+ ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)
67
+
68
+ backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
69
+
70
+ if defined?(JRUBY_VERSION)
71
+ cipher_suites = opts[:ssl_cipher_list] ? "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil # old name
72
+ cipher_suites = "#{cipher_suites}&cipher_suites=#{opts[:cipher_suites]}" if opts[:cipher_suites]
73
+ protocols = opts[:protocols] ? "&protocols=#{opts[:protocols]}" : nil
74
+
75
+ keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
76
+ keystore_additions = "#{keystore_additions}&keystore-type=#{opts[:keystore_type]}" if opts[:keystore_type]
77
+ if opts[:truststore]
78
+ truststore_additions = "&truststore=#{opts[:truststore]}"
79
+ truststore_additions = "#{truststore_additions}&truststore-pass=#{opts[:truststore_pass]}" if opts[:truststore_pass]
80
+ truststore_additions = "#{truststore_additions}&truststore-type=#{opts[:truststore_type]}" if opts[:truststore_type]
81
+ end
82
+
83
+ "ssl://#{host}:#{port}?#{keystore_additions}#{truststore_additions}#{cipher_suites}#{protocols}" \
84
+ "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
85
+ else
86
+ ssl_cipher_filter = opts[:ssl_cipher_filter] ? "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
87
+ v_flags = (ary = opts[:verification_flags]) ? "&verification_flags=#{Array(ary).join ','}" : nil
88
+
89
+ cert_flags = (cert = opts[:cert]) ? "cert=#{Puma::Util.escape(cert)}" : nil
90
+ key_flags = (key = opts[:key]) ? "&key=#{Puma::Util.escape(key)}" : nil
91
+
92
+ reuse_flag =
93
+ if (reuse = opts[:reuse])
94
+ if reuse == true
95
+ '&reuse=dflt'
96
+ elsif reuse.is_a?(Hash) && (reuse.key?(:size) || reuse.key?(:timeout))
97
+ val = +''
98
+ if (size = reuse[:size]) && Integer === size
99
+ val << size.to_s
100
+ end
101
+ if (timeout = reuse[:timeout]) && Integer === timeout
102
+ val << ",#{timeout}"
103
+ end
104
+ if val.empty?
105
+ nil
106
+ else
107
+ "&reuse=#{val}"
108
+ end
109
+ else
110
+ nil
111
+ end
112
+ else
113
+ nil
114
+ end
115
+
116
+ "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{ssl_cipher_filter}" \
117
+ "#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
118
+ end
119
+ end
35
120
 
36
121
  def initialize(options, config)
37
122
  @config = config
@@ -65,7 +150,7 @@ module Puma
65
150
  end
66
151
 
67
152
  def default_host
68
- @options[:default_host] || Configuration::DefaultTCPHost
153
+ @options[:default_host] || Configuration::DEFAULTS[:tcp_host]
69
154
  end
70
155
 
71
156
  def inject(&blk)
@@ -98,6 +183,9 @@ module Puma
98
183
  # [body]
99
184
  # ]
100
185
  # end
186
+ #
187
+ # @see Puma::Configuration#app
188
+ #
101
189
  def app(obj=nil, &block)
102
190
  obj ||= block
103
191
 
@@ -153,28 +241,34 @@ module Puma
153
241
  end
154
242
 
155
243
  # Bind the server to +url+. "tcp://", "unix://" and "ssl://" are the only
156
- # accepted protocols. Multiple urls can be bound to, calling `bind` does
244
+ # accepted protocols. Multiple urls can be bound to, calling +bind+ does
157
245
  # not overwrite previous bindings.
158
246
  #
159
247
  # The default is "tcp://0.0.0.0:9292".
160
248
  #
161
249
  # You can use query parameters within the url to specify options:
162
250
  #
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+.
251
+ # * Set the socket backlog depth with +backlog+, default is 1024.
252
+ # * Set up an SSL certificate with +key+ & +cert+.
253
+ # * Set up an SSL certificate for mTLS with +key+, +cert+, +ca+ and +verify_mode+.
254
+ # * Set whether to optimize for low latency instead of throughput with
255
+ # +low_latency+, default is to not optimize for low latency. This is done
256
+ # via +Socket::TCP_NODELAY+.
257
+ # * Set socket permissions with +umask+.
169
258
  #
170
259
  # @example Backlog depth
171
260
  # bind 'unix:///var/run/puma.sock?backlog=512'
172
261
  # @example SSL cert
173
262
  # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem'
263
+ # @example SSL cert for mutual TLS (mTLS)
264
+ # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem&ca=ca.pem&verify_mode=force_peer'
174
265
  # @example Disable optimization for low latency
175
266
  # bind 'tcp://0.0.0.0:9292?low_latency=false'
176
267
  # @example Socket permissions
177
268
  # bind 'unix:///var/run/puma.sock?umask=0111'
269
+ # @see Puma::Runner#load_and_bind
270
+ # @see Puma::Cluster#run
271
+ #
178
272
  def bind(url)
179
273
  @options[:binds] ||= []
180
274
  @options[:binds] << url
@@ -184,22 +278,49 @@ module Puma
184
278
  @options[:binds] = []
185
279
  end
186
280
 
281
+ # Bind to (systemd) activated sockets, regardless of configured binds.
282
+ #
283
+ # Systemd can present sockets as file descriptors that are already opened.
284
+ # By default Puma will use these but only if it was explicitly told to bind
285
+ # to the socket. If not, it will close the activated sockets. This means
286
+ # all configuration is duplicated.
287
+ #
288
+ # Binds can contain additional configuration, but only SSL config is really
289
+ # relevant since the unix and TCP socket options are ignored.
290
+ #
291
+ # This means there is a lot of duplicated configuration for no additional
292
+ # value in most setups. This method tells the launcher to bind to all
293
+ # activated sockets, regardless of existing bind.
294
+ #
295
+ # To clear configured binds, the value only can be passed. This will clear
296
+ # out any binds that may have been configured.
297
+ #
298
+ # @example Use any systemd activated sockets as well as configured binds
299
+ # bind_to_activated_sockets
300
+ #
301
+ # @example Only bind to systemd activated sockets, ignoring other binds
302
+ # bind_to_activated_sockets 'only'
303
+ def bind_to_activated_sockets(bind=true)
304
+ @options[:bind_to_activated_sockets] = bind
305
+ end
306
+
187
307
  # Define the TCP port to bind to. Use +bind+ for more advanced options.
188
308
  #
189
309
  # @example
190
310
  # port 9292
191
311
  def port(port, host=nil)
192
312
  host ||= default_host
193
- bind "tcp://#{host}:#{port}"
313
+ bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s
194
314
  end
195
315
 
196
- # Define how long persistent connections can be idle before Puma closes
197
- # them.
316
+ # Define how long persistent connections can be idle before Puma closes them.
317
+ # @see Puma::Server.new
198
318
  def persistent_timeout(seconds)
199
319
  @options[:persistent_timeout] = Integer(seconds)
200
320
  end
201
321
 
202
322
  # Define how long the tcp socket stays open, if no data has been received.
323
+ # @see Puma::Server.new
203
324
  def first_data_timeout(seconds)
204
325
  @options[:first_data_timeout] = Integer(seconds)
205
326
  end
@@ -210,24 +331,11 @@ module Puma
210
331
  @options[:clean_thread_locals] = which
211
332
  end
212
333
 
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
334
+ # When shutting down, drain the accept socket of pending connections and
335
+ # process them. This loops over the accept socket until there are no more
336
+ # read events and then stops looking and waits for the requests to finish.
337
+ # @see Puma::Server#graceful_shutdown
220
338
  #
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
339
  def drain_on_shutdown(which=true)
232
340
  @options[:drain_on_shutdown] = which
233
341
  end
@@ -250,6 +358,7 @@ module Puma
250
358
  #
251
359
  # Puma always waits a few seconds after killing a thread for it to try
252
360
  # to finish up it's work, even in :immediately mode.
361
+ # @see Puma::Server#graceful_shutdown
253
362
  def force_shutdown_after(val=:forever)
254
363
  i = case val
255
364
  when :forever
@@ -257,7 +366,7 @@ module Puma
257
366
  when :immediately
258
367
  0
259
368
  else
260
- Integer(val)
369
+ Float(val)
261
370
  end
262
371
 
263
372
  @options[:force_shutdown_after] = i
@@ -322,20 +431,21 @@ module Puma
322
431
  # @example
323
432
  # rackup '/u/apps/lolcat/config.ru'
324
433
  def rackup(path)
325
- @options[:rackup] = path.to_s
434
+ @options[:rackup] ||= path.to_s
326
435
  end
327
436
 
328
- # Run Puma in TCP mode
329
- #
330
- def tcp_mode!
331
- @options[:mode] = :tcp
437
+ # Allows setting `env['rack.url_scheme']`.
438
+ # Only necessary if X-Forwarded-Proto is not being set by your proxy
439
+ # Normal values are 'http' or 'https'.
440
+ def rack_url_scheme(scheme=nil)
441
+ @options[:rack_url_scheme] = scheme
332
442
  end
333
443
 
334
444
  def early_hints(answer=true)
335
445
  @options[:early_hints] = answer
336
446
  end
337
447
 
338
- # Redirect STDOUT and STDERR to files specified. The +append+ parameter
448
+ # Redirect +STDOUT+ and +STDERR+ to files specified. The +append+ parameter
339
449
  # specifies whether the output is appended, the default is +false+.
340
450
  #
341
451
  # @example
@@ -355,7 +465,10 @@ module Puma
355
465
  # Configure +min+ to be the minimum number of threads to use to answer
356
466
  # requests and +max+ the maximum.
357
467
  #
358
- # The default is "0, 16".
468
+ # The default is the environment variables +PUMA_MIN_THREADS+ / +PUMA_MAX_THREADS+
469
+ # (or +MIN_THREADS+ / +MAX_THREADS+ if the +PUMA_+ variables aren't set).
470
+ #
471
+ # If these environment variables aren't set, the default is "0, 5" in MRI or "0, 16" for other interpreters.
359
472
  #
360
473
  # @example
361
474
  # threads 0, 16
@@ -376,8 +489,19 @@ module Puma
376
489
  @options[:max_threads] = max
377
490
  end
378
491
 
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.
492
+ # Instead of using +bind+ and manually constructing a URI like:
493
+ #
494
+ # bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'
495
+ #
496
+ # you can use the this method.
497
+ #
498
+ # When binding on localhost you don't need to specify +cert+ and +key+,
499
+ # Puma will assume you are using the +localhost+ gem and try to load the
500
+ # appropriate files.
501
+ #
502
+ # When using the options hash parameter, the `reuse:` value is either
503
+ # `true`, which sets reuse 'on' with default values, or a hash, with `:size`
504
+ # and/or `:timeout` keys, each with integer values.
381
505
  #
382
506
  # @example
383
507
  # ssl_bind '127.0.0.1', '9292', {
@@ -385,29 +509,30 @@ module Puma
385
509
  # key: path_to_key,
386
510
  # ssl_cipher_filter: cipher_filter, # optional
387
511
  # verify_mode: verify_mode, # default 'none'
512
+ # verification_flags: flags, # optional, not supported by JRuby
513
+ # reuse: true # optional
388
514
  # }
389
- # @example For JRuby additional keys are required: keystore & keystore_pass.
515
+ #
516
+ # @example Using self-signed certificate with the +localhost+ gem:
517
+ # ssl_bind '127.0.0.1', '9292'
518
+ #
519
+ # @example Alternatively, you can provide +cert_pem+ and +key_pem+:
520
+ # ssl_bind '127.0.0.1', '9292', {
521
+ # cert_pem: File.read(path_to_cert),
522
+ # key_pem: File.read(path_to_key),
523
+ # reuse: {size: 2_000, timeout: 20} # optional
524
+ # }
525
+ #
526
+ # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
390
527
  # 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
528
  # keystore: path_to_keystore,
396
- # keystore_pass: password
529
+ # keystore_pass: password,
530
+ # ssl_cipher_list: cipher_list, # optional
531
+ # verify_mode: verify_mode # default 'none'
397
532
  # }
398
- 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
533
+ def ssl_bind(host, port, opts = {})
534
+ add_pem_values_to_options_store(opts)
535
+ bind self.class.ssl_bind_str(host, port, opts)
411
536
  end
412
537
 
413
538
  # Use +path+ as the file to store the server info state. This is
@@ -419,16 +544,46 @@ module Puma
419
544
  @options[:state] = path.to_s
420
545
  end
421
546
 
547
+ # Use +permission+ to restrict permissions for the state file.
548
+ #
549
+ # @example
550
+ # state_permission 0600
551
+ # @version 5.0.0
552
+ #
553
+ def state_permission(permission)
554
+ @options[:state_permission] = permission
555
+ end
556
+
422
557
  # How many worker processes to run. Typically this is set to
423
- # to the number of available cores.
558
+ # the number of available cores.
424
559
  #
425
- # The default is 0.
560
+ # The default is the value of the environment variable +WEB_CONCURRENCY+ if
561
+ # set, otherwise 0.
426
562
  #
427
563
  # @note Cluster mode only.
564
+ # @see Puma::Cluster
428
565
  def workers(count)
429
566
  @options[:workers] = count.to_i
430
567
  end
431
568
 
569
+ # Disable warning message when running in cluster mode with a single worker.
570
+ #
571
+ # Cluster mode has some overhead of running an additional 'control' process
572
+ # in order to manage the cluster. If only running a single worker it is
573
+ # likely not worth paying that overhead vs running in single mode with
574
+ # additional threads instead.
575
+ #
576
+ # There are some scenarios where running cluster mode with a single worker
577
+ # may still be warranted and valid under certain deployment scenarios, see
578
+ # https://github.com/puma/puma/issues/2534
579
+ #
580
+ # Moving from workers = 1 to workers = 0 will save 10-30% of memory use.
581
+ #
582
+ # @note Cluster mode only.
583
+ def silence_single_worker_warning
584
+ @options[:silence_single_worker_warning] = true
585
+ end
586
+
432
587
  # Code to run immediately before master process
433
588
  # forks workers (once on boot). These hooks can block if necessary
434
589
  # to wait for background operations unknown to Puma to finish before
@@ -455,12 +610,11 @@ module Puma
455
610
  #
456
611
  # @note Cluster mode only.
457
612
  # @example
458
- # on_worker_fork do
459
- # puts 'Before worker fork...'
613
+ # on_worker_boot do
614
+ # puts 'Before worker boot...'
460
615
  # end
461
- def on_worker_boot(&block)
462
- @options[:before_worker_boot] ||= []
463
- @options[:before_worker_boot] << block
616
+ def on_worker_boot(key = nil, &block)
617
+ process_hook :before_worker_boot, key, block, 'on_worker_boot'
464
618
  end
465
619
 
466
620
  # Code to run immediately before a worker shuts
@@ -475,9 +629,8 @@ module Puma
475
629
  # on_worker_shutdown do
476
630
  # puts 'On worker shutdown...'
477
631
  # end
478
- def on_worker_shutdown(&block)
479
- @options[:before_worker_shutdown] ||= []
480
- @options[:before_worker_shutdown] << block
632
+ def on_worker_shutdown(key = nil, &block)
633
+ process_hook :before_worker_shutdown, key, block, 'on_worker_shutdown'
481
634
  end
482
635
 
483
636
  # Code to run in the master right before a worker is started. The worker's
@@ -491,8 +644,7 @@ module Puma
491
644
  # puts 'Before worker fork...'
492
645
  # end
493
646
  def on_worker_fork(&block)
494
- @options[:before_worker_fork] ||= []
495
- @options[:before_worker_fork] << block
647
+ process_hook :before_worker_fork, nil, block, 'on_worker_fork'
496
648
  end
497
649
 
498
650
  # Code to run in the master after a worker has been started. The worker's
@@ -506,12 +658,33 @@ module Puma
506
658
  # puts 'After worker fork...'
507
659
  # end
508
660
  def after_worker_fork(&block)
509
- @options[:after_worker_fork] ||= []
510
- @options[:after_worker_fork] = block
661
+ process_hook :after_worker_fork, nil, block, 'after_worker_fork'
511
662
  end
512
663
 
513
664
  alias_method :after_worker_boot, :after_worker_fork
514
665
 
666
+ # When `fork_worker` is enabled, code to run in Worker 0
667
+ # before all other workers are re-forked from this process,
668
+ # after the server has temporarily stopped serving requests
669
+ # (once per complete refork cycle).
670
+ #
671
+ # This can be used to trigger extra garbage-collection to maximize
672
+ # copy-on-write efficiency, or close any connections to remote servers
673
+ # (database, Redis, ...) that were opened while the server was running.
674
+ #
675
+ # This can be called multiple times to add several hooks.
676
+ #
677
+ # @note Cluster mode with `fork_worker` enabled only.
678
+ # @example
679
+ # on_refork do
680
+ # 3.times {GC.start}
681
+ # end
682
+ # @version 5.0.0
683
+ #
684
+ def on_refork(key = nil, &block)
685
+ process_hook :before_refork, key, block, 'on_refork'
686
+ end
687
+
515
688
  # Code to run out-of-band when the worker is idle.
516
689
  # These hooks run immediately after a request has finished
517
690
  # processing and there are no busy threads on the worker.
@@ -522,8 +695,7 @@ module Puma
522
695
  #
523
696
  # This can be called multiple times to add several hooks.
524
697
  def out_of_band(&block)
525
- @options[:out_of_band] ||= []
526
- @options[:out_of_band] << block
698
+ process_hook :out_of_band, nil, block, 'out_of_band'
527
699
  end
528
700
 
529
701
  # The directory to operate out of.
@@ -536,19 +708,8 @@ module Puma
536
708
  @options[:directory] = dir.to_s
537
709
  end
538
710
 
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
711
  # Preload the application before starting the workers; this conflicts with
551
- # phased restart feature. This is off by default.
712
+ # phased restart feature. On by default if your app uses more than 1 worker.
552
713
  #
553
714
  # @note Cluster mode only.
554
715
  # @example
@@ -583,7 +744,7 @@ module Puma
583
744
  # new Bundler context and thus can float around as the release
584
745
  # dictates.
585
746
  #
586
- # See also: extra_runtime_dependencies
747
+ # @see extra_runtime_dependencies
587
748
  #
588
749
  # @note This is incompatible with +preload_app!+.
589
750
  # @note This is only supported for RubyGems 2.2+
@@ -600,6 +761,9 @@ module Puma
600
761
  #
601
762
  # @example
602
763
  # raise_exception_on_sigterm false
764
+ # @see Puma::Launcher#setup_signals
765
+ # @see Puma::Cluster#setup_signals
766
+ #
603
767
  def raise_exception_on_sigterm(answer=true)
604
768
  @options[:raise_exception_on_sigterm] = answer
605
769
  end
@@ -615,6 +779,8 @@ module Puma
615
779
  # extra_runtime_dependencies ['gem_name_1', 'gem_name_2']
616
780
  # @example
617
781
  # extra_runtime_dependencies ['puma_worker_killer', 'puma-heroku']
782
+ # @see Puma::Launcher#extra_runtime_deps_directories
783
+ #
618
784
  def extra_runtime_dependencies(answer = [])
619
785
  @options[:extra_runtime_dependencies] = Array(answer)
620
786
  end
@@ -632,6 +798,19 @@ module Puma
632
798
  @options[:tag] = string.to_s
633
799
  end
634
800
 
801
+ # Change the default interval for checking workers.
802
+ #
803
+ # The default value is 5 seconds.
804
+ #
805
+ # @note Cluster mode only.
806
+ # @example
807
+ # worker_check_interval 5
808
+ # @see Puma::Cluster#check_workers
809
+ #
810
+ def worker_check_interval(interval)
811
+ @options[:worker_check_interval] = Integer(interval)
812
+ end
813
+
635
814
  # Verifies that all workers have checked in to the master process within
636
815
  # the given timeout. If not the worker process will be restarted. This is
637
816
  # not a request timeout, it is to protect against a hung or dead process.
@@ -642,9 +821,11 @@ module Puma
642
821
  # @note Cluster mode only.
643
822
  # @example
644
823
  # worker_timeout 60
824
+ # @see Puma::Cluster::Worker#ping_timeout
825
+ #
645
826
  def worker_timeout(timeout)
646
827
  timeout = Integer(timeout)
647
- min = Const::WORKER_CHECK_INTERVAL
828
+ min = @options.fetch(:worker_check_interval, Configuration::DEFAULTS[:worker_check_interval])
648
829
 
649
830
  if timeout <= min
650
831
  raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
@@ -658,19 +839,48 @@ module Puma
658
839
  # If unspecified, this defaults to the value of worker_timeout.
659
840
  #
660
841
  # @note Cluster mode only.
661
- # @example:
842
+ #
843
+ # @example
662
844
  # worker_boot_timeout 60
845
+ # @see Puma::Cluster::Worker#ping_timeout
846
+ #
663
847
  def worker_boot_timeout(timeout)
664
848
  @options[:worker_boot_timeout] = Integer(timeout)
665
849
  end
666
850
 
667
- # Set the timeout for worker shutdown
851
+ # Set the timeout for worker shutdown.
668
852
  #
669
853
  # @note Cluster mode only.
854
+ # @see Puma::Cluster::Worker#term
855
+ #
670
856
  def worker_shutdown_timeout(timeout)
671
857
  @options[:worker_shutdown_timeout] = Integer(timeout)
672
858
  end
673
859
 
860
+ # Set the strategy for worker culling.
861
+ #
862
+ # There are two possible values:
863
+ #
864
+ # 1. **:youngest** - the youngest workers (i.e. the workers that were
865
+ # the most recently started) will be culled.
866
+ # 2. **:oldest** - the oldest workers (i.e. the workers that were started
867
+ # the longest time ago) will be culled.
868
+ #
869
+ # @note Cluster mode only.
870
+ # @example
871
+ # worker_culling_strategy :oldest
872
+ # @see Puma::Cluster#cull_workers
873
+ #
874
+ def worker_culling_strategy(strategy)
875
+ stategy = strategy.to_sym
876
+
877
+ if ![:youngest, :oldest].include?(strategy)
878
+ raise "Invalid value for worker_culling_strategy - #{stategy}"
879
+ end
880
+
881
+ @options[:worker_culling_strategy] = strategy
882
+ end
883
+
674
884
  # When set to true (the default), workers accept all requests
675
885
  # and queue them before passing them to the handlers.
676
886
  # When set to false, each worker process accepts exactly as
@@ -684,6 +894,7 @@ module Puma
684
894
  # slow clients will occupy a handler thread while the request
685
895
  # is being sent. A reverse proxy, such as nginx, can handle
686
896
  # slow clients and queue requests before they reach Puma.
897
+ # @see Puma::Server
687
898
  def queue_requests(answer=true)
688
899
  @options[:queue_requests] = answer
689
900
  end
@@ -691,29 +902,50 @@ module Puma
691
902
  # When a shutdown is requested, the backtraces of all the
692
903
  # threads will be written to $stdout. This can help figure
693
904
  # out why shutdown is hanging.
905
+ #
694
906
  def shutdown_debug(val=true)
695
907
  @options[:shutdown_debug] = val
696
908
  end
697
909
 
910
+
911
+ # Attempts to route traffic to less-busy workers by causing them to delay
912
+ # listening on the socket, allowing workers which are not processing any
913
+ # requests to pick up new requests first.
914
+ #
915
+ # Only works on MRI. For all other interpreters, this setting does nothing.
916
+ # @see Puma::Server#handle_servers
917
+ # @see Puma::ThreadPool#wait_for_less_busy_worker
918
+ # @version 5.0.0
919
+ #
920
+ def wait_for_less_busy_worker(val=0.005)
921
+ @options[:wait_for_less_busy_worker] = val.to_f
922
+ end
923
+
698
924
  # Control how the remote address of the connection is set. This
699
925
  # is configurable because to calculate the true socket peer address
700
926
  # a kernel syscall is required which for very fast rack handlers
701
927
  # slows down the handling significantly.
702
928
  #
703
- # There are 4 possible values:
704
- #
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.
929
+ # There are 5 possible values:
930
+ #
931
+ # 1. **:socket** (the default) - read the peername from the socket using the
932
+ # syscall. This is the normal behavior. If this fails for any reason (e.g.,
933
+ # if the peer disconnects between the connection being accepted and the getpeername
934
+ # system call), Puma will return "0.0.0.0"
935
+ # 2. **:localhost** - set the remote address to "127.0.0.1"
936
+ # 3. **header: <http_header>**- set the remote address to the value of the
937
+ # provided http header. For instance:
938
+ # `set_remote_address header: "X-Real-IP"`.
939
+ # Only the first word (as separated by spaces or comma) is used, allowing
940
+ # headers such as X-Forwarded-For to be used as well. If this header is absent,
941
+ # Puma will fall back to the behavior of :socket
942
+ # 4. **proxy_protocol: :v1**- set the remote address to the value read from the
943
+ # HAproxy PROXY protocol, version 1. If the request does not have the PROXY
944
+ # protocol attached to it, will fall back to :socket
945
+ # 5. **\<Any string\>** - this allows you to hardcode remote address to any value
946
+ # you wish. Because Puma never uses this field anyway, it's format is
947
+ # entirely in your hands.
948
+ #
717
949
  def set_remote_address(val=:socket)
718
950
  case val
719
951
  when :socket
@@ -728,6 +960,13 @@ module Puma
728
960
  if hdr = val[:header]
729
961
  @options[:remote_address] = :header
730
962
  @options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
963
+ elsif protocol_version = val[:proxy_protocol]
964
+ @options[:remote_address] = :proxy_protocol
965
+ protocol_version = protocol_version.downcase.to_sym
966
+ unless [:v1].include?(protocol_version)
967
+ raise "Invalid value for proxy_protocol - #{protocol_version.inspect}"
968
+ end
969
+ @options[:remote_address_proxy_protocol] = protocol_version
731
970
  else
732
971
  raise "Invalid value for set_remote_address - #{val.inspect}"
733
972
  end
@@ -736,5 +975,82 @@ module Puma
736
975
  end
737
976
  end
738
977
 
978
+ # When enabled, workers will be forked from worker 0 instead of from the master process.
979
+ # This option is similar to `preload_app` because the app is preloaded before forking,
980
+ # but it is compatible with phased restart.
981
+ #
982
+ # This option also enables the `refork` command (SIGURG), which optimizes copy-on-write performance
983
+ # in a running app.
984
+ #
985
+ # A refork will automatically trigger once after the specified number of requests
986
+ # (default 1000), or pass 0 to disable auto refork.
987
+ #
988
+ # @note Cluster mode only.
989
+ # @version 5.0.0
990
+ #
991
+ def fork_worker(after_requests=1000)
992
+ @options[:fork_worker] = Integer(after_requests)
993
+ end
994
+
995
+ # The number of requests to attempt inline before sending a client back to
996
+ # the reactor to be subject to normal ordering.
997
+ #
998
+ def max_fast_inline(num_of_requests)
999
+ @options[:max_fast_inline] = Float(num_of_requests)
1000
+ end
1001
+
1002
+ # Specify the backend for the IO selector.
1003
+ #
1004
+ # Provided values will be passed directly to +NIO::Selector.new+, with the
1005
+ # exception of +:auto+ which will let nio4r choose the backend.
1006
+ #
1007
+ # Check the documentation of +NIO::Selector.backends+ for the list of valid
1008
+ # options. Note that the available options on your system will depend on the
1009
+ # operating system. If you want to use the pure Ruby backend (not
1010
+ # recommended due to its comparatively low performance), set environment
1011
+ # variable +NIO4R_PURE+ to +true+.
1012
+ #
1013
+ # The default is +:auto+.
1014
+ #
1015
+ # @see https://github.com/socketry/nio4r/blob/master/lib/nio/selector.rb
1016
+ #
1017
+ def io_selector_backend(backend)
1018
+ @options[:io_selector_backend] = backend.to_sym
1019
+ end
1020
+
1021
+ def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
1022
+ @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
1023
+ end
1024
+
1025
+ private
1026
+
1027
+ # To avoid adding cert_pem and key_pem as URI params, we store them on the
1028
+ # options[:store] from where Puma binder knows how to find and extract them.
1029
+ def add_pem_values_to_options_store(opts)
1030
+ return if defined?(JRUBY_VERSION)
1031
+
1032
+ @options[:store] ||= []
1033
+
1034
+ # Store cert_pem and key_pem to options[:store] if present
1035
+ [:cert, :key].each do |v|
1036
+ opt_key = :"#{v}_pem"
1037
+ if opts[opt_key]
1038
+ index = @options[:store].length
1039
+ @options[:store] << opts[opt_key]
1040
+ opts[v] = "store:#{index}"
1041
+ end
1042
+ end
1043
+ end
1044
+
1045
+ def process_hook(options_key, key, block, meth)
1046
+ @options[options_key] ||= []
1047
+ if ON_WORKER_KEY.include? key.class
1048
+ @options[options_key] << [block, key.to_sym]
1049
+ elsif key.nil?
1050
+ @options[options_key] << block
1051
+ else
1052
+ raise "'#{method}' key must be String or Symbol"
1053
+ end
1054
+ end
739
1055
  end
740
1056
  end