puma 5.6.9-java → 6.6.0-java

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +465 -18
  3. data/README.md +152 -42
  4. data/bin/puma-wild +1 -1
  5. data/docs/compile_options.md +34 -0
  6. data/docs/fork_worker.md +12 -4
  7. data/docs/java_options.md +54 -0
  8. data/docs/kubernetes.md +12 -0
  9. data/docs/nginx.md +1 -1
  10. data/docs/plugins.md +4 -0
  11. data/docs/restart.md +1 -0
  12. data/docs/signals.md +2 -2
  13. data/docs/stats.md +8 -3
  14. data/docs/systemd.md +13 -7
  15. data/docs/testing_benchmarks_local_files.md +150 -0
  16. data/docs/testing_test_rackup_ci_files.md +36 -0
  17. data/ext/puma_http11/extconf.rb +27 -17
  18. data/ext/puma_http11/http11_parser.c +1 -1
  19. data/ext/puma_http11/http11_parser.h +1 -1
  20. data/ext/puma_http11/http11_parser.java.rl +2 -2
  21. data/ext/puma_http11/http11_parser.rl +2 -2
  22. data/ext/puma_http11/http11_parser_common.rl +2 -2
  23. data/ext/puma_http11/mini_ssl.c +137 -19
  24. data/ext/puma_http11/org/jruby/puma/Http11.java +31 -10
  25. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  26. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +157 -53
  27. data/ext/puma_http11/puma_http11.c +21 -10
  28. data/lib/puma/app/status.rb +4 -4
  29. data/lib/puma/binder.rb +60 -55
  30. data/lib/puma/cli.rb +22 -20
  31. data/lib/puma/client.rb +93 -30
  32. data/lib/puma/cluster/worker.rb +27 -17
  33. data/lib/puma/cluster/worker_handle.rb +8 -6
  34. data/lib/puma/cluster.rb +121 -47
  35. data/lib/puma/commonlogger.rb +21 -14
  36. data/lib/puma/configuration.rb +101 -65
  37. data/lib/puma/const.rb +141 -93
  38. data/lib/puma/control_cli.rb +19 -15
  39. data/lib/puma/detect.rb +7 -4
  40. data/lib/puma/dsl.rb +521 -88
  41. data/lib/puma/error_logger.rb +22 -13
  42. data/lib/puma/events.rb +6 -126
  43. data/lib/puma/io_buffer.rb +39 -4
  44. data/lib/puma/jruby_restart.rb +0 -15
  45. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  46. data/lib/puma/launcher.rb +121 -181
  47. data/lib/puma/log_writer.rb +147 -0
  48. data/lib/puma/minissl/context_builder.rb +27 -12
  49. data/lib/puma/minissl.rb +105 -11
  50. data/lib/puma/null_io.rb +42 -2
  51. data/lib/puma/plugin/systemd.rb +90 -0
  52. data/lib/puma/plugin/tmp_restart.rb +1 -1
  53. data/lib/puma/puma_http11.jar +0 -0
  54. data/lib/puma/rack/builder.rb +6 -6
  55. data/lib/puma/rack/urlmap.rb +1 -1
  56. data/lib/puma/rack_default.rb +19 -4
  57. data/lib/puma/reactor.rb +19 -10
  58. data/lib/puma/request.rb +368 -169
  59. data/lib/puma/runner.rb +65 -22
  60. data/lib/puma/sd_notify.rb +146 -0
  61. data/lib/puma/server.rb +161 -102
  62. data/lib/puma/single.rb +13 -11
  63. data/lib/puma/state_file.rb +3 -6
  64. data/lib/puma/thread_pool.rb +71 -21
  65. data/lib/puma/util.rb +1 -12
  66. data/lib/puma.rb +9 -10
  67. data/lib/rack/handler/puma.rb +116 -86
  68. data/tools/Dockerfile +2 -2
  69. metadata +17 -12
  70. data/lib/puma/queue_close.rb +0 -26
  71. data/lib/puma/systemd.rb +0 -46
  72. 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
