jun-puma 1.0.1-java → 1.0.2-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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/lib/puma/puma_http11.jar +0 -0
  3. metadata +3 -81
  4. data/bin/puma-wild +0 -25
  5. data/docs/architecture.md +0 -74
  6. data/docs/compile_options.md +0 -55
  7. data/docs/deployment.md +0 -102
  8. data/docs/fork_worker.md +0 -31
  9. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  10. data/docs/images/puma-connection-flow.png +0 -0
  11. data/docs/images/puma-general-arch.png +0 -0
  12. data/docs/jungle/README.md +0 -9
  13. data/docs/jungle/rc.d/README.md +0 -74
  14. data/docs/jungle/rc.d/puma +0 -61
  15. data/docs/jungle/rc.d/puma.conf +0 -10
  16. data/docs/kubernetes.md +0 -78
  17. data/docs/nginx.md +0 -80
  18. data/docs/plugins.md +0 -38
  19. data/docs/rails_dev_mode.md +0 -28
  20. data/docs/restart.md +0 -64
  21. data/docs/signals.md +0 -98
  22. data/docs/stats.md +0 -142
  23. data/docs/systemd.md +0 -244
  24. data/docs/testing_benchmarks_local_files.md +0 -150
  25. data/docs/testing_test_rackup_ci_files.md +0 -36
  26. data/ext/puma_http11/PumaHttp11Service.java +0 -17
  27. data/ext/puma_http11/ext_help.h +0 -15
  28. data/ext/puma_http11/http11_parser.c +0 -1057
  29. data/ext/puma_http11/http11_parser.h +0 -65
  30. data/ext/puma_http11/http11_parser.java.rl +0 -145
  31. data/ext/puma_http11/http11_parser.rl +0 -149
  32. data/ext/puma_http11/http11_parser_common.rl +0 -54
  33. data/ext/puma_http11/mini_ssl.c +0 -832
  34. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -15
  35. data/ext/puma_http11/org/jruby/puma/Http11.java +0 -226
  36. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +0 -455
  37. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +0 -508
  38. data/ext/puma_http11/puma_http11.c +0 -492
  39. data/lib/puma/app/status.rb +0 -96
  40. data/lib/puma/binder.rb +0 -501
  41. data/lib/puma/cli.rb +0 -243
  42. data/lib/puma/client.rb +0 -632
  43. data/lib/puma/cluster/worker.rb +0 -182
  44. data/lib/puma/cluster/worker_handle.rb +0 -97
  45. data/lib/puma/cluster.rb +0 -562
  46. data/lib/puma/commonlogger.rb +0 -115
  47. data/lib/puma/configuration.rb +0 -391
  48. data/lib/puma/const.rb +0 -289
  49. data/lib/puma/control_cli.rb +0 -316
  50. data/lib/puma/detect.rb +0 -45
  51. data/lib/puma/dsl.rb +0 -1204
  52. data/lib/puma/error_logger.rb +0 -113
  53. data/lib/puma/events.rb +0 -57
  54. data/lib/puma/io_buffer.rb +0 -46
  55. data/lib/puma/jruby_restart.rb +0 -27
  56. data/lib/puma/json_serialization.rb +0 -96
  57. data/lib/puma/launcher/bundle_pruner.rb +0 -104
  58. data/lib/puma/launcher.rb +0 -484
  59. data/lib/puma/log_writer.rb +0 -147
  60. data/lib/puma/minissl/context_builder.rb +0 -95
  61. data/lib/puma/minissl.rb +0 -458
  62. data/lib/puma/null_io.rb +0 -61
  63. data/lib/puma/plugin/systemd.rb +0 -90
  64. data/lib/puma/plugin/tmp_restart.rb +0 -36
  65. data/lib/puma/plugin.rb +0 -111
  66. data/lib/puma/rack/builder.rb +0 -297
  67. data/lib/puma/rack/urlmap.rb +0 -93
  68. data/lib/puma/rack_default.rb +0 -24
  69. data/lib/puma/reactor.rb +0 -125
  70. data/lib/puma/request.rb +0 -671
  71. data/lib/puma/runner.rb +0 -213
  72. data/lib/puma/sd_notify.rb +0 -149
  73. data/lib/puma/server.rb +0 -664
  74. data/lib/puma/single.rb +0 -69
  75. data/lib/puma/state_file.rb +0 -68
  76. data/lib/puma/thread_pool.rb +0 -434
  77. data/lib/puma/util.rb +0 -141
  78. data/lib/puma.rb +0 -78
  79. data/lib/rack/handler/puma.rb +0 -141
  80. data/tools/Dockerfile +0 -16
  81. data/tools/trickletest.rb +0 -44
