tiny-fast-gem 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/puma-8.0.2/History.md +3334 -0
  3. data/puma-8.0.2/LICENSE +29 -0
  4. data/puma-8.0.2/README.md +484 -0
  5. data/puma-8.0.2/bin/puma +10 -0
  6. data/puma-8.0.2/bin/puma-wild +25 -0
  7. data/puma-8.0.2/bin/pumactl +12 -0
  8. data/puma-8.0.2/docs/5.0-Upgrade.md +98 -0
  9. data/puma-8.0.2/docs/6.0-Upgrade.md +56 -0
  10. data/puma-8.0.2/docs/7.0-Upgrade.md +52 -0
  11. data/puma-8.0.2/docs/8.0-Upgrade.md +100 -0
  12. data/puma-8.0.2/docs/architecture.md +74 -0
  13. data/puma-8.0.2/docs/compile_options.md +55 -0
  14. data/puma-8.0.2/docs/deployment.md +137 -0
  15. data/puma-8.0.2/docs/fork_worker.md +41 -0
  16. data/puma-8.0.2/docs/grpc.md +62 -0
  17. data/puma-8.0.2/docs/images/favicon.svg +1 -0
  18. data/puma-8.0.2/docs/images/puma-connection-flow-no-reactor.png +0 -0
  19. data/puma-8.0.2/docs/images/puma-connection-flow.png +0 -0
  20. data/puma-8.0.2/docs/images/puma-general-arch.png +0 -0
  21. data/puma-8.0.2/docs/images/running-puma.svg +1 -0
  22. data/puma-8.0.2/docs/images/standard-logo.svg +1 -0
  23. data/puma-8.0.2/docs/java_options.md +54 -0
  24. data/puma-8.0.2/docs/jungle/README.md +9 -0
  25. data/puma-8.0.2/docs/jungle/rc.d/README.md +74 -0
  26. data/puma-8.0.2/docs/jungle/rc.d/puma +61 -0
  27. data/puma-8.0.2/docs/jungle/rc.d/puma.conf +10 -0
  28. data/puma-8.0.2/docs/kubernetes.md +73 -0
  29. data/puma-8.0.2/docs/nginx.md +80 -0
  30. data/puma-8.0.2/docs/plugins.md +42 -0
  31. data/puma-8.0.2/docs/rails_dev_mode.md +28 -0
  32. data/puma-8.0.2/docs/restart.md +65 -0
  33. data/puma-8.0.2/docs/signals.md +98 -0
  34. data/puma-8.0.2/docs/stats.md +148 -0
  35. data/puma-8.0.2/docs/systemd.md +253 -0
  36. data/puma-8.0.2/docs/testing_benchmarks_local_files.md +150 -0
  37. data/puma-8.0.2/docs/testing_test_rackup_ci_files.md +36 -0
  38. data/puma-8.0.2/ext/puma_http11/PumaHttp11Service.java +17 -0
  39. data/puma-8.0.2/ext/puma_http11/extconf.rb +65 -0
  40. data/puma-8.0.2/ext/puma_http11/http11_parser.c +1057 -0
  41. data/puma-8.0.2/ext/puma_http11/http11_parser.h +65 -0
  42. data/puma-8.0.2/ext/puma_http11/http11_parser.java.rl +131 -0
  43. data/puma-8.0.2/ext/puma_http11/http11_parser.rl +149 -0
  44. data/puma-8.0.2/ext/puma_http11/http11_parser_common.rl +54 -0
  45. data/puma-8.0.2/ext/puma_http11/mini_ssl.c +852 -0
  46. data/puma-8.0.2/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  47. data/puma-8.0.2/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
  48. data/puma-8.0.2/ext/puma_http11/org/jruby/puma/Http11.java +321 -0
  49. data/puma-8.0.2/ext/puma_http11/org/jruby/puma/Http11Parser.java +441 -0
  50. data/puma-8.0.2/ext/puma_http11/org/jruby/puma/MiniSSL.java +509 -0
  51. data/puma-8.0.2/ext/puma_http11/puma_http11.c +499 -0
  52. data/puma-8.0.2/lib/puma/app/status.rb +104 -0
  53. data/puma-8.0.2/lib/puma/binder.rb +511 -0
  54. data/puma-8.0.2/lib/puma/cli.rb +245 -0
  55. data/puma-8.0.2/lib/puma/client.rb +756 -0
  56. data/puma-8.0.2/lib/puma/client_env.rb +171 -0
  57. data/puma-8.0.2/lib/puma/cluster/worker.rb +183 -0
  58. data/puma-8.0.2/lib/puma/cluster/worker_handle.rb +127 -0
  59. data/puma-8.0.2/lib/puma/cluster.rb +634 -0
  60. data/puma-8.0.2/lib/puma/cluster_accept_loop_delay.rb +91 -0
  61. data/puma-8.0.2/lib/puma/commonlogger.rb +115 -0
  62. data/puma-8.0.2/lib/puma/configuration.rb +522 -0
  63. data/puma-8.0.2/lib/puma/const.rb +308 -0
  64. data/puma-8.0.2/lib/puma/control_cli.rb +320 -0
  65. data/puma-8.0.2/lib/puma/detect.rb +58 -0
  66. data/puma-8.0.2/lib/puma/dsl.rb +1562 -0
  67. data/puma-8.0.2/lib/puma/error_logger.rb +115 -0
  68. data/puma-8.0.2/lib/puma/events.rb +72 -0
  69. data/puma-8.0.2/lib/puma/io_buffer.rb +50 -0
  70. data/puma-8.0.2/lib/puma/jruby_restart.rb +11 -0
  71. data/puma-8.0.2/lib/puma/json_serialization.rb +96 -0
  72. data/puma-8.0.2/lib/puma/launcher/bundle_pruner.rb +102 -0
  73. data/puma-8.0.2/lib/puma/launcher.rb +501 -0
  74. data/puma-8.0.2/lib/puma/log_writer.rb +153 -0
  75. data/puma-8.0.2/lib/puma/minissl/context_builder.rb +96 -0
  76. data/puma-8.0.2/lib/puma/minissl.rb +458 -0
  77. data/puma-8.0.2/lib/puma/null_io.rb +101 -0
  78. data/puma-8.0.2/lib/puma/plugin/systemd.rb +90 -0
  79. data/puma-8.0.2/lib/puma/plugin/tmp_restart.rb +36 -0
  80. data/puma-8.0.2/lib/puma/plugin.rb +111 -0
  81. data/puma-8.0.2/lib/puma/rack/builder.rb +297 -0
  82. data/puma-8.0.2/lib/puma/rack/urlmap.rb +93 -0
  83. data/puma-8.0.2/lib/puma/rack_default.rb +24 -0
  84. data/puma-8.0.2/lib/puma/reactor.rb +131 -0
  85. data/puma-8.0.2/lib/puma/response.rb +532 -0
  86. data/puma-8.0.2/lib/puma/runner.rb +211 -0
  87. data/puma-8.0.2/lib/puma/sd_notify.rb +146 -0
  88. data/puma-8.0.2/lib/puma/server.rb +773 -0
  89. data/puma-8.0.2/lib/puma/server_plugin_control.rb +32 -0
  90. data/puma-8.0.2/lib/puma/single.rb +72 -0
  91. data/puma-8.0.2/lib/puma/state_file.rb +69 -0
  92. data/puma-8.0.2/lib/puma/thread_pool.rb +517 -0
  93. data/puma-8.0.2/lib/puma/util.rb +134 -0
  94. data/puma-8.0.2/lib/puma.rb +88 -0
  95. data/puma-8.0.2/lib/rack/handler/puma.rb +144 -0
  96. data/puma-8.0.2/tools/Dockerfile +26 -0
  97. data/puma-8.0.2/tools/trickletest.rb +44 -0
  98. data/tiny-fast-gem.gemspec +12 -0
  99. metadata +138 -0
