puma 5.1.1 → 5.3.1
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 +131 -10
- data/README.md +24 -2
- data/docs/architecture.md +22 -18
- data/docs/compile_options.md +6 -6
- data/docs/deployment.md +2 -2
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +66 -0
- data/docs/plugins.md +2 -2
- data/docs/rails_dev_mode.md +29 -0
- data/docs/restart.md +1 -1
- data/docs/stats.md +142 -0
- data/docs/systemd.md +1 -1
- data/ext/puma_http11/extconf.rb +14 -0
- data/ext/puma_http11/http11_parser.c +19 -21
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +162 -84
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +5 -7
- data/ext/puma_http11/puma_http11.c +2 -2
- data/lib/puma.rb +34 -8
- data/lib/puma/binder.rb +50 -43
- data/lib/puma/client.rb +5 -3
- data/lib/puma/cluster.rb +40 -8
- data/lib/puma/cluster/worker_handle.rb +4 -0
- data/lib/puma/configuration.rb +4 -1
- data/lib/puma/const.rb +3 -3
- data/lib/puma/control_cli.rb +5 -1
- data/lib/puma/detect.rb +14 -10
- data/lib/puma/dsl.rb +56 -4
- data/lib/puma/error_logger.rb +12 -5
- data/lib/puma/events.rb +2 -3
- data/lib/puma/launcher.rb +4 -3
- data/lib/puma/minissl.rb +48 -17
- data/lib/puma/minissl/context_builder.rb +6 -0
- data/lib/puma/null_io.rb +12 -0
- data/lib/puma/queue_close.rb +7 -7
- data/lib/puma/reactor.rb +7 -2
- data/lib/puma/request.rb +9 -4
- data/lib/puma/runner.rb +8 -3
- data/lib/puma/server.rb +46 -112
- data/lib/puma/thread_pool.rb +4 -3
- data/lib/rack/handler/puma.rb +1 -0
- metadata +6 -3
data/lib/puma/configuration.rb
CHANGED
@@ -193,6 +193,7 @@ module Puma
|
|
193
193
|
:debug => false,
|
194
194
|
:binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
|
195
195
|
:workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
|
196
|
+
:silence_single_worker_warning => false,
|
196
197
|
:mode => :http,
|
197
198
|
:worker_timeout => DefaultWorkerTimeout,
|
198
199
|
:worker_boot_timeout => DefaultWorkerTimeout,
|
@@ -205,7 +206,9 @@ module Puma
|
|
205
206
|
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
206
207
|
:first_data_timeout => Const::FIRST_DATA_TIMEOUT,
|
207
208
|
:raise_exception_on_sigterm => true,
|
208
|
-
:max_fast_inline => Const::MAX_FAST_INLINE
|
209
|
+
:max_fast_inline => Const::MAX_FAST_INLINE,
|
210
|
+
:io_selector_backend => :auto,
|
211
|
+
:mutate_stdout_and_stderr_to_sync_on_write => true,
|
209
212
|
}
|
210
213
|
end
|
211
214
|
|
data/lib/puma/const.rb
CHANGED
@@ -100,8 +100,8 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "5.
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "5.3.1".freeze
|
104
|
+
CODE_NAME = "Sweetnighter".freeze
|
105
105
|
|
106
106
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
107
107
|
|
@@ -235,7 +235,7 @@ module Puma
|
|
235
235
|
|
236
236
|
EARLY_HINTS = "rack.early_hints".freeze
|
237
237
|
|
238
|
-
#
|
238
|
+
# Minimum interval to checks worker health
|
239
239
|
WORKER_CHECK_INTERVAL = 5
|
240
240
|
|
241
241
|
# Illegal character in the key or value of response header
|
data/lib/puma/control_cli.rb
CHANGED
@@ -176,7 +176,9 @@ module Puma
|
|
176
176
|
when 'tcp'
|
177
177
|
TCPSocket.new uri.host, uri.port
|
178
178
|
when 'unix'
|
179
|
-
UNIXSocket
|
179
|
+
# check for abstract UNIXSocket
|
180
|
+
UNIXSocket.new(@control_url.start_with?('unix://@') ?
|
181
|
+
"\0#{uri.host}#{uri.path}" : "#{uri.host}#{uri.path}")
|
180
182
|
else
|
181
183
|
raise "Invalid scheme: #{uri.scheme}"
|
182
184
|
end
|
@@ -237,6 +239,7 @@ module Puma
|
|
237
239
|
|
238
240
|
if sig.nil?
|
239
241
|
@stdout.puts "'#{@command}' not available via pid only"
|
242
|
+
@stdout.flush unless @stdout.sync
|
240
243
|
return
|
241
244
|
elsif sig.start_with? 'SIG'
|
242
245
|
Process.kill sig, @pid
|
@@ -244,6 +247,7 @@ module Puma
|
|
244
247
|
begin
|
245
248
|
Process.kill 0, @pid
|
246
249
|
@stdout.puts 'Puma is started'
|
250
|
+
@stdout.flush unless @stdout.sync
|
247
251
|
rescue Errno::ESRCH
|
248
252
|
raise 'Puma is not running'
|
249
253
|
end
|
data/lib/puma/detect.rb
CHANGED
@@ -1,32 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This file can be loaded independently of puma.rb, so it cannot have any code
|
4
|
+
# that assumes puma.rb is loaded.
|
5
|
+
|
6
|
+
|
3
7
|
module Puma
|
4
|
-
#
|
5
|
-
|
8
|
+
# @version 5.2.1
|
9
|
+
HAS_FORK = ::Process.respond_to? :fork
|
6
10
|
|
7
|
-
|
8
|
-
HAS_SSL
|
9
|
-
end
|
11
|
+
IS_JRUBY = Object.const_defined? :JRUBY_VERSION
|
10
12
|
|
11
|
-
|
13
|
+
IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/ ||
|
14
|
+
IS_JRUBY && RUBY_DESCRIPTION =~ /mswin/)
|
15
|
+
|
16
|
+
# @version 5.2.0
|
17
|
+
IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
|
12
18
|
|
13
19
|
def self.jruby?
|
14
20
|
IS_JRUBY
|
15
21
|
end
|
16
22
|
|
17
|
-
IS_WINDOWS = RUBY_PLATFORM =~ /mswin|ming|cygwin/
|
18
|
-
|
19
23
|
def self.windows?
|
20
24
|
IS_WINDOWS
|
21
25
|
end
|
22
26
|
|
23
27
|
# @version 5.0.0
|
24
28
|
def self.mri?
|
25
|
-
|
29
|
+
IS_MRI
|
26
30
|
end
|
27
31
|
|
28
32
|
# @version 5.0.0
|
29
33
|
def self.forkable?
|
30
|
-
|
34
|
+
HAS_FORK
|
31
35
|
end
|
32
36
|
end
|
data/lib/puma/dsl.rb
CHANGED
@@ -51,14 +51,20 @@ module Puma
|
|
51
51
|
if defined?(JRUBY_VERSION)
|
52
52
|
ssl_cipher_list = opts[:ssl_cipher_list] ?
|
53
53
|
"&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil
|
54
|
+
|
54
55
|
keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
|
56
|
+
|
55
57
|
"ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
|
56
58
|
"&verify_mode=#{verify}#{tls_str}#{ca_additions}"
|
57
59
|
else
|
58
60
|
ssl_cipher_filter = opts[:ssl_cipher_filter] ?
|
59
61
|
"&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
|
62
|
+
|
63
|
+
v_flags = (ary = opts[:verification_flags]) ?
|
64
|
+
"&verification_flags=#{Array(ary).join ','}" : nil
|
65
|
+
|
60
66
|
"ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
|
61
|
-
"#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}"
|
67
|
+
"#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}"
|
62
68
|
end
|
63
69
|
end
|
64
70
|
|
@@ -251,7 +257,7 @@ module Puma
|
|
251
257
|
# port 9292
|
252
258
|
def port(port, host=nil)
|
253
259
|
host ||= default_host
|
254
|
-
bind
|
260
|
+
bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s
|
255
261
|
end
|
256
262
|
|
257
263
|
# Define how long persistent connections can be idle before Puma closes them.
|
@@ -399,7 +405,10 @@ module Puma
|
|
399
405
|
# Configure +min+ to be the minimum number of threads to use to answer
|
400
406
|
# requests and +max+ the maximum.
|
401
407
|
#
|
402
|
-
# The default is
|
408
|
+
# The default is the environment variables +PUMA_MIN_THREADS+ / +PUMA_MAX_THREADS+
|
409
|
+
# (or +MIN_THREADS+ / +MAX_THREADS+ if the +PUMA_+ variables aren't set).
|
410
|
+
#
|
411
|
+
# If these environment variables aren't set, the default is "0, 5" in MRI or "0, 16" for other interpreters.
|
403
412
|
#
|
404
413
|
# @example
|
405
414
|
# threads 0, 16
|
@@ -429,6 +438,7 @@ module Puma
|
|
429
438
|
# key: path_to_key,
|
430
439
|
# ssl_cipher_filter: cipher_filter, # optional
|
431
440
|
# verify_mode: verify_mode, # default 'none'
|
441
|
+
# verification_flags: flags, # optional, not supported by JRuby
|
432
442
|
# }
|
433
443
|
# @example For JRuby, two keys are required: keystore & keystore_pass.
|
434
444
|
# ssl_bind '127.0.0.1', '9292', {
|
@@ -463,7 +473,8 @@ module Puma
|
|
463
473
|
# How many worker processes to run. Typically this is set to
|
464
474
|
# the number of available cores.
|
465
475
|
#
|
466
|
-
# The default is
|
476
|
+
# The default is the value of the environment variable +WEB_CONCURRENCY+ if
|
477
|
+
# set, otherwise 0.
|
467
478
|
#
|
468
479
|
# @note Cluster mode only.
|
469
480
|
# @see Puma::Cluster
|
@@ -471,6 +482,24 @@ module Puma
|
|
471
482
|
@options[:workers] = count.to_i
|
472
483
|
end
|
473
484
|
|
485
|
+
# Disable warning message when running in cluster mode with a single worker.
|
486
|
+
#
|
487
|
+
# Cluster mode has some overhead of running an additional 'control' process
|
488
|
+
# in order to manage the cluster. If only running a single worker it is
|
489
|
+
# likely not worth paying that overhead vs running in single mode with
|
490
|
+
# additional threads instead.
|
491
|
+
#
|
492
|
+
# There are some scenarios where running cluster mode with a single worker
|
493
|
+
# may still be warranted and valid under certain deployment scenarios, see
|
494
|
+
# https://github.com/puma/puma/issues/2534
|
495
|
+
#
|
496
|
+
# Moving from workers = 1 to workers = 0 will save 10-30% of memory use.
|
497
|
+
#
|
498
|
+
# @note Cluster mode only.
|
499
|
+
def silence_single_worker_warning
|
500
|
+
@options[:silence_single_worker_warning] = true
|
501
|
+
end
|
502
|
+
|
474
503
|
# Code to run immediately before master process
|
475
504
|
# forks workers (once on boot). These hooks can block if necessary
|
476
505
|
# to wait for background operations unknown to Puma to finish before
|
@@ -858,5 +887,28 @@ module Puma
|
|
858
887
|
def max_fast_inline(num_of_requests)
|
859
888
|
@options[:max_fast_inline] = Float(num_of_requests)
|
860
889
|
end
|
890
|
+
|
891
|
+
# Specify the backend for the IO selector.
|
892
|
+
#
|
893
|
+
# Provided values will be passed directly to +NIO::Selector.new+, with the
|
894
|
+
# exception of +:auto+ which will let nio4r choose the backend.
|
895
|
+
#
|
896
|
+
# Check the documentation of +NIO::Selector.backends+ for the list of valid
|
897
|
+
# options. Note that the available options on your system will depend on the
|
898
|
+
# operating system. If you want to use the pure Ruby backend (not
|
899
|
+
# recommended due to its comparatively low performance), set environment
|
900
|
+
# variable +NIO4R_PURE+ to +true+.
|
901
|
+
#
|
902
|
+
# The default is +:auto+.
|
903
|
+
#
|
904
|
+
# @see https://github.com/socketry/nio4r/blob/master/lib/nio/selector.rb
|
905
|
+
#
|
906
|
+
def io_selector_backend(backend)
|
907
|
+
@options[:io_selector_backend] = backend.to_sym
|
908
|
+
end
|
909
|
+
|
910
|
+
def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
|
911
|
+
@options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
|
912
|
+
end
|
861
913
|
end
|
862
914
|
end
|
data/lib/puma/error_logger.rb
CHANGED
@@ -15,7 +15,6 @@ module Puma
|
|
15
15
|
|
16
16
|
def initialize(ioerr)
|
17
17
|
@ioerr = ioerr
|
18
|
-
@ioerr.sync = true
|
19
18
|
|
20
19
|
@debug = ENV.key? 'PUMA_DEBUG'
|
21
20
|
end
|
@@ -24,7 +23,7 @@ module Puma
|
|
24
23
|
new $stderr
|
25
24
|
end
|
26
25
|
|
27
|
-
# Print
|
26
|
+
# Print occurred error details.
|
28
27
|
# +options+ hash with additional options:
|
29
28
|
# - +error+ is an exception object
|
30
29
|
# - +req+ the http request
|
@@ -32,10 +31,10 @@ module Puma
|
|
32
31
|
# and before all remaining info.
|
33
32
|
#
|
34
33
|
def info(options={})
|
35
|
-
|
34
|
+
log title(options)
|
36
35
|
end
|
37
36
|
|
38
|
-
# Print
|
37
|
+
# Print occurred error details only if
|
39
38
|
# environment variable PUMA_DEBUG is defined.
|
40
39
|
# +options+ hash with additional options:
|
41
40
|
# - +error+ is an exception object
|
@@ -54,7 +53,7 @@ module Puma
|
|
54
53
|
string_block << request_dump(req) if request_parsed?(req)
|
55
54
|
string_block << error.backtrace if error
|
56
55
|
|
57
|
-
|
56
|
+
log string_block.join("\n")
|
58
57
|
end
|
59
58
|
|
60
59
|
def title(options={})
|
@@ -93,5 +92,13 @@ module Puma
|
|
93
92
|
def request_parsed?(req)
|
94
93
|
req && req.env[REQUEST_METHOD]
|
95
94
|
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def log(str)
|
99
|
+
ioerr.puts str
|
100
|
+
|
101
|
+
ioerr.flush unless ioerr.sync
|
102
|
+
end
|
96
103
|
end
|
97
104
|
end
|
data/lib/puma/events.rb
CHANGED
@@ -30,9 +30,6 @@ module Puma
|
|
30
30
|
@stdout = stdout
|
31
31
|
@stderr = stderr
|
32
32
|
|
33
|
-
@stdout.sync = true
|
34
|
-
@stderr.sync = true
|
35
|
-
|
36
33
|
@debug = ENV.key? 'PUMA_DEBUG'
|
37
34
|
@error_logger = ErrorLogger.new(@stderr)
|
38
35
|
|
@@ -66,6 +63,8 @@ module Puma
|
|
66
63
|
#
|
67
64
|
def log(str)
|
68
65
|
@stdout.puts format(str) if @stdout.respond_to? :puts
|
66
|
+
|
67
|
+
@stdout.flush unless @stdout.sync
|
69
68
|
rescue Errno::EPIPE
|
70
69
|
end
|
71
70
|
|
data/lib/puma/launcher.rb
CHANGED
@@ -139,7 +139,6 @@ module Puma
|
|
139
139
|
|
140
140
|
# Begin async shutdown of the server gracefully
|
141
141
|
def stop
|
142
|
-
@events.fire_on_stopped!
|
143
142
|
@status = :stop
|
144
143
|
@runner.stop
|
145
144
|
end
|
@@ -218,6 +217,10 @@ module Puma
|
|
218
217
|
def close_binder_listeners
|
219
218
|
@runner.close_control_listeners
|
220
219
|
@binder.close_listeners
|
220
|
+
unless @status == :restart
|
221
|
+
log "=== puma shutdown: #{Time.now} ==="
|
222
|
+
log "- Goodbye!"
|
223
|
+
end
|
221
224
|
end
|
222
225
|
|
223
226
|
# @!attribute [r] thread_status
|
@@ -375,8 +378,6 @@ module Puma
|
|
375
378
|
def graceful_stop
|
376
379
|
@events.fire_on_stopped!
|
377
380
|
@runner.stop_blocked
|
378
|
-
log "=== puma shutdown: #{Time.now} ==="
|
379
|
-
log "- Goodbye!"
|
380
381
|
end
|
381
382
|
|
382
383
|
def set_process_title
|
data/lib/puma/minissl.rb
CHANGED
@@ -73,7 +73,6 @@ module Puma
|
|
73
73
|
|
74
74
|
def engine_read_all
|
75
75
|
output = @engine.read
|
76
|
-
raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
|
77
76
|
while output and additional_output = @engine.read
|
78
77
|
output << additional_output
|
79
78
|
end
|
@@ -100,6 +99,7 @@ module Puma
|
|
100
99
|
# ourselves.
|
101
100
|
raise IO::EAGAINWaitReadable
|
102
101
|
elsif data.nil?
|
102
|
+
raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
|
103
103
|
return nil
|
104
104
|
end
|
105
105
|
|
@@ -117,22 +117,23 @@ module Puma
|
|
117
117
|
def write(data)
|
118
118
|
return 0 if data.empty?
|
119
119
|
|
120
|
-
|
120
|
+
data_size = data.bytesize
|
121
|
+
need = data_size
|
121
122
|
|
122
123
|
while true
|
123
124
|
wrote = @engine.write data
|
124
|
-
enc = @engine.extract
|
125
125
|
|
126
|
-
|
127
|
-
|
128
|
-
|
126
|
+
enc_wr = ''.dup
|
127
|
+
while (enc = @engine.extract)
|
128
|
+
enc_wr << enc
|
129
129
|
end
|
130
|
+
@socket.write enc_wr unless enc_wr.empty?
|
130
131
|
|
131
132
|
need -= wrote
|
132
133
|
|
133
|
-
return
|
134
|
+
return data_size if need == 0
|
134
135
|
|
135
|
-
data = data
|
136
|
+
data = data.byteslice(wrote..-1)
|
136
137
|
end
|
137
138
|
end
|
138
139
|
|
@@ -245,6 +246,7 @@ module Puma
|
|
245
246
|
attr_reader :cert
|
246
247
|
attr_reader :ca
|
247
248
|
attr_accessor :ssl_cipher_filter
|
249
|
+
attr_accessor :verification_flags
|
248
250
|
|
249
251
|
def key=(key)
|
250
252
|
raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
|
@@ -287,33 +289,58 @@ module Puma
|
|
287
289
|
VERIFY_PEER = 1
|
288
290
|
VERIFY_FAIL_IF_NO_PEER_CERT = 2
|
289
291
|
|
292
|
+
# https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
|
293
|
+
# /* Certificate verify flags */
|
294
|
+
VERIFICATION_FLAGS = {
|
295
|
+
"USE_CHECK_TIME" => 0x2,
|
296
|
+
"CRL_CHECK" => 0x4,
|
297
|
+
"CRL_CHECK_ALL" => 0x8,
|
298
|
+
"IGNORE_CRITICAL" => 0x10,
|
299
|
+
"X509_STRICT" => 0x20,
|
300
|
+
"ALLOW_PROXY_CERTS" => 0x40,
|
301
|
+
"POLICY_CHECK" => 0x80,
|
302
|
+
"EXPLICIT_POLICY" => 0x100,
|
303
|
+
"INHIBIT_ANY" => 0x200,
|
304
|
+
"INHIBIT_MAP" => 0x400,
|
305
|
+
"NOTIFY_POLICY" => 0x800,
|
306
|
+
"EXTENDED_CRL_SUPPORT" => 0x1000,
|
307
|
+
"USE_DELTAS" => 0x2000,
|
308
|
+
"CHECK_SS_SIGNATURE" => 0x4000,
|
309
|
+
"TRUSTED_FIRST" => 0x8000,
|
310
|
+
"SUITEB_128_LOS_ONLY" => 0x10000,
|
311
|
+
"SUITEB_192_LOS" => 0x20000,
|
312
|
+
"SUITEB_128_LOS" => 0x30000,
|
313
|
+
"PARTIAL_CHAIN" => 0x80000,
|
314
|
+
"NO_ALT_CHAINS" => 0x100000,
|
315
|
+
"NO_CHECK_TIME" => 0x200000
|
316
|
+
}.freeze
|
317
|
+
|
290
318
|
class Server
|
291
319
|
def initialize(socket, ctx)
|
292
320
|
@socket = socket
|
293
321
|
@ctx = ctx
|
294
|
-
|
295
|
-
|
296
|
-
# @!attribute [r] to_io
|
297
|
-
def to_io
|
298
|
-
@socket
|
322
|
+
@eng_ctx = IS_JRUBY ? @ctx : SSLContext.new(ctx)
|
299
323
|
end
|
300
324
|
|
301
325
|
def accept
|
302
326
|
@ctx.check
|
303
327
|
io = @socket.accept
|
304
|
-
engine = Engine.server @
|
305
|
-
|
328
|
+
engine = Engine.server @eng_ctx
|
306
329
|
Socket.new io, engine
|
307
330
|
end
|
308
331
|
|
309
332
|
def accept_nonblock
|
310
333
|
@ctx.check
|
311
334
|
io = @socket.accept_nonblock
|
312
|
-
engine = Engine.server @
|
313
|
-
|
335
|
+
engine = Engine.server @eng_ctx
|
314
336
|
Socket.new io, engine
|
315
337
|
end
|
316
338
|
|
339
|
+
# @!attribute [r] to_io
|
340
|
+
def to_io
|
341
|
+
@socket
|
342
|
+
end
|
343
|
+
|
317
344
|
# @!attribute [r] addr
|
318
345
|
# @version 5.0.0
|
319
346
|
def addr
|
@@ -323,6 +350,10 @@ module Puma
|
|
323
350
|
def close
|
324
351
|
@socket.close unless @socket.closed? # closed? call is for Windows
|
325
352
|
end
|
353
|
+
|
354
|
+
def closed?
|
355
|
+
@socket.closed?
|
356
|
+
end
|
326
357
|
end
|
327
358
|
end
|
328
359
|
end
|