data/lib/puma/cluster.rb DELETED
@@ -1,562 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'runner'
4
- require_relative 'util'
5
- require_relative 'plugin'
6
- require_relative 'cluster/worker_handle'
7
- require_relative 'cluster/worker'
8
-
9
- module Puma
10
- # This class is instantiated by the `Puma::Launcher` and used
11
- # to boot and serve a Ruby application when puma "workers" are needed
12
- # i.e. when using multi-processes. For example `$ puma -w 5`
13
- #
14
- # An instance of this class will spawn the number of processes passed in
15
- # via the `spawn_workers` method call. Each worker will have it's own
16
- # instance of a `Puma::Server`.
17
- class Cluster < Runner
18
- def initialize(launcher)
19
- super(launcher)
20
-
21
- @phase = 0
22
- @workers = []
23
- @next_check = Time.now
24
-
25
- @phased_restart = false
26
- end
27
-
28
- # Returns the list of cluster worker handles.
29
- # @return [Array<Puma::Cluster::WorkerHandle>]
30
- attr_reader :workers
31
-
32
- def stop_workers
33
- log "- Gracefully shutting down workers..."
34
- @workers.each { |x| x.term }
35
-
36
- begin
37
- loop do
38
- wait_workers
39
- break if @workers.reject {|w| w.pid.nil?}.empty?
40
- sleep 0.2
41
- end
42
- rescue Interrupt
43
- log "! Cancelled waiting for workers"
44
- end
45
- end
46
-
47
- def start_phased_restart
48
- @events.fire_on_restart!
49
- @phase += 1
50
- log "- Starting phased worker restart, phase: #{@phase}"
51
-
52
- # Be sure to change the directory again before loading
53
- # the app. This way we can pick up new code.
54
- dir = @launcher.restart_dir
55
- log "+ Changing to #{dir}"
56
- Dir.chdir dir
57
- end
58
-
59
- def redirect_io
60
- super
61
-
62
- @workers.each { |x| x.hup }
63
- end
64
-
65
- def spawn_workers
66
- diff = @options[:workers] - @workers.size
67
- return if diff < 1
68
-
69
- master = Process.pid
70
- if @options[:fork_worker]
71
- @fork_writer << "-1\n"
72
- end
73
-
74
- diff.times do
75
- idx = next_worker_index
76
-
77
- if @options[:fork_worker] && idx != 0
78
- @fork_writer << "#{idx}\n"
79
- pid = nil
80
- else
81
- pid = spawn_worker(idx, master)
82
- end
83
-
84
- debug "Spawned worker: #{pid}"
85
- @workers << WorkerHandle.new(idx, pid, @phase, @options)
86
- end
87
-
88
- if @options[:fork_worker] && all_workers_in_phase?
89
- @fork_writer << "0\n"
90
- end
91
- end
92
-
93
- # @version 5.0.0
94
- def spawn_worker(idx, master)
95
- @config.run_hooks(:before_worker_fork, idx, @log_writer)
96
-
97
- pid = fork { worker(idx, master) }
98
- if !pid
99
- log "! Complete inability to spawn new workers detected"
100
- log "! Seppuku is the only choice."
101
- exit! 1
102
- end
103
-
104
- @config.run_hooks(:after_worker_fork, idx, @log_writer)
105
- pid
106
- end
107
-
108
- def cull_workers
109
- diff = @workers.size - @options[:workers]
110
- return if diff < 1
111
- debug "Culling #{diff} workers"
112
-
113
- workers = workers_to_cull(diff)
114
- debug "Workers to cull: #{workers.inspect}"
115
-
116
- workers.each do |worker|
117
- log "- Worker #{worker.index} (PID: #{worker.pid}) terminating"
118
- worker.term
119
- end
120
- end
121
-
122
- def workers_to_cull(diff)
123
- workers = @workers.sort_by(&:started_at)
124
-
125
- # In fork_worker mode, worker 0 acts as our master process.
126
- # We should avoid culling it to preserve copy-on-write memory gains.
127
- workers.reject! { |w| w.index == 0 } if @options[:fork_worker]
128
-
129
- workers[cull_start_index(diff), diff]
130
- end
131
-
132
- def cull_start_index(diff)
133
- case @options[:worker_culling_strategy]
134
- when :oldest
135
- 0
136
- else # :youngest
137
- -diff
138
- end
139
- end
140
-
141
- # @!attribute [r] next_worker_index
142
- def next_worker_index
143
- occupied_positions = @workers.map(&:index)
144
- idx = 0
145
- idx += 1 until !occupied_positions.include?(idx)
146
- idx
147
- end
148
-
149
- def worker_at(idx)
150
- @workers.find { |w| w.index == idx }
151
- end
152
-
153
- def all_workers_booted?
154
- @workers.count { |w| !w.booted? } == 0
155
- end
156
-
157
- def all_workers_in_phase?
158
- @workers.all? { |w| w.phase == @phase }
159
- end
160
-
161
- def check_workers
162
- return if @next_check >= Time.now
163
-
164
- @next_check = Time.now + @options[:worker_check_interval]
165
-
166
- timeout_workers
167
- wait_workers
168
- cull_workers
169
- spawn_workers
170
-
171
- if all_workers_booted?
172
- # If we're running at proper capacity, check to see if
173
- # we need to phase any workers out (which will restart
174
- # in the right phase).
175
- #
176
- w = @workers.find { |x| x.phase != @phase }
177
-
178
- if w
179
- log "- Stopping #{w.pid} for phased upgrade..."
180
- unless w.term?
181
- w.term
182
- log "- #{w.signal} sent to #{w.pid}..."
183
- end
184
- end
185
- end
186
-
187
- t = @workers.reject(&:term?)
188
- t.map!(&:ping_timeout)
189
-
190
- @next_check = [t.min, @next_check].compact.min
191
- end
192
-
193
- def worker(index, master)
194
- @workers = []
195
-
196
- @master_read.close
197
- @suicide_pipe.close
198
- @fork_writer.close
199
-
200
- pipes = { check_pipe: @check_pipe, worker_write: @worker_write }
201
- if @options[:fork_worker]
202
- pipes[:fork_pipe] = @fork_pipe
203
- pipes[:wakeup] = @wakeup
204
- end
205
-
206
- server = start_server if preload?
207
- new_worker = Worker.new index: index,
208
- master: master,
209
- launcher: @launcher,
210
- pipes: pipes,
211
- server: server
212
- new_worker.run
213
- end
214
-
215
- def restart
216
- @restart = true
217
- stop
218
- end
219
-
220
- def phased_restart(refork = false)
221
- return false if @options[:preload_app] && !refork
222
-
223
- @phased_restart = true
224
- wakeup!
225
-
226
- true
227
- end
228
-
229
- def stop
230
- @status = :stop
231
- wakeup!
232
- end
233
-
234
- def stop_blocked
235
- @status = :stop if @status == :run
236
- wakeup!
237
- @control&.stop true
238
- Process.waitall
239
- end
240
-
241
- def halt
242
- @status = :halt
243
- wakeup!
244
- end
245
-
246
- def reload_worker_directory
247
- dir = @launcher.restart_dir
248
- log "+ Changing to #{dir}"
249
- Dir.chdir dir
250
- end
251
-
252
- # Inside of a child process, this will return all zeroes, as @workers is only populated in
253
- # the master process.
254
- # @!attribute [r] stats
255
- def stats
256
- old_worker_count = @workers.count { |w| w.phase != @phase }
257
- worker_status = @workers.map do |w|
258
- {
259
- started_at: utc_iso8601(w.started_at),
260
- pid: w.pid,
261
- index: w.index,
262
- phase: w.phase,
263
- booted: w.booted?,
264
- last_checkin: utc_iso8601(w.last_checkin),
265
- last_status: w.last_status,
266
- }
267
- end
268
-
269
- {
270
- started_at: utc_iso8601(@started_at),
271
- workers: @workers.size,
272
- phase: @phase,
273
- booted_workers: worker_status.count { |w| w[:booted] },
274
- old_workers: old_worker_count,
275
- worker_status: worker_status,
276
- }.merge(super)
277
- end
278
-
279
- def preload?
280
- @options[:preload_app]
281
- end
282
-
283
- # @version 5.0.0
284
- def fork_worker!
285
- if (worker = worker_at 0)
286
- worker.phase += 1
287
- end
288
- phased_restart(true)
289
- end
290
-
291
- # We do this in a separate method to keep the lambda scope
292
- # of the signals handlers as small as possible.
293
- def setup_signals
294
- if @options[:fork_worker]
295
- Signal.trap "SIGURG" do
296
- fork_worker!
297
- end
298
-
299
- # Auto-fork after the specified number of requests.
300
- if (fork_requests = @options[:fork_worker].to_i) > 0
301
- @events.register(:ping!) do |w|
302
- fork_worker! if w.index == 0 &&
303
- w.phase == 0 &&
304
- w.last_status[:requests_count] >= fork_requests
305
- end
306
- end
307
- end
308
-
309
- Signal.trap "SIGCHLD" do
310
- wakeup!
311
- end
312
-
313
- Signal.trap "TTIN" do
314
- @options[:workers] += 1
315
- wakeup!
316
- end
317
-
318
- Signal.trap "TTOU" do
319
- @options[:workers] -= 1 if @options[:workers] >= 2
320
- wakeup!
321
- end
322
-
323
- master_pid = Process.pid
324
-
325
- Signal.trap "SIGTERM" do
326
- # The worker installs their own SIGTERM when booted.
327
- # Until then, this is run by the worker and the worker
328
- # should just exit if they get it.
329
- if Process.pid != master_pid
330
- log "Early termination of worker"
331
- exit! 0
332
- else
333
- @launcher.close_binder_listeners
334
-
335
- stop_workers
336
- stop
337
- @events.fire_on_stopped!
338
- raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
339
- exit 0 # Clean exit, workers were stopped
340
- end
341
- end
342
- end
343
-
344
- def run
345
- @status = :run
346
-
347
- output_header "cluster"
348
-
349
- # This is aligned with the output from Runner, see Runner#output_header
350
- log "* Workers: #{@options[:workers]}"
351
-
352
- if preload?
353
- # Threads explicitly marked as fork safe will be ignored. Used in Rails,
354
- # but may be used by anyone. Note that we need to explicit
355
- # Process::Waiter check here because there's a bug in Ruby 2.6 and below
356
- # where calling thread_variable_get on a Process::Waiter will segfault.
357
- # We can drop that clause once those versions of Ruby are no longer
358
- # supported.
359
- fork_safe = ->(t) { !t.is_a?(Process::Waiter) && t.thread_variable_get(:fork_safe) }
360
-
361
- before = Thread.list.reject(&fork_safe)
362
-
363
- log "* Restarts: (\u2714) hot (\u2716) phased"
364
- log "* Preloading application"
365
- load_and_bind
366
-
367
- after = Thread.list.reject(&fork_safe)
368
-
369
- if after.size > before.size
370
- threads = (after - before)
371
- if threads.first.respond_to? :backtrace
372
- log "! WARNING: Detected #{after.size-before.size} Thread(s) started in app boot:"
373
- threads.each do |t|
374
- log "! #{t.inspect} - #{t.backtrace ? t.backtrace.first : ''}"
375
- end
376
- else
377
- log "! WARNING: Detected #{after.size-before.size} Thread(s) started in app boot"
378
- end
379
- end
380
- else
381
- log "* Restarts: (\u2714) hot (\u2714) phased"
382
-
383
- unless @config.app_configured?
384
- error "No application configured, nothing to run"
385
- exit 1
386
- end
387
-
388
- @launcher.binder.parse @options[:binds]
389
- end
390
-
391
- read, @wakeup = Puma::Util.pipe
392
-
393
- setup_signals
394
-
395
- # Used by the workers to detect if the master process dies.
396
- # If select says that @check_pipe is ready, it's because the
397
- # master has exited and @suicide_pipe has been automatically
398
- # closed.
399
- #
400
- @check_pipe, @suicide_pipe = Puma::Util.pipe
401
-
402
- # Separate pipe used by worker 0 to receive commands to
403
- # fork new worker processes.
404
- @fork_pipe, @fork_writer = Puma::Util.pipe
405
-
406
- log "Use Ctrl-C to stop"
407
-
408
- single_worker_warning
409
-
410
- redirect_io
411
-
412
- Plugins.fire_background
413
-
414
- @launcher.write_state
415
-
416
- start_control
417
-
418
- @master_read, @worker_write = read, @wakeup
419
-
420
- @config.run_hooks(:before_fork, nil, @log_writer)
421
-
422
- spawn_workers
423
-
424
- Signal.trap "SIGINT" do
425
- stop
426
- end
427
-
428
- begin
429
- booted = false
430
- in_phased_restart = false
431
- workers_not_booted = @options[:workers]
432
-
433
- while @status == :run
434
- begin
435
- if @phased_restart
436
- start_phased_restart
437
- @phased_restart = false
438
- in_phased_restart = true
439
- workers_not_booted = @options[:workers]
440
- end
441
-
442
- check_workers
443
-
444
- if read.wait_readable([0, @next_check - Time.now].max)
445
- req = read.read_nonblock(1)
446
-
447
- @next_check = Time.now if req == "!"
448
- next if !req || req == "!"
449
-
450
- result = read.gets
451
- pid = result.to_i
452
-
453
- if req == "b" || req == "f"
454
- pid, idx = result.split(':').map(&:to_i)
455
- w = worker_at idx
456
- w.pid = pid if w.pid.nil?
457
- end
458
-
459
- if w = @workers.find { |x| x.pid == pid }
460
- case req
461
- when "b"
462
- w.boot!
463
- log "- Worker #{w.index} (PID: #{pid}) booted in #{w.uptime.round(2)}s, phase: #{w.phase}"
464
- @next_check = Time.now
465
- workers_not_booted -= 1
466
- when "e"
467
- # external term, see worker method, Signal.trap "SIGTERM"
468
- w.term!
469
- when "t"
470
- w.term unless w.term?
471
- when "p"
472
- status = result.sub(/^\d+/,'').chomp
473
- w.ping!(status)
474
- @events.fire(:ping!, w)
475
-
476
- if in_phased_restart && workers_not_booted.positive? && w0 = worker_at(0)
477
- w0.ping!(status)
478
- @events.fire(:ping!, w0)
479
- end
480
-
481
- if !booted && @workers.none? {|worker| worker.last_status.empty?}
482
- @events.fire_on_booted!
483
- debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
484
- booted = true
485
- end
486
- end
487
- else
488
- log "! Out-of-sync worker list, no #{pid} worker"
489
- end
490
- end
491
- if in_phased_restart && workers_not_booted.zero?
492
- @events.fire_on_booted!
493
- debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
494
- in_phased_restart = false
495
- end
496
-
497
- rescue Interrupt
498
- @status = :stop
499
- end
500
- end
501
-
502
- stop_workers unless @status == :halt
503
- ensure
504
- @check_pipe.close
505
- @suicide_pipe.close
506
- read.close
507
- @wakeup.close
508
- end
509
- end
510
-
511
- private
512
-
513
- def single_worker_warning
514
- return if @options[:workers] != 1 || @options[:silence_single_worker_warning]
515
-
516
- log "! WARNING: Detected running cluster mode with 1 worker."
517
- log "! Running Puma in cluster mode with a single worker is often a misconfiguration."
518
- log "! Consider running Puma in single-mode (workers = 0) in order to reduce memory overhead."
519
- log "! Set the `silence_single_worker_warning` option to silence this warning message."
520
- end
521
-
522
- # loops thru @workers, removing workers that exited, and calling
523
- # `#term` if needed
524
- def wait_workers
525
- @workers.reject! do |w|
526
- next false if w.pid.nil?
527
- begin
528
- if Process.wait(w.pid, Process::WNOHANG)
529
- true
530
- else
531
- w.term if w.term?
532
- nil
533
- end
534
- rescue Errno::ECHILD
535
- begin
536
- Process.kill(0, w.pid)
537
- # child still alive but has another parent (e.g., using fork_worker)
538
- w.term if w.term?
539
- false
540
- rescue Errno::ESRCH, Errno::EPERM
541
- true # child is already terminated
542
- end
543
- end
544
- end
545
- end
546
-
547
- # @version 5.0.0
548
- def timeout_workers
549
- @workers.each do |w|
550
- if !w.term? && w.ping_timeout <= Time.now
551
- details = if w.booted?
552
- "(Worker #{w.index} failed to check in within #{@options[:worker_timeout]} seconds)"
553
- else
554
- "(Worker #{w.index} failed to boot within #{@options[:worker_boot_timeout]} seconds)"
555
- end
556
- log "! Terminating timed out worker #{details}: #{w.pid}"
557
- w.kill
558
- end
559
- end
560
- end
561
- end
562
- end
@@ -1,115 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Puma
4
- # Rack::CommonLogger forwards every request to the given +app+, and
5
- # logs a line in the
6
- # {Apache common log format}[https://httpd.apache.org/docs/2.4/logs.html#common]
7
- # to the +logger+.
8
- #
9
- # If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
10
- # an instance of Rack::NullLogger.
11
- #
12
- # +logger+ can be any class, including the standard library Logger, and is
13
- # expected to have either +write+ or +<<+ method, which accepts the CommonLogger::FORMAT.
14
- # According to the SPEC, the error stream must also respond to +puts+
15
- # (which takes a single argument that responds to +to_s+), and +flush+
16
- # (which is called without arguments in order to make the error appear for
17
- # sure)
18
- class CommonLogger
19
- # Common Log Format: https://httpd.apache.org/docs/2.4/logs.html#common
20
- #
21
- # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
22
- #
23
- # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
24
- FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
25
-
26
- HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
27
-
28
- LOG_TIME_FORMAT = '%d/%b/%Y:%H:%M:%S %z'
29
-
30
- CONTENT_LENGTH = 'Content-Length' # should be lower case from app,
31
- # Util::HeaderHash allows mixed
32
- HTTP_VERSION = Const::HTTP_VERSION
33
- HTTP_X_FORWARDED_FOR = Const::HTTP_X_FORWARDED_FOR
34
- PATH_INFO = Const::PATH_INFO
35
- QUERY_STRING = Const::QUERY_STRING
36
- REMOTE_ADDR = Const::REMOTE_ADDR
37
- REMOTE_USER = 'REMOTE_USER'
38
- REQUEST_METHOD = Const::REQUEST_METHOD
39
-
40
- def initialize(app, logger=nil)
41
- @app = app
42
- @logger = logger
43
- end
44
-
45
- def call(env)
46
- began_at = Time.now
47
- status, header, body = @app.call(env)
48
- header = Util::HeaderHash.new(header)
49
-
50
- # If we've been hijacked, then output a special line
51
- if env['rack.hijack_io']
52
- log_hijacking(env, 'HIJACK', header, began_at)
53
- else
54
- ary = env['rack.after_reply']
55
- ary << lambda { log(env, status, header, began_at) }
56
- end
57
-
58
- [status, header, body]
59
- end
60
-
61
- private
62
-
63
- def log_hijacking(env, status, header, began_at)
64
- now = Time.now
65
-
66
- msg = HIJACK_FORMAT % [
67
- env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
68
- env[REMOTE_USER] || "-",
69
- now.strftime(LOG_TIME_FORMAT),
70
- env[REQUEST_METHOD],
71
- env[PATH_INFO],
72
- env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
73
- env[HTTP_VERSION],
74
- now - began_at ]
75
-
76
- write(msg)
77
- end
78
-
79
- def log(env, status, header, began_at)
80
- now = Time.now
81
- length = extract_content_length(header)
82
-
83
- msg = FORMAT % [
84
- env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
85
- env[REMOTE_USER] || "-",
86
- now.strftime(LOG_TIME_FORMAT),
87
- env[REQUEST_METHOD],
88
- env[PATH_INFO],
89
- env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
90
- env[HTTP_VERSION],
91
- status.to_s[0..3],
92
- length,
93
- now - began_at ]
94
-
95
- write(msg)
96
- end
97
-
98
- def write(msg)
99
- logger = @logger || env['rack.errors']
100
-
101
- # Standard library logger doesn't support write but it supports << which actually
102
- # calls to write on the log device without formatting
103
- if logger.respond_to?(:write)
104
- logger.write(msg)
105
- else
106
- logger << msg
107
- end
108
- end
109
-
110
- def extract_content_length(headers)
111
- value = headers[CONTENT_LENGTH] or return '-'
112
- value.to_s == '0' ? '-' : value
113
- end
114
- end
115
- end