@@ -0,0 +1,501 @@
1
+ # frozen_string_literal: true
2
+
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'
10
+
11
+ module Puma
12
+ # Puma::Launcher is the single entry point for starting a Puma server based on user
13
+ # configuration. It is responsible for taking user supplied arguments and resolving them
14
+ # with configuration in `config/puma.rb` or `config/puma/<env>.rb`.
15
+ #
16
+ # It is responsible for either launching a cluster of Puma workers or a single
17
+ # puma server.
18
+ class Launcher
19
+ autoload :BundlePruner, 'puma/launcher/bundle_pruner'
20
+
21
+ # Returns an instance of Launcher
22
+ #
23
+ # +conf+ A Puma::Configuration object indicating how to run the server.
24
+ #
25
+ # +launcher_args+ A Hash that has a few optional keys.
26
+ # - +:log_writer+:: Expected to hold an object similar to `Puma::LogWriter.stdio`.
27
+ # This object will be responsible for broadcasting Puma's internal state
28
+ # to a logging destination.
29
+ # - +:events+:: Expected to hold an object similar to `Puma::Events`.
30
+ # - +:argv+:: Expected to be an array of strings.
31
+ # - +:env+:: Expected to hold a hash of environment variables.
32
+ #
33
+ # These arguments are re-used when restarting the puma server.
34
+ #
35
+ # Examples:
36
+ #
37
+ # conf = Puma::Configuration.new do |user_config|
38
+ # user_config.threads 1, 10
39
+ # user_config.app do |env|
40
+ # [200, {}, ["hello world"]]
41
+ # end
42
+ # end
43
+ # Puma::Launcher.new(conf, log_writer: Puma::LogWriter.stdio).run
44
+ def initialize(conf, launcher_args={})
45
+ ## Minimal initialization before potential early restart (e.g. from bundle pruning)
46
+
47
+ @config = conf
48
+ # Advertise the CLI Configuration before config files are loaded
49
+ Puma.cli_config = @config if defined?(Puma.cli_config)
50
+ @config.clamp
51
+
52
+ @options = @config.options
53
+
54
+ @log_writer = launcher_args[:log_writer] || LogWriter::DEFAULT
55
+ @log_writer.formatter = LogWriter::PidFormatter.new if clustered?
56
+ @log_writer.formatter = @options[:log_formatter] if @options[:log_formatter]
57
+ @log_writer.custom_logger = @options[:custom_logger] if @options[:custom_logger]
58
+ @options[:log_writer] = @log_writer
59
+ @options[:logger] = @log_writer if clustered?
60
+
61
+ @events = launcher_args[:events] || Events.new
62
+
63
+ @argv = launcher_args[:argv] || []
64
+ @original_argv = @argv.dup
65
+
66
+ ## End minimal initialization
67
+
68
+ generate_restart_data
69
+ Dir.chdir(@restart_dir)
70
+
71
+ prune_bundler!
72
+
73
+ env = launcher_args.delete(:env) || ENV
74
+
75
+ # Log after prune_bundler! to avoid duplicate logging if a restart occurs
76
+ log_config if env['PUMA_LOG_CONFIG']
77
+
78
+ @binder = Binder.new(@log_writer, @options)
79
+ @binder.create_inherited_fds(env).each { |k| env.delete k }
80
+ @binder.create_activated_fds(env).each { |k| env.delete k }
81
+
82
+ @environment = @config.environment
83
+
84
+ # Load the systemd integration if we detect systemd's NOTIFY_SOCKET.
85
+ # Skip this on JRuby though, because it is incompatible with the systemd
86
+ # integration due to https://github.com/jruby/jruby/issues/6504
87
+ if ENV["NOTIFY_SOCKET"] && !Puma.jruby? && !ENV["PUMA_SKIP_SYSTEMD"]
88
+ @config.plugins.create('systemd')
89
+ end
90
+
91
+ if @options[:bind_to_activated_sockets]
92
+ @options[:binds] = @binder.synthesize_binds_from_activated_fs(
93
+ @options[:binds],
94
+ @options[:bind_to_activated_sockets] == 'only'
95
+ )
96
+ end
97
+
98
+ if clustered? && !Puma.forkable?
99
+ unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
100
+ end
101
+
102
+ @environment = @options[:environment] if @options[:environment]
103
+ set_rack_environment
104
+
105
+ if clustered?
106
+ @runner = Cluster.new(self)
107
+ else
108
+ @runner = Single.new(self)
109
+ end
110
+ Puma.stats_object = @runner
111
+
112
+ @status = :run
113
+ end
114
+
115
+ attr_reader :binder, :log_writer, :events, :config, :options, :restart_dir
116
+
117
+ # Return stats about the server
118
+ def stats
119
+ @runner.stats
120
+ end
121
+
122
+ # Write a state file that can be used by pumactl to control
123
+ # the server
124
+ def write_state
125
+ write_pid
126
+
127
+ path = @options[:state]
128
+ permission = @options[:state_permission]
129
+ return unless path
130
+
131
+ require_relative 'state_file'
132
+
133
+ sf = StateFile.new
134
+ sf.pid = Process.pid
135
+ sf.control_url = @options[:control_url]
136
+ sf.control_auth_token = @options[:control_auth_token]
137
+ sf.running_from = File.expand_path('.')
138
+
139
+ sf.save path, permission
140
+ end
141
+
142
+ # Delete the configured pidfile
143
+ def delete_pidfile
144
+ path = @options[:pidfile]
145
+ begin
146
+ File.unlink(path) if path
147
+ rescue Errno::ENOENT
148
+ end
149
+ end
150
+
151
+ # Begin async shutdown of the server
152
+ def halt
153
+ @status = :halt
154
+ @runner.halt
155
+ end
156
+
157
+ # Begin async shutdown of the server gracefully
158
+ def stop
159
+ @status = :stop
160
+ @runner.stop
161
+ end
162
+
163
+ # Begin async restart of the server
164
+ def restart
165
+ @status = :restart
166
+ @runner.restart
167
+ end
168
+
169
+ # Begin a phased restart if supported
170
+ def phased_restart
171
+ unless @runner.respond_to?(:phased_restart) and @runner.phased_restart
172
+ log "* phased-restart called but not available, restarting normally."
173
+ return restart
174
+ end
175
+
176
+ if @options.file_options[:tag].nil?
177
+ dir = File.realdirpath(@restart_dir)
178
+ @options[:tag] = File.basename(dir)
179
+ set_process_title
180
+ end
181
+
182
+ true
183
+ end
184
+
185
+ # Begin a refork if supported
186
+ def refork
187
+ if clustered? && @runner.respond_to?(:fork_worker!) && @options[:fork_worker]
188
+ @runner.fork_worker!
189
+ true
190
+ else
191
+ log "* refork called but not available."
192
+ false
193
+ end
194
+ end
195
+
196
+ # Run the server. This blocks until the server is stopped
197
+ def run
198
+ previous_env = get_env
199
+
200
+ @config.clamp
201
+
202
+ @config.plugins.fire_starts self
203
+
204
+ setup_signals
205
+ set_process_title
206
+
207
+ # This blocks until the server is stopped
208
+ @runner.run
209
+
210
+ do_run_finished(previous_env)
211
+ end
212
+
213
+ # Return all tcp ports the launcher may be using, TCP or SSL
214
+ # @!attribute [r] connected_ports
215
+ # @version 5.0.0
216
+ def connected_ports
217
+ @binder.connected_ports
218
+ end
219
+
220
+ # @!attribute [r] restart_args
221
+ def restart_args
222
+ cmd = @options[:restart_cmd]
223
+ if cmd
224
+ cmd.split(' ') + @original_argv
225
+ else
226
+ @restart_argv
227
+ end
228
+ end
229
+
230
+ def close_binder_listeners
231
+ @runner.close_control_listeners
232
+ @binder.close_listeners
233
+ unless @status == :restart
234
+ log "=== puma shutdown: #{Time.now} ==="
235
+ log "- Goodbye!"
236
+ end
237
+ end
238
+
239
+ # @!attribute [r] thread_status
240
+ # @version 5.0.0
241
+ def thread_status
242
+ Thread.list.each do |thread|
243
+ name = "Thread: TID-#{thread.object_id.to_s(36)}"
244
+ name += " #{thread['label']}" if thread['label']
245
+ name += " #{thread.name}" if thread.respond_to?(:name) && thread.name
246
+ backtrace = thread.backtrace || ["<no backtrace available>"]
247
+
248
+ yield name, backtrace
249
+ end
250
+ end
251
+
252
+ private
253
+
254
+ def get_env
255
+ if defined?(Bundler)
256
+ env = Bundler::ORIGINAL_ENV.dup
257
+ # add -rbundler/setup so we load from Gemfile when restarting
258
+ bundle = "-rbundler/setup"
259
+ env["RUBYOPT"] = [env["RUBYOPT"], bundle].join(" ").lstrip unless env["RUBYOPT"].to_s.include?(bundle)
260
+ env
261
+ else
262
+ ENV.to_h
263
+ end
264
+ end
265
+
266
+ def do_run_finished(previous_env)
267
+ case @status
268
+ when :halt
269
+ do_forceful_stop
270
+ when :run, :stop
271
+ do_graceful_stop
272
+ when :restart
273
+ do_restart(previous_env)
274
+ end
275
+
276
+ close_binder_listeners unless @status == :restart
277
+ end
278
+
279
+ def do_forceful_stop
280
+ log "* Stopping immediately!"
281
+ @runner.stop_control
282
+ end
283
+
284
+ def do_graceful_stop
285
+ @events.fire_after_stopped!
286
+ @runner.stop_blocked
287
+ end
288
+
289
+ def do_restart(previous_env)
290
+ log "* Restarting..."
291
+ ENV.replace(previous_env)
292
+ @runner.stop_control
293
+ restart!
294
+ end
295
+
296
+ def restart!
297
+ @events.fire_before_restart!
298
+ @config.run_hooks :before_restart, self, @log_writer
299
+
300
+ if Puma.jruby?
301
+ close_binder_listeners
302
+
303
+ require_relative 'jruby_restart'
304
+ argv = restart_args
305
+ JRubyRestart.chdir(@restart_dir)
306
+ Kernel.exec(*argv)
307
+ elsif Puma.windows?
308
+ close_binder_listeners
309
+
310
+ argv = restart_args
311
+ Dir.chdir(@restart_dir)
312
+ Kernel.exec(*argv)
313
+ else
314
+ argv = restart_args
315
+ Dir.chdir(@restart_dir)
316
+ ENV.update(@binder.redirects_for_restart_env)
317
+ argv += [@binder.redirects_for_restart]
318
+ Kernel.exec(*argv)
319
+ end
320
+ end
321
+
322
+ # If configured, write the pid of the current process out
323
+ # to a file.
324
+ def write_pid
325
+ path = @options[:pidfile]
326
+ return unless path
327
+ cur_pid = Process.pid
328
+ File.write path, cur_pid, mode: 'wb:UTF-8'
329
+ at_exit do
330
+ delete_pidfile if cur_pid == Process.pid
331
+ end
332
+ end
333
+
334
+ def reload_worker_directory
335
+ @runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
336
+ end
337
+
338
+ def log(str)
339
+ @log_writer.log(str)
340
+ end
341
+
342
+ def clustered?
343
+ (@options[:workers] || 0) > 0
344
+ end
345
+
346
+ def unsupported(str)
347
+ @log_writer.error(str)
348
+ raise UnsupportedOption
349
+ end
350
+
351
+ def set_process_title
352
+ Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
353
+ end
354
+
355
+ # @!attribute [r] title
356
+ def title
357
+ buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
358
+ buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
359
+ buffer
360
+ end
361
+
362
+ def set_rack_environment
363
+ @options[:environment] = environment
364
+ ENV['RACK_ENV'] = environment
365
+ end
366
+
367
+ # @!attribute [r] environment
368
+ def environment
369
+ @environment
370
+ end
371
+
372
+ def prune_bundler?
373
+ @options[:prune_bundler] && clustered? && !@options[:preload_app]
374
+ end
375
+
376
+ def prune_bundler!
377
+ return unless prune_bundler?
378
+ BundlePruner.new(@original_argv, @options[:extra_runtime_dependencies], @log_writer).prune
379
+ end
380
+
381
+ def generate_restart_data
382
+ if dir = @options[:directory]
383
+ @restart_dir = dir
384
+
385
+ elsif Puma.windows?
386
+ # I guess the value of PWD is garbage on windows so don't bother
387
+ # using it.
388
+ @restart_dir = Dir.pwd
389
+
390
+ # Use the same trick as unicorn, namely favor PWD because
391
+ # it will contain an unresolved symlink, useful for when
392
+ # the pwd is /data/releases/current.
393
+ elsif dir = ENV['PWD']
394
+ s_env = File.stat(dir)
395
+ s_pwd = File.stat(Dir.pwd)
396
+
397
+ if s_env.ino == s_pwd.ino and (Puma.jruby? or s_env.dev == s_pwd.dev)
398
+ @restart_dir = dir
399
+ end
400
+ end
401
+
402
+ @restart_dir ||= Dir.pwd
403
+
404
+ # if $0 is a file in the current directory, then restart
405
+ # it the same, otherwise add -S on there because it was
406
+ # picked up in PATH.
407
+ #
408
+ if File.exist?($0)
409
+ arg0 = [Gem.ruby, $0]
410
+ else
411
+ arg0 = [Gem.ruby, "-S", $0]
412
+ end
413
+
414
+ # Detect and reinject -Ilib from the command line, used for testing without bundler
415
+ # cruby has an expanded path, jruby has just "lib"
416
+ lib = File.expand_path "lib"
417
+ arg0[1,0] = ["-I", lib] if [lib, "lib"].include?($LOAD_PATH[0])
418
+
419
+ if defined? Puma::WILD_ARGS
420
+ @restart_argv = arg0 + Puma::WILD_ARGS + @original_argv
421
+ else
422
+ @restart_argv = arg0 + @original_argv
423
+ end
424
+ end
425
+
426
+ def setup_signals
427
+ unless ENV["PUMA_SKIP_SIGUSR2"]
428
+ begin
429
+ Signal.trap "SIGUSR2" do
430
+ restart
431
+ end
432
+ rescue Exception
433
+ log "*** SIGUSR2 not implemented, signal based restart unavailable!"
434
+ end
435
+ end
436
+
437
+ unless Puma.jruby?
438
+ begin
439
+ Signal.trap "SIGUSR1" do
440
+ phased_restart
441
+ end
442
+ rescue Exception
443
+ log "*** SIGUSR1 not implemented, signal based restart unavailable!"
444
+ end
445
+ end
446
+
447
+ begin
448
+ Signal.trap "SIGTERM" do
449
+ # Shortcut the control flow in case raise_exception_on_sigterm is true
450
+ do_graceful_stop
451
+
452
+ raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
453
+ end
454
+ rescue Exception
455
+ log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
456
+ end
457
+
458
+ begin
459
+ Signal.trap "SIGINT" do
460
+ stop
461
+ end
462
+ rescue Exception
463
+ log "*** SIGINT not implemented, signal based gracefully stopping unavailable!"
464
+ end
465
+
466
+ begin
467
+ Signal.trap "SIGHUP" do
468
+ if @runner.redirected_io?
469
+ @runner.redirect_io
470
+ else
471
+ stop
472
+ end
473
+ end
474
+ rescue Exception
475
+ log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
476
+ end
477
+
478
+ begin
479
+ if Puma.backtrace_signal
480
+ Signal.trap Puma.backtrace_signal do
481
+ thread_status do |name, backtrace|
482
+ @log_writer.log(name)
483
+ @log_writer.log(backtrace.map { |bt| " #{bt}" })
484
+ end
485
+ end
486
+ end
487
+ rescue Exception
488
+ log "*** SIGINFO/SIGPWR not implemented, signal based backtrace unavailable!"
489
+ end
490
+ end
491
+
492
+ def log_config
493
+ log "Configuration:"
494
+
495
+ @config.final_options
496
+ .each { |config_key, value| log "- #{config_key}: #{value}" }
497
+
498
+ log "\n"
499
+ end
500
+ end
501
+ end
@@ -0,0 +1,153 @@
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=nil, &block)
94
+ if @debug
95
+ if block_given?
96
+ log("% #{yield}")
97
+ else
98
+ log("% #{str}")
99
+ end
100
+ end
101
+ end
102
+
103
+ # Write +str+ to +@stderr+
104
+ def error(str)
105
+ @error_logger.info(text: @formatter.call("ERROR: #{str}"))
106
+ exit 1
107
+ end
108
+
109
+ def format(str)
110
+ formatter.call(str)
111
+ end
112
+
113
+ # An HTTP connection error has occurred.
114
+ # +error+ a connection exception, +req+ the request,
115
+ # and +text+ additional info
116
+ # @version 5.0.0
117
+ def connection_error(error, req, text="HTTP connection error")
118
+ @error_logger.info(error: error, req: req, text: text)
119
+ end
120
+
121
+ # An HTTP parse error has occurred.
122
+ # +error+ a parsing exception,
123
+ # and +req+ the request.
124
+ def parse_error(error, req)
125
+ @error_logger.info(error: error, req: req, text: 'HTTP parse error, malformed request')
126
+ end
127
+
128
+ # An SSL error has occurred.
129
+ # @param error <Puma::MiniSSL::SSLError>
130
+ # @param ssl_socket <Puma::MiniSSL::Socket>
131
+ def ssl_error(error, ssl_socket)
132
+ peeraddr = ssl_socket.peeraddr.last rescue "<unknown>"
133
+ peercert = ssl_socket.peercert
134
+ subject = peercert&.subject
135
+ @error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
136
+ end
137
+
138
+ # An unknown error has occurred.
139
+ # +error+ an exception object, +req+ the request,
140
+ # and +text+ additional info
141
+ def unknown_error(error, req=nil, text="Unknown error")
142
+ @error_logger.info(error: error, req: req, text: text)
143
+ end
144
+
145
+ # Log occurred error debug dump.
146
+ # +error+ an exception object, +req+ the request,
147
+ # and +text+ additional info
148
+ # @version 5.0.0
149
+ def debug_error(error, req=nil, text="")
150
+ @error_logger.debug(error: error, req: req, text: text)
151
+ end
152
+ end
153
+ end