puma 5.6.4 → 6.3.0
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 +270 -4
- data/LICENSE +0 -0
- data/README.md +60 -20
- data/bin/puma-wild +1 -1
- data/docs/architecture.md +0 -0
- data/docs/compile_options.md +34 -0
- data/docs/deployment.md +0 -0
- data/docs/fork_worker.md +1 -3
- 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 +0 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +0 -0
- data/docs/rails_dev_mode.md +0 -0
- data/docs/restart.md +0 -0
- data/docs/signals.md +0 -0
- data/docs/stats.md +0 -0
- data/docs/systemd.md +1 -2
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/ext_help.h +0 -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 +93 -26
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
- 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 +7 -4
- data/lib/puma/binder.rb +49 -52
- data/lib/puma/cli.rb +12 -18
- data/lib/puma/client.rb +55 -16
- data/lib/puma/cluster/worker.rb +18 -11
- data/lib/puma/cluster/worker_handle.rb +4 -1
- data/lib/puma/cluster.rb +33 -30
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +76 -58
- data/lib/puma/const.rb +129 -92
- data/lib/puma/control_cli.rb +21 -18
- data/lib/puma/detect.rb +4 -0
- data/lib/puma/dsl.rb +187 -49
- 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/json_serialization.rb +0 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +113 -175
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +24 -12
- data/lib/puma/minissl.rb +108 -15
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +0 -0
- data/lib/puma/rack/builder.rb +6 -6
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +19 -10
- data/lib/puma/request.rb +365 -166
- data/lib/puma/runner.rb +52 -20
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +73 -73
- data/lib/puma/single.rb +13 -11
- data/lib/puma/state_file.rb +2 -4
- data/lib/puma/thread_pool.rb +23 -19
- data/lib/puma/util.rb +12 -14
- data/lib/puma.rb +12 -11
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +0 -0
- data/tools/trickletest.rb +0 -0
- metadata +10 -5
- data/lib/puma/queue_close.rb +0 -26
- data/lib/puma/systemd.rb +0 -46
@@ -1,9 +1,9 @@
|
|
1
1
|
module Puma
|
2
2
|
module MiniSSL
|
3
3
|
class ContextBuilder
|
4
|
-
def initialize(params,
|
4
|
+
def initialize(params, log_writer)
|
5
5
|
@params = params
|
6
|
-
@
|
6
|
+
@log_writer = log_writer
|
7
7
|
end
|
8
8
|
|
9
9
|
def context
|
@@ -11,27 +11,37 @@ module Puma
|
|
11
11
|
|
12
12
|
if defined?(JRUBY_VERSION)
|
13
13
|
unless params['keystore']
|
14
|
-
|
14
|
+
log_writer.error "Please specify the Java keystore via 'keystore='"
|
15
15
|
end
|
16
16
|
|
17
17
|
ctx.keystore = params['keystore']
|
18
18
|
|
19
19
|
unless params['keystore-pass']
|
20
|
-
|
20
|
+
log_writer.error "Please specify the Java keystore password via 'keystore-pass='"
|
21
21
|
end
|
22
22
|
|
23
23
|
ctx.keystore_pass = params['keystore-pass']
|
24
|
-
ctx.
|
24
|
+
ctx.keystore_type = params['keystore-type']
|
25
|
+
|
26
|
+
if truststore = params['truststore']
|
27
|
+
ctx.truststore = truststore.eql?('default') ? :default : truststore
|
28
|
+
ctx.truststore_pass = params['truststore-pass']
|
29
|
+
ctx.truststore_type = params['truststore-type']
|
30
|
+
end
|
31
|
+
|
32
|
+
ctx.cipher_suites = params['cipher_suites'] || params['ssl_cipher_list']
|
33
|
+
ctx.protocols = params['protocols'] if params['protocols']
|
25
34
|
else
|
26
35
|
if params['key'].nil? && params['key_pem'].nil?
|
27
|
-
|
36
|
+
log_writer.error "Please specify the SSL key via 'key=' or 'key_pem='"
|
28
37
|
end
|
29
38
|
|
30
39
|
ctx.key = params['key'] if params['key']
|
31
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']
|
32
42
|
|
33
43
|
if params['cert'].nil? && params['cert_pem'].nil?
|
34
|
-
|
44
|
+
log_writer.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
|
35
45
|
end
|
36
46
|
|
37
47
|
ctx.cert = params['cert'] if params['cert']
|
@@ -39,16 +49,18 @@ module Puma
|
|
39
49
|
|
40
50
|
if ['peer', 'force_peer'].include?(params['verify_mode'])
|
41
51
|
unless params['ca']
|
42
|
-
|
52
|
+
log_writer.error "Please specify the SSL ca via 'ca='"
|
43
53
|
end
|
44
54
|
end
|
45
55
|
|
46
56
|
ctx.ca = params['ca'] if params['ca']
|
47
57
|
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
58
|
+
|
59
|
+
ctx.reuse = params['reuse'] if params['reuse']
|
48
60
|
end
|
49
61
|
|
50
|
-
ctx.no_tlsv1
|
51
|
-
ctx.no_tlsv1_1 =
|
62
|
+
ctx.no_tlsv1 = params['no_tlsv1'] == 'true'
|
63
|
+
ctx.no_tlsv1_1 = params['no_tlsv1_1'] == 'true'
|
52
64
|
|
53
65
|
if params['verify_mode']
|
54
66
|
ctx.verify_mode = case params['verify_mode']
|
@@ -59,7 +71,7 @@ module Puma
|
|
59
71
|
when "none"
|
60
72
|
MiniSSL::VERIFY_NONE
|
61
73
|
else
|
62
|
-
|
74
|
+
log_writer.error "Please specify a valid verify_mode="
|
63
75
|
MiniSSL::VERIFY_NONE
|
64
76
|
end
|
65
77
|
end
|
@@ -75,7 +87,7 @@ module Puma
|
|
75
87
|
|
76
88
|
private
|
77
89
|
|
78
|
-
attr_reader :params, :
|
90
|
+
attr_reader :params, :log_writer
|
79
91
|
end
|
80
92
|
end
|
81
93
|
end
|
data/lib/puma/minissl.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
begin
|
4
|
-
require 'io/wait'
|
4
|
+
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
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`
|
10
|
+
# use require, see https://github.com/puma/puma/pull/2381
|
9
11
|
require 'puma/puma_http11'
|
10
12
|
|
11
13
|
module Puma
|
@@ -13,15 +15,16 @@ module Puma
|
|
13
15
|
# Define constant at runtime, as it's easy to determine at built time,
|
14
16
|
# but Puma could (it shouldn't) be loaded with an older OpenSSL version
|
15
17
|
# @version 5.0.0
|
16
|
-
HAS_TLS1_3 =
|
17
|
-
|
18
|
-
|
18
|
+
HAS_TLS1_3 = IS_JRUBY ||
|
19
|
+
((OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 &&
|
20
|
+
(OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1)
|
19
21
|
|
20
22
|
class Socket
|
21
23
|
def initialize(socket, engine)
|
22
24
|
@socket = socket
|
23
25
|
@engine = engine
|
24
26
|
@peercert = nil
|
27
|
+
@reuse = nil
|
25
28
|
end
|
26
29
|
|
27
30
|
# @!attribute [r] to_io
|
@@ -50,7 +53,7 @@ module Puma
|
|
50
53
|
# is made with TLSv1.3 as an available protocol
|
51
54
|
# @version 5.0.0
|
52
55
|
def bad_tlsv1_3?
|
53
|
-
HAS_TLS1_3 &&
|
56
|
+
HAS_TLS1_3 && ssl_version_state == ['TLSv1.3', 'SSLERR']
|
54
57
|
end
|
55
58
|
private :bad_tlsv1_3?
|
56
59
|
|
@@ -123,7 +126,7 @@ module Puma
|
|
123
126
|
while true
|
124
127
|
wrote = @engine.write data
|
125
128
|
|
126
|
-
enc_wr = ''
|
129
|
+
enc_wr = +''
|
127
130
|
while (enc = @engine.extract)
|
128
131
|
enc_wr << enc
|
129
132
|
end
|
@@ -195,10 +198,6 @@ module Puma
|
|
195
198
|
if IS_JRUBY
|
196
199
|
OPENSSL_NO_SSL3 = false
|
197
200
|
OPENSSL_NO_TLS1 = false
|
198
|
-
|
199
|
-
class SSLError < StandardError
|
200
|
-
# Define this for jruby even though it isn't used.
|
201
|
-
end
|
202
201
|
end
|
203
202
|
|
204
203
|
class Context
|
@@ -212,26 +211,74 @@ module Puma
|
|
212
211
|
@cert = nil
|
213
212
|
@key_pem = nil
|
214
213
|
@cert_pem = nil
|
214
|
+
@reuse = nil
|
215
|
+
@reuse_cache_size = nil
|
216
|
+
@reuse_timeout = nil
|
217
|
+
end
|
218
|
+
|
219
|
+
def check_file(file, desc)
|
220
|
+
raise ArgumentError, "#{desc} file '#{file}' does not exist" unless File.exist? file
|
221
|
+
raise ArgumentError, "#{desc} file '#{file}' is not readable" unless File.readable? file
|
215
222
|
end
|
216
223
|
|
217
224
|
if IS_JRUBY
|
218
225
|
# jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
|
219
226
|
attr_reader :keystore
|
227
|
+
attr_reader :keystore_type
|
220
228
|
attr_accessor :keystore_pass
|
221
|
-
|
229
|
+
attr_reader :truststore
|
230
|
+
attr_reader :truststore_type
|
231
|
+
attr_accessor :truststore_pass
|
232
|
+
attr_reader :cipher_suites
|
233
|
+
attr_reader :protocols
|
222
234
|
|
223
235
|
def keystore=(keystore)
|
224
|
-
|
236
|
+
check_file keystore, 'Keystore'
|
225
237
|
@keystore = keystore
|
226
238
|
end
|
227
239
|
|
240
|
+
def truststore=(truststore)
|
241
|
+
# NOTE: historically truststore was assumed the same as keystore, this is kept for backwards
|
242
|
+
# compatibility, to rely on JVM's trust defaults we allow setting `truststore = :default`
|
243
|
+
unless truststore.eql?(:default)
|
244
|
+
raise ArgumentError, "No such truststore file '#{truststore}'" unless File.exist?(truststore)
|
245
|
+
end
|
246
|
+
@truststore = truststore
|
247
|
+
end
|
248
|
+
|
249
|
+
def keystore_type=(type)
|
250
|
+
raise ArgumentError, "Invalid keystore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
|
251
|
+
@keystore_type = type
|
252
|
+
end
|
253
|
+
|
254
|
+
def truststore_type=(type)
|
255
|
+
raise ArgumentError, "Invalid truststore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
|
256
|
+
@truststore_type = type
|
257
|
+
end
|
258
|
+
|
259
|
+
def cipher_suites=(list)
|
260
|
+
list = list.split(',').map(&:strip) if list.is_a?(String)
|
261
|
+
@cipher_suites = list
|
262
|
+
end
|
263
|
+
|
264
|
+
# aliases for backwards compatibility
|
265
|
+
alias_method :ssl_cipher_list, :cipher_suites
|
266
|
+
alias_method :ssl_cipher_list=, :cipher_suites=
|
267
|
+
|
268
|
+
def protocols=(list)
|
269
|
+
list = list.split(',').map(&:strip) if list.is_a?(String)
|
270
|
+
@protocols = list
|
271
|
+
end
|
272
|
+
|
228
273
|
def check
|
229
274
|
raise "Keystore not configured" unless @keystore
|
275
|
+
# @truststore defaults to @keystore due backwards compatibility
|
230
276
|
end
|
231
277
|
|
232
278
|
else
|
233
279
|
# non-jruby Context properties
|
234
280
|
attr_reader :key
|
281
|
+
attr_reader :key_password_command
|
235
282
|
attr_reader :cert
|
236
283
|
attr_reader :ca
|
237
284
|
attr_reader :cert_pem
|
@@ -239,18 +286,24 @@ module Puma
|
|
239
286
|
attr_accessor :ssl_cipher_filter
|
240
287
|
attr_accessor :verification_flags
|
241
288
|
|
289
|
+
attr_reader :reuse, :reuse_cache_size, :reuse_timeout
|
290
|
+
|
242
291
|
def key=(key)
|
243
|
-
|
292
|
+
check_file key, 'Key'
|
244
293
|
@key = key
|
245
294
|
end
|
246
295
|
|
296
|
+
def key_password_command=(key_password_command)
|
297
|
+
@key_password_command = key_password_command
|
298
|
+
end
|
299
|
+
|
247
300
|
def cert=(cert)
|
248
|
-
|
301
|
+
check_file cert, 'Cert'
|
249
302
|
@cert = cert
|
250
303
|
end
|
251
304
|
|
252
305
|
def ca=(ca)
|
253
|
-
|
306
|
+
check_file ca, 'ca'
|
254
307
|
@ca = ca
|
255
308
|
end
|
256
309
|
|
@@ -268,6 +321,46 @@ module Puma
|
|
268
321
|
raise "Key not configured" if @key.nil? && @key_pem.nil?
|
269
322
|
raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
|
270
323
|
end
|
324
|
+
|
325
|
+
# Executes the command to return the password needed to decrypt the key.
|
326
|
+
def key_password
|
327
|
+
raise "Key password command not configured" if @key_password_command.nil?
|
328
|
+
|
329
|
+
stdout_str, stderr_str, status = Open3.capture3(@key_password_command)
|
330
|
+
|
331
|
+
return stdout_str.chomp if status.success?
|
332
|
+
|
333
|
+
raise "Key password failed with code #{status.exitstatus}: #{stderr_str}"
|
334
|
+
end
|
335
|
+
|
336
|
+
# Controls session reuse. Allowed values are as follows:
|
337
|
+
# * 'off' - matches the behavior of Puma 5.6 and earlier. This is included
|
338
|
+
# in case reuse 'on' is made the default in future Puma versions.
|
339
|
+
# * 'dflt' - sets session reuse on, with OpenSSL default cache size of
|
340
|
+
# 20k and default timeout of 300 seconds.
|
341
|
+
# * 's,t' - where s and t are integer strings, for size and timeout.
|
342
|
+
# * 's' - where s is an integer strings for size.
|
343
|
+
# * ',t' - where t is an integer strings for timeout.
|
344
|
+
#
|
345
|
+
def reuse=(reuse_str)
|
346
|
+
case reuse_str
|
347
|
+
when 'off'
|
348
|
+
@reuse = nil
|
349
|
+
when 'dflt'
|
350
|
+
@reuse = true
|
351
|
+
when /\A\d+\z/
|
352
|
+
@reuse = true
|
353
|
+
@reuse_cache_size = reuse_str.to_i
|
354
|
+
when /\A\d+,\d+\z/
|
355
|
+
@reuse = true
|
356
|
+
size, time = reuse_str.split ','
|
357
|
+
@reuse_cache_size = size.to_i
|
358
|
+
@reuse_timeout = time.to_i
|
359
|
+
when /\A,\d+\z/
|
360
|
+
@reuse = true
|
361
|
+
@reuse_timeout = reuse_str.delete(',').to_i
|
362
|
+
end
|
363
|
+
end
|
271
364
|
end
|
272
365
|
|
273
366
|
# disables TLSv1
|
data/lib/puma/null_io.rb
CHANGED
@@ -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
|
data/lib/puma/plugin.rb
CHANGED
File without changes
|
data/lib/puma/rack/builder.rb
CHANGED
@@ -102,13 +102,13 @@ module Puma::Rack
|
|
102
102
|
begin
|
103
103
|
info = []
|
104
104
|
server = Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
|
105
|
-
if server
|
105
|
+
if server&.respond_to?(:valid_options)
|
106
106
|
info << ""
|
107
107
|
info << "Server-specific options for #{server.name}:"
|
108
108
|
|
109
109
|
has_options = false
|
110
110
|
server.valid_options.each do |name, description|
|
111
|
-
next if
|
111
|
+
next if /^(Host|Port)[^a-zA-Z]/.match? name.to_s # ignore handler's host and port options, we do our own.
|
112
112
|
|
113
113
|
info << " -O %-21s %s" % [name, description]
|
114
114
|
has_options = true
|
@@ -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)
|
@@ -276,7 +276,7 @@ module Puma::Rack
|
|
276
276
|
app = @map ? generate_map(@run, @map) : @run
|
277
277
|
fail "missing run or map statement" unless app
|
278
278
|
app = @use.reverse.inject(app) { |a,e| e[a] }
|
279
|
-
@warmup
|
279
|
+
@warmup&.call app
|
280
280
|
app
|
281
281
|
end
|
282
282
|
|
@@ -287,7 +287,7 @@ module Puma::Rack
|
|
287
287
|
private
|
288
288
|
|
289
289
|
def generate_map(default_app, mapping)
|
290
|
-
|
290
|
+
require_relative 'urlmap'
|
291
291
|
|
292
292
|
mapped = default_app ? {'/' => default_app} : {}
|
293
293
|
mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b).to_app }
|
data/lib/puma/rack/urlmap.rb
CHANGED
File without changes
|
data/lib/puma/rack_default.rb
CHANGED
@@ -1,9 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
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
|
data/lib/puma/reactor.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'puma/queue_close' unless ::Queue.instance_methods.include? :close
|
4
|
-
|
5
3
|
module Puma
|
6
4
|
class UnsupportedBackend < StandardError; end
|
7
5
|
|
@@ -22,10 +20,12 @@ module Puma
|
|
22
20
|
# its timeout elapses, or when the Reactor shuts down.
|
23
21
|
def initialize(backend, &block)
|
24
22
|
require 'nio'
|
25
|
-
|
26
|
-
|
23
|
+
valid_backends = [:auto, *::NIO::Selector.backends]
|
24
|
+
unless valid_backends.include?(backend)
|
25
|
+
raise ArgumentError.new("unsupported IO selector backend: #{backend} (available backends: #{valid_backends.join(', ')})")
|
27
26
|
end
|
28
|
-
|
27
|
+
|
28
|
+
@selector = ::NIO::Selector.new(NIO::Selector.backends.delete(backend))
|
29
29
|
@input = Queue.new
|
30
30
|
@timeouts = []
|
31
31
|
@block = block
|
@@ -50,7 +50,7 @@ module Puma
|
|
50
50
|
@input << client
|
51
51
|
@selector.wakeup
|
52
52
|
true
|
53
|
-
rescue ClosedQueueError
|
53
|
+
rescue ClosedQueueError, IOError # Ignore if selector is already closed
|
54
54
|
false
|
55
55
|
end
|
56
56
|
|
@@ -61,12 +61,13 @@ module Puma
|
|
61
61
|
@selector.wakeup
|
62
62
|
rescue IOError # Ignore if selector is already closed
|
63
63
|
end
|
64
|
-
@thread
|
64
|
+
@thread&.join
|
65
65
|
end
|
66
66
|
|
67
67
|
private
|
68
68
|
|
69
69
|
def select_loop
|
70
|
+
close_selector = true
|
70
71
|
begin
|
71
72
|
until @input.closed? && @input.empty?
|
72
73
|
# Wakeup any registered object that receives incoming data.
|
@@ -76,7 +77,7 @@ module Puma
|
|
76
77
|
|
77
78
|
# Wakeup all objects that timed out.
|
78
79
|
timed_out = @timeouts.take_while {|t| t.timeout == 0}
|
79
|
-
timed_out.each
|
80
|
+
timed_out.each { |c| wakeup! c }
|
80
81
|
|
81
82
|
unless @input.empty?
|
82
83
|
until @input.empty?
|
@@ -89,11 +90,19 @@ module Puma
|
|
89
90
|
rescue StandardError => e
|
90
91
|
STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
|
91
92
|
STDERR.puts e.backtrace
|
92
|
-
|
93
|
+
|
94
|
+
# NoMethodError may be rarely raised when calling @selector.select, which
|
95
|
+
# is odd. Regardless, it may continue for thousands of calls if retried.
|
96
|
+
# Also, when it raises, @selector.close also raises an error.
|
97
|
+
if NoMethodError === e
|
98
|
+
close_selector = false
|
99
|
+
else
|
100
|
+
retry
|
101
|
+
end
|
93
102
|
end
|
94
103
|
# Wakeup all remaining objects on shutdown.
|
95
104
|
@timeouts.each(&@block)
|
96
|
-
@selector.close
|
105
|
+
@selector.close if close_selector
|
97
106
|
end
|
98
107
|
|
99
108
|
# Start monitoring the object.
|