puma 5.6.8 → 6.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +332 -16
- data/README.md +79 -29
- data/bin/puma-wild +1 -1
- data/docs/compile_options.md +34 -0
- data/docs/fork_worker.md +1 -3
- data/docs/kubernetes.md +12 -0
- data/docs/nginx.md +1 -1
- data/docs/restart.md +1 -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/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/org/jruby/puma/Http11.java +5 -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 +59 -19
- 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 +137 -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/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/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 +380 -172
- 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 +9 -10
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +2 -2
- metadata +11 -7
- data/lib/puma/queue_close.rb +0 -26
- data/lib/puma/systemd.rb +0 -46
- data/lib/rack/version_restriction.rb +0 -15
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
|