puma 6.4.0 → 6.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +235 -18
- data/README.md +87 -26
- data/docs/fork_worker.md +11 -1
- data/docs/java_options.md +54 -0
- data/docs/kubernetes.md +1 -1
- data/docs/plugins.md +4 -0
- data/docs/restart.md +1 -0
- data/docs/signals.md +2 -2
- data/docs/stats.md +8 -3
- data/docs/systemd.md +13 -6
- data/ext/puma_http11/extconf.rb +21 -14
- data/ext/puma_http11/mini_ssl.c +16 -2
- data/ext/puma_http11/org/jruby/puma/Http11.java +31 -8
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +2 -1
- data/ext/puma_http11/puma_http11.c +4 -1
- data/lib/puma/app/status.rb +1 -1
- data/lib/puma/binder.rb +12 -4
- data/lib/puma/cli.rb +9 -5
- data/lib/puma/client.rb +104 -16
- data/lib/puma/cluster/worker.rb +9 -6
- data/lib/puma/cluster/worker_handle.rb +4 -5
- data/lib/puma/cluster.rb +112 -27
- data/lib/puma/configuration.rb +37 -21
- data/lib/puma/const.rb +22 -3
- data/lib/puma/control_cli.rb +4 -4
- data/lib/puma/detect.rb +3 -4
- data/lib/puma/dsl.rb +303 -58
- data/lib/puma/error_logger.rb +4 -4
- data/lib/puma/jruby_restart.rb +0 -16
- data/lib/puma/launcher.rb +21 -8
- data/lib/puma/log_writer.rb +9 -9
- data/lib/puma/minissl/context_builder.rb +3 -0
- data/lib/puma/minissl.rb +6 -0
- data/lib/puma/null_io.rb +42 -2
- data/lib/puma/request.rb +35 -14
- data/lib/puma/runner.rb +10 -3
- data/lib/puma/sd_notify.rb +1 -4
- data/lib/puma/server.rb +53 -21
- data/lib/puma/single.rb +1 -1
- data/lib/puma/state_file.rb +2 -2
- data/lib/puma/thread_pool.rb +14 -2
- data/lib/puma/util.rb +1 -1
- data/lib/rack/handler/puma.rb +8 -5
- data/tools/Dockerfile +5 -3
- metadata +10 -12
data/lib/puma/error_logger.rb
CHANGED
|
@@ -15,14 +15,14 @@ module Puma
|
|
|
15
15
|
|
|
16
16
|
LOG_QUEUE = Queue.new
|
|
17
17
|
|
|
18
|
-
def initialize(ioerr)
|
|
18
|
+
def initialize(ioerr, env: ENV)
|
|
19
19
|
@ioerr = ioerr
|
|
20
20
|
|
|
21
|
-
@debug =
|
|
21
|
+
@debug = env.key?('PUMA_DEBUG')
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def self.stdio
|
|
25
|
-
new
|
|
24
|
+
def self.stdio(env: ENV)
|
|
25
|
+
new($stderr, env: env)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# Print occurred error details.
|
data/lib/puma/jruby_restart.rb
CHANGED
|
@@ -6,22 +6,6 @@ module Puma
|
|
|
6
6
|
module JRubyRestart
|
|
7
7
|
extend FFI::Library
|
|
8
8
|
ffi_lib 'c'
|
|
9
|
-
|
|
10
|
-
attach_function :execlp, [:string, :varargs], :int
|
|
11
9
|
attach_function :chdir, [:string], :int
|
|
12
|
-
attach_function :fork, [], :int
|
|
13
|
-
attach_function :exit, [:int], :void
|
|
14
|
-
attach_function :setsid, [], :int
|
|
15
|
-
|
|
16
|
-
def self.chdir_exec(dir, argv)
|
|
17
|
-
chdir(dir)
|
|
18
|
-
cmd = argv.first
|
|
19
|
-
argv = ([:string] * argv.size).zip(argv)
|
|
20
|
-
argv.flatten!
|
|
21
|
-
argv << :string
|
|
22
|
-
argv << nil
|
|
23
|
-
execlp(cmd, *argv)
|
|
24
|
-
raise SystemCallError.new(FFI.errno)
|
|
25
|
-
end
|
|
26
10
|
end
|
|
27
11
|
end
|
data/lib/puma/launcher.rb
CHANGED
|
@@ -46,6 +46,8 @@ module Puma
|
|
|
46
46
|
@original_argv = @argv.dup
|
|
47
47
|
@config = conf
|
|
48
48
|
|
|
49
|
+
env = launcher_args.delete(:env) || ENV
|
|
50
|
+
|
|
49
51
|
@config.options[:log_writer] = @log_writer
|
|
50
52
|
|
|
51
53
|
# Advertise the Configuration
|
|
@@ -62,7 +64,7 @@ module Puma
|
|
|
62
64
|
# Load the systemd integration if we detect systemd's NOTIFY_SOCKET.
|
|
63
65
|
# Skip this on JRuby though, because it is incompatible with the systemd
|
|
64
66
|
# integration due to https://github.com/jruby/jruby/issues/6504
|
|
65
|
-
if ENV["NOTIFY_SOCKET"] && !Puma.jruby?
|
|
67
|
+
if ENV["NOTIFY_SOCKET"] && !Puma.jruby? && !ENV["PUMA_SKIP_SYSTEMD"]
|
|
66
68
|
@config.plugins.create('systemd')
|
|
67
69
|
end
|
|
68
70
|
|
|
@@ -105,7 +107,7 @@ module Puma
|
|
|
105
107
|
|
|
106
108
|
@status = :run
|
|
107
109
|
|
|
108
|
-
log_config if
|
|
110
|
+
log_config if env['PUMA_LOG_CONFIG']
|
|
109
111
|
end
|
|
110
112
|
|
|
111
113
|
attr_reader :binder, :log_writer, :events, :config, :options, :restart_dir
|
|
@@ -165,6 +167,13 @@ module Puma
|
|
|
165
167
|
log "* phased-restart called but not available, restarting normally."
|
|
166
168
|
return restart
|
|
167
169
|
end
|
|
170
|
+
|
|
171
|
+
if @options.file_options[:tag].nil?
|
|
172
|
+
dir = File.realdirpath(@restart_dir)
|
|
173
|
+
@options[:tag] = File.basename(dir)
|
|
174
|
+
set_process_title
|
|
175
|
+
end
|
|
176
|
+
|
|
168
177
|
true
|
|
169
178
|
end
|
|
170
179
|
|
|
@@ -287,7 +296,9 @@ module Puma
|
|
|
287
296
|
close_binder_listeners
|
|
288
297
|
|
|
289
298
|
require_relative 'jruby_restart'
|
|
290
|
-
|
|
299
|
+
argv = restart_args
|
|
300
|
+
JRubyRestart.chdir(@restart_dir)
|
|
301
|
+
Kernel.exec(*argv)
|
|
291
302
|
elsif Puma.windows?
|
|
292
303
|
close_binder_listeners
|
|
293
304
|
|
|
@@ -408,12 +419,14 @@ module Puma
|
|
|
408
419
|
end
|
|
409
420
|
|
|
410
421
|
def setup_signals
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
422
|
+
unless ENV["PUMA_SKIP_SIGUSR2"]
|
|
423
|
+
begin
|
|
424
|
+
Signal.trap "SIGUSR2" do
|
|
425
|
+
restart
|
|
426
|
+
end
|
|
427
|
+
rescue Exception
|
|
428
|
+
log "*** SIGUSR2 not implemented, signal based restart unavailable!"
|
|
414
429
|
end
|
|
415
|
-
rescue Exception
|
|
416
|
-
log "*** SIGUSR2 not implemented, signal based restart unavailable!"
|
|
417
430
|
end
|
|
418
431
|
|
|
419
432
|
unless Puma.jruby?
|
data/lib/puma/log_writer.rb
CHANGED
|
@@ -31,31 +31,31 @@ module Puma
|
|
|
31
31
|
attr_accessor :formatter, :custom_logger
|
|
32
32
|
|
|
33
33
|
# Create a LogWriter that prints to +stdout+ and +stderr+.
|
|
34
|
-
def initialize(stdout, stderr)
|
|
34
|
+
def initialize(stdout, stderr, env: ENV)
|
|
35
35
|
@formatter = DefaultFormatter.new
|
|
36
36
|
@custom_logger = nil
|
|
37
37
|
@stdout = stdout
|
|
38
38
|
@stderr = stderr
|
|
39
39
|
|
|
40
|
-
@debug =
|
|
41
|
-
@error_logger = ErrorLogger.new(@stderr)
|
|
40
|
+
@debug = env.key?('PUMA_DEBUG')
|
|
41
|
+
@error_logger = ErrorLogger.new(@stderr, env: env)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
DEFAULT = new(STDOUT, STDERR)
|
|
45
45
|
|
|
46
46
|
# Returns an LogWriter object which writes its status to
|
|
47
47
|
# two StringIO objects.
|
|
48
|
-
def self.strings
|
|
49
|
-
LogWriter.new(StringIO.new, StringIO.new)
|
|
48
|
+
def self.strings(env: ENV)
|
|
49
|
+
LogWriter.new(StringIO.new, StringIO.new, env: env)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
def self.stdio
|
|
53
|
-
LogWriter.new($stdout, $stderr)
|
|
52
|
+
def self.stdio(env: ENV)
|
|
53
|
+
LogWriter.new($stdout, $stderr, env: env)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
def self.null
|
|
56
|
+
def self.null(env: ENV)
|
|
57
57
|
n = NullIO.new
|
|
58
|
-
LogWriter.new(n, n)
|
|
58
|
+
LogWriter.new(n, n, env: env)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
# Write +str+ to +@stdout+
|
|
@@ -51,10 +51,13 @@ module Puma
|
|
|
51
51
|
unless params['ca']
|
|
52
52
|
log_writer.error "Please specify the SSL ca via 'ca='"
|
|
53
53
|
end
|
|
54
|
+
# needed for Puma::MiniSSL::Socket#peercert, env['puma.peercert']
|
|
55
|
+
require 'openssl'
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
ctx.ca = params['ca'] if params['ca']
|
|
57
59
|
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
|
60
|
+
ctx.ssl_ciphersuites = params['ssl_ciphersuites'] if params['ssl_ciphersuites'] && HAS_TLS1_3
|
|
58
61
|
|
|
59
62
|
ctx.reuse = params['reuse'] if params['reuse']
|
|
60
63
|
end
|
data/lib/puma/minissl.rb
CHANGED
|
@@ -184,6 +184,11 @@ module Puma
|
|
|
184
184
|
@socket.peeraddr
|
|
185
185
|
end
|
|
186
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]
|
|
187
192
|
# @!attribute [r] peercert
|
|
188
193
|
def peercert
|
|
189
194
|
return @peercert if @peercert
|
|
@@ -284,6 +289,7 @@ module Puma
|
|
|
284
289
|
attr_reader :cert_pem
|
|
285
290
|
attr_reader :key_pem
|
|
286
291
|
attr_accessor :ssl_cipher_filter
|
|
292
|
+
attr_accessor :ssl_ciphersuites
|
|
287
293
|
attr_accessor :verification_flags
|
|
288
294
|
|
|
289
295
|
attr_reader :reuse, :reuse_cache_size, :reuse_timeout
|
data/lib/puma/null_io.rb
CHANGED
|
@@ -16,15 +16,38 @@ module Puma
|
|
|
16
16
|
def each
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
def pos
|
|
20
|
+
0
|
|
21
|
+
end
|
|
22
|
+
|
|
19
23
|
# Mimics IO#read with no data.
|
|
20
24
|
#
|
|
21
|
-
def read(
|
|
22
|
-
|
|
25
|
+
def read(length = nil, buffer = nil)
|
|
26
|
+
if length.to_i < 0
|
|
27
|
+
raise ArgumentError, "(negative length #{length} given)"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
buffer = if buffer.nil?
|
|
31
|
+
"".b
|
|
32
|
+
else
|
|
33
|
+
String.try_convert(buffer) or raise TypeError, "no implicit conversion of #{buffer.class} into String"
|
|
34
|
+
end
|
|
35
|
+
buffer.clear
|
|
36
|
+
if length.to_i > 0
|
|
37
|
+
nil
|
|
38
|
+
else
|
|
39
|
+
buffer
|
|
40
|
+
end
|
|
23
41
|
end
|
|
24
42
|
|
|
25
43
|
def rewind
|
|
26
44
|
end
|
|
27
45
|
|
|
46
|
+
def seek(pos, whence = 0)
|
|
47
|
+
raise ArgumentError, "negative length #{pos} given" if pos.negative?
|
|
48
|
+
0
|
|
49
|
+
end
|
|
50
|
+
|
|
28
51
|
def close
|
|
29
52
|
end
|
|
30
53
|
|
|
@@ -57,5 +80,22 @@ module Puma
|
|
|
57
80
|
def closed?
|
|
58
81
|
false
|
|
59
82
|
end
|
|
83
|
+
|
|
84
|
+
def set_encoding(enc)
|
|
85
|
+
self
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# per rack spec
|
|
89
|
+
def external_encoding
|
|
90
|
+
Encoding::ASCII_8BIT
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def binmode
|
|
94
|
+
self
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def binmode?
|
|
98
|
+
true
|
|
99
|
+
end
|
|
60
100
|
end
|
|
61
101
|
end
|
data/lib/puma/request.rb
CHANGED
|
@@ -53,7 +53,6 @@ module Puma
|
|
|
53
53
|
socket = client.io # io may be a MiniSSL::Socket
|
|
54
54
|
app_body = nil
|
|
55
55
|
|
|
56
|
-
|
|
57
56
|
return false if closed_socket?(socket)
|
|
58
57
|
|
|
59
58
|
if client.http_content_length_limit_exceeded
|
|
@@ -135,13 +134,16 @@ module Puma
|
|
|
135
134
|
io_buffer.reset
|
|
136
135
|
uncork_socket client.io
|
|
137
136
|
app_body.close if app_body.respond_to? :close
|
|
138
|
-
client
|
|
139
|
-
after_reply = env[RACK_AFTER_REPLY]
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
137
|
+
client&.tempfile_close
|
|
138
|
+
if after_reply = env[RACK_AFTER_REPLY]
|
|
139
|
+
after_reply.each do |o|
|
|
140
|
+
begin
|
|
141
|
+
o.call
|
|
142
|
+
rescue StandardError => e
|
|
143
|
+
@log_writer.debug_error e
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
145
147
|
end
|
|
146
148
|
|
|
147
149
|
# Assembles the headers and prepares the body for actually sending the
|
|
@@ -165,9 +167,14 @@ module Puma
|
|
|
165
167
|
# if the server is at capacity and the listener has a new connection ready.
|
|
166
168
|
# This allows Puma to service connections fairly when the number
|
|
167
169
|
# of concurrent connections exceeds the size of the threadpool.
|
|
168
|
-
force_keep_alive =
|
|
170
|
+
force_keep_alive = if @enable_keep_alives
|
|
171
|
+
requests < @max_fast_inline ||
|
|
169
172
|
@thread_pool.busy_threads < @max_threads ||
|
|
170
173
|
!client.listener.to_io.wait_readable(0)
|
|
174
|
+
else
|
|
175
|
+
# Always set force_keep_alive to false if the server has keep-alives not enabled.
|
|
176
|
+
false
|
|
177
|
+
end
|
|
171
178
|
|
|
172
179
|
resp_info = str_headers(env, status, headers, res_body, io_buffer, force_keep_alive)
|
|
173
180
|
|
|
@@ -187,7 +194,8 @@ module Puma
|
|
|
187
194
|
elsif res_body.is_a?(File) && res_body.respond_to?(:size)
|
|
188
195
|
body = res_body
|
|
189
196
|
content_length = body.size
|
|
190
|
-
elsif res_body.respond_to?(:to_path) &&
|
|
197
|
+
elsif res_body.respond_to?(:to_path) && (fn = res_body.to_path) &&
|
|
198
|
+
File.readable?(fn)
|
|
191
199
|
body = File.open fn, 'rb'
|
|
192
200
|
content_length = body.size
|
|
193
201
|
close_body = true
|
|
@@ -195,7 +203,7 @@ module Puma
|
|
|
195
203
|
body = res_body
|
|
196
204
|
end
|
|
197
205
|
elsif !res_body.is_a?(::File) && res_body.respond_to?(:to_path) &&
|
|
198
|
-
File.readable?(fn = res_body.to_path)
|
|
206
|
+
(fn = res_body.to_path) && File.readable?(fn = res_body.to_path)
|
|
199
207
|
body = File.open fn, 'rb'
|
|
200
208
|
content_length = body.size
|
|
201
209
|
close_body = true
|
|
@@ -495,6 +503,11 @@ module Puma
|
|
|
495
503
|
# compatibility, we'll convert them back. This code is written to
|
|
496
504
|
# avoid allocation in the common case (ie there are no headers
|
|
497
505
|
# with `,` in their names), that's why it has the extra conditionals.
|
|
506
|
+
#
|
|
507
|
+
# @note If a normalized version of a `,` header already exists, we ignore
|
|
508
|
+
# the `,` version. This prevents clobbering headers managed by proxies
|
|
509
|
+
# but not by clients (Like X-Forwarded-For).
|
|
510
|
+
#
|
|
498
511
|
# @param env [Hash] see Puma::Client#env, from request, modifies in place
|
|
499
512
|
# @version 5.0.3
|
|
500
513
|
#
|
|
@@ -503,23 +516,31 @@ module Puma
|
|
|
503
516
|
to_add = nil
|
|
504
517
|
|
|
505
518
|
env.each do |k,v|
|
|
506
|
-
if k.start_with?("HTTP_") && k.include?(",") && k
|
|
519
|
+
if k.start_with?("HTTP_") && k.include?(",") && !UNMASKABLE_HEADERS.key?(k)
|
|
507
520
|
if to_delete
|
|
508
521
|
to_delete << k
|
|
509
522
|
else
|
|
510
523
|
to_delete = [k]
|
|
511
524
|
end
|
|
512
525
|
|
|
526
|
+
new_k = k.tr(",", "_")
|
|
527
|
+
if env.key?(new_k)
|
|
528
|
+
next
|
|
529
|
+
end
|
|
530
|
+
|
|
513
531
|
unless to_add
|
|
514
532
|
to_add = {}
|
|
515
533
|
end
|
|
516
534
|
|
|
517
|
-
to_add[
|
|
535
|
+
to_add[new_k] = v
|
|
518
536
|
end
|
|
519
537
|
end
|
|
520
538
|
|
|
521
|
-
if to_delete
|
|
539
|
+
if to_delete # rubocop:disable Style/SafeNavigation
|
|
522
540
|
to_delete.each { |k| env.delete(k) }
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
if to_add
|
|
523
544
|
env.merge! to_add
|
|
524
545
|
end
|
|
525
546
|
end
|
data/lib/puma/runner.rb
CHANGED
|
@@ -8,6 +8,9 @@ module Puma
|
|
|
8
8
|
# serve requests. This class spawns a new instance of `Puma::Server` via
|
|
9
9
|
# a call to `start_server`.
|
|
10
10
|
class Runner
|
|
11
|
+
|
|
12
|
+
include ::Puma::Const::PipeRequest
|
|
13
|
+
|
|
11
14
|
def initialize(launcher)
|
|
12
15
|
@launcher = launcher
|
|
13
16
|
@log_writer = launcher.log_writer
|
|
@@ -27,7 +30,7 @@ module Puma
|
|
|
27
30
|
def wakeup!
|
|
28
31
|
return unless @wakeup
|
|
29
32
|
|
|
30
|
-
@wakeup.write
|
|
33
|
+
@wakeup.write PIPE_WAKEUP unless @wakeup.closed?
|
|
31
34
|
|
|
32
35
|
rescue SystemCallError, IOError
|
|
33
36
|
Puma::Util.purge_interrupt_queue
|
|
@@ -70,7 +73,7 @@ module Puma
|
|
|
70
73
|
|
|
71
74
|
app = Puma::App::Status.new @launcher, token
|
|
72
75
|
|
|
73
|
-
# A Reactor is not created
|
|
76
|
+
# A Reactor is not created and nio4r is not loaded when 'queue_requests: false'
|
|
74
77
|
# Use `nil` for events, no hooks in control server
|
|
75
78
|
control = Puma::Server.new app, nil,
|
|
76
79
|
{ min_threads: 0, max_threads: 1, queue_requests: false, log_writer: @log_writer }
|
|
@@ -91,7 +94,10 @@ module Puma
|
|
|
91
94
|
end
|
|
92
95
|
|
|
93
96
|
# @!attribute [r] ruby_engine
|
|
97
|
+
# @deprecated Use `RUBY_DESCRIPTION` instead
|
|
94
98
|
def ruby_engine
|
|
99
|
+
warn "Puma::Runner#ruby_engine is deprecated; use RUBY_DESCRIPTION instead. It will be removed in puma v7."
|
|
100
|
+
|
|
95
101
|
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
|
|
96
102
|
"ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
|
|
97
103
|
else
|
|
@@ -109,7 +115,8 @@ module Puma
|
|
|
109
115
|
environment = @options[:environment]
|
|
110
116
|
|
|
111
117
|
log "Puma starting in #{mode} mode..."
|
|
112
|
-
log "* Puma version: #{Puma::Const::PUMA_VERSION} (
|
|
118
|
+
log "* Puma version: #{Puma::Const::PUMA_VERSION} (\"#{Puma::Const::CODE_NAME}\")"
|
|
119
|
+
log "* Ruby version: #{RUBY_DESCRIPTION}"
|
|
113
120
|
log "* Min threads: #{min_t}"
|
|
114
121
|
log "* Max threads: #{max_t}"
|
|
115
122
|
log "* Environment: #{environment}"
|
data/lib/puma/sd_notify.rb
CHANGED
|
@@ -137,10 +137,7 @@ module Puma
|
|
|
137
137
|
ENV.delete("NOTIFY_SOCKET") if unset_env
|
|
138
138
|
|
|
139
139
|
begin
|
|
140
|
-
Addrinfo.unix(sock, :DGRAM).connect
|
|
141
|
-
s.close_on_exec = true
|
|
142
|
-
s.write(state)
|
|
143
|
-
end
|
|
140
|
+
Addrinfo.unix(sock, :DGRAM).connect { |s| s.write state }
|
|
144
141
|
rescue StandardError => e
|
|
145
142
|
raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
|
|
146
143
|
end
|
data/lib/puma/server.rb
CHANGED
|
@@ -18,6 +18,9 @@ require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
|
|
18
18
|
|
|
19
19
|
module Puma
|
|
20
20
|
|
|
21
|
+
# This method was private on Ruby 2.4 but became public on Ruby 2.5+:
|
|
22
|
+
Thread.send(:attr_accessor, :puma_server)
|
|
23
|
+
|
|
21
24
|
# The HTTP Server itself. Serves out a single Rack app.
|
|
22
25
|
#
|
|
23
26
|
# This class is used by the `Puma::Single` and `Puma::Cluster` classes
|
|
@@ -32,6 +35,7 @@ module Puma
|
|
|
32
35
|
include Puma::Const
|
|
33
36
|
include Request
|
|
34
37
|
|
|
38
|
+
attr_reader :options
|
|
35
39
|
attr_reader :thread
|
|
36
40
|
attr_reader :log_writer
|
|
37
41
|
attr_reader :events
|
|
@@ -46,7 +50,6 @@ module Puma
|
|
|
46
50
|
attr_accessor :app
|
|
47
51
|
attr_accessor :binder
|
|
48
52
|
|
|
49
|
-
THREAD_LOCAL_KEY = :puma_server
|
|
50
53
|
|
|
51
54
|
# Create a server for the rack app +app+.
|
|
52
55
|
#
|
|
@@ -81,6 +84,8 @@ module Puma
|
|
|
81
84
|
UserFileDefaultOptions.new(options, Configuration::DEFAULTS)
|
|
82
85
|
end
|
|
83
86
|
|
|
87
|
+
@clustered = (@options.fetch :workers, 0) > 0
|
|
88
|
+
@worker_write = @options[:worker_write]
|
|
84
89
|
@log_writer = @options.fetch :log_writer, LogWriter.stdio
|
|
85
90
|
@early_hints = @options[:early_hints]
|
|
86
91
|
@first_data_timeout = @options[:first_data_timeout]
|
|
@@ -90,6 +95,7 @@ module Puma
|
|
|
90
95
|
@max_threads = @options[:max_threads]
|
|
91
96
|
@queue_requests = @options[:queue_requests]
|
|
92
97
|
@max_fast_inline = @options[:max_fast_inline]
|
|
98
|
+
@enable_keep_alives = @options[:enable_keep_alives]
|
|
93
99
|
@io_selector_backend = @options[:io_selector_backend]
|
|
94
100
|
@http_content_length_limit = @options[:http_content_length_limit]
|
|
95
101
|
|
|
@@ -117,6 +123,8 @@ module Puma
|
|
|
117
123
|
@precheck_closing = true
|
|
118
124
|
|
|
119
125
|
@requests_count = 0
|
|
126
|
+
|
|
127
|
+
@idle_timeout_reached = false
|
|
120
128
|
end
|
|
121
129
|
|
|
122
130
|
def inherit_binder(bind)
|
|
@@ -126,7 +134,7 @@ module Puma
|
|
|
126
134
|
class << self
|
|
127
135
|
# @!attribute [r] current
|
|
128
136
|
def current
|
|
129
|
-
Thread.current
|
|
137
|
+
Thread.current.puma_server
|
|
130
138
|
end
|
|
131
139
|
|
|
132
140
|
# :nodoc:
|
|
@@ -225,6 +233,11 @@ module Puma
|
|
|
225
233
|
@thread_pool&.pool_capacity
|
|
226
234
|
end
|
|
227
235
|
|
|
236
|
+
# @!attribute [r] busy_threads
|
|
237
|
+
def busy_threads
|
|
238
|
+
@thread_pool&.busy_threads
|
|
239
|
+
end
|
|
240
|
+
|
|
228
241
|
# Runs the server.
|
|
229
242
|
#
|
|
230
243
|
# If +background+ is true (the default) then a thread is spun
|
|
@@ -238,16 +251,15 @@ module Puma
|
|
|
238
251
|
|
|
239
252
|
@status = :run
|
|
240
253
|
|
|
241
|
-
@thread_pool = ThreadPool.new(thread_name,
|
|
254
|
+
@thread_pool = ThreadPool.new(thread_name, options) { |client| process_client client }
|
|
242
255
|
|
|
243
256
|
if @queue_requests
|
|
244
257
|
@reactor = Reactor.new(@io_selector_backend) { |c| reactor_wakeup c }
|
|
245
258
|
@reactor.run
|
|
246
259
|
end
|
|
247
260
|
|
|
248
|
-
|
|
249
|
-
@thread_pool.
|
|
250
|
-
@thread_pool.auto_trim! if @options[:auto_trim_time]
|
|
261
|
+
@thread_pool.auto_reap! if options[:reaping_time]
|
|
262
|
+
@thread_pool.auto_trim! if @min_threads != @max_threads && options[:auto_trim_time]
|
|
251
263
|
|
|
252
264
|
@check, @notify = Puma::Util.pipe unless @notify
|
|
253
265
|
|
|
@@ -311,15 +323,15 @@ module Puma
|
|
|
311
323
|
sockets = [check] + @binder.ios
|
|
312
324
|
pool = @thread_pool
|
|
313
325
|
queue_requests = @queue_requests
|
|
314
|
-
drain =
|
|
326
|
+
drain = options[:drain_on_shutdown] ? 0 : nil
|
|
315
327
|
|
|
316
|
-
addr_send_name, addr_value = case
|
|
328
|
+
addr_send_name, addr_value = case options[:remote_address]
|
|
317
329
|
when :value
|
|
318
|
-
[:peerip=,
|
|
330
|
+
[:peerip=, options[:remote_address_value]]
|
|
319
331
|
when :header
|
|
320
|
-
[:remote_addr_header=,
|
|
332
|
+
[:remote_addr_header=, options[:remote_address_header]]
|
|
321
333
|
when :proxy_protocol
|
|
322
|
-
[:expect_proxy_proto=,
|
|
334
|
+
[:expect_proxy_proto=, options[:remote_address_proxy_protocol]]
|
|
323
335
|
else
|
|
324
336
|
[nil, nil]
|
|
325
337
|
end
|
|
@@ -328,16 +340,32 @@ module Puma
|
|
|
328
340
|
begin
|
|
329
341
|
ios = IO.select sockets, nil, nil, (shutting_down? ? 0 : @idle_timeout)
|
|
330
342
|
unless ios
|
|
331
|
-
|
|
343
|
+
unless shutting_down?
|
|
344
|
+
@idle_timeout_reached = true
|
|
345
|
+
|
|
346
|
+
if @clustered
|
|
347
|
+
@worker_write << "#{PipeRequest::PIPE_IDLE}#{Process.pid}\n" rescue nil
|
|
348
|
+
next
|
|
349
|
+
else
|
|
350
|
+
@log_writer.log "- Idle timeout reached"
|
|
351
|
+
@status = :stop
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
332
355
|
break
|
|
333
356
|
end
|
|
334
357
|
|
|
358
|
+
if @idle_timeout_reached && @clustered
|
|
359
|
+
@idle_timeout_reached = false
|
|
360
|
+
@worker_write << "#{PipeRequest::PIPE_IDLE}#{Process.pid}\n" rescue nil
|
|
361
|
+
end
|
|
362
|
+
|
|
335
363
|
ios.first.each do |sock|
|
|
336
364
|
if sock == check
|
|
337
365
|
break if handle_check
|
|
338
366
|
else
|
|
339
367
|
pool.wait_until_not_full
|
|
340
|
-
pool.wait_for_less_busy_worker(
|
|
368
|
+
pool.wait_for_less_busy_worker(options[:wait_for_less_busy_worker]) if @clustered
|
|
341
369
|
|
|
342
370
|
io = begin
|
|
343
371
|
sock.accept_nonblock
|
|
@@ -367,6 +395,7 @@ module Puma
|
|
|
367
395
|
@queue_requests = false
|
|
368
396
|
@reactor.shutdown
|
|
369
397
|
end
|
|
398
|
+
|
|
370
399
|
graceful_shutdown if @status == :stop || @status == :restart
|
|
371
400
|
rescue Exception => e
|
|
372
401
|
@log_writer.unknown_error e, nil, "Exception handling servers"
|
|
@@ -416,9 +445,9 @@ module Puma
|
|
|
416
445
|
# Return true if one or more requests were processed.
|
|
417
446
|
def process_client(client)
|
|
418
447
|
# Advertise this server into the thread
|
|
419
|
-
Thread.current
|
|
448
|
+
Thread.current.puma_server = self
|
|
420
449
|
|
|
421
|
-
clean_thread_locals =
|
|
450
|
+
clean_thread_locals = options[:clean_thread_locals]
|
|
422
451
|
close_socket = true
|
|
423
452
|
|
|
424
453
|
requests = 0
|
|
@@ -527,7 +556,7 @@ module Puma
|
|
|
527
556
|
# A fallback rack response if +@app+ raises as exception.
|
|
528
557
|
#
|
|
529
558
|
def lowlevel_error(e, env, status=500)
|
|
530
|
-
if handler =
|
|
559
|
+
if handler = options[:lowlevel_error_handler]
|
|
531
560
|
if handler.arity == 1
|
|
532
561
|
return handler.call(e)
|
|
533
562
|
elsif handler.arity == 2
|
|
@@ -548,14 +577,13 @@ module Puma
|
|
|
548
577
|
def response_to_error(client, requests, err, status_code)
|
|
549
578
|
status, headers, res_body = lowlevel_error(err, client.env, status_code)
|
|
550
579
|
prepare_response(status, headers, res_body, requests, client)
|
|
551
|
-
client.write_error(status_code)
|
|
552
580
|
end
|
|
553
581
|
private :response_to_error
|
|
554
582
|
|
|
555
583
|
# Wait for all outstanding requests to finish.
|
|
556
584
|
#
|
|
557
585
|
def graceful_shutdown
|
|
558
|
-
if
|
|
586
|
+
if options[:shutdown_debug]
|
|
559
587
|
threads = Thread.list
|
|
560
588
|
total = threads.size
|
|
561
589
|
|
|
@@ -575,7 +603,7 @@ module Puma
|
|
|
575
603
|
end
|
|
576
604
|
|
|
577
605
|
if @thread_pool
|
|
578
|
-
if timeout =
|
|
606
|
+
if timeout = options[:force_shutdown_after]
|
|
579
607
|
@thread_pool.shutdown timeout.to_f
|
|
580
608
|
else
|
|
581
609
|
@thread_pool.shutdown
|
|
@@ -622,13 +650,17 @@ module Puma
|
|
|
622
650
|
|
|
623
651
|
# List of methods invoked by #stats.
|
|
624
652
|
# @version 5.0.0
|
|
625
|
-
STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count].freeze
|
|
653
|
+
STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count, :busy_threads].freeze
|
|
626
654
|
|
|
627
655
|
# Returns a hash of stats about the running server for reporting purposes.
|
|
628
656
|
# @version 5.0.0
|
|
629
657
|
# @!attribute [r] stats
|
|
658
|
+
# @return [Hash] hash containing stat info from `Server` and `ThreadPool`
|
|
630
659
|
def stats
|
|
631
|
-
|
|
660
|
+
stats = @thread_pool&.stats || {}
|
|
661
|
+
stats[:max_threads] = @max_threads
|
|
662
|
+
stats[:requests_count] = @requests_count
|
|
663
|
+
stats
|
|
632
664
|
end
|
|
633
665
|
|
|
634
666
|
# below are 'delegations' to binder
|
data/lib/puma/single.rb
CHANGED
data/lib/puma/state_file.rb
CHANGED