puma 5.6.5 → 6.4.2
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 +338 -14
- data/LICENSE +0 -0
- data/README.md +79 -29
- data/bin/puma-wild +1 -1
- data/docs/architecture.md +0 -0
- data/docs/compile_options.md +34 -0
- data/docs/deployment.md +0 -0
- 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 +12 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +0 -0
- data/docs/rails_dev_mode.md +0 -0
- data/docs/restart.md +1 -0
- data/docs/signals.md +0 -0
- data/docs/stats.md +0 -0
- data/docs/systemd.md +3 -6
- 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 +16 -9
- data/ext/puma_http11/http11_parser.c +1 -1
- 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 +2 -2
- data/ext/puma_http11/mini_ssl.c +127 -19
- 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 +1 -1
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +157 -53
- data/ext/puma_http11/puma_http11.c +17 -9
- data/lib/puma/app/status.rb +4 -4
- data/lib/puma/binder.rb +50 -53
- data/lib/puma/cli.rb +16 -18
- data/lib/puma/client.rb +100 -26
- data/lib/puma/cluster/worker.rb +18 -11
- data/lib/puma/cluster/worker_handle.rb +4 -1
- data/lib/puma/cluster.rb +102 -40
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +77 -59
- data/lib/puma/const.rb +129 -92
- data/lib/puma/control_cli.rb +15 -11
- data/lib/puma/detect.rb +7 -4
- data/lib/puma/dsl.rb +250 -56
- data/lib/puma/error_logger.rb +18 -9
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +39 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/json_serialization.rb +0 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +102 -175
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +26 -12
- data/lib/puma/minissl.rb +104 -11
- data/lib/puma/null_io.rb +16 -2
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +0 -0
- data/lib/puma/rack/builder.rb +6 -6
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +19 -10
- data/lib/puma/request.rb +365 -170
- data/lib/puma/runner.rb +56 -20
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +137 -89
- data/lib/puma/single.rb +13 -11
- data/lib/puma/state_file.rb +3 -6
- data/lib/puma/thread_pool.rb +57 -19
- data/lib/puma/util.rb +0 -11
- data/lib/puma.rb +12 -11
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +2 -2
- data/tools/trickletest.rb +0 -0
- metadata +11 -6
- data/lib/puma/queue_close.rb +0 -26
- data/lib/puma/systemd.rb +0 -46
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,18 +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
|
-
:logger, :lowlevel_error_handler,
|
21
|
-
:before_worker_shutdown, :before_worker_boot, :before_worker_fork,
|
22
|
-
:after_worker_boot, :before_fork, :on_restart
|
23
|
-
]
|
19
|
+
autoload :BundlePruner, 'puma/launcher/bundle_pruner'
|
20
|
+
|
24
21
|
# Returns an instance of Launcher
|
25
22
|
#
|
26
23
|
# +conf+ A Puma::Configuration object indicating how to run the server.
|
27
24
|
#
|
28
25
|
# +launcher_args+ A Hash that currently has one required key `:events`,
|
29
|
-
# 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`,
|
30
27
|
# this object will be responsible for broadcasting Puma's internal state
|
31
28
|
# to a logging destination. An optional key `:argv` can be supplied,
|
32
29
|
# this should be an array of strings, these arguments are re-used when
|
@@ -40,25 +37,35 @@ module Puma
|
|
40
37
|
# [200, {}, ["hello world"]]
|
41
38
|
# end
|
42
39
|
# end
|
43
|
-
# Puma::Launcher.new(conf,
|
40
|
+
# Puma::Launcher.new(conf, log_writer: Puma::LogWriter.stdio).run
|
44
41
|
def initialize(conf, launcher_args={})
|
45
42
|
@runner = nil
|
46
|
-
@
|
43
|
+
@log_writer = launcher_args[:log_writer] || LogWriter::DEFAULT
|
44
|
+
@events = launcher_args[:events] || Events.new
|
47
45
|
@argv = launcher_args[:argv] || []
|
48
46
|
@original_argv = @argv.dup
|
49
47
|
@config = conf
|
50
48
|
|
51
|
-
@
|
52
|
-
@binder.create_inherited_fds(ENV).each { |k| ENV.delete k }
|
53
|
-
@binder.create_activated_fds(ENV).each { |k| ENV.delete k }
|
54
|
-
|
55
|
-
@environment = conf.environment
|
49
|
+
@config.options[:log_writer] = @log_writer
|
56
50
|
|
57
51
|
# Advertise the Configuration
|
58
52
|
Puma.cli_config = @config if defined?(Puma.cli_config)
|
59
53
|
|
60
54
|
@config.load
|
61
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
|
+
# Load the systemd integration if we detect systemd's NOTIFY_SOCKET.
|
63
|
+
# Skip this on JRuby though, because it is incompatible with the systemd
|
64
|
+
# integration due to https://github.com/jruby/jruby/issues/6504
|
65
|
+
if ENV["NOTIFY_SOCKET"] && !Puma.jruby?
|
66
|
+
@config.plugins.create('systemd')
|
67
|
+
end
|
68
|
+
|
62
69
|
if @config.options[:bind_to_activated_sockets]
|
63
70
|
@config.options[:binds] = @binder.synthesize_binds_from_activated_fs(
|
64
71
|
@config.options[:binds],
|
@@ -69,8 +76,10 @@ module Puma
|
|
69
76
|
@options = @config.options
|
70
77
|
@config.clamp
|
71
78
|
|
72
|
-
@
|
73
|
-
@
|
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]
|
74
83
|
|
75
84
|
generate_restart_data
|
76
85
|
|
@@ -80,17 +89,17 @@ module Puma
|
|
80
89
|
|
81
90
|
Dir.chdir(@restart_dir)
|
82
91
|
|
83
|
-
prune_bundler
|
92
|
+
prune_bundler!
|
84
93
|
|
85
94
|
@environment = @options[:environment] if @options[:environment]
|
86
95
|
set_rack_environment
|
87
96
|
|
88
97
|
if clustered?
|
89
|
-
@options[:logger] = @
|
98
|
+
@options[:logger] = @log_writer
|
90
99
|
|
91
|
-
@runner = Cluster.new(self
|
100
|
+
@runner = Cluster.new(self)
|
92
101
|
else
|
93
|
-
@runner = Single.new(self
|
102
|
+
@runner = Single.new(self)
|
94
103
|
end
|
95
104
|
Puma.stats_object = @runner
|
96
105
|
|
@@ -99,7 +108,7 @@ module Puma
|
|
99
108
|
log_config if ENV['PUMA_LOG_CONFIG']
|
100
109
|
end
|
101
110
|
|
102
|
-
attr_reader :binder, :events, :config, :options, :restart_dir
|
111
|
+
attr_reader :binder, :log_writer, :events, :config, :options, :restart_dir
|
103
112
|
|
104
113
|
# Return stats about the server
|
105
114
|
def stats
|
@@ -115,7 +124,7 @@ module Puma
|
|
115
124
|
permission = @options[:state_permission]
|
116
125
|
return unless path
|
117
126
|
|
118
|
-
|
127
|
+
require_relative 'state_file'
|
119
128
|
|
120
129
|
sf = StateFile.new
|
121
130
|
sf.pid = Process.pid
|
@@ -172,16 +181,7 @@ module Puma
|
|
172
181
|
|
173
182
|
# Run the server. This blocks until the server is stopped
|
174
183
|
def run
|
175
|
-
previous_env =
|
176
|
-
if defined?(Bundler)
|
177
|
-
env = Bundler::ORIGINAL_ENV.dup
|
178
|
-
# add -rbundler/setup so we load from Gemfile when restarting
|
179
|
-
bundle = "-rbundler/setup"
|
180
|
-
env["RUBYOPT"] = [env["RUBYOPT"], bundle].join(" ").lstrip unless env["RUBYOPT"].to_s.include?(bundle)
|
181
|
-
env
|
182
|
-
else
|
183
|
-
ENV.to_h
|
184
|
-
end
|
184
|
+
previous_env = get_env
|
185
185
|
|
186
186
|
@config.clamp
|
187
187
|
|
@@ -189,24 +189,11 @@ module Puma
|
|
189
189
|
|
190
190
|
setup_signals
|
191
191
|
set_process_title
|
192
|
-
|
192
|
+
|
193
|
+
# This blocks until the server is stopped
|
193
194
|
@runner.run
|
194
195
|
|
195
|
-
|
196
|
-
when :halt
|
197
|
-
log "* Stopping immediately!"
|
198
|
-
@runner.stop_control
|
199
|
-
when :run, :stop
|
200
|
-
graceful_stop
|
201
|
-
when :restart
|
202
|
-
log "* Restarting..."
|
203
|
-
ENV.replace(previous_env)
|
204
|
-
@runner.stop_control
|
205
|
-
restart!
|
206
|
-
when :exit
|
207
|
-
# nothing
|
208
|
-
end
|
209
|
-
close_binder_listeners unless @status == :restart
|
196
|
+
do_run_finished(previous_env)
|
210
197
|
end
|
211
198
|
|
212
199
|
# Return all tcp ports the launcher may be using, TCP or SSL
|
@@ -250,30 +237,56 @@ module Puma
|
|
250
237
|
|
251
238
|
private
|
252
239
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
240
|
+
def get_env
|
241
|
+
if defined?(Bundler)
|
242
|
+
env = Bundler::ORIGINAL_ENV.dup
|
243
|
+
# add -rbundler/setup so we load from Gemfile when restarting
|
244
|
+
bundle = "-rbundler/setup"
|
245
|
+
env["RUBYOPT"] = [env["RUBYOPT"], bundle].join(" ").lstrip unless env["RUBYOPT"].to_s.include?(bundle)
|
246
|
+
env
|
247
|
+
else
|
248
|
+
ENV.to_h
|
262
249
|
end
|
263
250
|
end
|
264
251
|
|
265
|
-
def
|
266
|
-
|
252
|
+
def do_run_finished(previous_env)
|
253
|
+
case @status
|
254
|
+
when :halt
|
255
|
+
do_forceful_stop
|
256
|
+
when :run, :stop
|
257
|
+
do_graceful_stop
|
258
|
+
when :restart
|
259
|
+
do_restart(previous_env)
|
260
|
+
end
|
261
|
+
|
262
|
+
close_binder_listeners unless @status == :restart
|
263
|
+
end
|
264
|
+
|
265
|
+
def do_forceful_stop
|
266
|
+
log "* Stopping immediately!"
|
267
|
+
@runner.stop_control
|
268
|
+
end
|
269
|
+
|
270
|
+
def do_graceful_stop
|
271
|
+
@events.fire_on_stopped!
|
272
|
+
@runner.stop_blocked
|
273
|
+
end
|
274
|
+
|
275
|
+
def do_restart(previous_env)
|
276
|
+
log "* Restarting..."
|
277
|
+
ENV.replace(previous_env)
|
278
|
+
@runner.stop_control
|
279
|
+
restart!
|
267
280
|
end
|
268
281
|
|
269
282
|
def restart!
|
270
283
|
@events.fire_on_restart!
|
271
|
-
@config.run_hooks :on_restart, self, @
|
284
|
+
@config.run_hooks :on_restart, self, @log_writer
|
272
285
|
|
273
286
|
if Puma.jruby?
|
274
287
|
close_binder_listeners
|
275
288
|
|
276
|
-
|
289
|
+
require_relative 'jruby_restart'
|
277
290
|
JRubyRestart.chdir_exec(@restart_dir, restart_args)
|
278
291
|
elsif Puma.windows?
|
279
292
|
close_binder_listeners
|
@@ -290,94 +303,24 @@ module Puma
|
|
290
303
|
end
|
291
304
|
end
|
292
305
|
|
293
|
-
#
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
Array(@options[:extra_runtime_dependencies]).map do |d_name|
|
303
|
-
if (spec = spec_for_gem(d_name))
|
304
|
-
require_paths_for_gem(spec)
|
305
|
-
else
|
306
|
-
log "* Could not load extra dependency: #{d_name}"
|
307
|
-
nil
|
308
|
-
end
|
309
|
-
end.flatten.compact
|
310
|
-
end
|
311
|
-
|
312
|
-
# @!attribute [r] puma_wild_location
|
313
|
-
def puma_wild_location
|
314
|
-
puma = spec_for_gem("puma")
|
315
|
-
dirs = require_paths_for_gem(puma)
|
316
|
-
puma_lib_dir = dirs.detect { |x| File.exist? File.join(x, '../bin/puma-wild') }
|
317
|
-
File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
|
318
|
-
end
|
319
|
-
|
320
|
-
def prune_bundler
|
321
|
-
return if ENV['PUMA_BUNDLER_PRUNED']
|
322
|
-
return unless defined?(Bundler)
|
323
|
-
require_rubygems_min_version!(Gem::Version.new("2.2"), "prune_bundler")
|
324
|
-
unless puma_wild_location
|
325
|
-
log "! Unable to prune Bundler environment, continuing"
|
326
|
-
return
|
327
|
-
end
|
328
|
-
|
329
|
-
dirs = files_to_require_after_prune
|
330
|
-
|
331
|
-
log '* Pruning Bundler environment'
|
332
|
-
home = ENV['GEM_HOME']
|
333
|
-
bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
|
334
|
-
bundle_app_config = Bundler.original_env['BUNDLE_APP_CONFIG']
|
335
|
-
with_unbundled_env do
|
336
|
-
ENV['GEM_HOME'] = home
|
337
|
-
ENV['BUNDLE_GEMFILE'] = bundle_gemfile
|
338
|
-
ENV['PUMA_BUNDLER_PRUNED'] = '1'
|
339
|
-
ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
|
340
|
-
args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':')] + @original_argv
|
341
|
-
# Ruby 2.0+ defaults to true which breaks socket activation
|
342
|
-
args += [{:close_others => false}]
|
343
|
-
Kernel.exec(*args)
|
344
|
-
end
|
345
|
-
end
|
346
|
-
|
347
|
-
#
|
348
|
-
# Puma's systemd integration allows Puma to inform systemd:
|
349
|
-
# 1. when it has successfully started
|
350
|
-
# 2. when it is starting shutdown
|
351
|
-
# 3. periodically for a liveness check with a watchdog thread
|
352
|
-
#
|
353
|
-
|
354
|
-
def integrate_with_systemd
|
355
|
-
return unless ENV["NOTIFY_SOCKET"]
|
356
|
-
|
357
|
-
begin
|
358
|
-
require 'puma/systemd'
|
359
|
-
rescue LoadError
|
360
|
-
log "Systemd integration failed. It looks like you're trying to use systemd notify but don't have sd_notify gem installed"
|
361
|
-
return
|
306
|
+
# If configured, write the pid of the current process out
|
307
|
+
# to a file.
|
308
|
+
def write_pid
|
309
|
+
path = @options[:pidfile]
|
310
|
+
return unless path
|
311
|
+
cur_pid = Process.pid
|
312
|
+
File.write path, cur_pid, mode: 'wb:UTF-8'
|
313
|
+
at_exit do
|
314
|
+
delete_pidfile if cur_pid == Process.pid
|
362
315
|
end
|
363
|
-
|
364
|
-
log "* Enabling systemd notification integration"
|
365
|
-
|
366
|
-
systemd = Systemd.new(@events)
|
367
|
-
systemd.hook_events
|
368
|
-
systemd.start_watchdog
|
369
|
-
end
|
370
|
-
|
371
|
-
def spec_for_gem(gem_name)
|
372
|
-
Bundler.rubygems.loaded_specs(gem_name)
|
373
316
|
end
|
374
317
|
|
375
|
-
def
|
376
|
-
|
318
|
+
def reload_worker_directory
|
319
|
+
@runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
|
377
320
|
end
|
378
321
|
|
379
322
|
def log(str)
|
380
|
-
@
|
323
|
+
@log_writer.log(str)
|
381
324
|
end
|
382
325
|
|
383
326
|
def clustered?
|
@@ -385,15 +328,10 @@ module Puma
|
|
385
328
|
end
|
386
329
|
|
387
330
|
def unsupported(str)
|
388
|
-
@
|
331
|
+
@log_writer.error(str)
|
389
332
|
raise UnsupportedOption
|
390
333
|
end
|
391
334
|
|
392
|
-
def graceful_stop
|
393
|
-
@events.fire_on_stopped!
|
394
|
-
@runner.stop_blocked
|
395
|
-
end
|
396
|
-
|
397
335
|
def set_process_title
|
398
336
|
Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
|
399
337
|
end
|
@@ -419,6 +357,11 @@ module Puma
|
|
419
357
|
@options[:prune_bundler] && clustered? && !@options[:preload_app]
|
420
358
|
end
|
421
359
|
|
360
|
+
def prune_bundler!
|
361
|
+
return unless prune_bundler?
|
362
|
+
BundlePruner.new(@original_argv, @options[:extra_runtime_dependencies], @log_writer).prune
|
363
|
+
end
|
364
|
+
|
422
365
|
def generate_restart_data
|
423
366
|
if dir = @options[:directory]
|
424
367
|
@restart_dir = dir
|
@@ -485,7 +428,8 @@ module Puma
|
|
485
428
|
|
486
429
|
begin
|
487
430
|
Signal.trap "SIGTERM" do
|
488
|
-
|
431
|
+
# Shortcut the control flow in case raise_exception_on_sigterm is true
|
432
|
+
do_graceful_stop
|
489
433
|
|
490
434
|
raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
|
491
435
|
end
|
@@ -517,8 +461,8 @@ module Puma
|
|
517
461
|
unless Puma.jruby? # INFO in use by JVM already
|
518
462
|
Signal.trap "SIGINFO" do
|
519
463
|
thread_status do |name, backtrace|
|
520
|
-
@
|
521
|
-
@
|
464
|
+
@log_writer.log(name)
|
465
|
+
@log_writer.log(backtrace.map { |bt| " #{bt}" })
|
522
466
|
end
|
523
467
|
end
|
524
468
|
end
|
@@ -528,23 +472,6 @@ module Puma
|
|
528
472
|
end
|
529
473
|
end
|
530
474
|
|
531
|
-
def require_rubygems_min_version!(min_version, feature)
|
532
|
-
return if min_version <= Gem::Version.new(Gem::VERSION)
|
533
|
-
|
534
|
-
raise "#{feature} is not supported on your version of RubyGems. " \
|
535
|
-
"You must have RubyGems #{min_version}+ to use this feature."
|
536
|
-
end
|
537
|
-
|
538
|
-
# @version 5.0.0
|
539
|
-
def with_unbundled_env
|
540
|
-
bundler_ver = Gem::Version.new(Bundler::VERSION)
|
541
|
-
if bundler_ver < Gem::Version.new('2.1.0')
|
542
|
-
Bundler.with_clean_env { yield }
|
543
|
-
else
|
544
|
-
Bundler.with_unbundled_env { yield }
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
475
|
def log_config
|
549
476
|
log "Configuration:"
|
550
477
|
|
@@ -0,0 +1,147 @@
|
|
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, :custom_logger
|
32
|
+
|
33
|
+
# Create a LogWriter that prints to +stdout+ and +stderr+.
|
34
|
+
def initialize(stdout, stderr)
|
35
|
+
@formatter = DefaultFormatter.new
|
36
|
+
@custom_logger = nil
|
37
|
+
@stdout = stdout
|
38
|
+
@stderr = stderr
|
39
|
+
|
40
|
+
@debug = ENV.key?('PUMA_DEBUG')
|
41
|
+
@error_logger = ErrorLogger.new(@stderr)
|
42
|
+
end
|
43
|
+
|
44
|
+
DEFAULT = new(STDOUT, STDERR)
|
45
|
+
|
46
|
+
# Returns an LogWriter object which writes its status to
|
47
|
+
# two StringIO objects.
|
48
|
+
def self.strings
|
49
|
+
LogWriter.new(StringIO.new, StringIO.new)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.stdio
|
53
|
+
LogWriter.new($stdout, $stderr)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.null
|
57
|
+
n = NullIO.new
|
58
|
+
LogWriter.new(n, n)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Write +str+ to +@stdout+
|
62
|
+
def log(str)
|
63
|
+
if @custom_logger&.respond_to?(:write)
|
64
|
+
@custom_logger.write(format(str))
|
65
|
+
else
|
66
|
+
internal_write "#{@formatter.call str}\n"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def write(str)
|
71
|
+
internal_write @formatter.call(str)
|
72
|
+
end
|
73
|
+
|
74
|
+
def internal_write(str)
|
75
|
+
LOG_QUEUE << str
|
76
|
+
while (w_str = LOG_QUEUE.pop(true)) do
|
77
|
+
begin
|
78
|
+
@stdout.is_a?(IO) and @stdout.wait_writable(1)
|
79
|
+
@stdout.write w_str
|
80
|
+
@stdout.flush unless @stdout.sync
|
81
|
+
rescue Errno::EPIPE, Errno::EBADF, IOError, Errno::EINVAL
|
82
|
+
# 'Invalid argument' (Errno::EINVAL) may be raised by flush
|
83
|
+
end
|
84
|
+
end
|
85
|
+
rescue ThreadError
|
86
|
+
end
|
87
|
+
private :internal_write
|
88
|
+
|
89
|
+
def debug?
|
90
|
+
@debug
|
91
|
+
end
|
92
|
+
|
93
|
+
def debug(str)
|
94
|
+
log("% #{str}") if @debug
|
95
|
+
end
|
96
|
+
|
97
|
+
# Write +str+ to +@stderr+
|
98
|
+
def error(str)
|
99
|
+
@error_logger.info(text: @formatter.call("ERROR: #{str}"))
|
100
|
+
exit 1
|
101
|
+
end
|
102
|
+
|
103
|
+
def format(str)
|
104
|
+
formatter.call(str)
|
105
|
+
end
|
106
|
+
|
107
|
+
# An HTTP connection error has occurred.
|
108
|
+
# +error+ a connection exception, +req+ the request,
|
109
|
+
# and +text+ additional info
|
110
|
+
# @version 5.0.0
|
111
|
+
def connection_error(error, req, text="HTTP connection error")
|
112
|
+
@error_logger.info(error: error, req: req, text: text)
|
113
|
+
end
|
114
|
+
|
115
|
+
# An HTTP parse error has occurred.
|
116
|
+
# +error+ a parsing exception,
|
117
|
+
# and +req+ the request.
|
118
|
+
def parse_error(error, req)
|
119
|
+
@error_logger.info(error: error, req: req, text: 'HTTP parse error, malformed request')
|
120
|
+
end
|
121
|
+
|
122
|
+
# An SSL error has occurred.
|
123
|
+
# @param error <Puma::MiniSSL::SSLError>
|
124
|
+
# @param ssl_socket <Puma::MiniSSL::Socket>
|
125
|
+
def ssl_error(error, ssl_socket)
|
126
|
+
peeraddr = ssl_socket.peeraddr.last rescue "<unknown>"
|
127
|
+
peercert = ssl_socket.peercert
|
128
|
+
subject = peercert&.subject
|
129
|
+
@error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
|
130
|
+
end
|
131
|
+
|
132
|
+
# An unknown error has occurred.
|
133
|
+
# +error+ an exception object, +req+ the request,
|
134
|
+
# and +text+ additional info
|
135
|
+
def unknown_error(error, req=nil, text="Unknown error")
|
136
|
+
@error_logger.info(error: error, req: req, text: text)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Log occurred error debug dump.
|
140
|
+
# +error+ an exception object, +req+ the request,
|
141
|
+
# and +text+ additional info
|
142
|
+
# @version 5.0.0
|
143
|
+
def debug_error(error, req=nil, text="")
|
144
|
+
@error_logger.debug(error: error, req: req, text: text)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -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,27 +11,37 @@ 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
35
|
if params['key'].nil? && params['key_pem'].nil?
|
27
|
-
|
36
|
+
log_writer.error "Please specify the SSL key via 'key=' or 'key_pem='"
|
28
37
|
end
|
29
38
|
|
30
39
|
ctx.key = params['key'] if params['key']
|
31
40
|
ctx.key_pem = params['key_pem'] if params['key_pem']
|
41
|
+
ctx.key_password_command = params['key_password_command'] if params['key_password_command']
|
32
42
|
|
33
43
|
if params['cert'].nil? && params['cert_pem'].nil?
|
34
|
-
|
44
|
+
log_writer.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
|
35
45
|
end
|
36
46
|
|
37
47
|
ctx.cert = params['cert'] if params['cert']
|
@@ -39,16 +49,20 @@ module Puma
|
|
39
49
|
|
40
50
|
if ['peer', 'force_peer'].include?(params['verify_mode'])
|
41
51
|
unless params['ca']
|
42
|
-
|
52
|
+
log_writer.error "Please specify the SSL ca via 'ca='"
|
43
53
|
end
|
54
|
+
# needed for Puma::MiniSSL::Socket#peercert, env['puma.peercert']
|
55
|
+
require 'openssl'
|
44
56
|
end
|
45
57
|
|
46
58
|
ctx.ca = params['ca'] if params['ca']
|
47
59
|
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
60
|
+
|
61
|
+
ctx.reuse = params['reuse'] if params['reuse']
|
48
62
|
end
|
49
63
|
|
50
|
-
ctx.no_tlsv1
|
51
|
-
ctx.no_tlsv1_1 =
|
64
|
+
ctx.no_tlsv1 = params['no_tlsv1'] == 'true'
|
65
|
+
ctx.no_tlsv1_1 = params['no_tlsv1_1'] == 'true'
|
52
66
|
|
53
67
|
if params['verify_mode']
|
54
68
|
ctx.verify_mode = case params['verify_mode']
|
@@ -59,7 +73,7 @@ module Puma
|
|
59
73
|
when "none"
|
60
74
|
MiniSSL::VERIFY_NONE
|
61
75
|
else
|
62
|
-
|
76
|
+
log_writer.error "Please specify a valid verify_mode="
|
63
77
|
MiniSSL::VERIFY_NONE
|
64
78
|
end
|
65
79
|
end
|
@@ -75,7 +89,7 @@ module Puma
|
|
75
89
|
|
76
90
|
private
|
77
91
|
|
78
|
-
attr_reader :params, :
|
92
|
+
attr_reader :params, :log_writer
|
79
93
|
end
|
80
94
|
end
|
81
95
|
end
|