puma 5.6.4 → 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.
- checksums.yaml +4 -4
- data/History.md +163 -3
- data/README.md +21 -17
- data/bin/puma-wild +1 -1
- data/docs/compile_options.md +34 -0
- data/docs/fork_worker.md +1 -3
- data/docs/nginx.md +1 -1
- 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 +18 -10
- 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 +63 -24
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +166 -65
- data/ext/puma_http11/puma_http11.c +17 -9
- data/lib/puma/app/status.rb +6 -3
- data/lib/puma/binder.rb +37 -43
- data/lib/puma/cli.rb +11 -17
- data/lib/puma/client.rb +26 -13
- data/lib/puma/cluster/worker.rb +13 -11
- data/lib/puma/cluster/worker_handle.rb +4 -1
- data/lib/puma/cluster.rb +31 -30
- data/lib/puma/configuration.rb +74 -58
- data/lib/puma/const.rb +76 -88
- data/lib/puma/control_cli.rb +21 -18
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +97 -49
- data/lib/puma/error_logger.rb +17 -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 +107 -156
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +23 -12
- data/lib/puma/minissl.rb +91 -15
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/rack/builder.rb +4 -4
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +4 -4
- data/lib/puma/request.rb +334 -162
- data/lib/puma/runner.rb +45 -20
- data/lib/puma/server.rb +55 -69
- data/lib/puma/single.rb +11 -11
- data/lib/puma/state_file.rb +2 -4
- data/lib/puma/systemd.rb +3 -2
- data/lib/puma/thread_pool.rb +16 -16
- data/lib/puma/util.rb +12 -14
- data/lib/puma.rb +12 -11
- data/lib/rack/handler/puma.rb +9 -9
- metadata +7 -3
- data/lib/puma/queue_close.rb +0 -26
data/lib/puma/control_cli.rb
CHANGED
@@ -17,26 +17,27 @@ module Puma
|
|
17
17
|
CMD_PATH_SIG_MAP = {
|
18
18
|
'gc' => nil,
|
19
19
|
'gc-stats' => nil,
|
20
|
-
'halt'
|
21
|
-
'
|
22
|
-
'
|
20
|
+
'halt' => 'SIGQUIT',
|
21
|
+
'info' => 'SIGINFO',
|
22
|
+
'phased-restart' => 'SIGUSR1',
|
23
|
+
'refork' => 'SIGURG',
|
23
24
|
'reload-worker-directory' => nil,
|
24
|
-
'
|
25
|
+
'reopen-log' => 'SIGHUP',
|
26
|
+
'restart' => 'SIGUSR2',
|
25
27
|
'start' => nil,
|
26
28
|
'stats' => nil,
|
27
29
|
'status' => '',
|
28
|
-
'stop'
|
29
|
-
'thread-backtraces' => nil
|
30
|
+
'stop' => 'SIGTERM',
|
31
|
+
'thread-backtraces' => nil,
|
32
|
+
'worker-count-down' => 'SIGTTOU',
|
33
|
+
'worker-count-up' => 'SIGTTIN'
|
30
34
|
}.freeze
|
31
35
|
|
32
|
-
# @deprecated 6.0.0
|
33
|
-
COMMANDS = CMD_PATH_SIG_MAP.keys.freeze
|
34
|
-
|
35
36
|
# commands that cannot be used in a request
|
36
|
-
NO_REQ_COMMANDS = %w
|
37
|
+
NO_REQ_COMMANDS = %w[info reopen-log worker-count-down worker-count-up].freeze
|
37
38
|
|
38
39
|
# @version 5.0.0
|
39
|
-
PRINTABLE_COMMANDS = %w
|
40
|
+
PRINTABLE_COMMANDS = %w[gc-stats stats thread-backtraces].freeze
|
40
41
|
|
41
42
|
def initialize(argv, stdout=STDOUT, stderr=STDERR)
|
42
43
|
@state = nil
|
@@ -185,8 +186,6 @@ module Puma
|
|
185
186
|
|
186
187
|
if @command == 'status'
|
187
188
|
message 'Puma is started'
|
188
|
-
elsif NO_REQ_COMMANDS.include? @command
|
189
|
-
raise "Invalid request command: #{@command}"
|
190
189
|
else
|
191
190
|
url = "/#{@command}"
|
192
191
|
|
@@ -242,7 +241,11 @@ module Puma
|
|
242
241
|
@stdout.flush unless @stdout.sync
|
243
242
|
return
|
244
243
|
elsif sig.start_with? 'SIG'
|
245
|
-
|
244
|
+
if Signal.list.key? sig.sub(/\ASIG/, '')
|
245
|
+
Process.kill sig, @pid
|
246
|
+
else
|
247
|
+
raise "Signal '#{sig}' not available'"
|
248
|
+
end
|
246
249
|
elsif @command == 'status'
|
247
250
|
begin
|
248
251
|
Process.kill 0, @pid
|
@@ -268,7 +271,7 @@ module Puma
|
|
268
271
|
return start if @command == 'start'
|
269
272
|
prepare_configuration
|
270
273
|
|
271
|
-
if Puma.windows? || @control_url
|
274
|
+
if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command)
|
272
275
|
send_request
|
273
276
|
else
|
274
277
|
send_signal
|
@@ -281,7 +284,7 @@ module Puma
|
|
281
284
|
|
282
285
|
private
|
283
286
|
def start
|
284
|
-
|
287
|
+
require_relative 'cli'
|
285
288
|
|
286
289
|
run_args = []
|
287
290
|
|
@@ -293,13 +296,13 @@ module Puma
|
|
293
296
|
run_args += ["-C", @config_file] if @config_file
|
294
297
|
run_args += ["-e", @environment] if @environment
|
295
298
|
|
296
|
-
|
299
|
+
log_writer = Puma::LogWriter.new(@stdout, @stderr)
|
297
300
|
|
298
301
|
# replace $0 because puma use it to generate restart command
|
299
302
|
puma_cmd = $0.gsub(/pumactl$/, 'puma')
|
300
303
|
$0 = puma_cmd if File.exist?(puma_cmd)
|
301
304
|
|
302
|
-
cli = Puma::CLI.new run_args,
|
305
|
+
cli = Puma::CLI.new run_args, log_writer
|
303
306
|
cli.run
|
304
307
|
end
|
305
308
|
end
|
data/lib/puma/detect.rb
CHANGED
data/lib/puma/dsl.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
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.
|
@@ -31,8 +32,24 @@ module Puma
|
|
31
32
|
# You can also find many examples being used by the test suite in
|
32
33
|
# +test/config+.
|
33
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
|
+
#
|
34
51
|
class DSL
|
35
|
-
|
52
|
+
ON_WORKER_KEY = [String, Symbol].freeze
|
36
53
|
|
37
54
|
# convenience method so logic can be used in CI
|
38
55
|
# @see ssl_bind
|
@@ -46,27 +63,58 @@ module Puma
|
|
46
63
|
else ''
|
47
64
|
end
|
48
65
|
|
49
|
-
ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
|
66
|
+
ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)
|
50
67
|
|
51
68
|
backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
|
52
69
|
|
53
70
|
if defined?(JRUBY_VERSION)
|
54
|
-
|
55
|
-
|
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
|
56
74
|
|
57
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
|
58
82
|
|
59
|
-
"ssl://#{host}:#{port}?#{keystore_additions}#{
|
83
|
+
"ssl://#{host}:#{port}?#{keystore_additions}#{truststore_additions}#{cipher_suites}#{protocols}" \
|
60
84
|
"&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
|
61
85
|
else
|
62
|
-
ssl_cipher_filter = opts[:ssl_cipher_filter] ?
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
67
115
|
|
68
|
-
"ssl://#{host}:#{port}
|
69
|
-
"#{
|
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}"
|
70
118
|
end
|
71
119
|
end
|
72
120
|
|
@@ -102,7 +150,7 @@ module Puma
|
|
102
150
|
end
|
103
151
|
|
104
152
|
def default_host
|
105
|
-
@options[:default_host] || Configuration::
|
153
|
+
@options[:default_host] || Configuration::DEFAULTS[:tcp_host]
|
106
154
|
end
|
107
155
|
|
108
156
|
def inject(&blk)
|
@@ -202,6 +250,7 @@ module Puma
|
|
202
250
|
#
|
203
251
|
# * Set the socket backlog depth with +backlog+, default is 1024.
|
204
252
|
# * Set up an SSL certificate with +key+ & +cert+.
|
253
|
+
# * Set up an SSL certificate for mTLS with +key+, +cert+, +ca+ and +verify_mode+.
|
205
254
|
# * Set whether to optimize for low latency instead of throughput with
|
206
255
|
# +low_latency+, default is to not optimize for low latency. This is done
|
207
256
|
# via +Socket::TCP_NODELAY+.
|
@@ -211,6 +260,8 @@ module Puma
|
|
211
260
|
# bind 'unix:///var/run/puma.sock?backlog=512'
|
212
261
|
# @example SSL cert
|
213
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'
|
214
265
|
# @example Disable optimization for low latency
|
215
266
|
# bind 'tcp://0.0.0.0:9292?low_latency=false'
|
216
267
|
# @example Socket permissions
|
@@ -448,6 +499,10 @@ module Puma
|
|
448
499
|
# Puma will assume you are using the +localhost+ gem and try to load the
|
449
500
|
# appropriate files.
|
450
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.
|
505
|
+
#
|
451
506
|
# @example
|
452
507
|
# ssl_bind '127.0.0.1', '9292', {
|
453
508
|
# cert: path_to_cert,
|
@@ -455,6 +510,7 @@ module Puma
|
|
455
510
|
# ssl_cipher_filter: cipher_filter, # optional
|
456
511
|
# verify_mode: verify_mode, # default 'none'
|
457
512
|
# verification_flags: flags, # optional, not supported by JRuby
|
513
|
+
# reuse: true # optional
|
458
514
|
# }
|
459
515
|
#
|
460
516
|
# @example Using self-signed certificate with the +localhost+ gem:
|
@@ -464,6 +520,7 @@ module Puma
|
|
464
520
|
# ssl_bind '127.0.0.1', '9292', {
|
465
521
|
# cert_pem: File.read(path_to_cert),
|
466
522
|
# key_pem: File.read(path_to_key),
|
523
|
+
# reuse: {size: 2_000, timeout: 20} # optional
|
467
524
|
# }
|
468
525
|
#
|
469
526
|
# @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
|
@@ -556,9 +613,8 @@ module Puma
|
|
556
613
|
# on_worker_boot do
|
557
614
|
# puts 'Before worker boot...'
|
558
615
|
# end
|
559
|
-
def on_worker_boot(&block)
|
560
|
-
|
561
|
-
@options[:before_worker_boot] << block
|
616
|
+
def on_worker_boot(key = nil, &block)
|
617
|
+
process_hook :before_worker_boot, key, block, 'on_worker_boot'
|
562
618
|
end
|
563
619
|
|
564
620
|
# Code to run immediately before a worker shuts
|
@@ -573,9 +629,8 @@ module Puma
|
|
573
629
|
# on_worker_shutdown do
|
574
630
|
# puts 'On worker shutdown...'
|
575
631
|
# end
|
576
|
-
def on_worker_shutdown(&block)
|
577
|
-
|
578
|
-
@options[:before_worker_shutdown] << block
|
632
|
+
def on_worker_shutdown(key = nil, &block)
|
633
|
+
process_hook :before_worker_shutdown, key, block, 'on_worker_shutdown'
|
579
634
|
end
|
580
635
|
|
581
636
|
# Code to run in the master right before a worker is started. The worker's
|
@@ -589,8 +644,7 @@ module Puma
|
|
589
644
|
# puts 'Before worker fork...'
|
590
645
|
# end
|
591
646
|
def on_worker_fork(&block)
|
592
|
-
|
593
|
-
@options[:before_worker_fork] << block
|
647
|
+
process_hook :before_worker_fork, nil, block, 'on_worker_fork'
|
594
648
|
end
|
595
649
|
|
596
650
|
# Code to run in the master after a worker has been started. The worker's
|
@@ -604,8 +658,7 @@ module Puma
|
|
604
658
|
# puts 'After worker fork...'
|
605
659
|
# end
|
606
660
|
def after_worker_fork(&block)
|
607
|
-
|
608
|
-
@options[:after_worker_fork] << block
|
661
|
+
process_hook :after_worker_fork, nil, block, 'after_worker_fork'
|
609
662
|
end
|
610
663
|
|
611
664
|
alias_method :after_worker_boot, :after_worker_fork
|
@@ -628,9 +681,8 @@ module Puma
|
|
628
681
|
# end
|
629
682
|
# @version 5.0.0
|
630
683
|
#
|
631
|
-
def on_refork(&block)
|
632
|
-
|
633
|
-
@options[:before_refork] << block
|
684
|
+
def on_refork(key = nil, &block)
|
685
|
+
process_hook :before_refork, key, block, 'on_refork'
|
634
686
|
end
|
635
687
|
|
636
688
|
# Code to run out-of-band when the worker is idle.
|
@@ -643,8 +695,7 @@ module Puma
|
|
643
695
|
#
|
644
696
|
# This can be called multiple times to add several hooks.
|
645
697
|
def out_of_band(&block)
|
646
|
-
|
647
|
-
@options[:out_of_band] << block
|
698
|
+
process_hook :out_of_band, nil, block, 'out_of_band'
|
648
699
|
end
|
649
700
|
|
650
701
|
# The directory to operate out of.
|
@@ -774,7 +825,7 @@ module Puma
|
|
774
825
|
#
|
775
826
|
def worker_timeout(timeout)
|
776
827
|
timeout = Integer(timeout)
|
777
|
-
min = @options.fetch(:worker_check_interval,
|
828
|
+
min = @options.fetch(:worker_check_interval, Configuration::DEFAULTS[:worker_check_interval])
|
778
829
|
|
779
830
|
if timeout <= min
|
780
831
|
raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
|
@@ -878,13 +929,16 @@ module Puma
|
|
878
929
|
# There are 5 possible values:
|
879
930
|
#
|
880
931
|
# 1. **:socket** (the default) - read the peername from the socket using the
|
881
|
-
# syscall. This is the normal behavior.
|
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"
|
882
935
|
# 2. **:localhost** - set the remote address to "127.0.0.1"
|
883
936
|
# 3. **header: <http_header>**- set the remote address to the value of the
|
884
937
|
# provided http header. For instance:
|
885
938
|
# `set_remote_address header: "X-Real-IP"`.
|
886
939
|
# Only the first word (as separated by spaces or comma) is used, allowing
|
887
|
-
# headers such as X-Forwarded-For to be used as well.
|
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
|
888
942
|
# 4. **proxy_protocol: :v1**- set the remote address to the value read from the
|
889
943
|
# HAproxy PROXY protocol, version 1. If the request does not have the PROXY
|
890
944
|
# protocol attached to it, will fall back to :socket
|
@@ -938,23 +992,6 @@ module Puma
|
|
938
992
|
@options[:fork_worker] = Integer(after_requests)
|
939
993
|
end
|
940
994
|
|
941
|
-
# When enabled, Puma will GC 4 times before forking workers.
|
942
|
-
# If available (Ruby 2.7+), we will also call GC.compact.
|
943
|
-
# Not recommended for non-MRI Rubies.
|
944
|
-
#
|
945
|
-
# Based on the work of Koichi Sasada and Aaron Patterson, this option may
|
946
|
-
# decrease memory utilization of preload-enabled cluster-mode Pumas. It will
|
947
|
-
# also increase time to boot and fork. See your logs for details on how much
|
948
|
-
# time this adds to your boot process. For most apps, it will be less than one
|
949
|
-
# second.
|
950
|
-
#
|
951
|
-
# @see Puma::Cluster#nakayoshi_gc
|
952
|
-
# @version 5.0.0
|
953
|
-
#
|
954
|
-
def nakayoshi_fork(enabled=true)
|
955
|
-
@options[:nakayoshi_fork] = enabled
|
956
|
-
end
|
957
|
-
|
958
995
|
# The number of requests to attempt inline before sending a client back to
|
959
996
|
# the reactor to be subject to normal ordering.
|
960
997
|
#
|
@@ -1004,5 +1041,16 @@ module Puma
|
|
1004
1041
|
end
|
1005
1042
|
end
|
1006
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
|
1007
1055
|
end
|
1008
1056
|
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,18 @@ 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
|
106
|
+
end
|
107
|
+
end
|
108
|
+
rescue ThreadError
|
102
109
|
end
|
110
|
+
private :internal_write
|
103
111
|
end
|
104
112
|
end
|
data/lib/puma/events.rb
CHANGED
@@ -1,52 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "puma/null_io"
|
4
|
-
require 'puma/error_logger'
|
5
|
-
require 'stringio'
|
6
|
-
|
7
3
|
module Puma
|
8
|
-
# The default implement of an event sink object used by Server
|
9
|
-
# for when certain kinds of events occur in the life of the server.
|
10
|
-
#
|
11
|
-
# The methods available are the events that the Server fires.
|
12
|
-
#
|
13
|
-
class Events
|
14
|
-
class DefaultFormatter
|
15
|
-
def call(str)
|
16
|
-
str
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class PidFormatter
|
21
|
-
def call(str)
|
22
|
-
"[#{$$}] #{str}"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Create an Events object that prints to +stdout+ and +stderr+.
|
27
|
-
#
|
28
|
-
def initialize(stdout, stderr)
|
29
|
-
@formatter = DefaultFormatter.new
|
30
|
-
@stdout = stdout
|
31
|
-
@stderr = stderr
|
32
4
|
|
33
|
-
|
34
|
-
|
5
|
+
# This is an event sink used by `Puma::Server` to handle
|
6
|
+
# lifecycle events such as :on_booted, :on_restart, and :on_stopped.
|
7
|
+
# Using `Puma::DSL` it is possible to register callback hooks
|
8
|
+
# for each event type.
|
9
|
+
class Events
|
35
10
|
|
11
|
+
def initialize
|
36
12
|
@hooks = Hash.new { |h,k| h[k] = [] }
|
37
13
|
end
|
38
14
|
|
39
|
-
attr_reader :stdout, :stderr
|
40
|
-
attr_accessor :formatter
|
41
|
-
|
42
15
|
# Fire callbacks for the named hook
|
43
|
-
#
|
44
16
|
def fire(hook, *args)
|
45
17
|
@hooks[hook].each { |t| t.call(*args) }
|
46
18
|
end
|
47
19
|
|
48
20
|
# Register a callback for a given hook
|
49
|
-
#
|
50
21
|
def register(hook, obj=nil, &blk)
|
51
22
|
if obj and blk
|
52
23
|
raise "Specify either an object or a block, not both"
|
@@ -59,79 +30,6 @@ module Puma
|
|
59
30
|
h
|
60
31
|
end
|
61
32
|
|
62
|
-
# Write +str+ to +@stdout+
|
63
|
-
#
|
64
|
-
def log(str)
|
65
|
-
@stdout.puts format(str) if @stdout.respond_to? :puts
|
66
|
-
|
67
|
-
@stdout.flush unless @stdout.sync
|
68
|
-
rescue Errno::EPIPE
|
69
|
-
end
|
70
|
-
|
71
|
-
def write(str)
|
72
|
-
@stdout.write format(str)
|
73
|
-
end
|
74
|
-
|
75
|
-
def debug(str)
|
76
|
-
log("% #{str}") if @debug
|
77
|
-
end
|
78
|
-
|
79
|
-
# Write +str+ to +@stderr+
|
80
|
-
#
|
81
|
-
def error(str)
|
82
|
-
@error_logger.info(text: format("ERROR: #{str}"))
|
83
|
-
exit 1
|
84
|
-
end
|
85
|
-
|
86
|
-
def format(str)
|
87
|
-
formatter.call(str)
|
88
|
-
end
|
89
|
-
|
90
|
-
# An HTTP connection error has occurred.
|
91
|
-
# +error+ a connection exception, +req+ the request,
|
92
|
-
# and +text+ additional info
|
93
|
-
# @version 5.0.0
|
94
|
-
#
|
95
|
-
def connection_error(error, req, text="HTTP connection error")
|
96
|
-
@error_logger.info(error: error, req: req, text: text)
|
97
|
-
end
|
98
|
-
|
99
|
-
# An HTTP parse error has occurred.
|
100
|
-
# +error+ a parsing exception,
|
101
|
-
# and +req+ the request.
|
102
|
-
#
|
103
|
-
def parse_error(error, req)
|
104
|
-
@error_logger.info(error: error, req: req, text: 'HTTP parse error, malformed request')
|
105
|
-
end
|
106
|
-
|
107
|
-
# An SSL error has occurred.
|
108
|
-
# @param error <Puma::MiniSSL::SSLError>
|
109
|
-
# @param ssl_socket <Puma::MiniSSL::Socket>
|
110
|
-
#
|
111
|
-
def ssl_error(error, ssl_socket)
|
112
|
-
peeraddr = ssl_socket.peeraddr.last rescue "<unknown>"
|
113
|
-
peercert = ssl_socket.peercert
|
114
|
-
subject = peercert ? peercert.subject : nil
|
115
|
-
@error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
|
116
|
-
end
|
117
|
-
|
118
|
-
# An unknown error has occurred.
|
119
|
-
# +error+ an exception object, +req+ the request,
|
120
|
-
# and +text+ additional info
|
121
|
-
#
|
122
|
-
def unknown_error(error, req=nil, text="Unknown error")
|
123
|
-
@error_logger.info(error: error, req: req, text: text)
|
124
|
-
end
|
125
|
-
|
126
|
-
# Log occurred error debug dump.
|
127
|
-
# +error+ an exception object, +req+ the request,
|
128
|
-
# and +text+ additional info
|
129
|
-
# @version 5.0.0
|
130
|
-
#
|
131
|
-
def debug_error(error, req=nil, text="")
|
132
|
-
@error_logger.debug(error: error, req: req, text: text)
|
133
|
-
end
|
134
|
-
|
135
33
|
def on_booted(&block)
|
136
34
|
register(:on_booted, &block)
|
137
35
|
end
|
@@ -155,23 +53,5 @@ module Puma
|
|
155
53
|
def fire_on_stopped!
|
156
54
|
fire(:on_stopped)
|
157
55
|
end
|
158
|
-
|
159
|
-
DEFAULT = new(STDOUT, STDERR)
|
160
|
-
|
161
|
-
# Returns an Events object which writes its status to 2 StringIO
|
162
|
-
# objects.
|
163
|
-
#
|
164
|
-
def self.strings
|
165
|
-
Events.new StringIO.new, StringIO.new
|
166
|
-
end
|
167
|
-
|
168
|
-
def self.stdio
|
169
|
-
Events.new $stdout, $stderr
|
170
|
-
end
|
171
|
-
|
172
|
-
def self.null
|
173
|
-
n = NullIO.new
|
174
|
-
Events.new n, n
|
175
|
-
end
|
176
56
|
end
|
177
57
|
end
|
data/lib/puma/io_buffer.rb
CHANGED
@@ -1,11 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'stringio'
|
4
|
+
|
3
5
|
module Puma
|
4
|
-
class IOBuffer <
|
5
|
-
def
|
6
|
-
|
6
|
+
class IOBuffer < StringIO
|
7
|
+
def initialize
|
8
|
+
super.binmode
|
9
|
+
end
|
10
|
+
|
11
|
+
def empty?
|
12
|
+
length.zero?
|
7
13
|
end
|
8
14
|
|
9
|
-
|
15
|
+
def reset
|
16
|
+
truncate 0
|
17
|
+
rewind
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
rewind
|
22
|
+
read
|
23
|
+
end
|
24
|
+
|
25
|
+
# Read & Reset - returns contents and resets
|
26
|
+
# @return [String] StringIO contents
|
27
|
+
def read_and_reset
|
28
|
+
rewind
|
29
|
+
str = read
|
30
|
+
truncate 0
|
31
|
+
rewind
|
32
|
+
str
|
33
|
+
end
|
34
|
+
|
35
|
+
alias_method :clear, :reset
|
36
|
+
|
37
|
+
# before Ruby 2.5, `write` would only take one argument
|
38
|
+
if RUBY_VERSION >= '2.5' && RUBY_ENGINE != 'truffleruby'
|
39
|
+
alias_method :append, :write
|
40
|
+
else
|
41
|
+
def append(*strs)
|
42
|
+
strs.each { |str| write str }
|
43
|
+
end
|
44
|
+
end
|
10
45
|
end
|
11
46
|
end
|
data/lib/puma/jruby_restart.rb
CHANGED