puma 5.2.2 → 6.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +483 -4
- data/README.md +101 -20
- data/bin/puma-wild +1 -1
- data/docs/architecture.md +50 -16
- data/docs/compile_options.md +38 -2
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +1 -3
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +1 -1
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +2 -3
- data/docs/restart.md +7 -7
- data/docs/signals.md +11 -10
- data/docs/stats.md +8 -8
- data/docs/systemd.md +65 -69
- 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 +44 -13
- data/ext/puma_http11/http11_parser.c +24 -11
- data/ext/puma_http11/http11_parser.h +2 -2
- 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 +3 -3
- data/ext/puma_http11/mini_ssl.c +150 -23
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +50 -48
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +188 -102
- data/ext/puma_http11/puma_http11.c +18 -10
- data/lib/puma/app/status.rb +10 -7
- data/lib/puma/binder.rb +112 -62
- data/lib/puma/cli.rb +24 -20
- data/lib/puma/client.rb +162 -36
- data/lib/puma/cluster/worker.rb +31 -27
- data/lib/puma/cluster/worker_handle.rb +12 -1
- data/lib/puma/cluster.rb +102 -61
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +78 -54
- data/lib/puma/const.rb +135 -97
- data/lib/puma/control_cli.rb +25 -20
- data/lib/puma/detect.rb +12 -2
- data/lib/puma/dsl.rb +308 -58
- data/lib/puma/error_logger.rb +20 -11
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +39 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/{json.rb → json_serialization.rb} +1 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +114 -173
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +30 -16
- data/lib/puma/minissl.rb +132 -38
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +2 -2
- data/lib/puma/rack/builder.rb +7 -7
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +19 -10
- data/lib/puma/request.rb +373 -153
- data/lib/puma/runner.rb +74 -28
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +127 -136
- data/lib/puma/single.rb +13 -11
- data/lib/puma/state_file.rb +39 -7
- data/lib/puma/thread_pool.rb +33 -26
- data/lib/puma/util.rb +20 -15
- data/lib/puma.rb +28 -11
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +1 -1
- metadata +15 -10
- data/lib/puma/queue_close.rb +0 -26
- data/lib/puma/systemd.rb +0 -46
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 }
|
@@ -43,6 +45,7 @@ module Puma
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def start_phased_restart
|
48
|
+
@events.fire_on_restart!
|
46
49
|
@phase += 1
|
47
50
|
log "- Starting phased worker restart, phase: #{@phase}"
|
48
51
|
|
@@ -91,7 +94,7 @@ module Puma
|
|
91
94
|
|
92
95
|
# @version 5.0.0
|
93
96
|
def spawn_worker(idx, master)
|
94
|
-
@
|
97
|
+
@config.run_hooks(:before_worker_fork, idx, @log_writer)
|
95
98
|
|
96
99
|
pid = fork { worker(idx, master) }
|
97
100
|
if !pid
|
@@ -100,31 +103,49 @@ module Puma
|
|
100
103
|
exit! 1
|
101
104
|
end
|
102
105
|
|
103
|
-
@
|
106
|
+
@config.run_hooks(:after_worker_fork, idx, @log_writer)
|
104
107
|
pid
|
105
108
|
end
|
106
109
|
|
107
110
|
def cull_workers
|
108
111
|
diff = @workers.size - @options[:workers]
|
109
112
|
return if diff < 1
|
113
|
+
debug "Culling #{diff} workers"
|
110
114
|
|
111
|
-
|
112
|
-
|
113
|
-
workers_to_cull = @workers[-diff,diff]
|
114
|
-
debug "Workers to cull: #{workers_to_cull.inspect}"
|
115
|
+
workers = workers_to_cull(diff)
|
116
|
+
debug "Workers to cull: #{workers.inspect}"
|
115
117
|
|
116
|
-
|
118
|
+
workers.each do |worker|
|
117
119
|
log "- Worker #{worker.index} (PID: #{worker.pid}) terminating"
|
118
120
|
worker.term
|
119
121
|
end
|
120
122
|
end
|
121
123
|
|
124
|
+
def workers_to_cull(diff)
|
125
|
+
workers = @workers.sort_by(&:started_at)
|
126
|
+
|
127
|
+
# In fork_worker mode, worker 0 acts as our master process.
|
128
|
+
# We should avoid culling it to preserve copy-on-write memory gains.
|
129
|
+
workers.reject! { |w| w.index == 0 } if @options[:fork_worker]
|
130
|
+
|
131
|
+
workers[cull_start_index(diff), diff]
|
132
|
+
end
|
133
|
+
|
134
|
+
def cull_start_index(diff)
|
135
|
+
case @options[:worker_culling_strategy]
|
136
|
+
when :oldest
|
137
|
+
0
|
138
|
+
else # :youngest
|
139
|
+
-diff
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
122
143
|
# @!attribute [r] next_worker_index
|
123
144
|
def next_worker_index
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
145
|
+
occupied_positions = @workers.map(&:index)
|
146
|
+
idx = 0
|
147
|
+
idx += 1 until !occupied_positions.include?(idx)
|
148
|
+
idx
|
128
149
|
end
|
129
150
|
|
130
151
|
def all_workers_booted?
|
@@ -134,7 +155,7 @@ module Puma
|
|
134
155
|
def check_workers
|
135
156
|
return if @next_check >= Time.now
|
136
157
|
|
137
|
-
@next_check = Time.now +
|
158
|
+
@next_check = Time.now + @options[:worker_check_interval]
|
138
159
|
|
139
160
|
timeout_workers
|
140
161
|
wait_workers
|
@@ -157,20 +178,10 @@ module Puma
|
|
157
178
|
end
|
158
179
|
end
|
159
180
|
|
160
|
-
|
161
|
-
|
162
|
-
@next_check
|
163
|
-
].compact.min
|
164
|
-
end
|
165
|
-
|
166
|
-
def wakeup!
|
167
|
-
return unless @wakeup
|
181
|
+
t = @workers.reject(&:term?)
|
182
|
+
t.map!(&:ping_timeout)
|
168
183
|
|
169
|
-
|
170
|
-
@wakeup.write "!" unless @wakeup.closed?
|
171
|
-
rescue SystemCallError, IOError
|
172
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
173
|
-
end
|
184
|
+
@next_check = [t.min, @next_check].compact.min
|
174
185
|
end
|
175
186
|
|
176
187
|
def worker(index, master)
|
@@ -200,8 +211,8 @@ module Puma
|
|
200
211
|
stop
|
201
212
|
end
|
202
213
|
|
203
|
-
def phased_restart
|
204
|
-
return false if @options[:preload_app]
|
214
|
+
def phased_restart(refork = false)
|
215
|
+
return false if @options[:preload_app] && !refork
|
205
216
|
|
206
217
|
@phased_restart = true
|
207
218
|
wakeup!
|
@@ -217,7 +228,7 @@ module Puma
|
|
217
228
|
def stop_blocked
|
218
229
|
@status = :stop if @status == :run
|
219
230
|
wakeup!
|
220
|
-
@control
|
231
|
+
@control&.stop true
|
221
232
|
Process.waitall
|
222
233
|
end
|
223
234
|
|
@@ -239,24 +250,24 @@ module Puma
|
|
239
250
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
240
251
|
worker_status = @workers.map do |w|
|
241
252
|
{
|
242
|
-
started_at: w.started_at
|
253
|
+
started_at: utc_iso8601(w.started_at),
|
243
254
|
pid: w.pid,
|
244
255
|
index: w.index,
|
245
256
|
phase: w.phase,
|
246
257
|
booted: w.booted?,
|
247
|
-
last_checkin: w.last_checkin
|
258
|
+
last_checkin: utc_iso8601(w.last_checkin),
|
248
259
|
last_status: w.last_status,
|
249
260
|
}
|
250
261
|
end
|
251
262
|
|
252
263
|
{
|
253
|
-
started_at: @started_at
|
264
|
+
started_at: utc_iso8601(@started_at),
|
254
265
|
workers: @workers.size,
|
255
266
|
phase: @phase,
|
256
267
|
booted_workers: worker_status.count { |w| w[:booted] },
|
257
268
|
old_workers: old_worker_count,
|
258
269
|
worker_status: worker_status,
|
259
|
-
}
|
270
|
+
}.merge(super)
|
260
271
|
end
|
261
272
|
|
262
273
|
def preload?
|
@@ -268,7 +279,7 @@ module Puma
|
|
268
279
|
if (worker = @workers.find { |w| w.index == 0 })
|
269
280
|
worker.phase += 1
|
270
281
|
end
|
271
|
-
phased_restart
|
282
|
+
phased_restart(true)
|
272
283
|
end
|
273
284
|
|
274
285
|
# We do this in a separate method to keep the lambda scope
|
@@ -281,7 +292,7 @@ module Puma
|
|
281
292
|
|
282
293
|
# Auto-fork after the specified number of requests.
|
283
294
|
if (fork_requests = @options[:fork_worker].to_i) > 0
|
284
|
-
@
|
295
|
+
@events.register(:ping!) do |w|
|
285
296
|
fork_worker! if w.index == 0 &&
|
286
297
|
w.phase == 0 &&
|
287
298
|
w.last_status[:requests_count] >= fork_requests
|
@@ -317,7 +328,7 @@ module Puma
|
|
317
328
|
|
318
329
|
stop_workers
|
319
330
|
stop
|
320
|
-
|
331
|
+
@events.fire_on_stopped!
|
321
332
|
raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
|
322
333
|
exit 0 # Clean exit, workers were stopped
|
323
334
|
end
|
@@ -332,16 +343,22 @@ module Puma
|
|
332
343
|
# This is aligned with the output from Runner, see Runner#output_header
|
333
344
|
log "* Workers: #{@options[:workers]}"
|
334
345
|
|
335
|
-
# Threads explicitly marked as fork safe will be ignored.
|
336
|
-
# Used in Rails, but may be used by anyone.
|
337
|
-
before = Thread.list.reject { |t| t.thread_variable_get(:fork_safe) }
|
338
|
-
|
339
346
|
if preload?
|
347
|
+
# Threads explicitly marked as fork safe will be ignored. Used in Rails,
|
348
|
+
# but may be used by anyone. Note that we need to explicit
|
349
|
+
# Process::Waiter check here because there's a bug in Ruby 2.6 and below
|
350
|
+
# where calling thread_variable_get on a Process::Waiter will segfault.
|
351
|
+
# We can drop that clause once those versions of Ruby are no longer
|
352
|
+
# supported.
|
353
|
+
fork_safe = ->(t) { !t.is_a?(Process::Waiter) && t.thread_variable_get(:fork_safe) }
|
354
|
+
|
355
|
+
before = Thread.list.reject(&fork_safe)
|
356
|
+
|
340
357
|
log "* Restarts: (\u2714) hot (\u2716) phased"
|
341
358
|
log "* Preloading application"
|
342
359
|
load_and_bind
|
343
360
|
|
344
|
-
after = Thread.list.reject
|
361
|
+
after = Thread.list.reject(&fork_safe)
|
345
362
|
|
346
363
|
if after.size > before.size
|
347
364
|
threads = (after - before)
|
@@ -357,12 +374,12 @@ module Puma
|
|
357
374
|
else
|
358
375
|
log "* Restarts: (\u2714) hot (\u2714) phased"
|
359
376
|
|
360
|
-
unless @
|
377
|
+
unless @config.app_configured?
|
361
378
|
error "No application configured, nothing to run"
|
362
379
|
exit 1
|
363
380
|
end
|
364
381
|
|
365
|
-
@launcher.binder.parse @options[:binds]
|
382
|
+
@launcher.binder.parse @options[:binds]
|
366
383
|
end
|
367
384
|
|
368
385
|
read, @wakeup = Puma::Util.pipe
|
@@ -382,6 +399,8 @@ module Puma
|
|
382
399
|
|
383
400
|
log "Use Ctrl-C to stop"
|
384
401
|
|
402
|
+
single_worker_warning
|
403
|
+
|
385
404
|
redirect_io
|
386
405
|
|
387
406
|
Plugins.fire_background
|
@@ -392,8 +411,7 @@ module Puma
|
|
392
411
|
|
393
412
|
@master_read, @worker_write = read, @wakeup
|
394
413
|
|
395
|
-
@
|
396
|
-
Puma::Util.nakayoshi_gc @events if @options[:nakayoshi_fork]
|
414
|
+
@config.run_hooks(:before_fork, nil, @log_writer)
|
397
415
|
|
398
416
|
spawn_workers
|
399
417
|
|
@@ -403,19 +421,21 @@ module Puma
|
|
403
421
|
|
404
422
|
begin
|
405
423
|
booted = false
|
424
|
+
in_phased_restart = false
|
425
|
+
workers_not_booted = @options[:workers]
|
406
426
|
|
407
427
|
while @status == :run
|
408
428
|
begin
|
409
429
|
if @phased_restart
|
410
430
|
start_phased_restart
|
411
431
|
@phased_restart = false
|
432
|
+
in_phased_restart = true
|
433
|
+
workers_not_booted = @options[:workers]
|
412
434
|
end
|
413
435
|
|
414
436
|
check_workers
|
415
437
|
|
416
|
-
|
417
|
-
|
418
|
-
if res
|
438
|
+
if read.wait_readable([0, @next_check - Time.now].max)
|
419
439
|
req = read.read_nonblock(1)
|
420
440
|
|
421
441
|
@next_check = Time.now if req == "!"
|
@@ -434,18 +454,20 @@ module Puma
|
|
434
454
|
case req
|
435
455
|
when "b"
|
436
456
|
w.boot!
|
437
|
-
log "- Worker #{w.index} (PID: #{pid}) booted, phase: #{w.phase}"
|
457
|
+
log "- Worker #{w.index} (PID: #{pid}) booted in #{w.uptime.round(2)}s, phase: #{w.phase}"
|
438
458
|
@next_check = Time.now
|
459
|
+
workers_not_booted -= 1
|
439
460
|
when "e"
|
440
461
|
# external term, see worker method, Signal.trap "SIGTERM"
|
441
|
-
w.
|
462
|
+
w.term!
|
442
463
|
when "t"
|
443
464
|
w.term unless w.term?
|
444
465
|
when "p"
|
445
466
|
w.ping!(result.sub(/^\d+/,'').chomp)
|
446
|
-
@
|
467
|
+
@events.fire(:ping!, w)
|
447
468
|
if !booted && @workers.none? {|worker| worker.last_status.empty?}
|
448
|
-
@
|
469
|
+
@events.fire_on_booted!
|
470
|
+
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
449
471
|
booted = true
|
450
472
|
end
|
451
473
|
end
|
@@ -453,6 +475,11 @@ module Puma
|
|
453
475
|
log "! Out-of-sync worker list, no #{pid} worker"
|
454
476
|
end
|
455
477
|
end
|
478
|
+
if in_phased_restart && workers_not_booted.zero?
|
479
|
+
@events.fire_on_booted!
|
480
|
+
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
481
|
+
in_phased_restart = false
|
482
|
+
end
|
456
483
|
|
457
484
|
rescue Interrupt
|
458
485
|
@status = :stop
|
@@ -470,6 +497,15 @@ module Puma
|
|
470
497
|
|
471
498
|
private
|
472
499
|
|
500
|
+
def single_worker_warning
|
501
|
+
return if @options[:workers] != 1 || @options[:silence_single_worker_warning]
|
502
|
+
|
503
|
+
log "! WARNING: Detected running cluster mode with 1 worker."
|
504
|
+
log "! Running Puma in cluster mode with a single worker is often a misconfiguration."
|
505
|
+
log "! Consider running Puma in single-mode (workers = 0) in order to reduce memory overhead."
|
506
|
+
log "! Set the `silence_single_worker_warning` option to silence this warning message."
|
507
|
+
end
|
508
|
+
|
473
509
|
# loops thru @workers, removing workers that exited, and calling
|
474
510
|
# `#term` if needed
|
475
511
|
def wait_workers
|
@@ -499,7 +535,12 @@ module Puma
|
|
499
535
|
def timeout_workers
|
500
536
|
@workers.each do |w|
|
501
537
|
if !w.term? && w.ping_timeout <= Time.now
|
502
|
-
|
538
|
+
details = if w.booted?
|
539
|
+
"(worker failed to check in within #{@options[:worker_timeout]} seconds)"
|
540
|
+
else
|
541
|
+
"(worker failed to boot within #{@options[:worker_boot_timeout]} seconds)"
|
542
|
+
end
|
543
|
+
log "! Terminating timed out worker #{details}: #{w.pid}"
|
503
544
|
w.kill
|
504
545
|
end
|
505
546
|
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,20 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require_relative 'rack/builder'
|
4
|
+
require_relative 'plugin'
|
5
|
+
require_relative 'const'
|
6
|
+
# note that dsl is loaded at end of file, requires ConfigDefault constants
|
6
7
|
|
7
8
|
module Puma
|
8
|
-
|
9
|
-
module ConfigDefault
|
10
|
-
DefaultRackup = "config.ru"
|
11
|
-
|
12
|
-
DefaultTCPHost = "0.0.0.0"
|
13
|
-
DefaultTCPPort = 9292
|
14
|
-
DefaultWorkerTimeout = 60
|
15
|
-
DefaultWorkerShutdownTimeout = 30
|
16
|
-
end
|
17
|
-
|
18
9
|
# A class used for storing "leveled" configuration options.
|
19
10
|
#
|
20
11
|
# In this class any "user" specified options take precedence over any
|
@@ -135,7 +126,50 @@ module Puma
|
|
135
126
|
# is done because an environment variable may have been modified while loading
|
136
127
|
# configuration files.
|
137
128
|
class Configuration
|
138
|
-
|
129
|
+
DEFAULTS = {
|
130
|
+
auto_trim_time: 30,
|
131
|
+
binds: ['tcp://0.0.0.0:9292'.freeze],
|
132
|
+
clean_thread_locals: false,
|
133
|
+
debug: false,
|
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
|
+
io_selector_backend: :auto,
|
139
|
+
log_requests: false,
|
140
|
+
logger: STDOUT,
|
141
|
+
# How many requests to attempt inline before sending a client back to
|
142
|
+
# the reactor to be subject to normal ordering. The idea here is that
|
143
|
+
# we amortize the cost of going back to the reactor for a well behaved
|
144
|
+
# but very "greedy" client across 10 requests. This prevents a not
|
145
|
+
# well behaved client from monopolizing the thread forever.
|
146
|
+
max_fast_inline: 10,
|
147
|
+
max_threads: Puma.mri? ? 5 : 16,
|
148
|
+
min_threads: 0,
|
149
|
+
mode: :http,
|
150
|
+
mutate_stdout_and_stderr_to_sync_on_write: true,
|
151
|
+
out_of_band: [],
|
152
|
+
# Number of seconds for another request within a persistent session.
|
153
|
+
persistent_timeout: 20,
|
154
|
+
queue_requests: true,
|
155
|
+
rackup: 'config.ru'.freeze,
|
156
|
+
raise_exception_on_sigterm: true,
|
157
|
+
reaping_time: 1,
|
158
|
+
remote_address: :socket,
|
159
|
+
silence_single_worker_warning: false,
|
160
|
+
silence_fork_callback_warning: false,
|
161
|
+
tag: File.basename(Dir.getwd),
|
162
|
+
tcp_host: '0.0.0.0'.freeze,
|
163
|
+
tcp_port: 9292,
|
164
|
+
wait_for_less_busy_worker: 0.005,
|
165
|
+
worker_boot_timeout: 60,
|
166
|
+
worker_check_interval: 5,
|
167
|
+
worker_culling_strategy: :youngest,
|
168
|
+
worker_shutdown_timeout: 30,
|
169
|
+
worker_timeout: 60,
|
170
|
+
workers: 0,
|
171
|
+
http_content_length_limit: nil
|
172
|
+
}
|
139
173
|
|
140
174
|
def initialize(user_options={}, default_options = {}, &block)
|
141
175
|
default_options = self.puma_default_options.merge(default_options)
|
@@ -180,34 +214,22 @@ module Puma
|
|
180
214
|
self
|
181
215
|
end
|
182
216
|
|
183
|
-
|
184
|
-
|
185
|
-
|
217
|
+
def puma_default_options
|
218
|
+
defaults = DEFAULTS.dup
|
219
|
+
puma_options_from_env.each { |k,v| defaults[k] = v if v }
|
220
|
+
defaults
|
186
221
|
end
|
187
222
|
|
188
|
-
def
|
223
|
+
def puma_options_from_env
|
224
|
+
min = ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS']
|
225
|
+
max = ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS']
|
226
|
+
workers = ENV['WEB_CONCURRENCY']
|
227
|
+
|
189
228
|
{
|
190
|
-
:
|
191
|
-
:
|
192
|
-
:
|
193
|
-
:
|
194
|
-
:binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
|
195
|
-
:workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
|
196
|
-
:mode => :http,
|
197
|
-
:worker_timeout => DefaultWorkerTimeout,
|
198
|
-
:worker_boot_timeout => DefaultWorkerTimeout,
|
199
|
-
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
200
|
-
:remote_address => :socket,
|
201
|
-
:tag => method(:infer_tag),
|
202
|
-
:environment => -> { ENV['RACK_ENV'] || ENV['RAILS_ENV'] || "development" },
|
203
|
-
:rackup => DefaultRackup,
|
204
|
-
:logger => STDOUT,
|
205
|
-
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
206
|
-
:first_data_timeout => Const::FIRST_DATA_TIMEOUT,
|
207
|
-
:raise_exception_on_sigterm => true,
|
208
|
-
:max_fast_inline => Const::MAX_FAST_INLINE,
|
209
|
-
:io_selector_backend => :auto,
|
210
|
-
:mutate_stdout_and_stderr_to_sync_on_write => true,
|
229
|
+
min_threads: min && Integer(min),
|
230
|
+
max_threads: max && Integer(max),
|
231
|
+
workers: workers && Integer(workers),
|
232
|
+
environment: ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'],
|
211
233
|
}
|
212
234
|
end
|
213
235
|
|
@@ -223,7 +245,7 @@ module Puma
|
|
223
245
|
return [] if files == ['-']
|
224
246
|
return files if files.any?
|
225
247
|
|
226
|
-
first_default_file = %W(config/puma/#{
|
248
|
+
first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
|
227
249
|
File.exist?(f)
|
228
250
|
end
|
229
251
|
|
@@ -266,7 +288,7 @@ module Puma
|
|
266
288
|
found = options[:app] || load_rackup
|
267
289
|
|
268
290
|
if @options[:log_requests]
|
269
|
-
|
291
|
+
require_relative 'commonlogger'
|
270
292
|
logger = @options[:logger]
|
271
293
|
found = CommonLogger.new(found, logger)
|
272
294
|
end
|
@@ -279,21 +301,25 @@ module Puma
|
|
279
301
|
@options[:environment]
|
280
302
|
end
|
281
303
|
|
282
|
-
def environment_str
|
283
|
-
environment.respond_to?(:call) ? environment.call : environment
|
284
|
-
end
|
285
|
-
|
286
304
|
def load_plugin(name)
|
287
305
|
@plugins.create name
|
288
306
|
end
|
289
307
|
|
290
|
-
|
308
|
+
# @param key [:Symbol] hook to run
|
309
|
+
# @param arg [Launcher, Int] `:on_restart` passes Launcher
|
310
|
+
#
|
311
|
+
def run_hooks(key, arg, log_writer, hook_data = nil)
|
291
312
|
@options.all_of(key).each do |b|
|
292
313
|
begin
|
293
|
-
b
|
314
|
+
if Array === b
|
315
|
+
hook_data[b[1]] ||= Hash.new
|
316
|
+
b[0].call arg, hook_data[b[1]]
|
317
|
+
else
|
318
|
+
b.call arg
|
319
|
+
end
|
294
320
|
rescue => e
|
295
|
-
|
296
|
-
|
321
|
+
log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
|
322
|
+
log_writer.debug e.backtrace.join("\n")
|
297
323
|
end
|
298
324
|
end
|
299
325
|
end
|
@@ -311,10 +337,6 @@ module Puma
|
|
311
337
|
|
312
338
|
private
|
313
339
|
|
314
|
-
def infer_tag
|
315
|
-
File.basename(Dir.getwd)
|
316
|
-
end
|
317
|
-
|
318
340
|
# Load and use the normal Rack builder if we can, otherwise
|
319
341
|
# fallback to our minimal version.
|
320
342
|
def rack_builder
|
@@ -342,6 +364,8 @@ module Puma
|
|
342
364
|
raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
|
343
365
|
|
344
366
|
rack_app, rack_options = rack_builder.parse_file(rackup)
|
367
|
+
rack_options = rack_options || {}
|
368
|
+
|
345
369
|
@options.file_options.merge!(rack_options)
|
346
370
|
|
347
371
|
config_ru_binds = []
|
@@ -362,4 +386,4 @@ module Puma
|
|
362
386
|
end
|
363
387
|
end
|
364
388
|
|
365
|
-
|
389
|
+
require_relative 'dsl'
|