puma 4.3.12 → 5.0.0.beta1
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 +58 -41
- data/LICENSE +23 -20
- data/README.md +17 -11
- data/bin/puma-wild +0 -0
- data/docs/architecture.md +0 -0
- data/docs/deployment.md +3 -1
- data/docs/fork_worker.md +31 -0
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +13 -0
- data/{tools → docs}/jungle/rc.d/README.md +0 -0
- data/{tools → docs}/jungle/rc.d/puma +0 -0
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/{tools → docs}/jungle/upstart/README.md +0 -0
- data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
- data/{tools → docs}/jungle/upstart/puma.conf +0 -0
- data/docs/nginx.md +0 -0
- data/docs/plugins.md +0 -0
- data/docs/restart.md +0 -0
- data/docs/signals.md +1 -0
- data/docs/systemd.md +1 -63
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +3 -10
- data/ext/puma_http11/http11_parser.c +11 -26
- data/ext/puma_http11/http11_parser.h +0 -0
- data/ext/puma_http11/http11_parser.java.rl +0 -0
- data/ext/puma_http11/http11_parser.rl +1 -3
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +47 -82
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +46 -48
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +0 -0
- data/ext/puma_http11/puma_http11.c +2 -38
- data/lib/puma/accept_nonblock.rb +0 -0
- data/lib/puma/app/status.rb +16 -5
- data/lib/puma/binder.rb +62 -60
- data/lib/puma/cli.rb +7 -15
- data/lib/puma/client.rb +38 -78
- data/lib/puma/cluster.rb +179 -74
- data/lib/puma/commonlogger.rb +0 -0
- data/lib/puma/configuration.rb +30 -42
- data/lib/puma/const.rb +5 -8
- data/lib/puma/control_cli.rb +27 -17
- data/lib/puma/detect.rb +8 -0
- data/lib/puma/dsl.rb +70 -34
- data/lib/puma/events.rb +0 -0
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/launcher.rb +41 -29
- data/lib/puma/minissl/context_builder.rb +0 -0
- data/lib/puma/minissl.rb +13 -8
- data/lib/puma/null_io.rb +1 -1
- data/lib/puma/plugin/tmp_restart.rb +0 -0
- data/lib/puma/plugin.rb +1 -10
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +0 -0
- data/lib/puma/reactor.rb +6 -1
- data/lib/puma/runner.rb +5 -34
- data/lib/puma/server.rb +74 -206
- data/lib/puma/single.rb +7 -64
- data/lib/puma/state_file.rb +5 -2
- data/lib/puma/thread_pool.rb +85 -47
- data/lib/puma/util.rb +0 -0
- data/lib/puma.rb +4 -0
- data/lib/rack/handler/puma.rb +1 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- data/tools/trickletest.rb +0 -0
- metadata +20 -24
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
data/lib/puma/cluster.rb
CHANGED
@@ -5,6 +5,7 @@ require 'puma/util'
|
|
5
5
|
require 'puma/plugin'
|
6
6
|
|
7
7
|
require 'time'
|
8
|
+
require 'json'
|
8
9
|
|
9
10
|
module Puma
|
10
11
|
# This class is instantiated by the `Puma::Launcher` and used
|
@@ -24,9 +25,8 @@ module Puma
|
|
24
25
|
|
25
26
|
@phase = 0
|
26
27
|
@workers = []
|
27
|
-
@next_check =
|
28
|
+
@next_check = Time.now
|
28
29
|
|
29
|
-
@phased_state = :idle
|
30
30
|
@phased_restart = false
|
31
31
|
end
|
32
32
|
|
@@ -37,7 +37,7 @@ module Puma
|
|
37
37
|
begin
|
38
38
|
loop do
|
39
39
|
wait_workers
|
40
|
-
break if @workers.empty?
|
40
|
+
break if @workers.reject {|w| w.pid.nil?}.empty?
|
41
41
|
sleep 0.2
|
42
42
|
end
|
43
43
|
rescue Interrupt
|
@@ -73,11 +73,12 @@ module Puma
|
|
73
73
|
@first_term_sent = nil
|
74
74
|
@started_at = Time.now
|
75
75
|
@last_checkin = Time.now
|
76
|
-
@last_status =
|
76
|
+
@last_status = {}
|
77
77
|
@term = false
|
78
78
|
end
|
79
79
|
|
80
80
|
attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
|
81
|
+
attr_writer :pid, :phase
|
81
82
|
|
82
83
|
def booted?
|
83
84
|
@stage == :booted
|
@@ -94,11 +95,15 @@ module Puma
|
|
94
95
|
|
95
96
|
def ping!(status)
|
96
97
|
@last_checkin = Time.now
|
97
|
-
@last_status = status
|
98
|
+
@last_status = JSON.parse(status, symbolize_names: true)
|
98
99
|
end
|
99
100
|
|
100
|
-
def ping_timeout
|
101
|
-
|
101
|
+
def ping_timeout
|
102
|
+
@last_checkin +
|
103
|
+
(booted? ?
|
104
|
+
@options[:worker_timeout] :
|
105
|
+
@options[:worker_boot_timeout]
|
106
|
+
)
|
102
107
|
end
|
103
108
|
|
104
109
|
def term
|
@@ -109,14 +114,14 @@ module Puma
|
|
109
114
|
@term ||= true
|
110
115
|
@first_term_sent ||= Time.now
|
111
116
|
end
|
112
|
-
Process.kill @signal, @pid
|
117
|
+
Process.kill @signal, @pid if @pid
|
113
118
|
rescue Errno::ESRCH
|
114
119
|
end
|
115
120
|
end
|
116
121
|
|
117
122
|
def kill
|
118
|
-
|
119
|
-
|
123
|
+
@signal = 'KILL'
|
124
|
+
term
|
120
125
|
end
|
121
126
|
|
122
127
|
def hup
|
@@ -130,27 +135,43 @@ module Puma
|
|
130
135
|
return if diff < 1
|
131
136
|
|
132
137
|
master = Process.pid
|
138
|
+
if @options[:fork_worker]
|
139
|
+
@fork_writer << "-1\n"
|
140
|
+
end
|
133
141
|
|
134
142
|
diff.times do
|
135
143
|
idx = next_worker_index
|
136
|
-
@launcher.config.run_hooks :before_worker_fork, idx
|
137
144
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
145
|
+
if @options[:fork_worker] && idx != 0
|
146
|
+
@fork_writer << "#{idx}\n"
|
147
|
+
pid = nil
|
148
|
+
else
|
149
|
+
pid = spawn_worker(idx, master)
|
143
150
|
end
|
144
151
|
|
145
152
|
debug "Spawned worker: #{pid}"
|
146
153
|
@workers << Worker.new(idx, pid, @phase, @options)
|
154
|
+
end
|
147
155
|
|
148
|
-
|
156
|
+
if @options[:fork_worker] &&
|
157
|
+
@workers.all? {|x| x.phase == @phase}
|
158
|
+
|
159
|
+
@fork_writer << "0\n"
|
149
160
|
end
|
161
|
+
end
|
150
162
|
|
151
|
-
|
152
|
-
|
163
|
+
def spawn_worker(idx, master)
|
164
|
+
@launcher.config.run_hooks :before_worker_fork, idx, @launcher.events
|
165
|
+
|
166
|
+
pid = fork { worker(idx, master) }
|
167
|
+
if !pid
|
168
|
+
log "! Complete inability to spawn new workers detected"
|
169
|
+
log "! Seppuku is the only choice."
|
170
|
+
exit! 1
|
153
171
|
end
|
172
|
+
|
173
|
+
@launcher.config.run_hooks :after_worker_fork, idx, @launcher.events
|
174
|
+
pid
|
154
175
|
end
|
155
176
|
|
156
177
|
def cull_workers
|
@@ -179,26 +200,12 @@ module Puma
|
|
179
200
|
@workers.count { |w| !w.booted? } == 0
|
180
201
|
end
|
181
202
|
|
182
|
-
def check_workers
|
183
|
-
return if
|
203
|
+
def check_workers
|
204
|
+
return if @next_check >= Time.now
|
184
205
|
|
185
206
|
@next_check = Time.now + Const::WORKER_CHECK_INTERVAL
|
186
207
|
|
187
|
-
|
188
|
-
|
189
|
-
@workers.each do |w|
|
190
|
-
next if !w.booted? && !w.ping_timeout?(@options[:worker_boot_timeout])
|
191
|
-
if w.ping_timeout?(@options[:worker_timeout])
|
192
|
-
log "! Terminating timed out worker: #{w.pid}"
|
193
|
-
w.kill
|
194
|
-
any = true
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
# If we killed any timed out workers, try to catch them
|
199
|
-
# during this loop by giving the kernel time to kill them.
|
200
|
-
sleep 1 if any
|
201
|
-
|
208
|
+
timeout_workers
|
202
209
|
wait_workers
|
203
210
|
cull_workers
|
204
211
|
spawn_workers
|
@@ -211,17 +218,18 @@ module Puma
|
|
211
218
|
w = @workers.find { |x| x.phase != @phase }
|
212
219
|
|
213
220
|
if w
|
214
|
-
|
215
|
-
@phased_state = :waiting
|
216
|
-
log "- Stopping #{w.pid} for phased upgrade..."
|
217
|
-
end
|
218
|
-
|
221
|
+
log "- Stopping #{w.pid} for phased upgrade..."
|
219
222
|
unless w.term?
|
220
223
|
w.term
|
221
224
|
log "- #{w.signal} sent to #{w.pid}..."
|
222
225
|
end
|
223
226
|
end
|
224
227
|
end
|
228
|
+
|
229
|
+
@next_check = [
|
230
|
+
@workers.reject(&:term?).map(&:ping_timeout).min,
|
231
|
+
@next_check
|
232
|
+
].compact.min
|
225
233
|
end
|
226
234
|
|
227
235
|
def wakeup!
|
@@ -241,9 +249,14 @@ module Puma
|
|
241
249
|
|
242
250
|
Signal.trap "SIGINT", "IGNORE"
|
243
251
|
|
252
|
+
fork_worker = @options[:fork_worker] && index == 0
|
253
|
+
|
244
254
|
@workers = []
|
245
|
-
|
246
|
-
|
255
|
+
if !@options[:fork_worker] || fork_worker
|
256
|
+
@master_read.close
|
257
|
+
@suicide_pipe.close
|
258
|
+
@fork_writer.close
|
259
|
+
end
|
247
260
|
|
248
261
|
Thread.new do
|
249
262
|
Puma.set_thread_name "worker check pipe"
|
@@ -264,17 +277,47 @@ module Puma
|
|
264
277
|
|
265
278
|
# Invoke any worker boot hooks so they can get
|
266
279
|
# things in shape before booting the app.
|
267
|
-
@launcher.config.run_hooks :before_worker_boot, index
|
280
|
+
@launcher.config.run_hooks :before_worker_boot, index, @launcher.events
|
281
|
+
|
282
|
+
server = @server ||= start_server
|
283
|
+
restart_server = Queue.new << true << false
|
268
284
|
|
269
|
-
|
285
|
+
if fork_worker
|
286
|
+
restart_server.clear
|
287
|
+
Signal.trap "SIGCHLD" do
|
288
|
+
Process.wait(-1, Process::WNOHANG) rescue nil
|
289
|
+
wakeup!
|
290
|
+
end
|
291
|
+
|
292
|
+
Thread.new do
|
293
|
+
Puma.set_thread_name "worker fork pipe"
|
294
|
+
while (idx = @fork_pipe.gets)
|
295
|
+
idx = idx.to_i
|
296
|
+
if idx == -1 # stop server
|
297
|
+
if restart_server.length > 0
|
298
|
+
restart_server.clear
|
299
|
+
server.begin_restart(true)
|
300
|
+
@launcher.config.run_hooks :before_refork, nil, @launcher.events
|
301
|
+
nakayoshi_gc
|
302
|
+
end
|
303
|
+
elsif idx == 0 # restart server
|
304
|
+
restart_server << true << false
|
305
|
+
else # fork worker
|
306
|
+
pid = spawn_worker(idx, master)
|
307
|
+
@worker_write << "f#{pid}:#{idx}\n" rescue nil
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
270
312
|
|
271
313
|
Signal.trap "SIGTERM" do
|
272
314
|
@worker_write << "e#{Process.pid}\n" rescue nil
|
273
315
|
server.stop
|
316
|
+
restart_server << false
|
274
317
|
end
|
275
318
|
|
276
319
|
begin
|
277
|
-
@worker_write << "b#{Process.pid}\n"
|
320
|
+
@worker_write << "b#{Process.pid}:#{index}\n"
|
278
321
|
rescue SystemCallError, IOError
|
279
322
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
280
323
|
STDERR.puts "Master seems to have exited, exiting."
|
@@ -283,17 +326,11 @@ module Puma
|
|
283
326
|
|
284
327
|
Thread.new(@worker_write) do |io|
|
285
328
|
Puma.set_thread_name "stat payload"
|
286
|
-
base_payload = "p#{Process.pid}"
|
287
329
|
|
288
330
|
while true
|
289
331
|
sleep Const::WORKER_CHECK_INTERVAL
|
290
332
|
begin
|
291
|
-
|
292
|
-
r = server.running || 0
|
293
|
-
t = server.pool_capacity || 0
|
294
|
-
m = server.max_threads || 0
|
295
|
-
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m} }\n!
|
296
|
-
io << payload
|
333
|
+
io << "p#{Process.pid}#{server.stats.to_json}\n"
|
297
334
|
rescue IOError
|
298
335
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
299
336
|
break
|
@@ -301,11 +338,11 @@ module Puma
|
|
301
338
|
end
|
302
339
|
end
|
303
340
|
|
304
|
-
server.run.join
|
341
|
+
server.run.join while restart_server.pop
|
305
342
|
|
306
343
|
# Invoke any worker shutdown hooks so they can prevent the worker
|
307
344
|
# exiting until any background operations are completed
|
308
|
-
@launcher.config.run_hooks :before_worker_shutdown, index
|
345
|
+
@launcher.config.run_hooks :before_worker_shutdown, index, @launcher.events
|
309
346
|
ensure
|
310
347
|
@worker_write << "t#{Process.pid}\n" rescue nil
|
311
348
|
@worker_write.close
|
@@ -352,18 +389,57 @@ module Puma
|
|
352
389
|
# the master process.
|
353
390
|
def stats
|
354
391
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
355
|
-
|
356
|
-
|
357
|
-
|
392
|
+
worker_status = @workers.map do |w|
|
393
|
+
{
|
394
|
+
started_at: w.started_at.utc.iso8601,
|
395
|
+
pid: w.pid,
|
396
|
+
index: w.index,
|
397
|
+
phase: w.phase,
|
398
|
+
booted: w.booted?,
|
399
|
+
last_checkin: w.last_checkin.utc.iso8601,
|
400
|
+
last_status: w.last_status,
|
401
|
+
}
|
402
|
+
end
|
403
|
+
|
404
|
+
{
|
405
|
+
started_at: @started_at.utc.iso8601,
|
406
|
+
workers: @workers.size,
|
407
|
+
phase: @phase,
|
408
|
+
booted_workers: worker_status.count { |w| w[:booted] },
|
409
|
+
old_workers: old_worker_count,
|
410
|
+
worker_status: worker_status,
|
411
|
+
}
|
358
412
|
end
|
359
413
|
|
360
414
|
def preload?
|
361
415
|
@options[:preload_app]
|
362
416
|
end
|
363
417
|
|
418
|
+
def fork_worker!
|
419
|
+
if (worker = @workers.find { |w| w.index == 0 })
|
420
|
+
worker.phase += 1
|
421
|
+
end
|
422
|
+
phased_restart
|
423
|
+
end
|
424
|
+
|
364
425
|
# We do this in a separate method to keep the lambda scope
|
365
426
|
# of the signals handlers as small as possible.
|
366
427
|
def setup_signals
|
428
|
+
if @options[:fork_worker]
|
429
|
+
Signal.trap "SIGURG" do
|
430
|
+
fork_worker!
|
431
|
+
end
|
432
|
+
|
433
|
+
# Auto-fork after the specified number of requests.
|
434
|
+
if (fork_requests = @options[:fork_worker].to_i) > 0
|
435
|
+
@launcher.events.register(:ping!) do |w|
|
436
|
+
fork_worker! if w.index == 0 &&
|
437
|
+
w.phase == 0 &&
|
438
|
+
w.last_status[:requests_count] >= fork_requests
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
367
443
|
Signal.trap "SIGCHLD" do
|
368
444
|
wakeup!
|
369
445
|
end
|
@@ -447,12 +523,11 @@ module Puma
|
|
447
523
|
#
|
448
524
|
@check_pipe, @suicide_pipe = Puma::Util.pipe
|
449
525
|
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
end
|
526
|
+
# Separate pipe used by worker 0 to receive commands to
|
527
|
+
# fork new worker processes.
|
528
|
+
@fork_pipe, @fork_writer = Puma::Util.pipe
|
529
|
+
|
530
|
+
log "Use Ctrl-C to stop"
|
456
531
|
|
457
532
|
redirect_io
|
458
533
|
|
@@ -464,7 +539,8 @@ module Puma
|
|
464
539
|
|
465
540
|
@master_read, @worker_write = read, @wakeup
|
466
541
|
|
467
|
-
@launcher.config.run_hooks :before_fork, nil
|
542
|
+
@launcher.config.run_hooks :before_fork, nil, @launcher.events
|
543
|
+
nakayoshi_gc
|
468
544
|
|
469
545
|
spawn_workers
|
470
546
|
|
@@ -475,8 +551,6 @@ module Puma
|
|
475
551
|
@launcher.events.fire_on_booted!
|
476
552
|
|
477
553
|
begin
|
478
|
-
force_check = false
|
479
|
-
|
480
554
|
while @status == :run
|
481
555
|
begin
|
482
556
|
if @phased_restart
|
@@ -484,34 +558,39 @@ module Puma
|
|
484
558
|
@phased_restart = false
|
485
559
|
end
|
486
560
|
|
487
|
-
check_workers
|
561
|
+
check_workers
|
488
562
|
|
489
|
-
|
490
|
-
|
491
|
-
res = IO.select([read], nil, nil, Const::WORKER_CHECK_INTERVAL)
|
563
|
+
res = IO.select([read], nil, nil, [0, @next_check - Time.now].max)
|
492
564
|
|
493
565
|
if res
|
494
566
|
req = read.read_nonblock(1)
|
495
567
|
|
568
|
+
@next_check = Time.now if req == "!"
|
496
569
|
next if !req || req == "!"
|
497
570
|
|
498
571
|
result = read.gets
|
499
572
|
pid = result.to_i
|
500
573
|
|
574
|
+
if req == "b" || req == "f"
|
575
|
+
pid, idx = result.split(':').map(&:to_i)
|
576
|
+
w = @workers.find {|x| x.index == idx}
|
577
|
+
w.pid = pid if w.pid.nil?
|
578
|
+
end
|
579
|
+
|
501
580
|
if w = @workers.find { |x| x.pid == pid }
|
502
581
|
case req
|
503
582
|
when "b"
|
504
583
|
w.boot!
|
505
584
|
log "- Worker #{w.index} (pid: #{pid}) booted, phase: #{w.phase}"
|
506
|
-
|
585
|
+
@next_check = Time.now
|
507
586
|
when "e"
|
508
587
|
# external term, see worker method, Signal.trap "SIGTERM"
|
509
588
|
w.instance_variable_set :@term, true
|
510
589
|
when "t"
|
511
590
|
w.term unless w.term?
|
512
|
-
force_check = true
|
513
591
|
when "p"
|
514
592
|
w.ping!(result.sub(/^\d+/,'').chomp)
|
593
|
+
@launcher.events.fire(:ping!, w)
|
515
594
|
end
|
516
595
|
else
|
517
596
|
log "! Out-of-sync worker list, no #{pid} worker"
|
@@ -538,6 +617,7 @@ module Puma
|
|
538
617
|
# `#term` if needed
|
539
618
|
def wait_workers
|
540
619
|
@workers.reject! do |w|
|
620
|
+
next false if w.pid.nil?
|
541
621
|
begin
|
542
622
|
if Process.wait(w.pid, Process::WNOHANG)
|
543
623
|
true
|
@@ -546,9 +626,34 @@ module Puma
|
|
546
626
|
nil
|
547
627
|
end
|
548
628
|
rescue Errno::ECHILD
|
549
|
-
|
629
|
+
begin
|
630
|
+
Process.kill(0, w.pid)
|
631
|
+
false # child still alive, but has another parent
|
632
|
+
rescue Errno::ESRCH, Errno::EPERM
|
633
|
+
true # child is already terminated
|
634
|
+
end
|
550
635
|
end
|
551
636
|
end
|
552
637
|
end
|
638
|
+
|
639
|
+
def timeout_workers
|
640
|
+
@workers.each do |w|
|
641
|
+
if !w.term? && w.ping_timeout <= Time.now
|
642
|
+
log "! Terminating timed out worker: #{w.pid}"
|
643
|
+
w.kill
|
644
|
+
end
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
def nakayoshi_gc
|
649
|
+
return unless @options[:nakayoshi_fork]
|
650
|
+
log "! Promoting existing objects to old generation..."
|
651
|
+
4.times { GC.start(full_mark: false) }
|
652
|
+
if GC.respond_to?(:compact)
|
653
|
+
log "! Compacting..."
|
654
|
+
GC.compact
|
655
|
+
end
|
656
|
+
log "! Friendly fork preparation complete."
|
657
|
+
end
|
553
658
|
end
|
554
659
|
end
|
data/lib/puma/commonlogger.rb
CHANGED
File without changes
|
data/lib/puma/configuration.rb
CHANGED
@@ -54,9 +54,7 @@ module Puma
|
|
54
54
|
attr_reader :user_options, :file_options, :default_options
|
55
55
|
|
56
56
|
def [](key)
|
57
|
-
|
58
|
-
return file_options[key] if file_options.key?(key)
|
59
|
-
return default_options[key] if default_options.key?(key)
|
57
|
+
fetch(key)
|
60
58
|
end
|
61
59
|
|
62
60
|
def []=(key, value)
|
@@ -64,7 +62,11 @@ module Puma
|
|
64
62
|
end
|
65
63
|
|
66
64
|
def fetch(key, default_value = nil)
|
67
|
-
|
65
|
+
return user_options[key] if user_options.key?(key)
|
66
|
+
return file_options[key] if file_options.key?(key)
|
67
|
+
return default_options[key] if default_options.key?(key)
|
68
|
+
|
69
|
+
default_value
|
68
70
|
end
|
69
71
|
|
70
72
|
def all_of(key)
|
@@ -106,7 +108,7 @@ module Puma
|
|
106
108
|
#
|
107
109
|
# It also handles loading plugins.
|
108
110
|
#
|
109
|
-
# > Note: `:port` and `:host` are not valid keys. By
|
111
|
+
# > Note: `:port` and `:host` are not valid keys. By the time they make it to the
|
110
112
|
# configuration options they are expected to be incorporated into a `:binds` key.
|
111
113
|
# Under the hood the DSL maps `port` and `host` calls to `:binds`
|
112
114
|
#
|
@@ -137,6 +139,10 @@ module Puma
|
|
137
139
|
@file_dsl = DSL.new(@options.file_options, self)
|
138
140
|
@default_dsl = DSL.new(@options.default_options, self)
|
139
141
|
|
142
|
+
if !@options[:prune_bundler]
|
143
|
+
default_options[:preload_app] = (@options[:workers] > 1) && Puma.forkable?
|
144
|
+
end
|
145
|
+
|
140
146
|
if block
|
141
147
|
configure(&block)
|
142
148
|
end
|
@@ -167,22 +173,25 @@ module Puma
|
|
167
173
|
self
|
168
174
|
end
|
169
175
|
|
176
|
+
def default_max_threads
|
177
|
+
Puma.mri? ? 5 : 16
|
178
|
+
end
|
179
|
+
|
170
180
|
def puma_default_options
|
171
181
|
{
|
172
|
-
:min_threads => 0,
|
173
|
-
:max_threads =>
|
182
|
+
:min_threads => Integer(ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS'] || 0),
|
183
|
+
:max_threads => Integer(ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS'] || default_max_threads),
|
174
184
|
:log_requests => false,
|
175
185
|
:debug => false,
|
176
186
|
:binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
|
177
|
-
:workers => 0,
|
178
|
-
:daemon => false,
|
187
|
+
:workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
|
179
188
|
:mode => :http,
|
180
189
|
:worker_timeout => DefaultWorkerTimeout,
|
181
190
|
:worker_boot_timeout => DefaultWorkerTimeout,
|
182
191
|
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
183
192
|
:remote_address => :socket,
|
184
193
|
:tag => method(:infer_tag),
|
185
|
-
:environment => -> { ENV['RACK_ENV'] || "development" },
|
194
|
+
:environment => -> { ENV['RACK_ENV'] || ENV['RAILS_ENV'] || "development" },
|
186
195
|
:rackup => DefaultRackup,
|
187
196
|
:logger => STDOUT,
|
188
197
|
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
@@ -245,14 +254,6 @@ module Puma
|
|
245
254
|
def app
|
246
255
|
found = options[:app] || load_rackup
|
247
256
|
|
248
|
-
if @options[:mode] == :tcp
|
249
|
-
require 'puma/tcp_logger'
|
250
|
-
|
251
|
-
logger = @options[:logger]
|
252
|
-
quiet = !@options[:log_requests]
|
253
|
-
return TCPLogger.new(logger, found, quiet)
|
254
|
-
end
|
255
|
-
|
256
257
|
if @options[:log_requests]
|
257
258
|
require 'puma/commonlogger'
|
258
259
|
logger = @options[:logger]
|
@@ -275,8 +276,15 @@ module Puma
|
|
275
276
|
@plugins.create name
|
276
277
|
end
|
277
278
|
|
278
|
-
def run_hooks(key, arg)
|
279
|
-
@options.all_of(key).each
|
279
|
+
def run_hooks(key, arg, events)
|
280
|
+
@options.all_of(key).each do |b|
|
281
|
+
begin
|
282
|
+
b.call arg
|
283
|
+
rescue => e
|
284
|
+
events.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
|
285
|
+
events.debug e.backtrace.join("\n")
|
286
|
+
end
|
287
|
+
end
|
280
288
|
end
|
281
289
|
|
282
290
|
def self.temp_path
|
@@ -332,29 +340,9 @@ module Puma
|
|
332
340
|
end
|
333
341
|
|
334
342
|
def self.random_token
|
335
|
-
|
336
|
-
require 'openssl'
|
337
|
-
rescue LoadError
|
338
|
-
end
|
339
|
-
|
340
|
-
count = 16
|
341
|
-
|
342
|
-
bytes = nil
|
343
|
-
|
344
|
-
if defined? OpenSSL::Random
|
345
|
-
bytes = OpenSSL::Random.random_bytes(count)
|
346
|
-
elsif File.exist?("/dev/urandom")
|
347
|
-
File.open('/dev/urandom') { |f| bytes = f.read(count) }
|
348
|
-
end
|
349
|
-
|
350
|
-
if bytes
|
351
|
-
token = "".dup
|
352
|
-
bytes.each_byte { |b| token << b.to_s(16) }
|
353
|
-
else
|
354
|
-
token = (0..count).to_a.map { rand(255).to_s(16) }.join
|
355
|
-
end
|
343
|
+
require 'securerandom' unless defined?(SecureRandom)
|
356
344
|
|
357
|
-
|
345
|
+
SecureRandom.hex(16)
|
358
346
|
end
|
359
347
|
end
|
360
348
|
end
|
data/lib/puma/const.rb
CHANGED
@@ -76,7 +76,7 @@ module Puma
|
|
76
76
|
508 => 'Loop Detected',
|
77
77
|
510 => 'Not Extended',
|
78
78
|
511 => 'Network Authentication Required'
|
79
|
-
}
|
79
|
+
}
|
80
80
|
|
81
81
|
# For some HTTP status codes the client only expects headers.
|
82
82
|
#
|
@@ -85,7 +85,7 @@ module Puma
|
|
85
85
|
204 => true,
|
86
86
|
205 => true,
|
87
87
|
304 => true
|
88
|
-
}
|
88
|
+
}
|
89
89
|
|
90
90
|
# Frequently used constants when constructing requests or responses. Many times
|
91
91
|
# the constant just refers to a string with the same contents. Using these constants
|
@@ -100,8 +100,8 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "5.0.0.beta1".freeze
|
104
|
+
CODE_NAME = "Spoony Bard".freeze
|
105
105
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
106
106
|
|
107
107
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
@@ -144,11 +144,9 @@ module Puma
|
|
144
144
|
408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze,
|
145
145
|
# Indicate that there was an internal error, obviously.
|
146
146
|
500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze,
|
147
|
-
# Incorrect or invalid header value
|
148
|
-
501 => "HTTP/1.1 501 Not Implemented\r\n\r\n".freeze,
|
149
147
|
# A common header for indicating the server is too busy. Not used yet.
|
150
148
|
503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
151
|
-
}
|
149
|
+
}
|
152
150
|
|
153
151
|
# The basic max request size we'll try to read.
|
154
152
|
CHUNK_SIZE = 16 * 1024
|
@@ -177,7 +175,6 @@ module Puma
|
|
177
175
|
PORT_443 = "443".freeze
|
178
176
|
LOCALHOST = "localhost".freeze
|
179
177
|
LOCALHOST_IP = "127.0.0.1".freeze
|
180
|
-
LOCALHOST_ADDR = "127.0.0.1:0".freeze
|
181
178
|
|
182
179
|
SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
|
183
180
|
HTTP_11 = "HTTP/1.1".freeze
|