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.
- checksums.yaml +4 -4
- data/History.md +465 -18
- data/README.md +152 -42
- data/bin/puma-wild +1 -1
- data/docs/compile_options.md +34 -0
- data/docs/fork_worker.md +12 -4
- data/docs/java_options.md +54 -0
- data/docs/kubernetes.md +12 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +4 -0
- data/docs/restart.md +1 -0
- data/docs/signals.md +2 -2
- data/docs/stats.md +8 -3
- data/docs/systemd.md +13 -7
- 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 +27 -17
- 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 +137 -19
- data/ext/puma_http11/org/jruby/puma/Http11.java +31 -10
- 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 +21 -10
- data/lib/puma/app/status.rb +4 -4
- data/lib/puma/binder.rb +60 -55
- data/lib/puma/cli.rb +22 -20
- data/lib/puma/client.rb +93 -30
- data/lib/puma/cluster/worker.rb +27 -17
- data/lib/puma/cluster/worker_handle.rb +8 -6
- data/lib/puma/cluster.rb +121 -47
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +101 -65
- data/lib/puma/const.rb +141 -93
- data/lib/puma/control_cli.rb +19 -15
- data/lib/puma/detect.rb +7 -4
- data/lib/puma/dsl.rb +521 -88
- data/lib/puma/error_logger.rb +22 -13
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +39 -4
- data/lib/puma/jruby_restart.rb +0 -15
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +121 -181
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +27 -12
- data/lib/puma/minissl.rb +105 -11
- data/lib/puma/null_io.rb +42 -2
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/rack/builder.rb +6 -6
- data/lib/puma/rack/urlmap.rb +1 -1
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +19 -10
- data/lib/puma/request.rb +368 -169
- data/lib/puma/runner.rb +65 -22
- data/lib/puma/sd_notify.rb +146 -0
- data/lib/puma/server.rb +161 -102
- data/lib/puma/single.rb +13 -11
- data/lib/puma/state_file.rb +3 -6
- data/lib/puma/thread_pool.rb +71 -21
- data/lib/puma/util.rb +1 -12
- data/lib/puma.rb +9 -10
- data/lib/rack/handler/puma.rb +116 -86
- data/tools/Dockerfile +2 -2
- metadata +17 -12
- 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/cluster.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
require 'time'
|
3
|
+
require_relative 'runner'
|
4
|
+
require_relative 'util'
|
5
|
+
require_relative 'plugin'
|
6
|
+
require_relative 'cluster/worker_handle'
|
7
|
+
require_relative 'cluster/worker'
|
10
8
|
|
11
9
|
module Puma
|
12
10
|
# This class is instantiated by the `Puma::Launcher` and used
|
@@ -17,8 +15,8 @@ module Puma
|
|
17
15
|
# via the `spawn_workers` method call. Each worker will have it's own
|
18
16
|
# instance of a `Puma::Server`.
|
19
17
|
class Cluster < Runner
|
20
|
-
def initialize(
|
21
|
-
super
|
18
|
+
def initialize(launcher)
|
19
|
+
super(launcher)
|
22
20
|
|
23
21
|
@phase = 0
|
24
22
|
@workers = []
|
@@ -27,6 +25,10 @@ module Puma
|
|
27
25
|
@phased_restart = false
|
28
26
|
end
|
29
27
|
|
28
|
+
# Returns the list of cluster worker handles.
|
29
|
+
# @return [Array<Puma::Cluster::WorkerHandle>]
|
30
|
+
attr_reader :workers
|
31
|
+
|
30
32
|
def stop_workers
|
31
33
|
log "- Gracefully shutting down workers..."
|
32
34
|
@workers.each { |x| x.term }
|
@@ -83,16 +85,18 @@ module Puma
|
|
83
85
|
@workers << WorkerHandle.new(idx, pid, @phase, @options)
|
84
86
|
end
|
85
87
|
|
86
|
-
if @options[:fork_worker] &&
|
87
|
-
@workers.all? {|x| x.phase == @phase}
|
88
|
-
|
88
|
+
if @options[:fork_worker] && all_workers_in_phase?
|
89
89
|
@fork_writer << "0\n"
|
90
|
+
|
91
|
+
if worker_at(0).phase > 0
|
92
|
+
@fork_writer << "-2\n"
|
93
|
+
end
|
90
94
|
end
|
91
95
|
end
|
92
96
|
|
93
97
|
# @version 5.0.0
|
94
98
|
def spawn_worker(idx, master)
|
95
|
-
@
|
99
|
+
@config.run_hooks(:before_worker_fork, idx, @log_writer)
|
96
100
|
|
97
101
|
pid = fork { worker(idx, master) }
|
98
102
|
if !pid
|
@@ -101,7 +105,7 @@ module Puma
|
|
101
105
|
exit! 1
|
102
106
|
end
|
103
107
|
|
104
|
-
@
|
108
|
+
@config.run_hooks(:after_worker_fork, idx, @log_writer)
|
105
109
|
pid
|
106
110
|
end
|
107
111
|
|
@@ -146,10 +150,22 @@ module Puma
|
|
146
150
|
idx
|
147
151
|
end
|
148
152
|
|
153
|
+
def worker_at(idx)
|
154
|
+
@workers.find { |w| w.index == idx }
|
155
|
+
end
|
156
|
+
|
149
157
|
def all_workers_booted?
|
150
158
|
@workers.count { |w| !w.booted? } == 0
|
151
159
|
end
|
152
160
|
|
161
|
+
def all_workers_in_phase?
|
162
|
+
@workers.all? { |w| w.phase == @phase }
|
163
|
+
end
|
164
|
+
|
165
|
+
def all_workers_idle_timed_out?
|
166
|
+
(@workers.map(&:pid) - idle_timed_out_worker_pids).empty?
|
167
|
+
end
|
168
|
+
|
153
169
|
def check_workers
|
154
170
|
return if @next_check >= Time.now
|
155
171
|
|
@@ -176,10 +192,10 @@ module Puma
|
|
176
192
|
end
|
177
193
|
end
|
178
194
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
].compact.min
|
195
|
+
t = @workers.reject(&:term?)
|
196
|
+
t.map!(&:ping_timeout)
|
197
|
+
|
198
|
+
@next_check = [t.min, @next_check].compact.min
|
183
199
|
end
|
184
200
|
|
185
201
|
def worker(index, master)
|
@@ -209,8 +225,8 @@ module Puma
|
|
209
225
|
stop
|
210
226
|
end
|
211
227
|
|
212
|
-
def phased_restart
|
213
|
-
return false if @options[:preload_app]
|
228
|
+
def phased_restart(refork = false)
|
229
|
+
return false if @options[:preload_app] && !refork
|
214
230
|
|
215
231
|
@phased_restart = true
|
216
232
|
wakeup!
|
@@ -226,7 +242,7 @@ module Puma
|
|
226
242
|
def stop_blocked
|
227
243
|
@status = :stop if @status == :run
|
228
244
|
wakeup!
|
229
|
-
@control
|
245
|
+
@control&.stop true
|
230
246
|
Process.waitall
|
231
247
|
end
|
232
248
|
|
@@ -248,24 +264,24 @@ module Puma
|
|
248
264
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
249
265
|
worker_status = @workers.map do |w|
|
250
266
|
{
|
251
|
-
started_at: w.started_at
|
267
|
+
started_at: utc_iso8601(w.started_at),
|
252
268
|
pid: w.pid,
|
253
269
|
index: w.index,
|
254
270
|
phase: w.phase,
|
255
271
|
booted: w.booted?,
|
256
|
-
last_checkin: w.last_checkin
|
272
|
+
last_checkin: utc_iso8601(w.last_checkin),
|
257
273
|
last_status: w.last_status,
|
258
274
|
}
|
259
275
|
end
|
260
276
|
|
261
277
|
{
|
262
|
-
started_at: @started_at
|
278
|
+
started_at: utc_iso8601(@started_at),
|
263
279
|
workers: @workers.size,
|
264
280
|
phase: @phase,
|
265
281
|
booted_workers: worker_status.count { |w| w[:booted] },
|
266
282
|
old_workers: old_worker_count,
|
267
283
|
worker_status: worker_status,
|
268
|
-
}
|
284
|
+
}.merge(super)
|
269
285
|
end
|
270
286
|
|
271
287
|
def preload?
|
@@ -274,10 +290,10 @@ module Puma
|
|
274
290
|
|
275
291
|
# @version 5.0.0
|
276
292
|
def fork_worker!
|
277
|
-
if (worker =
|
293
|
+
if (worker = worker_at 0)
|
278
294
|
worker.phase += 1
|
279
295
|
end
|
280
|
-
phased_restart
|
296
|
+
phased_restart(true)
|
281
297
|
end
|
282
298
|
|
283
299
|
# We do this in a separate method to keep the lambda scope
|
@@ -290,7 +306,7 @@ module Puma
|
|
290
306
|
|
291
307
|
# Auto-fork after the specified number of requests.
|
292
308
|
if (fork_requests = @options[:fork_worker].to_i) > 0
|
293
|
-
@
|
309
|
+
@events.register(:ping!) do |w|
|
294
310
|
fork_worker! if w.index == 0 &&
|
295
311
|
w.phase == 0 &&
|
296
312
|
w.last_status[:requests_count] >= fork_requests
|
@@ -372,12 +388,12 @@ module Puma
|
|
372
388
|
else
|
373
389
|
log "* Restarts: (\u2714) hot (\u2714) phased"
|
374
390
|
|
375
|
-
unless @
|
391
|
+
unless @config.app_configured?
|
376
392
|
error "No application configured, nothing to run"
|
377
393
|
exit 1
|
378
394
|
end
|
379
395
|
|
380
|
-
@launcher.binder.parse @options[:binds]
|
396
|
+
@launcher.binder.parse @options[:binds]
|
381
397
|
end
|
382
398
|
|
383
399
|
read, @wakeup = Puma::Util.pipe
|
@@ -409,8 +425,9 @@ module Puma
|
|
409
425
|
|
410
426
|
@master_read, @worker_write = read, @wakeup
|
411
427
|
|
412
|
-
@
|
413
|
-
|
428
|
+
@options[:worker_write] = @worker_write
|
429
|
+
|
430
|
+
@config.run_hooks(:before_fork, nil, @log_writer)
|
414
431
|
|
415
432
|
spawn_workers
|
416
433
|
|
@@ -425,6 +442,11 @@ module Puma
|
|
425
442
|
|
426
443
|
while @status == :run
|
427
444
|
begin
|
445
|
+
if @options[:idle_timeout] && all_workers_idle_timed_out?
|
446
|
+
log "- All workers reached idle timeout"
|
447
|
+
break
|
448
|
+
end
|
449
|
+
|
428
450
|
if @phased_restart
|
429
451
|
start_phased_restart
|
430
452
|
@phased_restart = false
|
@@ -436,48 +458,66 @@ module Puma
|
|
436
458
|
|
437
459
|
if read.wait_readable([0, @next_check - Time.now].max)
|
438
460
|
req = read.read_nonblock(1)
|
461
|
+
next unless req
|
439
462
|
|
440
|
-
|
441
|
-
|
463
|
+
if req == PIPE_WAKEUP
|
464
|
+
@next_check = Time.now
|
465
|
+
next
|
466
|
+
end
|
442
467
|
|
443
468
|
result = read.gets
|
444
469
|
pid = result.to_i
|
445
470
|
|
446
|
-
if req ==
|
471
|
+
if req == PIPE_BOOT || req == PIPE_FORK
|
447
472
|
pid, idx = result.split(':').map(&:to_i)
|
448
|
-
w =
|
473
|
+
w = worker_at idx
|
449
474
|
w.pid = pid if w.pid.nil?
|
450
475
|
end
|
451
476
|
|
452
477
|
if w = @workers.find { |x| x.pid == pid }
|
453
478
|
case req
|
454
|
-
when
|
479
|
+
when PIPE_BOOT
|
455
480
|
w.boot!
|
456
481
|
log "- Worker #{w.index} (PID: #{pid}) booted in #{w.uptime.round(2)}s, phase: #{w.phase}"
|
457
482
|
@next_check = Time.now
|
458
483
|
workers_not_booted -= 1
|
459
|
-
when
|
484
|
+
when PIPE_EXTERNAL_TERM
|
460
485
|
# external term, see worker method, Signal.trap "SIGTERM"
|
461
486
|
w.term!
|
462
|
-
when
|
487
|
+
when PIPE_TERM
|
463
488
|
w.term unless w.term?
|
464
|
-
when
|
465
|
-
|
466
|
-
|
489
|
+
when PIPE_PING
|
490
|
+
status = result.sub(/^\d+/,'').chomp
|
491
|
+
w.ping!(status)
|
492
|
+
@events.fire(:ping!, w)
|
493
|
+
|
494
|
+
if in_phased_restart && @options[:fork_worker] && workers_not_booted.positive? && w0 = worker_at(0)
|
495
|
+
w0.ping!(status)
|
496
|
+
@events.fire(:ping!, w0)
|
497
|
+
end
|
498
|
+
|
467
499
|
if !booted && @workers.none? {|worker| worker.last_status.empty?}
|
468
|
-
@
|
500
|
+
@events.fire_on_booted!
|
501
|
+
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
469
502
|
booted = true
|
470
503
|
end
|
504
|
+
when PIPE_IDLE
|
505
|
+
if idle_workers[pid]
|
506
|
+
idle_workers.delete pid
|
507
|
+
else
|
508
|
+
idle_workers[pid] = true
|
509
|
+
end
|
471
510
|
end
|
472
511
|
else
|
473
512
|
log "! Out-of-sync worker list, no #{pid} worker"
|
474
513
|
end
|
475
514
|
end
|
515
|
+
|
476
516
|
if in_phased_restart && workers_not_booted.zero?
|
477
517
|
@events.fire_on_booted!
|
518
|
+
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
478
519
|
in_phased_restart = false
|
479
520
|
end
|
480
|
-
|
481
521
|
rescue Interrupt
|
482
522
|
@status = :stop
|
483
523
|
end
|
@@ -506,10 +546,31 @@ module Puma
|
|
506
546
|
# loops thru @workers, removing workers that exited, and calling
|
507
547
|
# `#term` if needed
|
508
548
|
def wait_workers
|
549
|
+
# Reap all children, known workers or otherwise.
|
550
|
+
# If puma has PID 1, as it's common in containerized environments,
|
551
|
+
# then it's responsible for reaping orphaned processes, so we must reap
|
552
|
+
# all our dead children, regardless of whether they are workers we spawned
|
553
|
+
# or some reattached processes.
|
554
|
+
reaped_children = {}
|
555
|
+
loop do
|
556
|
+
begin
|
557
|
+
pid, status = Process.wait2(-1, Process::WNOHANG)
|
558
|
+
break unless pid
|
559
|
+
reaped_children[pid] = status
|
560
|
+
rescue Errno::ECHILD
|
561
|
+
break
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
509
565
|
@workers.reject! do |w|
|
510
566
|
next false if w.pid.nil?
|
511
567
|
begin
|
512
|
-
|
568
|
+
# We may need to check the PID individually because:
|
569
|
+
# 1. From Ruby versions 2.6 to 3.2, `Process.detach` can prevent or delay
|
570
|
+
# `Process.wait2(-1)` from detecting a terminated process: https://bugs.ruby-lang.org/issues/19837.
|
571
|
+
# 2. When `fork_worker` is enabled, some worker may not be direct children,
|
572
|
+
# but grand children. Because of this they won't be reaped by `Process.wait2(-1)`.
|
573
|
+
if reaped_children.delete(w.pid) || Process.wait(w.pid, Process::WNOHANG)
|
513
574
|
true
|
514
575
|
else
|
515
576
|
w.term if w.term?
|
@@ -526,6 +587,11 @@ module Puma
|
|
526
587
|
end
|
527
588
|
end
|
528
589
|
end
|
590
|
+
|
591
|
+
# Log unknown children
|
592
|
+
reaped_children.each do |pid, status|
|
593
|
+
log "! reaped unknown child process pid=#{pid} status=#{status}"
|
594
|
+
end
|
529
595
|
end
|
530
596
|
|
531
597
|
# @version 5.0.0
|
@@ -533,14 +599,22 @@ module Puma
|
|
533
599
|
@workers.each do |w|
|
534
600
|
if !w.term? && w.ping_timeout <= Time.now
|
535
601
|
details = if w.booted?
|
536
|
-
"(
|
602
|
+
"(Worker #{w.index} failed to check in within #{@options[:worker_timeout]} seconds)"
|
537
603
|
else
|
538
|
-
"(
|
604
|
+
"(Worker #{w.index} failed to boot within #{@options[:worker_boot_timeout]} seconds)"
|
539
605
|
end
|
540
606
|
log "! Terminating timed out worker #{details}: #{w.pid}"
|
541
607
|
w.kill
|
542
608
|
end
|
543
609
|
end
|
544
610
|
end
|
611
|
+
|
612
|
+
def idle_timed_out_worker_pids
|
613
|
+
idle_workers.keys
|
614
|
+
end
|
615
|
+
|
616
|
+
def idle_workers
|
617
|
+
@idle_workers ||= {}
|
618
|
+
end
|
545
619
|
end
|
546
620
|
end
|
data/lib/puma/commonlogger.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Puma
|
4
4
|
# Rack::CommonLogger forwards every request to the given +app+, and
|
5
5
|
# logs a line in the
|
6
|
-
# {Apache common log format}[https://httpd.apache.org/docs/
|
6
|
+
# {Apache common log format}[https://httpd.apache.org/docs/2.4/logs.html#common]
|
7
7
|
# to the +logger+.
|
8
8
|
#
|
9
9
|
# If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
|
@@ -16,7 +16,7 @@ module Puma
|
|
16
16
|
# (which is called without arguments in order to make the error appear for
|
17
17
|
# sure)
|
18
18
|
class CommonLogger
|
19
|
-
# Common Log Format: https://httpd.apache.org/docs/
|
19
|
+
# Common Log Format: https://httpd.apache.org/docs/2.4/logs.html#common
|
20
20
|
#
|
21
21
|
# lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
|
22
22
|
#
|
@@ -25,10 +25,17 @@ module Puma
|
|
25
25
|
|
26
26
|
HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
32
39
|
|
33
40
|
def initialize(app, logger=nil)
|
34
41
|
@app = app
|
@@ -57,13 +64,13 @@ module Puma
|
|
57
64
|
now = Time.now
|
58
65
|
|
59
66
|
msg = HIJACK_FORMAT % [
|
60
|
-
env[
|
61
|
-
env[
|
62
|
-
now.strftime(
|
67
|
+
env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
|
68
|
+
env[REMOTE_USER] || "-",
|
69
|
+
now.strftime(LOG_TIME_FORMAT),
|
63
70
|
env[REQUEST_METHOD],
|
64
71
|
env[PATH_INFO],
|
65
72
|
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
66
|
-
env[
|
73
|
+
env[HTTP_VERSION],
|
67
74
|
now - began_at ]
|
68
75
|
|
69
76
|
write(msg)
|
@@ -74,13 +81,13 @@ module Puma
|
|
74
81
|
length = extract_content_length(header)
|
75
82
|
|
76
83
|
msg = FORMAT % [
|
77
|
-
env[
|
78
|
-
env[
|
79
|
-
now.strftime(
|
84
|
+
env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
|
85
|
+
env[REMOTE_USER] || "-",
|
86
|
+
now.strftime(LOG_TIME_FORMAT),
|
80
87
|
env[REQUEST_METHOD],
|
81
88
|
env[PATH_INFO],
|
82
89
|
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
83
|
-
env[
|
90
|
+
env[HTTP_VERSION],
|
84
91
|
status.to_s[0..3],
|
85
92
|
length,
|
86
93
|
now - began_at ]
|
data/lib/puma/configuration.rb
CHANGED
@@ -1,21 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require_relative 'plugin'
|
4
|
+
require_relative 'const'
|
5
|
+
require_relative 'dsl'
|
6
6
|
|
7
7
|
module Puma
|
8
|
-
|
9
|
-
module ConfigDefault
|
10
|
-
DefaultRackup = "config.ru"
|
11
|
-
|
12
|
-
DefaultTCPHost = "0.0.0.0"
|
13
|
-
DefaultTCPPort = 9292
|
14
|
-
DefaultWorkerCheckInterval = 5
|
15
|
-
DefaultWorkerTimeout = 60
|
16
|
-
DefaultWorkerShutdownTimeout = 30
|
17
|
-
end
|
18
|
-
|
19
8
|
# A class used for storing "leveled" configuration options.
|
20
9
|
#
|
21
10
|
# In this class any "user" specified options take precedence over any
|
@@ -136,10 +125,56 @@ module Puma
|
|
136
125
|
# is done because an environment variable may have been modified while loading
|
137
126
|
# configuration files.
|
138
127
|
class Configuration
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
128
|
+
DEFAULTS = {
|
129
|
+
auto_trim_time: 30,
|
130
|
+
binds: ['tcp://0.0.0.0:9292'.freeze],
|
131
|
+
clean_thread_locals: false,
|
132
|
+
debug: false,
|
133
|
+
enable_keep_alives: true,
|
134
|
+
early_hints: nil,
|
135
|
+
environment: 'development'.freeze,
|
136
|
+
# Number of seconds to wait until we get the first data for the request.
|
137
|
+
first_data_timeout: 30,
|
138
|
+
# Number of seconds to wait until the next request before shutting down.
|
139
|
+
idle_timeout: nil,
|
140
|
+
io_selector_backend: :auto,
|
141
|
+
log_requests: false,
|
142
|
+
logger: STDOUT,
|
143
|
+
# How many requests to attempt inline before sending a client back to
|
144
|
+
# the reactor to be subject to normal ordering. The idea here is that
|
145
|
+
# we amortize the cost of going back to the reactor for a well behaved
|
146
|
+
# but very "greedy" client across 10 requests. This prevents a not
|
147
|
+
# well behaved client from monopolizing the thread forever.
|
148
|
+
max_fast_inline: 10,
|
149
|
+
max_threads: Puma.mri? ? 5 : 16,
|
150
|
+
min_threads: 0,
|
151
|
+
mode: :http,
|
152
|
+
mutate_stdout_and_stderr_to_sync_on_write: true,
|
153
|
+
out_of_band: [],
|
154
|
+
# Number of seconds for another request within a persistent session.
|
155
|
+
persistent_timeout: 20,
|
156
|
+
queue_requests: true,
|
157
|
+
rackup: 'config.ru'.freeze,
|
158
|
+
raise_exception_on_sigterm: true,
|
159
|
+
reaping_time: 1,
|
160
|
+
remote_address: :socket,
|
161
|
+
silence_single_worker_warning: false,
|
162
|
+
silence_fork_callback_warning: false,
|
163
|
+
tag: File.basename(Dir.getwd),
|
164
|
+
tcp_host: '0.0.0.0'.freeze,
|
165
|
+
tcp_port: 9292,
|
166
|
+
wait_for_less_busy_worker: 0.005,
|
167
|
+
worker_boot_timeout: 60,
|
168
|
+
worker_check_interval: 5,
|
169
|
+
worker_culling_strategy: :youngest,
|
170
|
+
worker_shutdown_timeout: 30,
|
171
|
+
worker_timeout: 60,
|
172
|
+
workers: 0,
|
173
|
+
http_content_length_limit: nil
|
174
|
+
}
|
175
|
+
|
176
|
+
def initialize(user_options={}, default_options = {}, env = ENV, &block)
|
177
|
+
default_options = self.puma_default_options(env).merge(default_options)
|
143
178
|
|
144
179
|
@options = UserFileDefaultOptions.new(user_options, default_options)
|
145
180
|
@plugins = PluginLoader.new
|
@@ -151,6 +186,8 @@ module Puma
|
|
151
186
|
default_options[:preload_app] = (@options[:workers] > 1) && Puma.forkable?
|
152
187
|
end
|
153
188
|
|
189
|
+
@puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED'
|
190
|
+
|
154
191
|
if block
|
155
192
|
configure(&block)
|
156
193
|
end
|
@@ -181,37 +218,27 @@ module Puma
|
|
181
218
|
self
|
182
219
|
end
|
183
220
|
|
184
|
-
|
185
|
-
|
186
|
-
|
221
|
+
def puma_default_options(env = ENV)
|
222
|
+
defaults = DEFAULTS.dup
|
223
|
+
puma_options_from_env(env).each { |k,v| defaults[k] = v if v }
|
224
|
+
defaults
|
187
225
|
end
|
188
226
|
|
189
|
-
def
|
227
|
+
def puma_options_from_env(env = ENV)
|
228
|
+
min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
|
229
|
+
max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
|
230
|
+
workers = if env['WEB_CONCURRENCY'] == 'auto'
|
231
|
+
require_processor_counter
|
232
|
+
::Concurrent.available_processor_count
|
233
|
+
else
|
234
|
+
env['WEB_CONCURRENCY']
|
235
|
+
end
|
236
|
+
|
190
237
|
{
|
191
|
-
:
|
192
|
-
:
|
193
|
-
:
|
194
|
-
:
|
195
|
-
:binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
|
196
|
-
:workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
|
197
|
-
:silence_single_worker_warning => false,
|
198
|
-
:mode => :http,
|
199
|
-
:worker_check_interval => DefaultWorkerCheckInterval,
|
200
|
-
:worker_timeout => DefaultWorkerTimeout,
|
201
|
-
:worker_boot_timeout => DefaultWorkerTimeout,
|
202
|
-
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
203
|
-
:worker_culling_strategy => :youngest,
|
204
|
-
:remote_address => :socket,
|
205
|
-
:tag => method(:infer_tag),
|
206
|
-
:environment => -> { ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' },
|
207
|
-
:rackup => DefaultRackup,
|
208
|
-
:logger => STDOUT,
|
209
|
-
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
210
|
-
:first_data_timeout => Const::FIRST_DATA_TIMEOUT,
|
211
|
-
:raise_exception_on_sigterm => true,
|
212
|
-
:max_fast_inline => Const::MAX_FAST_INLINE,
|
213
|
-
:io_selector_backend => :auto,
|
214
|
-
:mutate_stdout_and_stderr_to_sync_on_write => true,
|
238
|
+
min_threads: min && min != "" && Integer(min),
|
239
|
+
max_threads: max && max != "" && Integer(max),
|
240
|
+
workers: workers && workers != "" && Integer(workers),
|
241
|
+
environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
|
215
242
|
}
|
216
243
|
end
|
217
244
|
|
@@ -227,7 +254,7 @@ module Puma
|
|
227
254
|
return [] if files == ['-']
|
228
255
|
return files if files.any?
|
229
256
|
|
230
|
-
first_default_file = %W(config/puma/#{
|
257
|
+
first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
|
231
258
|
File.exist?(f)
|
232
259
|
end
|
233
260
|
|
@@ -270,7 +297,7 @@ module Puma
|
|
270
297
|
found = options[:app] || load_rackup
|
271
298
|
|
272
299
|
if @options[:log_requests]
|
273
|
-
|
300
|
+
require_relative 'commonlogger'
|
274
301
|
logger = @options[:logger]
|
275
302
|
found = CommonLogger.new(found, logger)
|
276
303
|
end
|
@@ -283,21 +310,27 @@ module Puma
|
|
283
310
|
@options[:environment]
|
284
311
|
end
|
285
312
|
|
286
|
-
def environment_str
|
287
|
-
environment.respond_to?(:call) ? environment.call : environment
|
288
|
-
end
|
289
|
-
|
290
313
|
def load_plugin(name)
|
291
314
|
@plugins.create name
|
292
315
|
end
|
293
316
|
|
294
|
-
|
317
|
+
# @param key [:Symbol] hook to run
|
318
|
+
# @param arg [Launcher, Int] `:on_restart` passes Launcher
|
319
|
+
#
|
320
|
+
def run_hooks(key, arg, log_writer, hook_data = nil)
|
321
|
+
log_writer.debug "Running #{key} hooks"
|
322
|
+
|
295
323
|
@options.all_of(key).each do |b|
|
296
324
|
begin
|
297
|
-
b
|
325
|
+
if Array === b
|
326
|
+
hook_data[b[1]] ||= Hash.new
|
327
|
+
b[0].call arg, hook_data[b[1]]
|
328
|
+
else
|
329
|
+
b.call arg
|
330
|
+
end
|
298
331
|
rescue => e
|
299
|
-
|
300
|
-
|
332
|
+
log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
|
333
|
+
log_writer.debug e.backtrace.join("\n")
|
301
334
|
end
|
302
335
|
end
|
303
336
|
end
|
@@ -315,8 +348,14 @@ module Puma
|
|
315
348
|
|
316
349
|
private
|
317
350
|
|
318
|
-
def
|
319
|
-
|
351
|
+
def require_processor_counter
|
352
|
+
require 'concurrent/utility/processor_counter'
|
353
|
+
rescue LoadError
|
354
|
+
warn <<~MESSAGE
|
355
|
+
WEB_CONCURRENCY=auto requires the "concurrent-ruby" gem to be installed.
|
356
|
+
Please add "concurrent-ruby" to your Gemfile.
|
357
|
+
MESSAGE
|
358
|
+
raise
|
320
359
|
end
|
321
360
|
|
322
361
|
# Load and use the normal Rack builder if we can, otherwise
|
@@ -324,7 +363,7 @@ module Puma
|
|
324
363
|
def rack_builder
|
325
364
|
# Load bundler now if we can so that we can pickup rack from
|
326
365
|
# a Gemfile
|
327
|
-
if
|
366
|
+
if @puma_bundler_pruned
|
328
367
|
begin
|
329
368
|
require 'bundler/setup'
|
330
369
|
rescue LoadError
|
@@ -334,11 +373,10 @@ module Puma
|
|
334
373
|
begin
|
335
374
|
require 'rack'
|
336
375
|
require 'rack/builder'
|
376
|
+
::Rack::Builder
|
337
377
|
rescue LoadError
|
338
|
-
|
339
|
-
|
340
|
-
else
|
341
|
-
return ::Rack::Builder
|
378
|
+
require_relative 'rack/builder'
|
379
|
+
Puma::Rack::Builder
|
342
380
|
end
|
343
381
|
end
|
344
382
|
|
@@ -367,5 +405,3 @@ module Puma
|
|
367
405
|
end
|
368
406
|
end
|
369
407
|
end
|
370
|
-
|
371
|
-
require 'puma/dsl'
|