- require 'puma/events'
4
- require 'puma/detect'
5
- require 'puma/cluster'
6
- require 'puma/single'
7
- require 'puma/const'
8
- require 'puma/binder'
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
- # @deprecated 6.0.0
19
- KEYS_NOT_TO_PERSIST_IN_STATE = [
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::Events.stdio`,
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,37 @@ module Puma
40
37
  # [200, {}, ["hello world"]]
41
38
  # end
42
39
  # end
43
- # Puma::Launcher.new(conf, events: Puma::Events.stdio).run
40
+ # Puma::Launcher.new(conf, log_writer: Puma::LogWriter.stdio).run
44
41
  def initialize(conf, launcher_args={})
45
42
  @runner = nil
46
- @events = launcher_args[:events] || Events::DEFAULT
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
- @binder = Binder.new(@events, conf)
52
- @binder.create_inherited_fds(ENV).each { |k| ENV.delete k }
53
- @binder.create_activated_fds(ENV).each { |k| ENV.delete k }
49
+ env = launcher_args.delete(:env) || ENV
54
50
 
55
- @environment = conf.environment
51
+ @config.options[:log_writer] = @log_writer
56
52
 
57
53
  # Advertise the Configuration
58
54
  Puma.cli_config = @config if defined?(Puma.cli_config)
59
55
 
60
56
  @config.load
61
57
 
58
+ @binder = Binder.new(@log_writer, conf)
59
+ @binder.create_inherited_fds(ENV).each { |k| ENV.delete k }
60
+ @binder.create_activated_fds(ENV).each { |k| ENV.delete k }
61
+
62
+ @environment = conf.environment
63
+
64
+ # Load the systemd integration if we detect systemd's NOTIFY_SOCKET.
65
+ # Skip this on JRuby though, because it is incompatible with the systemd
66
+ # integration due to https://github.com/jruby/jruby/issues/6504
67
+ if ENV["NOTIFY_SOCKET"] && !Puma.jruby? && !ENV["PUMA_SKIP_SYSTEMD"]
68
+ @config.plugins.create('systemd')
69
+ end
70
+
62
71
  if @config.options[:bind_to_activated_sockets]
63
72
  @config.options[:binds] = @binder.synthesize_binds_from_activated_fs(
64
73
  @config.options[:binds],
@@ -69,8 +78,10 @@ module Puma
69
78
  @options = @config.options
70
79
  @config.clamp
71
80
 
72
- @events.formatter = Events::PidFormatter.new if clustered?
73
- @events.formatter = options[:log_formatter] if @options[:log_formatter]
81
+ @log_writer.formatter = LogWriter::PidFormatter.new if clustered?
82
+ @log_writer.formatter = options[:log_formatter] if @options[:log_formatter]
83
+
84
+ @log_writer.custom_logger = options[:custom_logger] if @options[:custom_logger]
74
85
 
75
86
  generate_restart_data
76
87
 
@@ -80,26 +91,26 @@ module Puma
80
91
 
81
92
  Dir.chdir(@restart_dir)
82
93
 
83
- prune_bundler if prune_bundler?
94
+ prune_bundler!
84
95
 
85
96
  @environment = @options[:environment] if @options[:environment]
86
97
  set_rack_environment
87
98
 
88
99
  if clustered?
89
- @options[:logger] = @events
100
+ @options[:logger] = @log_writer
90
101
 
91
- @runner = Cluster.new(self, @events)
102
+ @runner = Cluster.new(self)
92
103
  else
93
- @runner = Single.new(self, @events)
104
+ @runner = Single.new(self)
94
105
  end
95
106
  Puma.stats_object = @runner
96
107
 
97
108
  @status = :run
98
109
 
99
- log_config if ENV['PUMA_LOG_CONFIG']
110
+ log_config if env['PUMA_LOG_CONFIG']
100
111
  end
101
112
 
102
- attr_reader :binder, :events, :config, :options, :restart_dir
113
+ attr_reader :binder, :log_writer, :events, :config, :options, :restart_dir
103
114
 
104
115
  # Return stats about the server
105
116
  def stats
@@ -115,7 +126,7 @@ module Puma
115
126
  permission = @options[:state_permission]
116
127
  return unless path
117
128
 
118
- require 'puma/state_file'
129
+ require_relative 'state_file'
119
130
 
120
131
  sf = StateFile.new
121
132
  sf.pid = Process.pid
@@ -156,6 +167,13 @@ module Puma
156
167
  log "* phased-restart called but not available, restarting normally."
157
168
  return restart
158
169
  end
170
+
171
+ if @options.file_options[:tag].nil?
172
+ dir = File.realdirpath(@restart_dir)
173
+ @options[:tag] = File.basename(dir)
174
+ set_process_title
175
+ end
176
+
159
177
  true
160
178
  end
161
179
 
@@ -172,16 +190,7 @@ module Puma
172
190
 
173
191
  # Run the server. This blocks until the server is stopped
174
192
  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
193
+ previous_env = get_env
185
194
 
186
195
  @config.clamp
187
196
 
@@ -189,24 +198,11 @@ module Puma
189
198
 
190
199
  setup_signals
191
200
  set_process_title
192
- integrate_with_systemd
201
+
202
+ # This blocks until the server is stopped
193
203
  @runner.run
194
204
 
195
- case @status
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
205
+ do_run_finished(previous_env)
210
206
  end
211
207
 
212
208
  # Return all tcp ports the launcher may be using, TCP or SSL
@@ -250,31 +246,59 @@ module Puma
250
246
 
251
247
  private
252
248
 
253
- # If configured, write the pid of the current process out
254
- # to a file.
255
- def write_pid
256
- path = @options[:pidfile]
257
- return unless path
258
- cur_pid = Process.pid
259
- File.write path, cur_pid, mode: 'wb:UTF-8'
260
- at_exit do
261
- delete_pidfile if cur_pid == Process.pid
249
+ def get_env
250
+ if defined?(Bundler)
251
+ env = Bundler::ORIGINAL_ENV.dup
252
+ # add -rbundler/setup so we load from Gemfile when restarting
253
+ bundle = "-rbundler/setup"
254
+ env["RUBYOPT"] = [env["RUBYOPT"], bundle].join(" ").lstrip unless env["RUBYOPT"].to_s.include?(bundle)
255
+ env
256
+ else
257
+ ENV.to_h
262
258
  end
263
259
  end
264
260
 
265
- def reload_worker_directory
266
- @runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
261
+ def do_run_finished(previous_env)
262
+ case @status
263
+ when :halt
264
+ do_forceful_stop
265
+ when :run, :stop
266
+ do_graceful_stop
267
+ when :restart
268
+ do_restart(previous_env)
269
+ end
270
+
271
+ close_binder_listeners unless @status == :restart
272
+ end
273
+
274
+ def do_forceful_stop
275
+ log "* Stopping immediately!"
276
+ @runner.stop_control
277
+ end
278
+
279
+ def do_graceful_stop
280
+ @events.fire_on_stopped!
281
+ @runner.stop_blocked
282
+ end
283
+
284
+ def do_restart(previous_env)
285
+ log "* Restarting..."
286
+ ENV.replace(previous_env)
287
+ @runner.stop_control
288
+ restart!
267
289
  end
268
290
 
269
291
  def restart!
270
292
  @events.fire_on_restart!
271
- @config.run_hooks :on_restart, self, @events
293
+ @config.run_hooks :on_restart, self, @log_writer
272
294
 
273
295
  if Puma.jruby?
274
296
  close_binder_listeners
275
297
 
276
- require 'puma/jruby_restart'
277
- JRubyRestart.chdir_exec(@restart_dir, restart_args)
298
+ require_relative 'jruby_restart'
299
+ argv = restart_args
300
+ JRubyRestart.chdir(@restart_dir)
301
+ Kernel.exec(*argv)
278
302
  elsif Puma.windows?
279
303
  close_binder_listeners
280
304
 
@@ -290,94 +314,24 @@ module Puma
290
314
  end
291
315
  end
292
316
 
293
- # @!attribute [r] files_to_require_after_prune
294
- def files_to_require_after_prune
295
- puma = spec_for_gem("puma")
296
-
297
- require_paths_for_gem(puma) + extra_runtime_deps_directories
298
- end
299
-
300
- # @!attribute [r] extra_runtime_deps_directories
301
- def extra_runtime_deps_directories
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
317
+ # If configured, write the pid of the current process out
318
+ # to a file.
319
+ def write_pid
320
+ path = @options[:pidfile]
321
+ return unless path
322
+ cur_pid = Process.pid
323
+ File.write path, cur_pid, mode: 'wb:UTF-8'
324
+ at_exit do
325
+ delete_pidfile if cur_pid == Process.pid
362
326
  end
363
-
364
- log "* Enabling systemd notification integration"
365
-
366
- systemd = Systemd.new(@events)
367
- systemd.hook_events
368
- systemd.start_watchdog
369
327
  end
370
328
 
371
- def spec_for_gem(gem_name)
372
- Bundler.rubygems.loaded_specs(gem_name)
373
- end
374
-
375
- def require_paths_for_gem(gem_spec)
376
- gem_spec.full_require_paths
329
+ def reload_worker_directory
330
+ @runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
377
331
  end
378
332
 
379
333
  def log(str)
380
- @events.log str
334
+ @log_writer.log(str)
381
335
  end
382
336
 
383
337
  def clustered?
@@ -385,15 +339,10 @@ module Puma
385
339
  end
386
340
 
387
341
  def unsupported(str)
388
- @events.error(str)
342
+ @log_writer.error(str)
389
343
  raise UnsupportedOption
390
344
  end
391
345
 
392
- def graceful_stop
393
- @events.fire_on_stopped!
394
- @runner.stop_blocked
395
- end
396
-
397
346
  def set_process_title
398
347
  Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
399
348
  end
@@ -419,6 +368,11 @@ module Puma
419
368
  @options[:prune_bundler] && clustered? && !@options[:preload_app]
420
369
  end
421
370
 
371
+ def prune_bundler!
372
+ return unless prune_bundler?
373
+ BundlePruner.new(@original_argv, @options[:extra_runtime_dependencies], @log_writer).prune
374
+ end
375
+
422
376
  def generate_restart_data
423
377
  if dir = @options[:directory]
424
378
  @restart_dir = dir
@@ -465,12 +419,14 @@ module Puma
465
419
  end
466
420
 
467
421
  def setup_signals
468
- begin
469
- Signal.trap "SIGUSR2" do
470
- restart
422
+ unless ENV["PUMA_SKIP_SIGUSR2"]
423
+ begin
424
+ Signal.trap "SIGUSR2" do
425
+ restart
426
+ end
427
+ rescue Exception
428
+ log "*** SIGUSR2 not implemented, signal based restart unavailable!"
471
429
  end
472
- rescue Exception
473
- log "*** SIGUSR2 not implemented, signal based restart unavailable!"
474
430
  end
475
431
 
476
432
  unless Puma.jruby?
@@ -485,7 +441,8 @@ module Puma
485
441
 
486
442
  begin
487
443
  Signal.trap "SIGTERM" do
488
- graceful_stop
444
+ # Shortcut the control flow in case raise_exception_on_sigterm is true
445
+ do_graceful_stop
489
446
 
490
447
  raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
491
448
  end
@@ -517,8 +474,8 @@ module Puma
517
474
  unless Puma.jruby? # INFO in use by JVM already
518
475
  Signal.trap "SIGINFO" do
519
476
  thread_status do |name, backtrace|
520
- @events.log name
521
- @events.log backtrace.map { |bt| " #{bt}" }
477
+ @log_writer.log(name)
478
+ @log_writer.log(backtrace.map { |bt| " #{bt}" })
522
479
  end
523
480
  end
524
481
  end
@@ -528,23 +485,6 @@ module Puma
528
485
  end
529
486
  end
530
487
 
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
488
  def log_config
549
489
  log "Configuration:"
550
490
 
@@ -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, env: ENV)
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, env: env)
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(env: ENV)
49
+ LogWriter.new(StringIO.new, StringIO.new, env: env)
50
+ end
51
+
52
+ def self.stdio(env: ENV)
53
+ LogWriter.new($stdout, $stderr, env: env)
54
+ end
55
+
56
+ def self.null(env: ENV)
57
+ n = NullIO.new
58
+ LogWriter.new(n, n, env: env)
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