puma 5.6.8 → 6.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +332 -16
- data/README.md +79 -29
- data/bin/puma-wild +1 -1
- data/docs/compile_options.md +34 -0
- data/docs/fork_worker.md +1 -3
- data/docs/kubernetes.md +12 -0
- data/docs/nginx.md +1 -1
- data/docs/restart.md +1 -0
- data/docs/systemd.md +3 -6
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/extconf.rb +16 -9
- data/ext/puma_http11/http11_parser.c +1 -1
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +2 -2
- data/ext/puma_http11/http11_parser.rl +2 -2
- data/ext/puma_http11/http11_parser_common.rl +2 -2
- data/ext/puma_http11/mini_ssl.c +127 -19
- data/ext/puma_http11/org/jruby/puma/Http11.java +5 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +157 -53
- data/ext/puma_http11/puma_http11.c +17 -9
- data/lib/puma/app/status.rb +4 -4
- data/lib/puma/binder.rb +50 -53
- data/lib/puma/cli.rb +16 -18
- data/lib/puma/client.rb +59 -19
- data/lib/puma/cluster/worker.rb +18 -11
- data/lib/puma/cluster/worker_handle.rb +4 -1
- data/lib/puma/cluster.rb +102 -40
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +77 -59
- data/lib/puma/const.rb +137 -92
- data/lib/puma/control_cli.rb +15 -11
- data/lib/puma/detect.rb +7 -4
- data/lib/puma/dsl.rb +250 -56
- data/lib/puma/error_logger.rb +18 -9
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +39 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +102 -175
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +26 -12
- data/lib/puma/minissl.rb +104 -11
- data/lib/puma/null_io.rb +16 -2
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/rack/builder.rb +6 -6
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +19 -10
- data/lib/puma/request.rb +380 -172
- data/lib/puma/runner.rb +56 -20
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +137 -89
- data/lib/puma/single.rb +13 -11
- data/lib/puma/state_file.rb +3 -6
- data/lib/puma/thread_pool.rb +57 -19
- data/lib/puma/util.rb +0 -11
- data/lib/puma.rb +9 -10
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +2 -2
- metadata +11 -7
- data/lib/puma/queue_close.rb +0 -26
- data/lib/puma/systemd.rb +0 -46
- data/lib/rack/version_restriction.rb +0 -15
data/lib/puma/control_cli.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'optparse'
|
4
|
-
require_relative 'state_file'
|
5
4
|
require_relative 'const'
|
6
5
|
require_relative 'detect'
|
7
|
-
require_relative 'configuration'
|
8
6
|
require 'uri'
|
9
7
|
require 'socket'
|
10
8
|
|
@@ -33,9 +31,6 @@ module Puma
|
|
33
31
|
'worker-count-up' => 'SIGTTIN'
|
34
32
|
}.freeze
|
35
33
|
|
36
|
-
# @deprecated 6.0.0
|
37
|
-
COMMANDS = CMD_PATH_SIG_MAP.keys.freeze
|
38
|
-
|
39
34
|
# commands that cannot be used in a request
|
40
35
|
NO_REQ_COMMANDS = %w[info reopen-log worker-count-down worker-count-up].freeze
|
41
36
|
|
@@ -129,6 +124,9 @@ module Puma
|
|
129
124
|
end
|
130
125
|
|
131
126
|
if @config_file
|
127
|
+
require_relative 'configuration'
|
128
|
+
require_relative 'log_writer'
|
129
|
+
|
132
130
|
config = Puma::Configuration.new({ config_files: [@config_file] }, {})
|
133
131
|
config.load
|
134
132
|
@state ||= config.options[:state]
|
@@ -152,6 +150,8 @@ module Puma
|
|
152
150
|
raise "State file not found: #{@state}"
|
153
151
|
end
|
154
152
|
|
153
|
+
require_relative 'state_file'
|
154
|
+
|
155
155
|
sf = Puma::StateFile.new
|
156
156
|
sf.load @state
|
157
157
|
|
@@ -167,22 +167,26 @@ module Puma
|
|
167
167
|
def send_request
|
168
168
|
uri = URI.parse @control_url
|
169
169
|
|
170
|
+
host = uri.host
|
171
|
+
|
170
172
|
# create server object by scheme
|
171
173
|
server =
|
172
174
|
case uri.scheme
|
173
175
|
when 'ssl'
|
174
176
|
require 'openssl'
|
177
|
+
host = host[1..-2] if host&.start_with? '['
|
175
178
|
OpenSSL::SSL::SSLSocket.new(
|
176
|
-
TCPSocket.new(
|
179
|
+
TCPSocket.new(host, uri.port),
|
177
180
|
OpenSSL::SSL::SSLContext.new)
|
178
181
|
.tap { |ssl| ssl.sync_close = true } # default is false
|
179
182
|
.tap(&:connect)
|
180
183
|
when 'tcp'
|
181
|
-
|
184
|
+
host = host[1..-2] if host&.start_with? '['
|
185
|
+
TCPSocket.new host, uri.port
|
182
186
|
when 'unix'
|
183
187
|
# check for abstract UNIXSocket
|
184
188
|
UNIXSocket.new(@control_url.start_with?('unix://@') ?
|
185
|
-
"\0#{
|
189
|
+
"\0#{host}#{uri.path}" : "#{host}#{uri.path}")
|
186
190
|
else
|
187
191
|
raise "Invalid scheme: #{uri.scheme}"
|
188
192
|
end
|
@@ -287,7 +291,7 @@ module Puma
|
|
287
291
|
|
288
292
|
private
|
289
293
|
def start
|
290
|
-
|
294
|
+
require_relative 'cli'
|
291
295
|
|
292
296
|
run_args = []
|
293
297
|
|
@@ -299,13 +303,13 @@ module Puma
|
|
299
303
|
run_args += ["-C", @config_file] if @config_file
|
300
304
|
run_args += ["-e", @environment] if @environment
|
301
305
|
|
302
|
-
|
306
|
+
log_writer = Puma::LogWriter.new(@stdout, @stderr)
|
303
307
|
|
304
308
|
# replace $0 because puma use it to generate restart command
|
305
309
|
puma_cmd = $0.gsub(/pumactl$/, 'puma')
|
306
310
|
$0 = puma_cmd if File.exist?(puma_cmd)
|
307
311
|
|
308
|
-
cli = Puma::CLI.new run_args,
|
312
|
+
cli = Puma::CLI.new run_args, log_writer
|
309
313
|
cli.run
|
310
314
|
end
|
311
315
|
end
|
data/lib/puma/detect.rb
CHANGED
@@ -8,15 +8,18 @@ module Puma
|
|
8
8
|
# @version 5.2.1
|
9
9
|
HAS_FORK = ::Process.respond_to? :fork
|
10
10
|
|
11
|
+
HAS_NATIVE_IO_WAIT = ::IO.public_instance_methods(false).include? :wait_readable
|
12
|
+
|
11
13
|
IS_JRUBY = Object.const_defined? :JRUBY_VERSION
|
12
14
|
|
13
|
-
IS_OSX =
|
15
|
+
IS_OSX = RUBY_DESCRIPTION.include? 'darwin'
|
16
|
+
|
17
|
+
IS_WINDOWS = RUBY_DESCRIPTION.match?(/mswin|ming|cygwin/)
|
14
18
|
|
15
|
-
|
16
|
-
IS_JRUBY && RUBY_DESCRIPTION.include?('mswin')
|
19
|
+
IS_LINUX = !(IS_OSX || IS_WINDOWS)
|
17
20
|
|
18
21
|
# @version 5.2.0
|
19
|
-
IS_MRI =
|
22
|
+
IS_MRI = RUBY_ENGINE == 'ruby'
|
20
23
|
|
21
24
|
def self.jruby?
|
22
25
|
IS_JRUBY
|
data/lib/puma/dsl.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative 'const'
|
4
|
+
require_relative 'util'
|
5
5
|
|
6
6
|
module Puma
|
7
7
|
# The methods that are available for use inside the configuration file.
|
@@ -32,8 +32,24 @@ module Puma
|
|
32
32
|
# You can also find many examples being used by the test suite in
|
33
33
|
# +test/config+.
|
34
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
|
+
#
|
35
51
|
class DSL
|
36
|
-
|
52
|
+
ON_WORKER_KEY = [String, Symbol].freeze
|
37
53
|
|
38
54
|
# convenience method so logic can be used in CI
|
39
55
|
# @see ssl_bind
|
@@ -49,28 +65,58 @@ module Puma
|
|
49
65
|
|
50
66
|
ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)
|
51
67
|
|
68
|
+
low_latency_str = opts.key?(:low_latency) ? "&low_latency=#{opts[:low_latency]}" : ''
|
52
69
|
backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
|
53
70
|
|
54
71
|
if defined?(JRUBY_VERSION)
|
55
|
-
|
56
|
-
|
72
|
+
cipher_suites = opts[:ssl_cipher_list] ? "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil # old name
|
73
|
+
cipher_suites = "#{cipher_suites}&cipher_suites=#{opts[:cipher_suites]}" if opts[:cipher_suites]
|
74
|
+
protocols = opts[:protocols] ? "&protocols=#{opts[:protocols]}" : nil
|
57
75
|
|
58
76
|
keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
|
77
|
+
keystore_additions = "#{keystore_additions}&keystore-type=#{opts[:keystore_type]}" if opts[:keystore_type]
|
78
|
+
if opts[:truststore]
|
79
|
+
truststore_additions = "&truststore=#{opts[:truststore]}"
|
80
|
+
truststore_additions = "#{truststore_additions}&truststore-pass=#{opts[:truststore_pass]}" if opts[:truststore_pass]
|
81
|
+
truststore_additions = "#{truststore_additions}&truststore-type=#{opts[:truststore_type]}" if opts[:truststore_type]
|
82
|
+
end
|
59
83
|
|
60
|
-
"ssl://#{host}:#{port}?#{keystore_additions}#{
|
84
|
+
"ssl://#{host}:#{port}?#{keystore_additions}#{truststore_additions}#{cipher_suites}#{protocols}" \
|
61
85
|
"&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
|
62
86
|
else
|
63
|
-
ssl_cipher_filter = opts[:ssl_cipher_filter] ?
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
87
|
+
ssl_cipher_filter = opts[:ssl_cipher_filter] ? "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
|
88
|
+
v_flags = (ary = opts[:verification_flags]) ? "&verification_flags=#{Array(ary).join ','}" : nil
|
89
|
+
|
90
|
+
cert_flags = (cert = opts[:cert]) ? "cert=#{Puma::Util.escape(cert)}" : nil
|
91
|
+
key_flags = (key = opts[:key]) ? "&key=#{Puma::Util.escape(key)}" : nil
|
92
|
+
password_flags = (password_command = opts[:key_password_command]) ? "&key_password_command=#{Puma::Util.escape(password_command)}" : nil
|
93
|
+
|
94
|
+
reuse_flag =
|
95
|
+
if (reuse = opts[:reuse])
|
96
|
+
if reuse == true
|
97
|
+
'&reuse=dflt'
|
98
|
+
elsif reuse.is_a?(Hash) && (reuse.key?(:size) || reuse.key?(:timeout))
|
99
|
+
val = +''
|
100
|
+
if (size = reuse[:size]) && Integer === size
|
101
|
+
val << size.to_s
|
102
|
+
end
|
103
|
+
if (timeout = reuse[:timeout]) && Integer === timeout
|
104
|
+
val << ",#{timeout}"
|
105
|
+
end
|
106
|
+
if val.empty?
|
107
|
+
nil
|
108
|
+
else
|
109
|
+
"&reuse=#{val}"
|
110
|
+
end
|
111
|
+
else
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
else
|
115
|
+
nil
|
116
|
+
end
|
71
117
|
|
72
|
-
"ssl://#{host}:#{port}?#{cert_flags}#{key_flags}" \
|
73
|
-
"#{
|
118
|
+
"ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{password_flags}#{ssl_cipher_filter}" \
|
119
|
+
"#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}#{low_latency_str}"
|
74
120
|
end
|
75
121
|
end
|
76
122
|
|
@@ -106,7 +152,7 @@ module Puma
|
|
106
152
|
end
|
107
153
|
|
108
154
|
def default_host
|
109
|
-
@options[:default_host] || Configuration::
|
155
|
+
@options[:default_host] || Configuration::DEFAULTS[:tcp_host]
|
110
156
|
end
|
111
157
|
|
112
158
|
def inject(&blk)
|
@@ -206,6 +252,7 @@ module Puma
|
|
206
252
|
#
|
207
253
|
# * Set the socket backlog depth with +backlog+, default is 1024.
|
208
254
|
# * Set up an SSL certificate with +key+ & +cert+.
|
255
|
+
# * Set up an SSL certificate for mTLS with +key+, +cert+, +ca+ and +verify_mode+.
|
209
256
|
# * Set whether to optimize for low latency instead of throughput with
|
210
257
|
# +low_latency+, default is to not optimize for low latency. This is done
|
211
258
|
# via +Socket::TCP_NODELAY+.
|
@@ -215,6 +262,8 @@ module Puma
|
|
215
262
|
# bind 'unix:///var/run/puma.sock?backlog=512'
|
216
263
|
# @example SSL cert
|
217
264
|
# bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem'
|
265
|
+
# @example SSL cert for mutual TLS (mTLS)
|
266
|
+
# bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem&ca=ca.pem&verify_mode=force_peer'
|
218
267
|
# @example Disable optimization for low latency
|
219
268
|
# bind 'tcp://0.0.0.0:9292?low_latency=false'
|
220
269
|
# @example Socket permissions
|
@@ -266,16 +315,22 @@ module Puma
|
|
266
315
|
bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s
|
267
316
|
end
|
268
317
|
|
318
|
+
# Define how long the tcp socket stays open, if no data has been received.
|
319
|
+
# @see Puma::Server.new
|
320
|
+
def first_data_timeout(seconds)
|
321
|
+
@options[:first_data_timeout] = Integer(seconds)
|
322
|
+
end
|
323
|
+
|
269
324
|
# Define how long persistent connections can be idle before Puma closes them.
|
270
325
|
# @see Puma::Server.new
|
271
326
|
def persistent_timeout(seconds)
|
272
327
|
@options[:persistent_timeout] = Integer(seconds)
|
273
328
|
end
|
274
329
|
|
275
|
-
#
|
330
|
+
# If a new request is not received within this number of seconds, begin shutting down.
|
276
331
|
# @see Puma::Server.new
|
277
|
-
def
|
278
|
-
@options[:
|
332
|
+
def idle_timeout(seconds)
|
333
|
+
@options[:idle_timeout] = Integer(seconds)
|
279
334
|
end
|
280
335
|
|
281
336
|
# Work around leaky apps that leave garbage in Thread locals
|
@@ -371,6 +426,11 @@ module Puma
|
|
371
426
|
@options[:log_requests] = which
|
372
427
|
end
|
373
428
|
|
429
|
+
# Pass in a custom logging class instance
|
430
|
+
def custom_logger(custom_logger)
|
431
|
+
@options[:custom_logger] = custom_logger
|
432
|
+
end
|
433
|
+
|
374
434
|
# Show debugging info
|
375
435
|
#
|
376
436
|
def debug
|
@@ -452,6 +512,16 @@ module Puma
|
|
452
512
|
# Puma will assume you are using the +localhost+ gem and try to load the
|
453
513
|
# appropriate files.
|
454
514
|
#
|
515
|
+
# When using the options hash parameter, the `reuse:` value is either
|
516
|
+
# `true`, which sets reuse 'on' with default values, or a hash, with `:size`
|
517
|
+
# and/or `:timeout` keys, each with integer values.
|
518
|
+
#
|
519
|
+
# The `cert:` options hash parameter can be the path to a certificate
|
520
|
+
# file including all intermediate certificates in PEM format.
|
521
|
+
#
|
522
|
+
# The `cert_pem:` options hash parameter can be String containing the
|
523
|
+
# cerificate and all intermediate certificates in PEM format.
|
524
|
+
#
|
455
525
|
# @example
|
456
526
|
# ssl_bind '127.0.0.1', '9292', {
|
457
527
|
# cert: path_to_cert,
|
@@ -459,6 +529,7 @@ module Puma
|
|
459
529
|
# ssl_cipher_filter: cipher_filter, # optional
|
460
530
|
# verify_mode: verify_mode, # default 'none'
|
461
531
|
# verification_flags: flags, # optional, not supported by JRuby
|
532
|
+
# reuse: true # optional
|
462
533
|
# }
|
463
534
|
#
|
464
535
|
# @example Using self-signed certificate with the +localhost+ gem:
|
@@ -468,6 +539,7 @@ module Puma
|
|
468
539
|
# ssl_bind '127.0.0.1', '9292', {
|
469
540
|
# cert_pem: File.read(path_to_cert),
|
470
541
|
# key_pem: File.read(path_to_key),
|
542
|
+
# reuse: {size: 2_000, timeout: 20} # optional
|
471
543
|
# }
|
472
544
|
#
|
473
545
|
# @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
|
@@ -531,6 +603,11 @@ module Puma
|
|
531
603
|
@options[:silence_single_worker_warning] = true
|
532
604
|
end
|
533
605
|
|
606
|
+
# Disable warning message when running single mode with callback hook defined.
|
607
|
+
def silence_fork_callback_warning
|
608
|
+
@options[:silence_fork_callback_warning] = true
|
609
|
+
end
|
610
|
+
|
534
611
|
# Code to run immediately before master process
|
535
612
|
# forks workers (once on boot). These hooks can block if necessary
|
536
613
|
# to wait for background operations unknown to Puma to finish before
|
@@ -546,6 +623,8 @@ module Puma
|
|
546
623
|
# puts "Starting workers..."
|
547
624
|
# end
|
548
625
|
def before_fork(&block)
|
626
|
+
warn_if_in_single_mode('before_fork')
|
627
|
+
|
549
628
|
@options[:before_fork] ||= []
|
550
629
|
@options[:before_fork] << block
|
551
630
|
end
|
@@ -560,9 +639,10 @@ module Puma
|
|
560
639
|
# on_worker_boot do
|
561
640
|
# puts 'Before worker boot...'
|
562
641
|
# end
|
563
|
-
def on_worker_boot(&block)
|
564
|
-
|
565
|
-
|
642
|
+
def on_worker_boot(key = nil, &block)
|
643
|
+
warn_if_in_single_mode('on_worker_boot')
|
644
|
+
|
645
|
+
process_hook :before_worker_boot, key, block, 'on_worker_boot'
|
566
646
|
end
|
567
647
|
|
568
648
|
# Code to run immediately before a worker shuts
|
@@ -577,9 +657,10 @@ module Puma
|
|
577
657
|
# on_worker_shutdown do
|
578
658
|
# puts 'On worker shutdown...'
|
579
659
|
# end
|
580
|
-
def on_worker_shutdown(&block)
|
581
|
-
|
582
|
-
|
660
|
+
def on_worker_shutdown(key = nil, &block)
|
661
|
+
warn_if_in_single_mode('on_worker_shutdown')
|
662
|
+
|
663
|
+
process_hook :before_worker_shutdown, key, block, 'on_worker_shutdown'
|
583
664
|
end
|
584
665
|
|
585
666
|
# Code to run in the master right before a worker is started. The worker's
|
@@ -593,8 +674,9 @@ module Puma
|
|
593
674
|
# puts 'Before worker fork...'
|
594
675
|
# end
|
595
676
|
def on_worker_fork(&block)
|
596
|
-
|
597
|
-
|
677
|
+
warn_if_in_single_mode('on_worker_fork')
|
678
|
+
|
679
|
+
process_hook :before_worker_fork, nil, block, 'on_worker_fork'
|
598
680
|
end
|
599
681
|
|
600
682
|
# Code to run in the master after a worker has been started. The worker's
|
@@ -608,12 +690,23 @@ module Puma
|
|
608
690
|
# puts 'After worker fork...'
|
609
691
|
# end
|
610
692
|
def after_worker_fork(&block)
|
611
|
-
|
612
|
-
|
693
|
+
warn_if_in_single_mode('after_worker_fork')
|
694
|
+
|
695
|
+
process_hook :after_worker_fork, nil, block, 'after_worker_fork'
|
613
696
|
end
|
614
697
|
|
615
698
|
alias_method :after_worker_boot, :after_worker_fork
|
616
699
|
|
700
|
+
# Code to run after puma is booted (works for both: single and clustered)
|
701
|
+
#
|
702
|
+
# @example
|
703
|
+
# on_booted do
|
704
|
+
# puts 'After booting...'
|
705
|
+
# end
|
706
|
+
def on_booted(&block)
|
707
|
+
@config.options[:events].on_booted(&block)
|
708
|
+
end
|
709
|
+
|
617
710
|
# When `fork_worker` is enabled, code to run in Worker 0
|
618
711
|
# before all other workers are re-forked from this process,
|
619
712
|
# after the server has temporarily stopped serving requests
|
@@ -632,9 +725,53 @@ module Puma
|
|
632
725
|
# end
|
633
726
|
# @version 5.0.0
|
634
727
|
#
|
635
|
-
def on_refork(&block)
|
636
|
-
|
637
|
-
|
728
|
+
def on_refork(key = nil, &block)
|
729
|
+
process_hook :before_refork, key, block, 'on_refork'
|
730
|
+
end
|
731
|
+
|
732
|
+
# Provide a block to be executed just before a thread is added to the thread
|
733
|
+
# pool. Be careful: while the block executes, thread creation is delayed, and
|
734
|
+
# probably a request will have to wait too! The new thread will not be added to
|
735
|
+
# the threadpool until the provided block returns.
|
736
|
+
#
|
737
|
+
# Return values are ignored.
|
738
|
+
# Raising an exception will log a warning.
|
739
|
+
#
|
740
|
+
# This hook is useful for doing something when the thread pool grows.
|
741
|
+
#
|
742
|
+
# This can be called multiple times to add several hooks.
|
743
|
+
#
|
744
|
+
# @example
|
745
|
+
# on_thread_start do
|
746
|
+
# puts 'On thread start...'
|
747
|
+
# end
|
748
|
+
def on_thread_start(&block)
|
749
|
+
@options[:before_thread_start] ||= []
|
750
|
+
@options[:before_thread_start] << block
|
751
|
+
end
|
752
|
+
|
753
|
+
# Provide a block to be executed after a thread is trimmed from the thread
|
754
|
+
# pool. Be careful: while this block executes, Puma's main loop is
|
755
|
+
# blocked, so no new requests will be picked up.
|
756
|
+
#
|
757
|
+
# This hook only runs when a thread in the threadpool is trimmed by Puma.
|
758
|
+
# It does not run when a thread dies due to exceptions or any other cause.
|
759
|
+
#
|
760
|
+
# Return values are ignored.
|
761
|
+
# Raising an exception will log a warning.
|
762
|
+
#
|
763
|
+
# This hook is useful for cleaning up thread local resources when a thread
|
764
|
+
# is trimmed.
|
765
|
+
#
|
766
|
+
# This can be called multiple times to add several hooks.
|
767
|
+
#
|
768
|
+
# @example
|
769
|
+
# on_thread_exit do
|
770
|
+
# puts 'On thread exit...'
|
771
|
+
# end
|
772
|
+
def on_thread_exit(&block)
|
773
|
+
@options[:before_thread_exit] ||= []
|
774
|
+
@options[:before_thread_exit] << block
|
638
775
|
end
|
639
776
|
|
640
777
|
# Code to run out-of-band when the worker is idle.
|
@@ -647,8 +784,7 @@ module Puma
|
|
647
784
|
#
|
648
785
|
# This can be called multiple times to add several hooks.
|
649
786
|
def out_of_band(&block)
|
650
|
-
|
651
|
-
@options[:out_of_band] << block
|
787
|
+
process_hook :out_of_band, nil, block, 'out_of_band'
|
652
788
|
end
|
653
789
|
|
654
790
|
# The directory to operate out of.
|
@@ -769,7 +905,8 @@ module Puma
|
|
769
905
|
# not a request timeout, it is to protect against a hung or dead process.
|
770
906
|
# Setting this value will not protect against slow requests.
|
771
907
|
#
|
772
|
-
#
|
908
|
+
# This value must be greater than worker_check_interval.
|
909
|
+
# The default value is 60 seconds.
|
773
910
|
#
|
774
911
|
# @note Cluster mode only.
|
775
912
|
# @example
|
@@ -778,7 +915,7 @@ module Puma
|
|
778
915
|
#
|
779
916
|
def worker_timeout(timeout)
|
780
917
|
timeout = Integer(timeout)
|
781
|
-
min = @options.fetch(:worker_check_interval,
|
918
|
+
min = @options.fetch(:worker_check_interval, Configuration::DEFAULTS[:worker_check_interval])
|
782
919
|
|
783
920
|
if timeout <= min
|
784
921
|
raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
|
@@ -882,13 +1019,16 @@ module Puma
|
|
882
1019
|
# There are 5 possible values:
|
883
1020
|
#
|
884
1021
|
# 1. **:socket** (the default) - read the peername from the socket using the
|
885
|
-
# syscall. This is the normal behavior.
|
1022
|
+
# syscall. This is the normal behavior. If this fails for any reason (e.g.,
|
1023
|
+
# if the peer disconnects between the connection being accepted and the getpeername
|
1024
|
+
# system call), Puma will return "0.0.0.0"
|
886
1025
|
# 2. **:localhost** - set the remote address to "127.0.0.1"
|
887
1026
|
# 3. **header: <http_header>**- set the remote address to the value of the
|
888
1027
|
# provided http header. For instance:
|
889
1028
|
# `set_remote_address header: "X-Real-IP"`.
|
890
1029
|
# Only the first word (as separated by spaces or comma) is used, allowing
|
891
|
-
# headers such as X-Forwarded-For to be used as well.
|
1030
|
+
# headers such as X-Forwarded-For to be used as well. If this header is absent,
|
1031
|
+
# Puma will fall back to the behavior of :socket
|
892
1032
|
# 4. **proxy_protocol: :v1**- set the remote address to the value read from the
|
893
1033
|
# HAproxy PROXY protocol, version 1. If the request does not have the PROXY
|
894
1034
|
# protocol attached to it, will fall back to :socket
|
@@ -942,23 +1082,6 @@ module Puma
|
|
942
1082
|
@options[:fork_worker] = Integer(after_requests)
|
943
1083
|
end
|
944
1084
|
|
945
|
-
# When enabled, Puma will GC 4 times before forking workers.
|
946
|
-
# If available (Ruby 2.7+), we will also call GC.compact.
|
947
|
-
# Not recommended for non-MRI Rubies.
|
948
|
-
#
|
949
|
-
# Based on the work of Koichi Sasada and Aaron Patterson, this option may
|
950
|
-
# decrease memory utilization of preload-enabled cluster-mode Pumas. It will
|
951
|
-
# also increase time to boot and fork. See your logs for details on how much
|
952
|
-
# time this adds to your boot process. For most apps, it will be less than one
|
953
|
-
# second.
|
954
|
-
#
|
955
|
-
# @see Puma::Cluster#nakayoshi_gc
|
956
|
-
# @version 5.0.0
|
957
|
-
#
|
958
|
-
def nakayoshi_fork(enabled=true)
|
959
|
-
@options[:nakayoshi_fork] = enabled
|
960
|
-
end
|
961
|
-
|
962
1085
|
# The number of requests to attempt inline before sending a client back to
|
963
1086
|
# the reactor to be subject to normal ordering.
|
964
1087
|
#
|
@@ -989,6 +1112,51 @@ module Puma
|
|
989
1112
|
@options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
|
990
1113
|
end
|
991
1114
|
|
1115
|
+
# Specify how big the request payload should be, in bytes.
|
1116
|
+
# This limit is compared against Content-Length HTTP header.
|
1117
|
+
# If the payload size (CONTENT_LENGTH) is larger than http_content_length_limit,
|
1118
|
+
# HTTP 413 status code is returned.
|
1119
|
+
#
|
1120
|
+
# When no Content-Length http header is present, it is compared against the
|
1121
|
+
# size of the body of the request.
|
1122
|
+
#
|
1123
|
+
# The default value for http_content_length_limit is nil.
|
1124
|
+
def http_content_length_limit(limit)
|
1125
|
+
@options[:http_content_length_limit] = limit
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
# Supported http methods, which will replace `Puma::Const::SUPPORTED_HTTP_METHODS`.
|
1129
|
+
# The value of `:any` will allows all methods, otherwise, the value must be
|
1130
|
+
# an array of strings. Note that methods are all uppercase.
|
1131
|
+
#
|
1132
|
+
# `Puma::Const::SUPPORTED_HTTP_METHODS` is conservative, if you want a
|
1133
|
+
# complete set of methods, the methods defined by the
|
1134
|
+
# [IANA Method Registry](https://www.iana.org/assignments/http-methods/http-methods.xhtml)
|
1135
|
+
# are pre-defined as the constant `Puma::Const::IANA_HTTP_METHODS`.
|
1136
|
+
#
|
1137
|
+
# @note If the `methods` value is `:any`, no method check with be performed,
|
1138
|
+
# similar to Puma v5 and earlier.
|
1139
|
+
#
|
1140
|
+
# @example Adds 'PROPFIND' to existing supported methods
|
1141
|
+
# supported_http_methods(Puma::Const::SUPPORTED_HTTP_METHODS + ['PROPFIND'])
|
1142
|
+
# @example Restricts methods to the array elements
|
1143
|
+
# supported_http_methods %w[HEAD GET POST PUT DELETE OPTIONS PROPFIND]
|
1144
|
+
# @example Restricts methods to the methods in the IANA Registry
|
1145
|
+
# supported_http_methods Puma::Const::IANA_HTTP_METHODS
|
1146
|
+
# @example Allows any method
|
1147
|
+
# supported_http_methods :any
|
1148
|
+
#
|
1149
|
+
def supported_http_methods(methods)
|
1150
|
+
if methods == :any
|
1151
|
+
@options[:supported_http_methods] = :any
|
1152
|
+
elsif Array === methods && methods == (ary = methods.grep(String).uniq) &&
|
1153
|
+
!ary.empty?
|
1154
|
+
@options[:supported_http_methods] = ary
|
1155
|
+
else
|
1156
|
+
raise "supported_http_methods must be ':any' or a unique array of strings"
|
1157
|
+
end
|
1158
|
+
end
|
1159
|
+
|
992
1160
|
private
|
993
1161
|
|
994
1162
|
# To avoid adding cert_pem and key_pem as URI params, we store them on the
|
@@ -1008,5 +1176,31 @@ module Puma
|
|
1008
1176
|
end
|
1009
1177
|
end
|
1010
1178
|
end
|
1179
|
+
|
1180
|
+
def process_hook(options_key, key, block, meth)
|
1181
|
+
@options[options_key] ||= []
|
1182
|
+
if ON_WORKER_KEY.include? key.class
|
1183
|
+
@options[options_key] << [block, key.to_sym]
|
1184
|
+
elsif key.nil?
|
1185
|
+
@options[options_key] << block
|
1186
|
+
else
|
1187
|
+
raise "'#{meth}' key must be String or Symbol"
|
1188
|
+
end
|
1189
|
+
end
|
1190
|
+
|
1191
|
+
def warn_if_in_single_mode(hook_name)
|
1192
|
+
return if @options[:silence_fork_callback_warning]
|
1193
|
+
# user_options (CLI) have precedence over config file
|
1194
|
+
workers_val = @config.options.user_options[:workers] || @options[:workers] ||
|
1195
|
+
@config.puma_default_options[:workers] || 0
|
1196
|
+
if workers_val == 0
|
1197
|
+
log_string =
|
1198
|
+
"Warning: You specified code to run in a `#{hook_name}` block, " \
|
1199
|
+
"but Puma is not configured to run in cluster mode (worker count > 0 ), " \
|
1200
|
+
"so your `#{hook_name}` block did not run"
|
1201
|
+
|
1202
|
+
LogWriter.stdio.log(log_string)
|
1203
|
+
end
|
1204
|
+
end
|
1011
1205
|
end
|
1012
1206
|
end
|
data/lib/puma/error_logger.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'const'
|
4
4
|
|
5
5
|
module Puma
|
6
6
|
# The implementation of a detailed error logging.
|
@@ -13,6 +13,8 @@ module Puma
|
|
13
13
|
|
14
14
|
REQUEST_FORMAT = %{"%s %s%s" - (%s)}
|
15
15
|
|
16
|
+
LOG_QUEUE = Queue.new
|
17
|
+
|
16
18
|
def initialize(ioerr)
|
17
19
|
@ioerr = ioerr
|
18
20
|
|
@@ -31,7 +33,7 @@ module Puma
|
|
31
33
|
# and before all remaining info.
|
32
34
|
#
|
33
35
|
def info(options={})
|
34
|
-
|
36
|
+
internal_write title(options)
|
35
37
|
end
|
36
38
|
|
37
39
|
# Print occurred error details only if
|
@@ -53,7 +55,7 @@ module Puma
|
|
53
55
|
string_block << request_dump(req) if request_parsed?(req)
|
54
56
|
string_block << error.backtrace if error
|
55
57
|
|
56
|
-
|
58
|
+
internal_write string_block.join("\n")
|
57
59
|
end
|
58
60
|
|
59
61
|
def title(options={})
|
@@ -93,12 +95,19 @@ module Puma
|
|
93
95
|
req && req.env[REQUEST_METHOD]
|
94
96
|
end
|
95
97
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
98
|
+
def internal_write(str)
|
99
|
+
LOG_QUEUE << str
|
100
|
+
while (w_str = LOG_QUEUE.pop(true)) do
|
101
|
+
begin
|
102
|
+
@ioerr.is_a?(IO) and @ioerr.wait_writable(1)
|
103
|
+
@ioerr.write "#{w_str}\n"
|
104
|
+
@ioerr.flush unless @ioerr.sync
|
105
|
+
rescue Errno::EPIPE, Errno::EBADF, IOError, Errno::EINVAL
|
106
|
+
# 'Invalid argument' (Errno::EINVAL) may be raised by flush
|
107
|
+
end
|
108
|
+
end
|
109
|
+
rescue ThreadError
|
102
110
|
end
|
111
|
+
private :internal_write
|
103
112
|
end
|
104
113
|
end
|