puma 4.3.12 → 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 +1591 -521
- data/LICENSE +23 -20
- data/README.md +130 -42
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +63 -26
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +60 -69
- data/docs/fork_worker.md +31 -0
- data/docs/jungle/README.md +9 -0
- data/{tools → docs}/jungle/rc.d/README.md +1 -1
- data/{tools → docs}/jungle/rc.d/puma +2 -2
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +46 -23
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +85 -128
- 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 +2 -4
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +49 -12
- data/ext/puma_http11/http11_parser.c +46 -48
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +3 -3
- data/ext/puma_http11/http11_parser.rl +3 -3
- data/ext/puma_http11/http11_parser_common.rl +2 -2
- data/ext/puma_http11/mini_ssl.c +250 -93
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +6 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +4 -6
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
- data/ext/puma_http11/puma_http11.c +46 -57
- data/lib/puma/app/status.rb +52 -38
- data/lib/puma/binder.rb +232 -119
- data/lib/puma/cli.rb +33 -33
- data/lib/puma/client.rb +125 -87
- data/lib/puma/cluster/worker.rb +175 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +224 -229
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +112 -87
- data/lib/puma/const.rb +25 -22
- data/lib/puma/control_cli.rb +99 -79
- data/lib/puma/detect.rb +31 -2
- data/lib/puma/dsl.rb +423 -110
- data/lib/puma/error_logger.rb +112 -0
- data/lib/puma/events.rb +16 -115
- data/lib/puma/io_buffer.rb +34 -2
- data/lib/puma/jruby_restart.rb +2 -59
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +170 -148
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +35 -19
- data/lib/puma/minissl.rb +213 -55
- data/lib/puma/null_io.rb +18 -1
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +3 -12
- data/lib/puma/rack/builder.rb +5 -9
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +85 -369
- data/lib/puma/request.rb +607 -0
- data/lib/puma/runner.rb +83 -77
- data/lib/puma/server.rb +305 -789
- data/lib/puma/single.rb +18 -74
- data/lib/puma/state_file.rb +45 -8
- data/lib/puma/systemd.rb +47 -0
- data/lib/puma/thread_pool.rb +137 -66
- data/lib/puma/util.rb +21 -4
- data/lib/puma.rb +54 -5
- data/lib/rack/handler/puma.rb +11 -12
- data/tools/{docker/Dockerfile → Dockerfile} +1 -1
- metadata +31 -23
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/accept_nonblock.rb +0 -29
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
- data/tools/jungle/upstart/README.md +0 -61
- data/tools/jungle/upstart/puma-manager.conf +0 -31
- data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/launcher.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
require_relative 'log_writer'
|
4
|
+
require_relative 'events'
|
5
|
+
require_relative 'detect'
|
6
|
+
require_relative 'cluster'
|
7
|
+
require_relative 'single'
|
8
|
+
require_relative 'const'
|
9
|
+
require_relative 'binder'
|
9
10
|
|
10
11
|
module Puma
|
11
12
|
# Puma::Launcher is the single entry point for starting a Puma server based on user
|
@@ -15,17 +16,14 @@ module Puma
|
|
15
16
|
# It is responsible for either launching a cluster of Puma workers or a single
|
16
17
|
# puma server.
|
17
18
|
class Launcher
|
18
|
-
|
19
|
-
|
20
|
-
:before_worker_shutdown, :before_worker_boot, :before_worker_fork,
|
21
|
-
:after_worker_boot, :before_fork, :on_restart
|
22
|
-
]
|
19
|
+
autoload :BundlePruner, 'puma/launcher/bundle_pruner'
|
20
|
+
|
23
21
|
# Returns an instance of Launcher
|
24
22
|
#
|
25
23
|
# +conf+ A Puma::Configuration object indicating how to run the server.
|
26
24
|
#
|
27
25
|
# +launcher_args+ A Hash that currently has one required key `:events`,
|
28
|
-
# this is expected to hold an object similar to an `Puma::
|
26
|
+
# this is expected to hold an object similar to an `Puma::LogWriter.stdio`,
|
29
27
|
# this object will be responsible for broadcasting Puma's internal state
|
30
28
|
# to a logging destination. An optional key `:argv` can be supplied,
|
31
29
|
# this should be an array of strings, these arguments are re-used when
|
@@ -39,60 +37,69 @@ module Puma
|
|
39
37
|
# [200, {}, ["hello world"]]
|
40
38
|
# end
|
41
39
|
# end
|
42
|
-
# Puma::Launcher.new(conf,
|
40
|
+
# Puma::Launcher.new(conf, log_writer: Puma::LogWriter.stdio).run
|
43
41
|
def initialize(conf, launcher_args={})
|
44
42
|
@runner = nil
|
45
|
-
@
|
43
|
+
@log_writer = launcher_args[:log_writer] || LogWriter::DEFAULT
|
44
|
+
@events = launcher_args[:events] || Events.new
|
46
45
|
@argv = launcher_args[:argv] || []
|
47
46
|
@original_argv = @argv.dup
|
48
47
|
@config = conf
|
49
48
|
|
50
|
-
@
|
51
|
-
@binder.import_from_env
|
52
|
-
|
53
|
-
@environment = conf.environment
|
49
|
+
@config.options[:log_writer] = @log_writer
|
54
50
|
|
55
51
|
# Advertise the Configuration
|
56
52
|
Puma.cli_config = @config if defined?(Puma.cli_config)
|
57
53
|
|
58
54
|
@config.load
|
59
55
|
|
56
|
+
@binder = Binder.new(@log_writer, conf)
|
57
|
+
@binder.create_inherited_fds(ENV).each { |k| ENV.delete k }
|
58
|
+
@binder.create_activated_fds(ENV).each { |k| ENV.delete k }
|
59
|
+
|
60
|
+
@environment = conf.environment
|
61
|
+
|
62
|
+
if @config.options[:bind_to_activated_sockets]
|
63
|
+
@config.options[:binds] = @binder.synthesize_binds_from_activated_fs(
|
64
|
+
@config.options[:binds],
|
65
|
+
@config.options[:bind_to_activated_sockets] == 'only'
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
60
69
|
@options = @config.options
|
61
70
|
@config.clamp
|
62
71
|
|
63
|
-
@
|
64
|
-
@
|
72
|
+
@log_writer.formatter = LogWriter::PidFormatter.new if clustered?
|
73
|
+
@log_writer.formatter = options[:log_formatter] if @options[:log_formatter]
|
65
74
|
|
66
75
|
generate_restart_data
|
67
76
|
|
68
|
-
if clustered? && !
|
77
|
+
if clustered? && !Puma.forkable?
|
69
78
|
unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
|
70
79
|
end
|
71
80
|
|
72
|
-
if @options[:daemon] && Puma.windows?
|
73
|
-
unsupported 'daemon mode not supported on Windows'
|
74
|
-
end
|
75
|
-
|
76
81
|
Dir.chdir(@restart_dir)
|
77
82
|
|
78
|
-
prune_bundler
|
83
|
+
prune_bundler!
|
79
84
|
|
80
85
|
@environment = @options[:environment] if @options[:environment]
|
81
86
|
set_rack_environment
|
82
87
|
|
83
88
|
if clustered?
|
84
|
-
@options[:logger] = @
|
89
|
+
@options[:logger] = @log_writer
|
85
90
|
|
86
|
-
@runner = Cluster.new(self
|
91
|
+
@runner = Cluster.new(self)
|
87
92
|
else
|
88
|
-
@runner = Single.new(self
|
93
|
+
@runner = Single.new(self)
|
89
94
|
end
|
90
95
|
Puma.stats_object = @runner
|
91
96
|
|
92
97
|
@status = :run
|
98
|
+
|
99
|
+
log_config if ENV['PUMA_LOG_CONFIG']
|
93
100
|
end
|
94
101
|
|
95
|
-
attr_reader :binder, :events, :config, :options, :restart_dir
|
102
|
+
attr_reader :binder, :log_writer, :events, :config, :options, :restart_dir
|
96
103
|
|
97
104
|
# Return stats about the server
|
98
105
|
def stats
|
@@ -105,16 +112,18 @@ module Puma
|
|
105
112
|
write_pid
|
106
113
|
|
107
114
|
path = @options[:state]
|
115
|
+
permission = @options[:state_permission]
|
108
116
|
return unless path
|
109
117
|
|
110
|
-
|
118
|
+
require_relative 'state_file'
|
111
119
|
|
112
120
|
sf = StateFile.new
|
113
121
|
sf.pid = Process.pid
|
114
122
|
sf.control_url = @options[:control_url]
|
115
123
|
sf.control_auth_token = @options[:control_auth_token]
|
124
|
+
sf.running_from = File.expand_path('.')
|
116
125
|
|
117
|
-
sf.save path
|
126
|
+
sf.save path, permission
|
118
127
|
end
|
119
128
|
|
120
129
|
# Delete the configured pidfile
|
@@ -150,18 +159,20 @@ module Puma
|
|
150
159
|
true
|
151
160
|
end
|
152
161
|
|
162
|
+
# Begin a refork if supported
|
163
|
+
def refork
|
164
|
+
if clustered? && @runner.respond_to?(:fork_worker!) && @options[:fork_worker]
|
165
|
+
@runner.fork_worker!
|
166
|
+
true
|
167
|
+
else
|
168
|
+
log "* refork called but not available."
|
169
|
+
false
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
153
173
|
# Run the server. This blocks until the server is stopped
|
154
174
|
def run
|
155
|
-
previous_env =
|
156
|
-
if defined?(Bundler)
|
157
|
-
env = Bundler::ORIGINAL_ENV.dup
|
158
|
-
# add -rbundler/setup so we load from Gemfile when restarting
|
159
|
-
bundle = "-rbundler/setup"
|
160
|
-
env["RUBYOPT"] = [env["RUBYOPT"], bundle].join(" ").lstrip unless env["RUBYOPT"].to_s.include?(bundle)
|
161
|
-
env
|
162
|
-
else
|
163
|
-
ENV.to_h
|
164
|
-
end
|
175
|
+
previous_env = get_env
|
165
176
|
|
166
177
|
@config.clamp
|
167
178
|
|
@@ -169,29 +180,22 @@ module Puma
|
|
169
180
|
|
170
181
|
setup_signals
|
171
182
|
set_process_title
|
183
|
+
integrate_with_systemd
|
184
|
+
|
185
|
+
# This blocks until the server is stopped
|
172
186
|
@runner.run
|
173
187
|
|
174
|
-
|
175
|
-
when :halt
|
176
|
-
log "* Stopping immediately!"
|
177
|
-
when :run, :stop
|
178
|
-
graceful_stop
|
179
|
-
when :restart
|
180
|
-
log "* Restarting..."
|
181
|
-
ENV.replace(previous_env)
|
182
|
-
@runner.before_restart
|
183
|
-
restart!
|
184
|
-
when :exit
|
185
|
-
# nothing
|
186
|
-
end
|
187
|
-
@binder.close_unix_paths
|
188
|
+
do_run_finished(previous_env)
|
188
189
|
end
|
189
190
|
|
190
|
-
# Return
|
191
|
-
|
192
|
-
|
191
|
+
# Return all tcp ports the launcher may be using, TCP or SSL
|
192
|
+
# @!attribute [r] connected_ports
|
193
|
+
# @version 5.0.0
|
194
|
+
def connected_ports
|
195
|
+
@binder.connected_ports
|
193
196
|
end
|
194
197
|
|
198
|
+
# @!attribute [r] restart_args
|
195
199
|
def restart_args
|
196
200
|
cmd = @options[:restart_cmd]
|
197
201
|
if cmd
|
@@ -202,35 +206,79 @@ module Puma
|
|
202
206
|
end
|
203
207
|
|
204
208
|
def close_binder_listeners
|
209
|
+
@runner.close_control_listeners
|
205
210
|
@binder.close_listeners
|
211
|
+
unless @status == :restart
|
212
|
+
log "=== puma shutdown: #{Time.now} ==="
|
213
|
+
log "- Goodbye!"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# @!attribute [r] thread_status
|
218
|
+
# @version 5.0.0
|
219
|
+
def thread_status
|
220
|
+
Thread.list.each do |thread|
|
221
|
+
name = "Thread: TID-#{thread.object_id.to_s(36)}"
|
222
|
+
name += " #{thread['label']}" if thread['label']
|
223
|
+
name += " #{thread.name}" if thread.respond_to?(:name) && thread.name
|
224
|
+
backtrace = thread.backtrace || ["<no backtrace available>"]
|
225
|
+
|
226
|
+
yield name, backtrace
|
227
|
+
end
|
206
228
|
end
|
207
229
|
|
208
230
|
private
|
209
231
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
232
|
+
def get_env
|
233
|
+
if defined?(Bundler)
|
234
|
+
env = Bundler::ORIGINAL_ENV.dup
|
235
|
+
# add -rbundler/setup so we load from Gemfile when restarting
|
236
|
+
bundle = "-rbundler/setup"
|
237
|
+
env["RUBYOPT"] = [env["RUBYOPT"], bundle].join(" ").lstrip unless env["RUBYOPT"].to_s.include?(bundle)
|
238
|
+
env
|
239
|
+
else
|
240
|
+
ENV.to_h
|
241
|
+
end
|
242
|
+
end
|
215
243
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
244
|
+
def do_run_finished(previous_env)
|
245
|
+
case @status
|
246
|
+
when :halt
|
247
|
+
do_forceful_stop
|
248
|
+
when :run, :stop
|
249
|
+
do_graceful_stop
|
250
|
+
when :restart
|
251
|
+
do_restart(previous_env)
|
220
252
|
end
|
253
|
+
|
254
|
+
close_binder_listeners unless @status == :restart
|
221
255
|
end
|
222
256
|
|
223
|
-
def
|
224
|
-
|
257
|
+
def do_forceful_stop
|
258
|
+
log "* Stopping immediately!"
|
259
|
+
@runner.stop_control
|
260
|
+
end
|
261
|
+
|
262
|
+
def do_graceful_stop
|
263
|
+
@events.fire_on_stopped!
|
264
|
+
@runner.stop_blocked
|
265
|
+
end
|
266
|
+
|
267
|
+
def do_restart(previous_env)
|
268
|
+
log "* Restarting..."
|
269
|
+
ENV.replace(previous_env)
|
270
|
+
@runner.stop_control
|
271
|
+
restart!
|
225
272
|
end
|
226
273
|
|
227
274
|
def restart!
|
228
|
-
@
|
275
|
+
@events.fire_on_restart!
|
276
|
+
@config.run_hooks :on_restart, self, @log_writer
|
229
277
|
|
230
278
|
if Puma.jruby?
|
231
279
|
close_binder_listeners
|
232
280
|
|
233
|
-
|
281
|
+
require_relative 'jruby_restart'
|
234
282
|
JRubyRestart.chdir_exec(@restart_dir, restart_args)
|
235
283
|
elsif Puma.windows?
|
236
284
|
close_binder_listeners
|
@@ -241,71 +289,51 @@ module Puma
|
|
241
289
|
else
|
242
290
|
argv = restart_args
|
243
291
|
Dir.chdir(@restart_dir)
|
292
|
+
ENV.update(@binder.redirects_for_restart_env)
|
244
293
|
argv += [@binder.redirects_for_restart]
|
245
294
|
Kernel.exec(*argv)
|
246
295
|
end
|
247
296
|
end
|
248
297
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
298
|
+
# If configured, write the pid of the current process out
|
299
|
+
# to a file.
|
300
|
+
def write_pid
|
301
|
+
path = @options[:pidfile]
|
302
|
+
return unless path
|
303
|
+
cur_pid = Process.pid
|
304
|
+
File.write path, cur_pid, mode: 'wb:UTF-8'
|
305
|
+
at_exit do
|
306
|
+
delete_pidfile if cur_pid == Process.pid
|
254
307
|
end
|
255
|
-
|
256
|
-
[deps, require_paths_for_gem(puma) + extra_runtime_deps_directories]
|
257
308
|
end
|
258
309
|
|
259
|
-
def
|
260
|
-
|
261
|
-
if (spec = spec_for_gem(d_name))
|
262
|
-
require_paths_for_gem(spec)
|
263
|
-
else
|
264
|
-
log "* Could not load extra dependency: #{d_name}"
|
265
|
-
nil
|
266
|
-
end
|
267
|
-
end.flatten.compact
|
310
|
+
def reload_worker_directory
|
311
|
+
@runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
|
268
312
|
end
|
269
313
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
314
|
+
# Puma's systemd integration allows Puma to inform systemd:
|
315
|
+
# 1. when it has successfully started
|
316
|
+
# 2. when it is starting shutdown
|
317
|
+
# 3. periodically for a liveness check with a watchdog thread
|
318
|
+
def integrate_with_systemd
|
319
|
+
return unless ENV["NOTIFY_SOCKET"]
|
276
320
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
log "! Unable to prune Bundler environment, continuing"
|
321
|
+
begin
|
322
|
+
require_relative 'systemd'
|
323
|
+
rescue LoadError
|
324
|
+
log "Systemd integration failed. It looks like you're trying to use systemd notify but don't have sd_notify gem installed"
|
282
325
|
return
|
283
326
|
end
|
284
327
|
|
285
|
-
|
286
|
-
|
287
|
-
log '* Pruning Bundler environment'
|
288
|
-
home = ENV['GEM_HOME']
|
289
|
-
Bundler.with_clean_env do
|
290
|
-
ENV['GEM_HOME'] = home
|
291
|
-
ENV['PUMA_BUNDLER_PRUNED'] = '1'
|
292
|
-
args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':'), deps.join(',')] + @original_argv
|
293
|
-
# Ruby 2.0+ defaults to true which breaks socket activation
|
294
|
-
args += [{:close_others => false}]
|
295
|
-
Kernel.exec(*args)
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
def spec_for_gem(gem_name)
|
300
|
-
Bundler.rubygems.loaded_specs(gem_name)
|
301
|
-
end
|
328
|
+
log "* Enabling systemd notification integration"
|
302
329
|
|
303
|
-
|
304
|
-
|
330
|
+
systemd = Systemd.new(@log_writer, @events)
|
331
|
+
systemd.hook_events
|
332
|
+
systemd.start_watchdog
|
305
333
|
end
|
306
334
|
|
307
335
|
def log(str)
|
308
|
-
@
|
336
|
+
@log_writer.log(str)
|
309
337
|
end
|
310
338
|
|
311
339
|
def clustered?
|
@@ -313,35 +341,15 @@ module Puma
|
|
313
341
|
end
|
314
342
|
|
315
343
|
def unsupported(str)
|
316
|
-
@
|
344
|
+
@log_writer.error(str)
|
317
345
|
raise UnsupportedOption
|
318
346
|
end
|
319
347
|
|
320
|
-
def graceful_stop
|
321
|
-
@runner.stop_blocked
|
322
|
-
log "=== puma shutdown: #{Time.now} ==="
|
323
|
-
log "- Goodbye!"
|
324
|
-
end
|
325
|
-
|
326
|
-
def log_thread_status
|
327
|
-
Thread.list.each do |thread|
|
328
|
-
log "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
|
329
|
-
logstr = "Thread: TID-#{thread.object_id.to_s(36)}"
|
330
|
-
logstr += " #{thread.name}" if thread.respond_to?(:name)
|
331
|
-
log logstr
|
332
|
-
|
333
|
-
if thread.backtrace
|
334
|
-
log thread.backtrace.join("\n")
|
335
|
-
else
|
336
|
-
log "<no backtrace available>"
|
337
|
-
end
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
348
|
def set_process_title
|
342
349
|
Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
|
343
350
|
end
|
344
351
|
|
352
|
+
# @!attribute [r] title
|
345
353
|
def title
|
346
354
|
buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
|
347
355
|
buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
|
@@ -353,6 +361,7 @@ module Puma
|
|
353
361
|
ENV['RACK_ENV'] = environment
|
354
362
|
end
|
355
363
|
|
364
|
+
# @!attribute [r] environment
|
356
365
|
def environment
|
357
366
|
@environment
|
358
367
|
end
|
@@ -361,6 +370,11 @@ module Puma
|
|
361
370
|
@options[:prune_bundler] && clustered? && !@options[:preload_app]
|
362
371
|
end
|
363
372
|
|
373
|
+
def prune_bundler!
|
374
|
+
return unless prune_bundler?
|
375
|
+
BundlePruner.new(@original_argv, @options[:extra_runtime_dependencies], @log_writer).prune
|
376
|
+
end
|
377
|
+
|
364
378
|
def generate_restart_data
|
365
379
|
if dir = @options[:directory]
|
366
380
|
@restart_dir = dir
|
@@ -427,7 +441,8 @@ module Puma
|
|
427
441
|
|
428
442
|
begin
|
429
443
|
Signal.trap "SIGTERM" do
|
430
|
-
|
444
|
+
# Shortcut the control flow in case raise_exception_on_sigterm is true
|
445
|
+
do_graceful_stop
|
431
446
|
|
432
447
|
raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
|
433
448
|
end
|
@@ -456,8 +471,13 @@ module Puma
|
|
456
471
|
end
|
457
472
|
|
458
473
|
begin
|
459
|
-
|
460
|
-
|
474
|
+
unless Puma.jruby? # INFO in use by JVM already
|
475
|
+
Signal.trap "SIGINFO" do
|
476
|
+
thread_status do |name, backtrace|
|
477
|
+
@log_writer.log(name)
|
478
|
+
@log_writer.log(backtrace.map { |bt| " #{bt}" })
|
479
|
+
end
|
480
|
+
end
|
461
481
|
end
|
462
482
|
rescue Exception
|
463
483
|
# Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
|
@@ -465,11 +485,13 @@ module Puma
|
|
465
485
|
end
|
466
486
|
end
|
467
487
|
|
468
|
-
def
|
469
|
-
|
488
|
+
def log_config
|
489
|
+
log "Configuration:"
|
490
|
+
|
491
|
+
@config.final_options
|
492
|
+
.each { |config_key, value| log "- #{config_key}: #{value}" }
|
470
493
|
|
471
|
-
|
472
|
-
"You must have RubyGems #{min_version}+ to use this feature."
|
494
|
+
log "\n"
|
473
495
|
end
|
474
496
|
end
|
475
497
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'null_io'
|
4
|
+
require_relative 'error_logger'
|
5
|
+
require 'stringio'
|
6
|
+
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
7
|
+
|
8
|
+
module Puma
|
9
|
+
|
10
|
+
# Handles logging concerns for both standard messages
|
11
|
+
# (+stdout+) and errors (+stderr+).
|
12
|
+
class LogWriter
|
13
|
+
|
14
|
+
class DefaultFormatter
|
15
|
+
def call(str)
|
16
|
+
str
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class PidFormatter
|
21
|
+
def call(str)
|
22
|
+
"[#{$$}] #{str}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
LOG_QUEUE = Queue.new
|
27
|
+
|
28
|
+
attr_reader :stdout,
|
29
|
+
:stderr
|
30
|
+
|
31
|
+
attr_accessor :formatter
|
32
|
+
|
33
|
+
# Create a LogWriter that prints to +stdout+ and +stderr+.
|
34
|
+
def initialize(stdout, stderr)
|
35
|
+
@formatter = DefaultFormatter.new
|
36
|
+
@stdout = stdout
|
37
|
+
@stderr = stderr
|
38
|
+
|
39
|
+
@debug = ENV.key?('PUMA_DEBUG')
|
40
|
+
@error_logger = ErrorLogger.new(@stderr)
|
41
|
+
end
|
42
|
+
|
43
|
+
DEFAULT = new(STDOUT, STDERR)
|
44
|
+
|
45
|
+
# Returns an LogWriter object which writes its status to
|
46
|
+
# two StringIO objects.
|
47
|
+
def self.strings
|
48
|
+
LogWriter.new(StringIO.new, StringIO.new)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.stdio
|
52
|
+
LogWriter.new($stdout, $stderr)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.null
|
56
|
+
n = NullIO.new
|
57
|
+
LogWriter.new(n, n)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Write +str+ to +@stdout+
|
61
|
+
def log(str)
|
62
|
+
internal_write "#{@formatter.call str}\n"
|
63
|
+
end
|
64
|
+
|
65
|
+
def write(str)
|
66
|
+
internal_write @formatter.call(str)
|
67
|
+
end
|
68
|
+
|
69
|
+
def internal_write(str)
|
70
|
+
LOG_QUEUE << str
|
71
|
+
while (w_str = LOG_QUEUE.pop(true)) do
|
72
|
+
begin
|
73
|
+
@stdout.is_a?(IO) and @stdout.wait_writable(1)
|
74
|
+
@stdout.write w_str
|
75
|
+
@stdout.flush unless @stdout.sync
|
76
|
+
rescue Errno::EPIPE, Errno::EBADF, IOError
|
77
|
+
end
|
78
|
+
end
|
79
|
+
rescue ThreadError
|
80
|
+
end
|
81
|
+
private :internal_write
|
82
|
+
|
83
|
+
def debug(str)
|
84
|
+
log("% #{str}") if @debug
|
85
|
+
end
|
86
|
+
|
87
|
+
# Write +str+ to +@stderr+
|
88
|
+
def error(str)
|
89
|
+
@error_logger.info(text: @formatter.call("ERROR: #{str}"))
|
90
|
+
exit 1
|
91
|
+
end
|
92
|
+
|
93
|
+
def format(str)
|
94
|
+
formatter.call(str)
|
95
|
+
end
|
96
|
+
|
97
|
+
# An HTTP connection error has occurred.
|
98
|
+
# +error+ a connection exception, +req+ the request,
|
99
|
+
# and +text+ additional info
|
100
|
+
# @version 5.0.0
|
101
|
+
def connection_error(error, req, text="HTTP connection error")
|
102
|
+
@error_logger.info(error: error, req: req, text: text)
|
103
|
+
end
|
104
|
+
|
105
|
+
# An HTTP parse error has occurred.
|
106
|
+
# +error+ a parsing exception,
|
107
|
+
# and +req+ the request.
|
108
|
+
def parse_error(error, req)
|
109
|
+
@error_logger.info(error: error, req: req, text: 'HTTP parse error, malformed request')
|
110
|
+
end
|
111
|
+
|
112
|
+
# An SSL error has occurred.
|
113
|
+
# @param error <Puma::MiniSSL::SSLError>
|
114
|
+
# @param ssl_socket <Puma::MiniSSL::Socket>
|
115
|
+
def ssl_error(error, ssl_socket)
|
116
|
+
peeraddr = ssl_socket.peeraddr.last rescue "<unknown>"
|
117
|
+
peercert = ssl_socket.peercert
|
118
|
+
subject = peercert ? peercert.subject : nil
|
119
|
+
@error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
|
120
|
+
end
|
121
|
+
|
122
|
+
# An unknown error has occurred.
|
123
|
+
# +error+ an exception object, +req+ the request,
|
124
|
+
# and +text+ additional info
|
125
|
+
def unknown_error(error, req=nil, text="Unknown error")
|
126
|
+
@error_logger.info(error: error, req: req, text: text)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Log occurred error debug dump.
|
130
|
+
# +error+ an exception object, +req+ the request,
|
131
|
+
# and +text+ additional info
|
132
|
+
# @version 5.0.0
|
133
|
+
def debug_error(error, req=nil, text="")
|
134
|
+
@error_logger.debug(error: error, req: req, text: text)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|