puma 5.3.2 → 6.0.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 +284 -11
- data/LICENSE +0 -0
- data/README.md +61 -16
- data/bin/puma-wild +1 -1
- data/docs/architecture.md +49 -16
- data/docs/compile_options.md +38 -2
- data/docs/deployment.md +53 -67
- 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 +0 -0
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +2 -3
- data/docs/restart.md +6 -6
- data/docs/signals.md +11 -10
- data/docs/stats.md +8 -8
- data/docs/systemd.md +64 -67
- 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 +44 -13
- data/ext/puma_http11/http11_parser.c +24 -11
- 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 +3 -3
- data/ext/puma_http11/mini_ssl.c +122 -23
- 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 +50 -48
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +188 -102
- data/ext/puma_http11/puma_http11.c +18 -10
- data/lib/puma/app/status.rb +9 -6
- data/lib/puma/binder.rb +81 -42
- data/lib/puma/cli.rb +23 -19
- data/lib/puma/client.rb +124 -30
- data/lib/puma/cluster/worker.rb +21 -29
- data/lib/puma/cluster/worker_handle.rb +8 -1
- data/lib/puma/cluster.rb +57 -48
- data/lib/puma/commonlogger.rb +0 -0
- data/lib/puma/configuration.rb +74 -55
- data/lib/puma/const.rb +21 -24
- data/lib/puma/control_cli.rb +22 -19
- data/lib/puma/detect.rb +10 -2
- data/lib/puma/dsl.rb +196 -57
- data/lib/puma/error_logger.rb +17 -9
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +29 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/{json.rb → json_serialization.rb} +1 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +108 -154
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +29 -16
- data/lib/puma/minissl.rb +115 -38
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +2 -2
- data/lib/puma/rack/builder.rb +5 -5
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +3 -3
- data/lib/puma/request.rb +293 -153
- data/lib/puma/runner.rb +63 -28
- data/lib/puma/server.rb +83 -88
- data/lib/puma/single.rb +10 -10
- data/lib/puma/state_file.rb +39 -7
- data/lib/puma/systemd.rb +3 -2
- data/lib/puma/thread_pool.rb +22 -17
- data/lib/puma/util.rb +20 -15
- data/lib/puma.rb +12 -9
- data/lib/rack/handler/puma.rb +9 -9
- data/tools/Dockerfile +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +13 -9
- data/lib/puma/queue_close.rb +0 -26
@@ -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,42 +11,55 @@ 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
|
-
|
27
|
-
|
35
|
+
if params['key'].nil? && params['key_pem'].nil?
|
36
|
+
log_writer.error "Please specify the SSL key via 'key=' or 'key_pem='"
|
28
37
|
end
|
29
38
|
|
30
|
-
ctx.key = params['key']
|
39
|
+
ctx.key = params['key'] if params['key']
|
40
|
+
ctx.key_pem = params['key_pem'] if params['key_pem']
|
31
41
|
|
32
|
-
|
33
|
-
|
42
|
+
if params['cert'].nil? && params['cert_pem'].nil?
|
43
|
+
log_writer.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
|
34
44
|
end
|
35
45
|
|
36
|
-
ctx.cert = params['cert']
|
46
|
+
ctx.cert = params['cert'] if params['cert']
|
47
|
+
ctx.cert_pem = params['cert_pem'] if params['cert_pem']
|
37
48
|
|
38
49
|
if ['peer', 'force_peer'].include?(params['verify_mode'])
|
39
50
|
unless params['ca']
|
40
|
-
|
51
|
+
log_writer.error "Please specify the SSL ca via 'ca='"
|
41
52
|
end
|
42
53
|
end
|
43
54
|
|
44
55
|
ctx.ca = params['ca'] if params['ca']
|
45
56
|
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
57
|
+
|
58
|
+
ctx.reuse = params['reuse'] if params['reuse']
|
46
59
|
end
|
47
60
|
|
48
|
-
ctx.no_tlsv1
|
49
|
-
ctx.no_tlsv1_1 =
|
61
|
+
ctx.no_tlsv1 = params['no_tlsv1'] == 'true'
|
62
|
+
ctx.no_tlsv1_1 = params['no_tlsv1_1'] == 'true'
|
50
63
|
|
51
64
|
if params['verify_mode']
|
52
65
|
ctx.verify_mode = case params['verify_mode']
|
@@ -57,7 +70,7 @@ module Puma
|
|
57
70
|
when "none"
|
58
71
|
MiniSSL::VERIFY_NONE
|
59
72
|
else
|
60
|
-
|
73
|
+
log_writer.error "Please specify a valid verify_mode="
|
61
74
|
MiniSSL::VERIFY_NONE
|
62
75
|
end
|
63
76
|
end
|
@@ -73,7 +86,7 @@ module Puma
|
|
73
86
|
|
74
87
|
private
|
75
88
|
|
76
|
-
attr_reader :params, :
|
89
|
+
attr_reader :params, :log_writer
|
77
90
|
end
|
78
91
|
end
|
79
92
|
end
|
data/lib/puma/minissl.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
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
8
|
# need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3`
|
9
|
+
# use require, see https://github.com/puma/puma/pull/2381
|
9
10
|
require 'puma/puma_http11'
|
10
11
|
|
11
12
|
module Puma
|
@@ -13,15 +14,16 @@ module Puma
|
|
13
14
|
# Define constant at runtime, as it's easy to determine at built time,
|
14
15
|
# but Puma could (it shouldn't) be loaded with an older OpenSSL version
|
15
16
|
# @version 5.0.0
|
16
|
-
HAS_TLS1_3 =
|
17
|
-
|
18
|
-
|
17
|
+
HAS_TLS1_3 = IS_JRUBY ||
|
18
|
+
((OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 &&
|
19
|
+
(OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1)
|
19
20
|
|
20
21
|
class Socket
|
21
22
|
def initialize(socket, engine)
|
22
23
|
@socket = socket
|
23
24
|
@engine = engine
|
24
25
|
@peercert = nil
|
26
|
+
@reuse = nil
|
25
27
|
end
|
26
28
|
|
27
29
|
# @!attribute [r] to_io
|
@@ -50,7 +52,7 @@ module Puma
|
|
50
52
|
# is made with TLSv1.3 as an available protocol
|
51
53
|
# @version 5.0.0
|
52
54
|
def bad_tlsv1_3?
|
53
|
-
HAS_TLS1_3 &&
|
55
|
+
HAS_TLS1_3 && ssl_version_state == ['TLSv1.3', 'SSLERR']
|
54
56
|
end
|
55
57
|
private :bad_tlsv1_3?
|
56
58
|
|
@@ -123,7 +125,7 @@ module Puma
|
|
123
125
|
while true
|
124
126
|
wrote = @engine.write data
|
125
127
|
|
126
|
-
enc_wr = ''
|
128
|
+
enc_wr = +''
|
127
129
|
while (enc = @engine.extract)
|
128
130
|
enc_wr << enc
|
129
131
|
end
|
@@ -161,30 +163,15 @@ module Puma
|
|
161
163
|
@socket.flush
|
162
164
|
end
|
163
165
|
|
164
|
-
def read_and_drop(timeout = 1)
|
165
|
-
return :timeout unless IO.select([@socket], nil, nil, timeout)
|
166
|
-
case @socket.read_nonblock(1024, exception: false)
|
167
|
-
when nil
|
168
|
-
:eof
|
169
|
-
when :wait_readable
|
170
|
-
:eagain
|
171
|
-
else
|
172
|
-
:drop
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def should_drop_bytes?
|
177
|
-
@engine.init? || !@engine.shutdown
|
178
|
-
end
|
179
|
-
|
180
166
|
def close
|
181
167
|
begin
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
168
|
+
unless @engine.shutdown
|
169
|
+
while alert_data = @engine.extract
|
170
|
+
@socket.write alert_data
|
171
|
+
end
|
172
|
+
end
|
186
173
|
rescue IOError, SystemCallError
|
187
|
-
|
174
|
+
Puma::Util.purge_interrupt_queue
|
188
175
|
# nothing
|
189
176
|
ensure
|
190
177
|
@socket.close
|
@@ -210,10 +197,6 @@ module Puma
|
|
210
197
|
if IS_JRUBY
|
211
198
|
OPENSSL_NO_SSL3 = false
|
212
199
|
OPENSSL_NO_TLS1 = false
|
213
|
-
|
214
|
-
class SSLError < StandardError
|
215
|
-
# Define this for jruby even though it isn't used.
|
216
|
-
end
|
217
200
|
end
|
218
201
|
|
219
202
|
class Context
|
@@ -223,21 +206,72 @@ module Puma
|
|
223
206
|
def initialize
|
224
207
|
@no_tlsv1 = false
|
225
208
|
@no_tlsv1_1 = false
|
209
|
+
@key = nil
|
210
|
+
@cert = nil
|
211
|
+
@key_pem = nil
|
212
|
+
@cert_pem = nil
|
213
|
+
@reuse = nil
|
214
|
+
@reuse_cache_size = nil
|
215
|
+
@reuse_timeout = nil
|
216
|
+
end
|
217
|
+
|
218
|
+
def check_file(file, desc)
|
219
|
+
raise ArgumentError, "#{desc} file '#{file}' does not exist" unless File.exist? file
|
220
|
+
raise ArgumentError, "#{desc} file '#{file}' is not readable" unless File.readable? file
|
226
221
|
end
|
227
222
|
|
228
223
|
if IS_JRUBY
|
229
224
|
# jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
|
230
225
|
attr_reader :keystore
|
226
|
+
attr_reader :keystore_type
|
231
227
|
attr_accessor :keystore_pass
|
232
|
-
|
228
|
+
attr_reader :truststore
|
229
|
+
attr_reader :truststore_type
|
230
|
+
attr_accessor :truststore_pass
|
231
|
+
attr_reader :cipher_suites
|
232
|
+
attr_reader :protocols
|
233
233
|
|
234
234
|
def keystore=(keystore)
|
235
|
-
|
235
|
+
check_file keystore, 'Keystore'
|
236
236
|
@keystore = keystore
|
237
237
|
end
|
238
238
|
|
239
|
+
def truststore=(truststore)
|
240
|
+
# NOTE: historically truststore was assumed the same as keystore, this is kept for backwards
|
241
|
+
# compatibility, to rely on JVM's trust defaults we allow setting `truststore = :default`
|
242
|
+
unless truststore.eql?(:default)
|
243
|
+
raise ArgumentError, "No such truststore file '#{truststore}'" unless File.exist?(truststore)
|
244
|
+
end
|
245
|
+
@truststore = truststore
|
246
|
+
end
|
247
|
+
|
248
|
+
def keystore_type=(type)
|
249
|
+
raise ArgumentError, "Invalid keystore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
|
250
|
+
@keystore_type = type
|
251
|
+
end
|
252
|
+
|
253
|
+
def truststore_type=(type)
|
254
|
+
raise ArgumentError, "Invalid truststore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
|
255
|
+
@truststore_type = type
|
256
|
+
end
|
257
|
+
|
258
|
+
def cipher_suites=(list)
|
259
|
+
list = list.split(',').map(&:strip) if list.is_a?(String)
|
260
|
+
@cipher_suites = list
|
261
|
+
end
|
262
|
+
|
263
|
+
# aliases for backwards compatibility
|
264
|
+
alias_method :ssl_cipher_list, :cipher_suites
|
265
|
+
alias_method :ssl_cipher_list=, :cipher_suites=
|
266
|
+
|
267
|
+
def protocols=(list)
|
268
|
+
list = list.split(',').map(&:strip) if list.is_a?(String)
|
269
|
+
@protocols = list
|
270
|
+
end
|
271
|
+
|
239
272
|
def check
|
240
273
|
raise "Keystore not configured" unless @keystore
|
274
|
+
# @truststore defaults to @keystore due backwards compatibility
|
241
275
|
end
|
242
276
|
|
243
277
|
else
|
@@ -245,27 +279,70 @@ module Puma
|
|
245
279
|
attr_reader :key
|
246
280
|
attr_reader :cert
|
247
281
|
attr_reader :ca
|
282
|
+
attr_reader :cert_pem
|
283
|
+
attr_reader :key_pem
|
248
284
|
attr_accessor :ssl_cipher_filter
|
249
285
|
attr_accessor :verification_flags
|
250
286
|
|
287
|
+
attr_reader :reuse, :reuse_cache_size, :reuse_timeout
|
288
|
+
|
251
289
|
def key=(key)
|
252
|
-
|
290
|
+
check_file key, 'Key'
|
253
291
|
@key = key
|
254
292
|
end
|
255
293
|
|
256
294
|
def cert=(cert)
|
257
|
-
|
295
|
+
check_file cert, 'Cert'
|
258
296
|
@cert = cert
|
259
297
|
end
|
260
298
|
|
261
299
|
def ca=(ca)
|
262
|
-
|
300
|
+
check_file ca, 'ca'
|
263
301
|
@ca = ca
|
264
302
|
end
|
265
303
|
|
304
|
+
def cert_pem=(cert_pem)
|
305
|
+
raise ArgumentError, "'cert_pem' is not a String" unless cert_pem.is_a? String
|
306
|
+
@cert_pem = cert_pem
|
307
|
+
end
|
308
|
+
|
309
|
+
def key_pem=(key_pem)
|
310
|
+
raise ArgumentError, "'key_pem' is not a String" unless key_pem.is_a? String
|
311
|
+
@key_pem = key_pem
|
312
|
+
end
|
313
|
+
|
266
314
|
def check
|
267
|
-
raise "Key not configured"
|
268
|
-
raise "Cert not configured"
|
315
|
+
raise "Key not configured" if @key.nil? && @key_pem.nil?
|
316
|
+
raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
|
317
|
+
end
|
318
|
+
|
319
|
+
# Controls session reuse. Allowed values are as follows:
|
320
|
+
# * 'off' - matches the behavior of Puma 5.6 and earlier. This is included
|
321
|
+
# in case reuse 'on' is made the default in future Puma versions.
|
322
|
+
# * 'dflt' - sets session reuse on, with OpenSSL default cache size of
|
323
|
+
# 20k and default timeout of 300 seconds.
|
324
|
+
# * 's,t' - where s and t are integer strings, for size and timeout.
|
325
|
+
# * 's' - where s is an integer strings for size.
|
326
|
+
# * ',t' - where t is an integer strings for timeout.
|
327
|
+
#
|
328
|
+
def reuse=(reuse_str)
|
329
|
+
case reuse_str
|
330
|
+
when 'off'
|
331
|
+
@reuse = nil
|
332
|
+
when 'dflt'
|
333
|
+
@reuse = true
|
334
|
+
when /\A\d+\z/
|
335
|
+
@reuse = true
|
336
|
+
@reuse_cache_size = reuse_str.to_i
|
337
|
+
when /\A\d+,\d+\z/
|
338
|
+
@reuse = true
|
339
|
+
size, time = reuse_str.split ','
|
340
|
+
@reuse_cache_size = size.to_i
|
341
|
+
@reuse_timeout = time.to_i
|
342
|
+
when /\A,\d+\z/
|
343
|
+
@reuse = true
|
344
|
+
@reuse_timeout = reuse_str.delete(',').to_i
|
345
|
+
end
|
269
346
|
end
|
270
347
|
end
|
271
348
|
|
data/lib/puma/null_io.rb
CHANGED
data/lib/puma/plugin.rb
CHANGED
@@ -64,7 +64,7 @@ module Puma
|
|
64
64
|
def fire_background
|
65
65
|
@background.each_with_index do |b, i|
|
66
66
|
Thread.new do
|
67
|
-
Puma.set_thread_name "
|
67
|
+
Puma.set_thread_name "plgn bg #{i}"
|
68
68
|
b.call
|
69
69
|
end
|
70
70
|
end
|
@@ -91,7 +91,7 @@ module Puma
|
|
91
91
|
path = ary.first[CALLER_FILE]
|
92
92
|
|
93
93
|
m = %r!puma/plugin/([^/]*)\.rb$!.match(path)
|
94
|
-
|
94
|
+
m[1]
|
95
95
|
end
|
96
96
|
|
97
97
|
def self.create(&blk)
|
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
|
@@ -165,7 +165,7 @@ module Puma::Rack
|
|
165
165
|
require config
|
166
166
|
app = Object.const_get(::File.basename(config, '.rb').capitalize)
|
167
167
|
end
|
168
|
-
|
168
|
+
[app, options]
|
169
169
|
end
|
170
170
|
|
171
171
|
def self.new_from_string(builder_script, file="(rackup)")
|
@@ -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
data/lib/puma/reactor.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'queue_close' unless ::Queue.instance_methods.include? :close
|
4
4
|
|
5
5
|
module Puma
|
6
6
|
class UnsupportedBackend < StandardError; end
|
@@ -61,7 +61,7 @@ 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
|
@@ -76,7 +76,7 @@ module Puma
|
|
76
76
|
|
77
77
|
# Wakeup all objects that timed out.
|
78
78
|
timed_out = @timeouts.take_while {|t| t.timeout == 0}
|
79
|
-
timed_out.each
|
79
|
+
timed_out.each { |c| wakeup! c }
|
80
80
|
|
81
81
|
unless @input.empty?
|
82
82
|
until @input.empty?
|