puma 6.4.1 → 7.2.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 +407 -8
- data/README.md +109 -49
- data/docs/deployment.md +58 -23
- data/docs/fork_worker.md +11 -1
- data/docs/java_options.md +54 -0
- data/docs/jungle/README.md +1 -1
- data/docs/kubernetes.md +11 -16
- data/docs/plugins.md +6 -2
- data/docs/restart.md +2 -2
- data/docs/signals.md +21 -21
- data/docs/stats.md +11 -5
- data/docs/systemd.md +14 -5
- data/ext/puma_http11/extconf.rb +20 -32
- data/ext/puma_http11/mini_ssl.c +29 -9
- data/ext/puma_http11/org/jruby/puma/Http11.java +40 -9
- data/ext/puma_http11/puma_http11.c +125 -118
- data/lib/puma/app/status.rb +11 -3
- data/lib/puma/binder.rb +21 -11
- data/lib/puma/cli.rb +10 -8
- data/lib/puma/client.rb +183 -83
- data/lib/puma/cluster/worker.rb +24 -21
- data/lib/puma/cluster/worker_handle.rb +38 -8
- data/lib/puma/cluster.rb +73 -47
- data/lib/puma/cluster_accept_loop_delay.rb +91 -0
- data/lib/puma/commonlogger.rb +3 -3
- data/lib/puma/configuration.rb +131 -60
- data/lib/puma/const.rb +31 -12
- data/lib/puma/control_cli.rb +10 -6
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +411 -121
- data/lib/puma/error_logger.rb +7 -5
- data/lib/puma/events.rb +25 -10
- data/lib/puma/io_buffer.rb +8 -4
- data/lib/puma/jruby_restart.rb +0 -16
- data/lib/puma/launcher/bundle_pruner.rb +1 -1
- data/lib/puma/launcher.rb +73 -55
- data/lib/puma/log_writer.rb +9 -9
- data/lib/puma/minissl/context_builder.rb +1 -0
- data/lib/puma/minissl.rb +1 -1
- data/lib/puma/null_io.rb +26 -0
- data/lib/puma/plugin/systemd.rb +3 -3
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/reactor.rb +19 -13
- data/lib/puma/request.rb +71 -39
- data/lib/puma/runner.rb +15 -17
- data/lib/puma/sd_notify.rb +1 -4
- data/lib/puma/server.rb +134 -73
- data/lib/puma/single.rb +7 -4
- data/lib/puma/state_file.rb +3 -2
- data/lib/puma/thread_pool.rb +57 -80
- data/lib/puma/util.rb +0 -7
- data/lib/puma.rb +10 -0
- data/lib/rack/handler/puma.rb +10 -7
- data/tools/Dockerfile +15 -5
- metadata +14 -15
- data/ext/puma_http11/ext_help.h +0 -15
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.
|
|
@@ -78,10 +78,12 @@ module Puma
|
|
|
78
78
|
def request_title(req)
|
|
79
79
|
env = req.env
|
|
80
80
|
|
|
81
|
+
query_string = env[QUERY_STRING]
|
|
82
|
+
|
|
81
83
|
REQUEST_FORMAT % [
|
|
82
84
|
env[REQUEST_METHOD],
|
|
83
85
|
env[REQUEST_PATH] || env[PATH_INFO],
|
|
84
|
-
|
|
86
|
+
query_string.nil? || query_string.empty? ? "" : "?#{query_string}",
|
|
85
87
|
env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-"
|
|
86
88
|
]
|
|
87
89
|
end
|
data/lib/puma/events.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Puma
|
|
4
4
|
|
|
5
5
|
# This is an event sink used by `Puma::Server` to handle
|
|
6
|
-
# lifecycle events such as :
|
|
6
|
+
# lifecycle events such as :after_booted, :before_restart, and :after_stopped.
|
|
7
7
|
# Using `Puma::DSL` it is possible to register callback hooks
|
|
8
8
|
# for each event type.
|
|
9
9
|
class Events
|
|
@@ -30,28 +30,43 @@ module Puma
|
|
|
30
30
|
h
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def after_booted(&block)
|
|
34
|
+
register(:after_booted, &block)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def before_restart(&block)
|
|
38
|
+
register(:before_restart, &block)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def after_stopped(&block)
|
|
42
|
+
register(:after_stopped, &block)
|
|
43
|
+
end
|
|
44
|
+
|
|
33
45
|
def on_booted(&block)
|
|
34
|
-
|
|
46
|
+
Puma.deprecate_method_change :on_booted, __callee__, :after_booted
|
|
47
|
+
after_booted(&block)
|
|
35
48
|
end
|
|
36
49
|
|
|
37
50
|
def on_restart(&block)
|
|
38
|
-
|
|
51
|
+
Puma.deprecate_method_change :on_restart, __callee__, :before_restart
|
|
52
|
+
before_restart(&block)
|
|
39
53
|
end
|
|
40
54
|
|
|
41
55
|
def on_stopped(&block)
|
|
42
|
-
|
|
56
|
+
Puma.deprecate_method_change :on_stopped, __callee__, :after_stopped
|
|
57
|
+
after_stopped(&block)
|
|
43
58
|
end
|
|
44
59
|
|
|
45
|
-
def
|
|
46
|
-
fire(:
|
|
60
|
+
def fire_after_booted!
|
|
61
|
+
fire(:after_booted)
|
|
47
62
|
end
|
|
48
63
|
|
|
49
|
-
def
|
|
50
|
-
fire(:
|
|
64
|
+
def fire_before_restart!
|
|
65
|
+
fire(:before_restart)
|
|
51
66
|
end
|
|
52
67
|
|
|
53
|
-
def
|
|
54
|
-
fire(:
|
|
68
|
+
def fire_after_stopped!
|
|
69
|
+
fire(:after_stopped)
|
|
55
70
|
end
|
|
56
71
|
end
|
|
57
72
|
end
|
data/lib/puma/io_buffer.rb
CHANGED
|
@@ -34,13 +34,17 @@ module Puma
|
|
|
34
34
|
|
|
35
35
|
alias_method :clear, :reset
|
|
36
36
|
|
|
37
|
-
#
|
|
38
|
-
if
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
# Create an `IoBuffer#append` method that accepts multiple strings and writes them
|
|
38
|
+
if RUBY_ENGINE == 'truffleruby'
|
|
39
|
+
# truffleruby (24.2.1, like ruby 3.3.7)
|
|
40
|
+
# StringIO.new.write("a", "b") # => `write': wrong number of arguments (given 2, expected 1) (ArgumentError)
|
|
41
41
|
def append(*strs)
|
|
42
42
|
strs.each { |str| write str }
|
|
43
43
|
end
|
|
44
|
+
else
|
|
45
|
+
# Ruby 3+
|
|
46
|
+
# StringIO.new.write("a", "b") # => 2
|
|
47
|
+
alias_method :append, :write
|
|
44
48
|
end
|
|
45
49
|
end
|
|
46
50
|
end
|
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
|
|
@@ -37,7 +37,7 @@ module Puma
|
|
|
37
37
|
ENV['PUMA_BUNDLER_PRUNED'] = '1'
|
|
38
38
|
ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
|
|
39
39
|
args = [Gem.ruby, puma_wild_path, '-I', dirs.join(':')] + @original_argv
|
|
40
|
-
#
|
|
40
|
+
# Defaults to true which breaks socket activation
|
|
41
41
|
args += [{:close_others => false}]
|
|
42
42
|
Kernel.exec(*args)
|
|
43
43
|
end
|
data/lib/puma/launcher.rb
CHANGED
|
@@ -22,12 +22,15 @@ module Puma
|
|
|
22
22
|
#
|
|
23
23
|
# +conf+ A Puma::Configuration object indicating how to run the server.
|
|
24
24
|
#
|
|
25
|
-
# +launcher_args+ A Hash that
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
25
|
+
# +launcher_args+ A Hash that has a few optional keys.
|
|
26
|
+
# - +:log_writer+:: Expected to hold an object similar to `Puma::LogWriter.stdio`.
|
|
27
|
+
# This object will be responsible for broadcasting Puma's internal state
|
|
28
|
+
# to a logging destination.
|
|
29
|
+
# - +:events+:: Expected to hold an object similar to `Puma::Events`.
|
|
30
|
+
# - +:argv+:: Expected to be an array of strings.
|
|
31
|
+
# - +:env+:: Expected to hold a hash of environment variables.
|
|
32
|
+
#
|
|
33
|
+
# These arguments are re-used when restarting the puma server.
|
|
31
34
|
#
|
|
32
35
|
# Examples:
|
|
33
36
|
#
|
|
@@ -39,64 +42,67 @@ module Puma
|
|
|
39
42
|
# end
|
|
40
43
|
# Puma::Launcher.new(conf, log_writer: Puma::LogWriter.stdio).run
|
|
41
44
|
def initialize(conf, launcher_args={})
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@
|
|
45
|
-
|
|
45
|
+
## Minimal initialization before potential early restart (e.g. from bundle pruning)
|
|
46
|
+
|
|
47
|
+
@config = conf
|
|
48
|
+
# Advertise the CLI Configuration before config files are loaded
|
|
49
|
+
Puma.cli_config = @config if defined?(Puma.cli_config)
|
|
50
|
+
@config.clamp
|
|
51
|
+
|
|
52
|
+
@options = @config.options
|
|
53
|
+
|
|
54
|
+
@log_writer = launcher_args[:log_writer] || LogWriter::DEFAULT
|
|
55
|
+
@log_writer.formatter = LogWriter::PidFormatter.new if clustered?
|
|
56
|
+
@log_writer.formatter = @options[:log_formatter] if @options[:log_formatter]
|
|
57
|
+
@log_writer.custom_logger = @options[:custom_logger] if @options[:custom_logger]
|
|
58
|
+
@options[:log_writer] = @log_writer
|
|
59
|
+
@options[:logger] = @log_writer if clustered?
|
|
60
|
+
|
|
61
|
+
@events = launcher_args[:events] || Events.new
|
|
62
|
+
|
|
63
|
+
@argv = launcher_args[:argv] || []
|
|
46
64
|
@original_argv = @argv.dup
|
|
47
|
-
@config = conf
|
|
48
65
|
|
|
49
|
-
|
|
66
|
+
## End minimal initialization
|
|
50
67
|
|
|
51
|
-
|
|
52
|
-
|
|
68
|
+
generate_restart_data
|
|
69
|
+
Dir.chdir(@restart_dir)
|
|
70
|
+
|
|
71
|
+
prune_bundler!
|
|
72
|
+
|
|
73
|
+
env = launcher_args.delete(:env) || ENV
|
|
53
74
|
|
|
54
|
-
|
|
75
|
+
# Log after prune_bundler! to avoid duplicate logging if a restart occurs
|
|
76
|
+
log_config if env['PUMA_LOG_CONFIG']
|
|
55
77
|
|
|
56
|
-
@binder
|
|
57
|
-
@binder.create_inherited_fds(
|
|
58
|
-
@binder.create_activated_fds(
|
|
78
|
+
@binder = Binder.new(@log_writer, @options)
|
|
79
|
+
@binder.create_inherited_fds(env).each { |k| env.delete k }
|
|
80
|
+
@binder.create_activated_fds(env).each { |k| env.delete k }
|
|
59
81
|
|
|
60
|
-
@environment =
|
|
82
|
+
@environment = @config.environment
|
|
61
83
|
|
|
62
84
|
# Load the systemd integration if we detect systemd's NOTIFY_SOCKET.
|
|
63
85
|
# Skip this on JRuby though, because it is incompatible with the systemd
|
|
64
86
|
# integration due to https://github.com/jruby/jruby/issues/6504
|
|
65
|
-
if ENV["NOTIFY_SOCKET"] && !Puma.jruby?
|
|
87
|
+
if ENV["NOTIFY_SOCKET"] && !Puma.jruby? && !ENV["PUMA_SKIP_SYSTEMD"]
|
|
66
88
|
@config.plugins.create('systemd')
|
|
67
89
|
end
|
|
68
90
|
|
|
69
|
-
if @
|
|
70
|
-
@
|
|
71
|
-
@
|
|
72
|
-
@
|
|
91
|
+
if @options[:bind_to_activated_sockets]
|
|
92
|
+
@options[:binds] = @binder.synthesize_binds_from_activated_fs(
|
|
93
|
+
@options[:binds],
|
|
94
|
+
@options[:bind_to_activated_sockets] == 'only'
|
|
73
95
|
)
|
|
74
96
|
end
|
|
75
97
|
|
|
76
|
-
@options = @config.options
|
|
77
|
-
@config.clamp
|
|
78
|
-
|
|
79
|
-
@log_writer.formatter = LogWriter::PidFormatter.new if clustered?
|
|
80
|
-
@log_writer.formatter = options[:log_formatter] if @options[:log_formatter]
|
|
81
|
-
|
|
82
|
-
@log_writer.custom_logger = options[:custom_logger] if @options[:custom_logger]
|
|
83
|
-
|
|
84
|
-
generate_restart_data
|
|
85
|
-
|
|
86
98
|
if clustered? && !Puma.forkable?
|
|
87
99
|
unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
|
|
88
100
|
end
|
|
89
101
|
|
|
90
|
-
Dir.chdir(@restart_dir)
|
|
91
|
-
|
|
92
|
-
prune_bundler!
|
|
93
|
-
|
|
94
102
|
@environment = @options[:environment] if @options[:environment]
|
|
95
103
|
set_rack_environment
|
|
96
104
|
|
|
97
105
|
if clustered?
|
|
98
|
-
@options[:logger] = @log_writer
|
|
99
|
-
|
|
100
106
|
@runner = Cluster.new(self)
|
|
101
107
|
else
|
|
102
108
|
@runner = Single.new(self)
|
|
@@ -104,8 +110,6 @@ module Puma
|
|
|
104
110
|
Puma.stats_object = @runner
|
|
105
111
|
|
|
106
112
|
@status = :run
|
|
107
|
-
|
|
108
|
-
log_config if ENV['PUMA_LOG_CONFIG']
|
|
109
113
|
end
|
|
110
114
|
|
|
111
115
|
attr_reader :binder, :log_writer, :events, :config, :options, :restart_dir
|
|
@@ -138,7 +142,10 @@ module Puma
|
|
|
138
142
|
# Delete the configured pidfile
|
|
139
143
|
def delete_pidfile
|
|
140
144
|
path = @options[:pidfile]
|
|
141
|
-
|
|
145
|
+
begin
|
|
146
|
+
File.unlink(path) if path
|
|
147
|
+
rescue Errno::ENOENT
|
|
148
|
+
end
|
|
142
149
|
end
|
|
143
150
|
|
|
144
151
|
# Begin async shutdown of the server
|
|
@@ -165,6 +172,13 @@ module Puma
|
|
|
165
172
|
log "* phased-restart called but not available, restarting normally."
|
|
166
173
|
return restart
|
|
167
174
|
end
|
|
175
|
+
|
|
176
|
+
if @options.file_options[:tag].nil?
|
|
177
|
+
dir = File.realdirpath(@restart_dir)
|
|
178
|
+
@options[:tag] = File.basename(dir)
|
|
179
|
+
set_process_title
|
|
180
|
+
end
|
|
181
|
+
|
|
168
182
|
true
|
|
169
183
|
end
|
|
170
184
|
|
|
@@ -268,7 +282,7 @@ module Puma
|
|
|
268
282
|
end
|
|
269
283
|
|
|
270
284
|
def do_graceful_stop
|
|
271
|
-
@events.
|
|
285
|
+
@events.fire_after_stopped!
|
|
272
286
|
@runner.stop_blocked
|
|
273
287
|
end
|
|
274
288
|
|
|
@@ -280,14 +294,16 @@ module Puma
|
|
|
280
294
|
end
|
|
281
295
|
|
|
282
296
|
def restart!
|
|
283
|
-
@events.
|
|
284
|
-
@config.run_hooks :
|
|
297
|
+
@events.fire_before_restart!
|
|
298
|
+
@config.run_hooks :before_restart, self, @log_writer
|
|
285
299
|
|
|
286
300
|
if Puma.jruby?
|
|
287
301
|
close_binder_listeners
|
|
288
302
|
|
|
289
303
|
require_relative 'jruby_restart'
|
|
290
|
-
|
|
304
|
+
argv = restart_args
|
|
305
|
+
JRubyRestart.chdir(@restart_dir)
|
|
306
|
+
Kernel.exec(*argv)
|
|
291
307
|
elsif Puma.windows?
|
|
292
308
|
close_binder_listeners
|
|
293
309
|
|
|
@@ -371,9 +387,9 @@ module Puma
|
|
|
371
387
|
# using it.
|
|
372
388
|
@restart_dir = Dir.pwd
|
|
373
389
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
390
|
+
# Use the same trick as unicorn, namely favor PWD because
|
|
391
|
+
# it will contain an unresolved symlink, useful for when
|
|
392
|
+
# the pwd is /data/releases/current.
|
|
377
393
|
elsif dir = ENV['PWD']
|
|
378
394
|
s_env = File.stat(dir)
|
|
379
395
|
s_pwd = File.stat(Dir.pwd)
|
|
@@ -408,12 +424,14 @@ module Puma
|
|
|
408
424
|
end
|
|
409
425
|
|
|
410
426
|
def setup_signals
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
427
|
+
unless ENV["PUMA_SKIP_SIGUSR2"]
|
|
428
|
+
begin
|
|
429
|
+
Signal.trap "SIGUSR2" do
|
|
430
|
+
restart
|
|
431
|
+
end
|
|
432
|
+
rescue Exception
|
|
433
|
+
log "*** SIGUSR2 not implemented, signal based restart unavailable!"
|
|
414
434
|
end
|
|
415
|
-
rescue Exception
|
|
416
|
-
log "*** SIGUSR2 not implemented, signal based restart unavailable!"
|
|
417
435
|
end
|
|
418
436
|
|
|
419
437
|
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+
|
|
@@ -57,6 +57,7 @@ module Puma
|
|
|
57
57
|
|
|
58
58
|
ctx.ca = params['ca'] if params['ca']
|
|
59
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
|
|
60
61
|
|
|
61
62
|
ctx.reuse = params['reuse'] if params['reuse']
|
|
62
63
|
end
|
data/lib/puma/minissl.rb
CHANGED
|
@@ -172,7 +172,6 @@ module Puma
|
|
|
172
172
|
end
|
|
173
173
|
end
|
|
174
174
|
rescue IOError, SystemCallError
|
|
175
|
-
Puma::Util.purge_interrupt_queue
|
|
176
175
|
# nothing
|
|
177
176
|
ensure
|
|
178
177
|
@socket.close
|
|
@@ -289,6 +288,7 @@ module Puma
|
|
|
289
288
|
attr_reader :cert_pem
|
|
290
289
|
attr_reader :key_pem
|
|
291
290
|
attr_accessor :ssl_cipher_filter
|
|
291
|
+
attr_accessor :ssl_ciphersuites
|
|
292
292
|
attr_accessor :verification_flags
|
|
293
293
|
|
|
294
294
|
attr_reader :reuse, :reuse_cache_size, :reuse_timeout
|
data/lib/puma/null_io.rb
CHANGED
|
@@ -16,6 +16,10 @@ 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
25
|
def read(length = nil, buffer = nil)
|
|
@@ -39,6 +43,11 @@ module Puma
|
|
|
39
43
|
def rewind
|
|
40
44
|
end
|
|
41
45
|
|
|
46
|
+
def seek(pos, whence = 0)
|
|
47
|
+
raise ArgumentError, "negative length #{pos} given" if pos.negative?
|
|
48
|
+
0
|
|
49
|
+
end
|
|
50
|
+
|
|
42
51
|
def close
|
|
43
52
|
end
|
|
44
53
|
|
|
@@ -71,5 +80,22 @@ module Puma
|
|
|
71
80
|
def closed?
|
|
72
81
|
false
|
|
73
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
|
|
74
100
|
end
|
|
75
101
|
end
|
data/lib/puma/plugin/systemd.rb
CHANGED
|
@@ -14,9 +14,9 @@ Puma::Plugin.create do
|
|
|
14
14
|
launcher.log_writer.log "* Enabling systemd notification integration"
|
|
15
15
|
|
|
16
16
|
# hook_events
|
|
17
|
-
launcher.events.
|
|
18
|
-
launcher.events.
|
|
19
|
-
launcher.events.
|
|
17
|
+
launcher.events.after_booted { Puma::SdNotify.ready }
|
|
18
|
+
launcher.events.after_stopped { Puma::SdNotify.stopping }
|
|
19
|
+
launcher.events.before_restart { Puma::SdNotify.reloading }
|
|
20
20
|
|
|
21
21
|
# start watchdog
|
|
22
22
|
if Puma::SdNotify.watchdog?
|
data/lib/puma/rack/urlmap.rb
CHANGED
|
@@ -70,7 +70,7 @@ module Puma::Rack
|
|
|
70
70
|
return app.call(env)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
[404, {'
|
|
73
|
+
[404, {'content-type' => "text/plain", "x-cascade" => "pass"}, ["Not Found: #{path}"]]
|
|
74
74
|
|
|
75
75
|
ensure
|
|
76
76
|
env['PATH_INFO'] = path
|
data/lib/puma/reactor.rb
CHANGED
|
@@ -15,6 +15,12 @@ module Puma
|
|
|
15
15
|
#
|
|
16
16
|
# The implementation uses a Queue to synchronize adding new objects from the internal select loop.
|
|
17
17
|
class Reactor
|
|
18
|
+
|
|
19
|
+
# @!attribute [rw] reactor_max
|
|
20
|
+
# Maximum number of clients in the selector. Reset with calls to `Server.stats`.
|
|
21
|
+
attr_accessor :reactor_max
|
|
22
|
+
attr_reader :reactor_size
|
|
23
|
+
|
|
18
24
|
# Create a new Reactor to monitor IO objects added by #add.
|
|
19
25
|
# The provided block will be invoked when an IO has data available to read,
|
|
20
26
|
# its timeout elapses, or when the Reactor shuts down.
|
|
@@ -29,6 +35,8 @@ module Puma
|
|
|
29
35
|
@input = Queue.new
|
|
30
36
|
@timeouts = []
|
|
31
37
|
@block = block
|
|
38
|
+
@reactor_size = 0
|
|
39
|
+
@reactor_max = 0
|
|
32
40
|
end
|
|
33
41
|
|
|
34
42
|
# Run the internal select loop, using a background thread by default.
|
|
@@ -67,17 +75,18 @@ module Puma
|
|
|
67
75
|
private
|
|
68
76
|
|
|
69
77
|
def select_loop
|
|
70
|
-
close_selector = true
|
|
71
78
|
begin
|
|
72
79
|
until @input.closed? && @input.empty?
|
|
73
80
|
# Wakeup any registered object that receives incoming data.
|
|
74
81
|
# Block until the earliest timeout or Selector#wakeup is called.
|
|
75
82
|
timeout = (earliest = @timeouts.first) && earliest.timeout
|
|
76
|
-
@selector.select(timeout)
|
|
83
|
+
@selector.select(timeout) do |monitor|
|
|
84
|
+
wakeup!(monitor.value)
|
|
85
|
+
end
|
|
77
86
|
|
|
78
87
|
# Wakeup all objects that timed out.
|
|
79
|
-
timed_out = @timeouts.take_while {|
|
|
80
|
-
timed_out.each { |
|
|
88
|
+
timed_out = @timeouts.take_while { |client| client.timeout == 0 }
|
|
89
|
+
timed_out.each { |client| wakeup!(client) }
|
|
81
90
|
|
|
82
91
|
unless @input.empty?
|
|
83
92
|
until @input.empty?
|
|
@@ -91,23 +100,19 @@ module Puma
|
|
|
91
100
|
STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
|
|
92
101
|
STDERR.puts e.backtrace
|
|
93
102
|
|
|
94
|
-
|
|
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
|
|
103
|
+
retry
|
|
102
104
|
end
|
|
105
|
+
|
|
103
106
|
# Wakeup all remaining objects on shutdown.
|
|
104
107
|
@timeouts.each(&@block)
|
|
105
|
-
@selector.close
|
|
108
|
+
@selector.close
|
|
106
109
|
end
|
|
107
110
|
|
|
108
111
|
# Start monitoring the object.
|
|
109
112
|
def register(client)
|
|
110
113
|
@selector.register(client.to_io, :r).value = client
|
|
114
|
+
@reactor_size += 1
|
|
115
|
+
@reactor_max = @reactor_size if @reactor_max < @reactor_size
|
|
111
116
|
@timeouts << client
|
|
112
117
|
rescue ArgumentError
|
|
113
118
|
# unreadable clients raise error when processed by NIO
|
|
@@ -118,6 +123,7 @@ module Puma
|
|
|
118
123
|
def wakeup!(client)
|
|
119
124
|
if @block.call client
|
|
120
125
|
@selector.deregister client.to_io
|
|
126
|
+
@reactor_size -= 1
|
|
121
127
|
@timeouts.delete client
|
|
122
128
|
end
|
|
123
129
|
end
|