puma 6.0.2 → 6.4.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 +213 -7
- data/LICENSE +0 -0
- data/README.md +59 -13
- data/bin/puma-wild +0 -0
- data/docs/architecture.md +0 -0
- data/docs/compile_options.md +0 -0
- data/docs/deployment.md +0 -0
- data/docs/fork_worker.md +0 -0
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +0 -0
- data/docs/jungle/rc.d/README.md +0 -0
- data/docs/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +12 -0
- data/docs/nginx.md +0 -0
- data/docs/plugins.md +0 -0
- data/docs/rails_dev_mode.md +0 -0
- data/docs/restart.md +1 -0
- data/docs/signals.md +0 -0
- data/docs/stats.md +0 -0
- data/docs/systemd.md +3 -6
- data/docs/testing_benchmarks_local_files.md +0 -0
- data/docs/testing_test_rackup_ci_files.md +0 -0
- data/ext/puma_http11/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +5 -1
- data/ext/puma_http11/http11_parser.c +0 -0
- data/ext/puma_http11/http11_parser.h +0 -0
- data/ext/puma_http11/http11_parser.java.rl +0 -0
- data/ext/puma_http11/http11_parser.rl +0 -0
- data/ext/puma_http11/http11_parser_common.rl +0 -0
- data/ext/puma_http11/mini_ssl.c +96 -9
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +0 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +2 -1
- data/ext/puma_http11/puma_http11.c +0 -0
- data/lib/puma/app/status.rb +1 -1
- data/lib/puma/binder.rb +14 -11
- data/lib/puma/cli.rb +5 -1
- data/lib/puma/client.rb +77 -16
- data/lib/puma/cluster/worker.rb +5 -0
- data/lib/puma/cluster/worker_handle.rb +0 -0
- data/lib/puma/cluster.rb +71 -10
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +6 -4
- data/lib/puma/const.rb +58 -9
- data/lib/puma/control_cli.rb +12 -5
- data/lib/puma/detect.rb +5 -4
- data/lib/puma/dsl.rb +157 -7
- data/lib/puma/error_logger.rb +2 -1
- data/lib/puma/events.rb +0 -0
- data/lib/puma/io_buffer.rb +0 -0
- data/lib/puma/jruby_restart.rb +0 -0
- data/lib/puma/json_serialization.rb +0 -0
- data/lib/puma/launcher/bundle_pruner.rb +0 -0
- data/lib/puma/launcher.rb +9 -22
- data/lib/puma/log_writer.rb +14 -4
- data/lib/puma/minissl/context_builder.rb +3 -0
- data/lib/puma/minissl.rb +22 -0
- data/lib/puma/null_io.rb +16 -2
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +0 -0
- data/lib/puma/plugin.rb +0 -0
- data/lib/puma/rack/builder.rb +2 -2
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/rack_default.rb +18 -3
- data/lib/puma/reactor.rb +16 -7
- data/lib/puma/request.rb +91 -64
- data/lib/puma/runner.rb +13 -2
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +91 -27
- data/lib/puma/single.rb +2 -0
- data/lib/puma/state_file.rb +2 -2
- data/lib/puma/thread_pool.rb +41 -3
- data/lib/puma/util.rb +0 -0
- data/lib/puma.rb +0 -0
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +2 -2
- data/tools/trickletest.rb +0 -0
- metadata +5 -4
- data/lib/puma/systemd.rb +0 -47
data/lib/puma/dsl.rb
CHANGED
@@ -65,6 +65,7 @@ module Puma
|
|
65
65
|
|
66
66
|
ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)
|
67
67
|
|
68
|
+
low_latency_str = opts.key?(:low_latency) ? "&low_latency=#{opts[:low_latency]}" : ''
|
68
69
|
backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
|
69
70
|
|
70
71
|
if defined?(JRUBY_VERSION)
|
@@ -88,6 +89,7 @@ module Puma
|
|
88
89
|
|
89
90
|
cert_flags = (cert = opts[:cert]) ? "cert=#{Puma::Util.escape(cert)}" : nil
|
90
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
|
91
93
|
|
92
94
|
reuse_flag =
|
93
95
|
if (reuse = opts[:reuse])
|
@@ -113,8 +115,8 @@ module Puma
|
|
113
115
|
nil
|
114
116
|
end
|
115
117
|
|
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
|
+
"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}"
|
118
120
|
end
|
119
121
|
end
|
120
122
|
|
@@ -313,16 +315,22 @@ module Puma
|
|
313
315
|
bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s
|
314
316
|
end
|
315
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
|
+
|
316
324
|
# Define how long persistent connections can be idle before Puma closes them.
|
317
325
|
# @see Puma::Server.new
|
318
326
|
def persistent_timeout(seconds)
|
319
327
|
@options[:persistent_timeout] = Integer(seconds)
|
320
328
|
end
|
321
329
|
|
322
|
-
#
|
330
|
+
# If a new request is not received within this number of seconds, begin shutting down.
|
323
331
|
# @see Puma::Server.new
|
324
|
-
def
|
325
|
-
@options[:
|
332
|
+
def idle_timeout(seconds)
|
333
|
+
@options[:idle_timeout] = Integer(seconds)
|
326
334
|
end
|
327
335
|
|
328
336
|
# Work around leaky apps that leave garbage in Thread locals
|
@@ -418,6 +426,11 @@ module Puma
|
|
418
426
|
@options[:log_requests] = which
|
419
427
|
end
|
420
428
|
|
429
|
+
# Pass in a custom logging class instance
|
430
|
+
def custom_logger(custom_logger)
|
431
|
+
@options[:custom_logger] = custom_logger
|
432
|
+
end
|
433
|
+
|
421
434
|
# Show debugging info
|
422
435
|
#
|
423
436
|
def debug
|
@@ -503,6 +516,12 @@ module Puma
|
|
503
516
|
# `true`, which sets reuse 'on' with default values, or a hash, with `:size`
|
504
517
|
# and/or `:timeout` keys, each with integer values.
|
505
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
|
+
#
|
506
525
|
# @example
|
507
526
|
# ssl_bind '127.0.0.1', '9292', {
|
508
527
|
# cert: path_to_cert,
|
@@ -584,6 +603,11 @@ module Puma
|
|
584
603
|
@options[:silence_single_worker_warning] = true
|
585
604
|
end
|
586
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
|
+
|
587
611
|
# Code to run immediately before master process
|
588
612
|
# forks workers (once on boot). These hooks can block if necessary
|
589
613
|
# to wait for background operations unknown to Puma to finish before
|
@@ -599,6 +623,8 @@ module Puma
|
|
599
623
|
# puts "Starting workers..."
|
600
624
|
# end
|
601
625
|
def before_fork(&block)
|
626
|
+
warn_if_in_single_mode('before_fork')
|
627
|
+
|
602
628
|
@options[:before_fork] ||= []
|
603
629
|
@options[:before_fork] << block
|
604
630
|
end
|
@@ -614,6 +640,8 @@ module Puma
|
|
614
640
|
# puts 'Before worker boot...'
|
615
641
|
# end
|
616
642
|
def on_worker_boot(key = nil, &block)
|
643
|
+
warn_if_in_single_mode('on_worker_boot')
|
644
|
+
|
617
645
|
process_hook :before_worker_boot, key, block, 'on_worker_boot'
|
618
646
|
end
|
619
647
|
|
@@ -630,6 +658,8 @@ module Puma
|
|
630
658
|
# puts 'On worker shutdown...'
|
631
659
|
# end
|
632
660
|
def on_worker_shutdown(key = nil, &block)
|
661
|
+
warn_if_in_single_mode('on_worker_shutdown')
|
662
|
+
|
633
663
|
process_hook :before_worker_shutdown, key, block, 'on_worker_shutdown'
|
634
664
|
end
|
635
665
|
|
@@ -644,6 +674,8 @@ module Puma
|
|
644
674
|
# puts 'Before worker fork...'
|
645
675
|
# end
|
646
676
|
def on_worker_fork(&block)
|
677
|
+
warn_if_in_single_mode('on_worker_fork')
|
678
|
+
|
647
679
|
process_hook :before_worker_fork, nil, block, 'on_worker_fork'
|
648
680
|
end
|
649
681
|
|
@@ -658,11 +690,23 @@ module Puma
|
|
658
690
|
# puts 'After worker fork...'
|
659
691
|
# end
|
660
692
|
def after_worker_fork(&block)
|
693
|
+
warn_if_in_single_mode('after_worker_fork')
|
694
|
+
|
661
695
|
process_hook :after_worker_fork, nil, block, 'after_worker_fork'
|
662
696
|
end
|
663
697
|
|
664
698
|
alias_method :after_worker_boot, :after_worker_fork
|
665
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
|
+
|
666
710
|
# When `fork_worker` is enabled, code to run in Worker 0
|
667
711
|
# before all other workers are re-forked from this process,
|
668
712
|
# after the server has temporarily stopped serving requests
|
@@ -685,6 +729,51 @@ module Puma
|
|
685
729
|
process_hook :before_refork, key, block, 'on_refork'
|
686
730
|
end
|
687
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
|
775
|
+
end
|
776
|
+
|
688
777
|
# Code to run out-of-band when the worker is idle.
|
689
778
|
# These hooks run immediately after a request has finished
|
690
779
|
# processing and there are no busy threads on the worker.
|
@@ -816,7 +905,8 @@ module Puma
|
|
816
905
|
# not a request timeout, it is to protect against a hung or dead process.
|
817
906
|
# Setting this value will not protect against slow requests.
|
818
907
|
#
|
819
|
-
#
|
908
|
+
# This value must be greater than worker_check_interval.
|
909
|
+
# The default value is 60 seconds.
|
820
910
|
#
|
821
911
|
# @note Cluster mode only.
|
822
912
|
# @example
|
@@ -1022,6 +1112,51 @@ module Puma
|
|
1022
1112
|
@options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
|
1023
1113
|
end
|
1024
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
|
+
|
1025
1160
|
private
|
1026
1161
|
|
1027
1162
|
# To avoid adding cert_pem and key_pem as URI params, we store them on the
|
@@ -1049,7 +1184,22 @@ module Puma
|
|
1049
1184
|
elsif key.nil?
|
1050
1185
|
@options[options_key] << block
|
1051
1186
|
else
|
1052
|
-
raise "'#{
|
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)
|
1053
1203
|
end
|
1054
1204
|
end
|
1055
1205
|
end
|
data/lib/puma/error_logger.rb
CHANGED
@@ -102,7 +102,8 @@ module Puma
|
|
102
102
|
@ioerr.is_a?(IO) and @ioerr.wait_writable(1)
|
103
103
|
@ioerr.write "#{w_str}\n"
|
104
104
|
@ioerr.flush unless @ioerr.sync
|
105
|
-
rescue Errno::EPIPE, Errno::EBADF, IOError
|
105
|
+
rescue Errno::EPIPE, Errno::EBADF, IOError, Errno::EINVAL
|
106
|
+
# 'Invalid argument' (Errno::EINVAL) may be raised by flush
|
106
107
|
end
|
107
108
|
end
|
108
109
|
rescue ThreadError
|
data/lib/puma/events.rb
CHANGED
File without changes
|
data/lib/puma/io_buffer.rb
CHANGED
File without changes
|
data/lib/puma/jruby_restart.rb
CHANGED
File without changes
|
File without changes
|
File without changes
|
data/lib/puma/launcher.rb
CHANGED
@@ -59,6 +59,13 @@ module Puma
|
|
59
59
|
|
60
60
|
@environment = conf.environment
|
61
61
|
|
62
|
+
# Load the systemd integration if we detect systemd's NOTIFY_SOCKET.
|
63
|
+
# Skip this on JRuby though, because it is incompatible with the systemd
|
64
|
+
# integration due to https://github.com/jruby/jruby/issues/6504
|
65
|
+
if ENV["NOTIFY_SOCKET"] && !Puma.jruby?
|
66
|
+
@config.plugins.create('systemd')
|
67
|
+
end
|
68
|
+
|
62
69
|
if @config.options[:bind_to_activated_sockets]
|
63
70
|
@config.options[:binds] = @binder.synthesize_binds_from_activated_fs(
|
64
71
|
@config.options[:binds],
|
@@ -72,6 +79,8 @@ module Puma
|
|
72
79
|
@log_writer.formatter = LogWriter::PidFormatter.new if clustered?
|
73
80
|
@log_writer.formatter = options[:log_formatter] if @options[:log_formatter]
|
74
81
|
|
82
|
+
@log_writer.custom_logger = options[:custom_logger] if @options[:custom_logger]
|
83
|
+
|
75
84
|
generate_restart_data
|
76
85
|
|
77
86
|
if clustered? && !Puma.forkable?
|
@@ -180,7 +189,6 @@ module Puma
|
|
180
189
|
|
181
190
|
setup_signals
|
182
191
|
set_process_title
|
183
|
-
integrate_with_systemd
|
184
192
|
|
185
193
|
# This blocks until the server is stopped
|
186
194
|
@runner.run
|
@@ -311,27 +319,6 @@ module Puma
|
|
311
319
|
@runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
|
312
320
|
end
|
313
321
|
|
314
|
-
# Puma's systemd integration allows Puma to inform systemd:
|
315
|
-
# 1. when it has successfully started
|
316
|
-
# 2. when it is starting shutdown
|
317
|
-
# 3. periodically for a liveness check with a watchdog thread
|
318
|
-
def integrate_with_systemd
|
319
|
-
return unless ENV["NOTIFY_SOCKET"]
|
320
|
-
|
321
|
-
begin
|
322
|
-
require_relative 'systemd'
|
323
|
-
rescue LoadError
|
324
|
-
log "Systemd integration failed. It looks like you're trying to use systemd notify but don't have sd_notify gem installed"
|
325
|
-
return
|
326
|
-
end
|
327
|
-
|
328
|
-
log "* Enabling systemd notification integration"
|
329
|
-
|
330
|
-
systemd = Systemd.new(@log_writer, @events)
|
331
|
-
systemd.hook_events
|
332
|
-
systemd.start_watchdog
|
333
|
-
end
|
334
|
-
|
335
322
|
def log(str)
|
336
323
|
@log_writer.log(str)
|
337
324
|
end
|
data/lib/puma/log_writer.rb
CHANGED
@@ -28,11 +28,12 @@ module Puma
|
|
28
28
|
attr_reader :stdout,
|
29
29
|
:stderr
|
30
30
|
|
31
|
-
attr_accessor :formatter
|
31
|
+
attr_accessor :formatter, :custom_logger
|
32
32
|
|
33
33
|
# Create a LogWriter that prints to +stdout+ and +stderr+.
|
34
34
|
def initialize(stdout, stderr)
|
35
35
|
@formatter = DefaultFormatter.new
|
36
|
+
@custom_logger = nil
|
36
37
|
@stdout = stdout
|
37
38
|
@stderr = stderr
|
38
39
|
|
@@ -59,7 +60,11 @@ module Puma
|
|
59
60
|
|
60
61
|
# Write +str+ to +@stdout+
|
61
62
|
def log(str)
|
62
|
-
|
63
|
+
if @custom_logger&.respond_to?(:write)
|
64
|
+
@custom_logger.write(format(str))
|
65
|
+
else
|
66
|
+
internal_write "#{@formatter.call str}\n"
|
67
|
+
end
|
63
68
|
end
|
64
69
|
|
65
70
|
def write(str)
|
@@ -73,13 +78,18 @@ module Puma
|
|
73
78
|
@stdout.is_a?(IO) and @stdout.wait_writable(1)
|
74
79
|
@stdout.write w_str
|
75
80
|
@stdout.flush unless @stdout.sync
|
76
|
-
rescue Errno::EPIPE, Errno::EBADF, IOError
|
81
|
+
rescue Errno::EPIPE, Errno::EBADF, IOError, Errno::EINVAL
|
82
|
+
# 'Invalid argument' (Errno::EINVAL) may be raised by flush
|
77
83
|
end
|
78
84
|
end
|
79
85
|
rescue ThreadError
|
80
86
|
end
|
81
87
|
private :internal_write
|
82
88
|
|
89
|
+
def debug?
|
90
|
+
@debug
|
91
|
+
end
|
92
|
+
|
83
93
|
def debug(str)
|
84
94
|
log("% #{str}") if @debug
|
85
95
|
end
|
@@ -115,7 +125,7 @@ module Puma
|
|
115
125
|
def ssl_error(error, ssl_socket)
|
116
126
|
peeraddr = ssl_socket.peeraddr.last rescue "<unknown>"
|
117
127
|
peercert = ssl_socket.peercert
|
118
|
-
subject = peercert
|
128
|
+
subject = peercert&.subject
|
119
129
|
@error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
|
120
130
|
end
|
121
131
|
|
@@ -38,6 +38,7 @@ module Puma
|
|
38
38
|
|
39
39
|
ctx.key = params['key'] if params['key']
|
40
40
|
ctx.key_pem = params['key_pem'] if params['key_pem']
|
41
|
+
ctx.key_password_command = params['key_password_command'] if params['key_password_command']
|
41
42
|
|
42
43
|
if params['cert'].nil? && params['cert_pem'].nil?
|
43
44
|
log_writer.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
|
@@ -50,6 +51,8 @@ module Puma
|
|
50
51
|
unless params['ca']
|
51
52
|
log_writer.error "Please specify the SSL ca via 'ca='"
|
52
53
|
end
|
54
|
+
# needed for Puma::MiniSSL::Socket#peercert, env['puma.peercert']
|
55
|
+
require 'openssl'
|
53
56
|
end
|
54
57
|
|
55
58
|
ctx.ca = params['ca'] if params['ca']
|
data/lib/puma/minissl.rb
CHANGED
@@ -5,6 +5,7 @@ begin
|
|
5
5
|
rescue LoadError
|
6
6
|
end
|
7
7
|
|
8
|
+
require 'open3'
|
8
9
|
# need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3`
|
9
10
|
# use require, see https://github.com/puma/puma/pull/2381
|
10
11
|
require 'puma/puma_http11'
|
@@ -183,6 +184,11 @@ module Puma
|
|
183
184
|
@socket.peeraddr
|
184
185
|
end
|
185
186
|
|
187
|
+
# OpenSSL is loaded in `MiniSSL::ContextBuilder` when
|
188
|
+
# `MiniSSL::Context#verify_mode` is not `VERIFY_NONE`.
|
189
|
+
# When `VERIFY_NONE`, `MiniSSL::Engine#peercert` is nil, regardless of
|
190
|
+
# whether the client sends a cert.
|
191
|
+
# @return [OpenSSL::X509::Certificate, nil]
|
186
192
|
# @!attribute [r] peercert
|
187
193
|
def peercert
|
188
194
|
return @peercert if @peercert
|
@@ -277,6 +283,7 @@ module Puma
|
|
277
283
|
else
|
278
284
|
# non-jruby Context properties
|
279
285
|
attr_reader :key
|
286
|
+
attr_reader :key_password_command
|
280
287
|
attr_reader :cert
|
281
288
|
attr_reader :ca
|
282
289
|
attr_reader :cert_pem
|
@@ -291,6 +298,10 @@ module Puma
|
|
291
298
|
@key = key
|
292
299
|
end
|
293
300
|
|
301
|
+
def key_password_command=(key_password_command)
|
302
|
+
@key_password_command = key_password_command
|
303
|
+
end
|
304
|
+
|
294
305
|
def cert=(cert)
|
295
306
|
check_file cert, 'Cert'
|
296
307
|
@cert = cert
|
@@ -316,6 +327,17 @@ module Puma
|
|
316
327
|
raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
|
317
328
|
end
|
318
329
|
|
330
|
+
# Executes the command to return the password needed to decrypt the key.
|
331
|
+
def key_password
|
332
|
+
raise "Key password command not configured" if @key_password_command.nil?
|
333
|
+
|
334
|
+
stdout_str, stderr_str, status = Open3.capture3(@key_password_command)
|
335
|
+
|
336
|
+
return stdout_str.chomp if status.success?
|
337
|
+
|
338
|
+
raise "Key password failed with code #{status.exitstatus}: #{stderr_str}"
|
339
|
+
end
|
340
|
+
|
319
341
|
# Controls session reuse. Allowed values are as follows:
|
320
342
|
# * 'off' - matches the behavior of Puma 5.6 and earlier. This is included
|
321
343
|
# in case reuse 'on' is made the default in future Puma versions.
|
data/lib/puma/null_io.rb
CHANGED
@@ -18,8 +18,22 @@ module Puma
|
|
18
18
|
|
19
19
|
# Mimics IO#read with no data.
|
20
20
|
#
|
21
|
-
def read(
|
22
|
-
|
21
|
+
def read(length = nil, buffer = nil)
|
22
|
+
if length.to_i < 0
|
23
|
+
raise ArgumentError, "(negative length #{length} given)"
|
24
|
+
end
|
25
|
+
|
26
|
+
buffer = if buffer.nil?
|
27
|
+
"".b
|
28
|
+
else
|
29
|
+
String.try_convert(buffer) or raise TypeError, "no implicit conversion of #{buffer.class} into String"
|
30
|
+
end
|
31
|
+
buffer.clear
|
32
|
+
if length.to_i > 0
|
33
|
+
nil
|
34
|
+
else
|
35
|
+
buffer
|
36
|
+
end
|
23
37
|
end
|
24
38
|
|
25
39
|
def rewind
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../plugin'
|
4
|
+
|
5
|
+
# Puma's systemd integration allows Puma to inform systemd:
|
6
|
+
# 1. when it has successfully started
|
7
|
+
# 2. when it is starting shutdown
|
8
|
+
# 3. periodically for a liveness check with a watchdog thread
|
9
|
+
# 4. periodically set the status
|
10
|
+
Puma::Plugin.create do
|
11
|
+
def start(launcher)
|
12
|
+
require_relative '../sd_notify'
|
13
|
+
|
14
|
+
launcher.log_writer.log "* Enabling systemd notification integration"
|
15
|
+
|
16
|
+
# hook_events
|
17
|
+
launcher.events.on_booted { Puma::SdNotify.ready }
|
18
|
+
launcher.events.on_stopped { Puma::SdNotify.stopping }
|
19
|
+
launcher.events.on_restart { Puma::SdNotify.reloading }
|
20
|
+
|
21
|
+
# start watchdog
|
22
|
+
if Puma::SdNotify.watchdog?
|
23
|
+
ping_f = watchdog_sleep_time
|
24
|
+
|
25
|
+
in_background do
|
26
|
+
launcher.log_writer.log "Pinging systemd watchdog every #{ping_f.round(1)} sec"
|
27
|
+
loop do
|
28
|
+
sleep ping_f
|
29
|
+
Puma::SdNotify.watchdog
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# start status loop
|
35
|
+
instance = self
|
36
|
+
sleep_time = 1.0
|
37
|
+
in_background do
|
38
|
+
launcher.log_writer.log "Sending status to systemd every #{sleep_time.round(1)} sec"
|
39
|
+
|
40
|
+
loop do
|
41
|
+
sleep sleep_time
|
42
|
+
# TODO: error handling?
|
43
|
+
Puma::SdNotify.status(instance.status)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def status
|
49
|
+
if clustered?
|
50
|
+
messages = stats[:worker_status].map do |worker|
|
51
|
+
common_message(worker[:last_status])
|
52
|
+
end.join(',')
|
53
|
+
|
54
|
+
"Puma #{Puma::Const::VERSION}: cluster: #{booted_workers}/#{workers}, worker_status: [#{messages}]"
|
55
|
+
else
|
56
|
+
"Puma #{Puma::Const::VERSION}: worker: #{common_message(stats)}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def watchdog_sleep_time
|
63
|
+
usec = Integer(ENV["WATCHDOG_USEC"])
|
64
|
+
|
65
|
+
sec_f = usec / 1_000_000.0
|
66
|
+
# "It is recommended that a daemon sends a keep-alive notification message
|
67
|
+
# to the service manager every half of the time returned here."
|
68
|
+
sec_f / 2
|
69
|
+
end
|
70
|
+
|
71
|
+
def stats
|
72
|
+
Puma.stats_hash
|
73
|
+
end
|
74
|
+
|
75
|
+
def clustered?
|
76
|
+
stats.has_key?(:workers)
|
77
|
+
end
|
78
|
+
|
79
|
+
def workers
|
80
|
+
stats.fetch(:workers, 1)
|
81
|
+
end
|
82
|
+
|
83
|
+
def booted_workers
|
84
|
+
stats.fetch(:booted_workers, 1)
|
85
|
+
end
|
86
|
+
|
87
|
+
def common_message(stats)
|
88
|
+
"{ #{stats[:running]}/#{stats[:max_threads]} threads, #{stats[:pool_capacity]} available, #{stats[:backlog]} backlog }"
|
89
|
+
end
|
90
|
+
end
|
File without changes
|
data/lib/puma/plugin.rb
CHANGED
File without changes
|
data/lib/puma/rack/builder.rb
CHANGED
@@ -173,7 +173,7 @@ module Puma::Rack
|
|
173
173
|
TOPLEVEL_BINDING, file, 0
|
174
174
|
end
|
175
175
|
|
176
|
-
def initialize(default_app = nil
|
176
|
+
def initialize(default_app = nil, &block)
|
177
177
|
@use, @map, @run, @warmup = [], nil, default_app, nil
|
178
178
|
|
179
179
|
# Conditionally load rack now, so that any rack middlewares,
|
@@ -183,7 +183,7 @@ module Puma::Rack
|
|
183
183
|
rescue LoadError
|
184
184
|
end
|
185
185
|
|
186
|
-
instance_eval(&block) if
|
186
|
+
instance_eval(&block) if block
|
187
187
|
end
|
188
188
|
|
189
189
|
def self.app(default_app = nil, &block)
|
data/lib/puma/rack/urlmap.rb
CHANGED
@@ -34,7 +34,7 @@ module Puma::Rack
|
|
34
34
|
end
|
35
35
|
|
36
36
|
location = location.chomp('/')
|
37
|
-
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)",
|
37
|
+
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", Regexp::NOENCODING)
|
38
38
|
|
39
39
|
[host, location, match, app]
|
40
40
|
}.sort_by do |(host, location, _, _)|
|
data/lib/puma/rack_default.rb
CHANGED
@@ -2,8 +2,23 @@
|
|
2
2
|
|
3
3
|
require_relative '../rack/handler/puma'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# rackup was removed in Rack 3, it is now a separate gem
|
6
|
+
if Object.const_defined? :Rackup
|
7
|
+
module Rackup
|
8
|
+
module Handler
|
9
|
+
def self.default(options = {})
|
10
|
+
::Rackup::Handler::Puma
|
11
|
+
end
|
12
|
+
end
|
8
13
|
end
|
14
|
+
elsif Object.const_defined?(:Rack) && Rack.release < '3'
|
15
|
+
module Rack
|
16
|
+
module Handler
|
17
|
+
def self.default(options = {})
|
18
|
+
::Rack::Handler::Puma
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
raise "Rack 3 must be used with the Rackup gem"
|
9
24
|
end
|