puma 6.0.2 → 6.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|