puma 3.4.0 → 3.12.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 +5 -5
- data/{History.txt → History.md} +356 -74
- data/README.md +143 -227
- data/docs/architecture.md +36 -0
- data/{DEPLOYMENT.md → docs/deployment.md} +1 -1
- 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/plugins.md +28 -0
- data/docs/restart.md +39 -0
- data/docs/signals.md +56 -3
- data/docs/systemd.md +124 -22
- data/ext/puma_http11/extconf.rb +2 -0
- data/ext/puma_http11/http11_parser.c +291 -447
- data/ext/puma_http11/http11_parser.h +1 -0
- data/ext/puma_http11/http11_parser.rl +10 -9
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/io_buffer.c +7 -7
- data/ext/puma_http11/mini_ssl.c +67 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +76 -94
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -2
- data/ext/puma_http11/puma_http11.c +1 -0
- data/lib/puma.rb +13 -5
- data/lib/puma/app/status.rb +8 -0
- data/lib/puma/binder.rb +46 -21
- data/lib/puma/cli.rb +49 -33
- data/lib/puma/client.rb +149 -4
- data/lib/puma/cluster.rb +55 -13
- data/lib/puma/commonlogger.rb +19 -20
- data/lib/puma/compat.rb +3 -7
- data/lib/puma/configuration.rb +136 -131
- data/lib/puma/const.rb +19 -37
- data/lib/puma/control_cli.rb +38 -35
- data/lib/puma/convenient.rb +3 -3
- data/lib/puma/detect.rb +3 -1
- data/lib/puma/dsl.rb +86 -57
- data/lib/puma/events.rb +17 -13
- data/lib/puma/io_buffer.rb +1 -1
- data/lib/puma/jruby_restart.rb +0 -1
- data/lib/puma/launcher.rb +61 -30
- data/lib/puma/minissl.rb +85 -4
- data/lib/puma/null_io.rb +6 -13
- data/lib/puma/plugin.rb +12 -1
- data/lib/puma/plugin/tmp_restart.rb +1 -2
- data/lib/puma/rack/builder.rb +3 -0
- data/lib/puma/rack/urlmap.rb +9 -8
- data/lib/puma/reactor.rb +144 -0
- data/lib/puma/runner.rb +27 -1
- data/lib/puma/server.rb +135 -33
- data/lib/puma/single.rb +17 -3
- data/lib/puma/tcp_logger.rb +8 -1
- data/lib/puma/thread_pool.rb +70 -20
- data/lib/puma/util.rb +1 -5
- data/lib/rack/handler/puma.rb +58 -17
- data/tools/jungle/README.md +12 -2
- data/tools/jungle/init.d/README.md +9 -2
- data/tools/jungle/init.d/puma +85 -58
- data/tools/jungle/init.d/run-puma +16 -1
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- data/tools/jungle/upstart/puma.conf +1 -1
- data/tools/trickletest.rb +1 -1
- metadata +22 -94
- data/Gemfile +0 -13
- data/Manifest.txt +0 -78
- data/Rakefile +0 -158
- data/docs/config.md +0 -0
- data/lib/puma/rack/backports/uri/common_18.rb +0 -59
- data/lib/puma/rack/backports/uri/common_192.rb +0 -55
- data/puma.gemspec +0 -52
data/lib/puma/cluster.rb
CHANGED
@@ -1,8 +1,24 @@
|
|
1
1
|
require 'puma/runner'
|
2
|
+
require 'puma/util'
|
3
|
+
require 'puma/plugin'
|
4
|
+
|
2
5
|
require 'time'
|
3
6
|
|
4
7
|
module Puma
|
8
|
+
# This class is instantiated by the `Puma::Launcher` and used
|
9
|
+
# to boot and serve a Ruby application when puma "workers" are needed
|
10
|
+
# i.e. when using multi-processes. For example `$ puma -w 5`
|
11
|
+
#
|
12
|
+
# At the core of this class is running an instance of `Puma::Server` which
|
13
|
+
# gets created via the `start_server` method from the `Puma::Runner` class
|
14
|
+
# that this inherits from.
|
15
|
+
#
|
16
|
+
# An instance of this class will spawn the number of processes passed in
|
17
|
+
# via the `spawn_workers` method call. Each worker will have it's own
|
18
|
+
# instance of a `Puma::Server`.
|
5
19
|
class Cluster < Runner
|
20
|
+
WORKER_CHECK_INTERVAL = 5
|
21
|
+
|
6
22
|
def initialize(cli, events)
|
7
23
|
super cli, events
|
8
24
|
|
@@ -19,7 +35,7 @@ module Puma
|
|
19
35
|
@workers.each { |x| x.term }
|
20
36
|
|
21
37
|
begin
|
22
|
-
Process.
|
38
|
+
@workers.each { |w| Process.waitpid(w.pid) }
|
23
39
|
rescue Interrupt
|
24
40
|
log "! Cancelled waiting for workers"
|
25
41
|
end
|
@@ -110,6 +126,7 @@ module Puma
|
|
110
126
|
|
111
127
|
def spawn_workers
|
112
128
|
diff = @options[:workers] - @workers.size
|
129
|
+
return if diff < 1
|
113
130
|
|
114
131
|
master = Process.pid
|
115
132
|
|
@@ -135,6 +152,21 @@ module Puma
|
|
135
152
|
end
|
136
153
|
end
|
137
154
|
|
155
|
+
def cull_workers
|
156
|
+
diff = @workers.size - @options[:workers]
|
157
|
+
return if diff < 1
|
158
|
+
|
159
|
+
debug "Culling #{diff.inspect} workers"
|
160
|
+
|
161
|
+
workers_to_cull = @workers[-diff,diff]
|
162
|
+
debug "Workers to cull: #{workers_to_cull.inspect}"
|
163
|
+
|
164
|
+
workers_to_cull.each do |worker|
|
165
|
+
log "- Worker #{worker.index} (pid: #{worker.pid}) terminating"
|
166
|
+
worker.term
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
138
170
|
def next_worker_index
|
139
171
|
all_positions = 0...@options[:workers]
|
140
172
|
occupied_positions = @workers.map { |w| w.index }
|
@@ -149,7 +181,7 @@ module Puma
|
|
149
181
|
def check_workers(force=false)
|
150
182
|
return if !force && @next_check && @next_check >= Time.now
|
151
183
|
|
152
|
-
@next_check = Time.now +
|
184
|
+
@next_check = Time.now + WORKER_CHECK_INTERVAL
|
153
185
|
|
154
186
|
any = false
|
155
187
|
|
@@ -175,6 +207,7 @@ module Puma
|
|
175
207
|
|
176
208
|
@workers.delete_if(&:dead?)
|
177
209
|
|
210
|
+
cull_workers
|
178
211
|
spawn_workers
|
179
212
|
|
180
213
|
if all_workers_booted?
|
@@ -202,12 +235,13 @@ module Puma
|
|
202
235
|
begin
|
203
236
|
@wakeup.write "!" unless @wakeup.closed?
|
204
237
|
rescue SystemCallError, IOError
|
238
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
205
239
|
end
|
206
240
|
end
|
207
241
|
|
208
242
|
def worker(index, master)
|
209
|
-
title
|
210
|
-
title
|
243
|
+
title = "puma: cluster worker #{index}: #{master}"
|
244
|
+
title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
|
211
245
|
$0 = title
|
212
246
|
|
213
247
|
Signal.trap "SIGINT", "IGNORE"
|
@@ -245,6 +279,7 @@ module Puma
|
|
245
279
|
begin
|
246
280
|
@worker_write << "b#{Process.pid}\n"
|
247
281
|
rescue SystemCallError, IOError
|
282
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
248
283
|
STDERR.puts "Master seems to have exited, exiting."
|
249
284
|
return
|
250
285
|
end
|
@@ -253,13 +288,16 @@ module Puma
|
|
253
288
|
base_payload = "p#{Process.pid}"
|
254
289
|
|
255
290
|
while true
|
256
|
-
sleep
|
291
|
+
sleep WORKER_CHECK_INTERVAL
|
257
292
|
begin
|
258
|
-
b = server.backlog
|
259
|
-
r = server.running
|
260
|
-
|
293
|
+
b = server.backlog || 0
|
294
|
+
r = server.running || 0
|
295
|
+
t = server.pool_capacity || 0
|
296
|
+
m = server.max_threads || 0
|
297
|
+
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m} }\n!
|
261
298
|
io << payload
|
262
299
|
rescue IOError
|
300
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
263
301
|
break
|
264
302
|
end
|
265
303
|
end
|
@@ -315,7 +353,7 @@ module Puma
|
|
315
353
|
def stats
|
316
354
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
317
355
|
booted_worker_count = @workers.count { |w| w.booted? }
|
318
|
-
worker_status = '[' + @workers.map{ |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
|
356
|
+
worker_status = '[' + @workers.map { |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
|
319
357
|
%Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }!
|
320
358
|
end
|
321
359
|
|
@@ -323,7 +361,7 @@ module Puma
|
|
323
361
|
@options[:preload_app]
|
324
362
|
end
|
325
363
|
|
326
|
-
# We do this in a separate method to keep the
|
364
|
+
# We do this in a separate method to keep the lambda scope
|
327
365
|
# of the signals handlers as small as possible.
|
328
366
|
def setup_signals
|
329
367
|
Signal.trap "SIGCHLD" do
|
@@ -337,7 +375,6 @@ module Puma
|
|
337
375
|
|
338
376
|
Signal.trap "TTOU" do
|
339
377
|
@options[:workers] -= 1 if @options[:workers] >= 2
|
340
|
-
@workers.last.term
|
341
378
|
wakeup!
|
342
379
|
end
|
343
380
|
|
@@ -351,7 +388,10 @@ module Puma
|
|
351
388
|
log "Early termination of worker"
|
352
389
|
exit! 0
|
353
390
|
else
|
391
|
+
stop_workers
|
354
392
|
stop
|
393
|
+
|
394
|
+
raise SignalException, "SIGTERM"
|
355
395
|
end
|
356
396
|
end
|
357
397
|
end
|
@@ -413,10 +453,12 @@ module Puma
|
|
413
453
|
|
414
454
|
redirect_io
|
415
455
|
|
416
|
-
|
456
|
+
Plugins.fire_background
|
417
457
|
|
418
458
|
@launcher.write_state
|
419
459
|
|
460
|
+
start_control
|
461
|
+
|
420
462
|
@master_read, @worker_write = read, @wakeup
|
421
463
|
|
422
464
|
@launcher.config.run_hooks :before_fork, nil
|
@@ -443,7 +485,7 @@ module Puma
|
|
443
485
|
|
444
486
|
force_check = false
|
445
487
|
|
446
|
-
res = IO.select([read], nil, nil,
|
488
|
+
res = IO.select([read], nil, nil, WORKER_CHECK_INTERVAL)
|
447
489
|
|
448
490
|
if res
|
449
491
|
req = read.read_nonblock(1)
|
data/lib/puma/commonlogger.rb
CHANGED
@@ -21,6 +21,13 @@ module Puma
|
|
21
21
|
# %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
|
22
22
|
FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
|
23
23
|
|
24
|
+
HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
|
25
|
+
|
26
|
+
CONTENT_LENGTH = 'Content-Length'.freeze
|
27
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
28
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
29
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
30
|
+
|
24
31
|
def initialize(app, logger=nil)
|
25
32
|
@app = app
|
26
33
|
@logger = logger
|
@@ -42,36 +49,23 @@ module Puma
|
|
42
49
|
[status, header, body]
|
43
50
|
end
|
44
51
|
|
45
|
-
HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
|
46
|
-
|
47
52
|
private
|
48
53
|
|
49
54
|
def log_hijacking(env, status, header, began_at)
|
50
55
|
now = Time.now
|
51
56
|
|
52
|
-
|
53
|
-
logger.write HIJACK_FORMAT % [
|
57
|
+
msg = HIJACK_FORMAT % [
|
54
58
|
env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
|
55
59
|
env["REMOTE_USER"] || "-",
|
56
60
|
now.strftime("%d/%b/%Y %H:%M:%S"),
|
57
|
-
env[
|
58
|
-
env[
|
59
|
-
env[
|
61
|
+
env[REQUEST_METHOD],
|
62
|
+
env[PATH_INFO],
|
63
|
+
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
60
64
|
env["HTTP_VERSION"],
|
61
65
|
now - began_at ]
|
62
|
-
end
|
63
66
|
|
64
|
-
|
65
|
-
|
66
|
-
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
67
|
-
QUERY_STRING = 'QUERY_STRING'.freeze
|
68
|
-
CACHE_CONTROL = 'Cache-Control'.freeze
|
69
|
-
CONTENT_LENGTH = 'Content-Length'.freeze
|
70
|
-
CONTENT_TYPE = 'Content-Type'.freeze
|
71
|
-
|
72
|
-
GET = 'GET'.freeze
|
73
|
-
HEAD = 'HEAD'.freeze
|
74
|
-
|
67
|
+
write(msg)
|
68
|
+
end
|
75
69
|
|
76
70
|
def log(env, status, header, began_at)
|
77
71
|
now = Time.now
|
@@ -83,13 +77,18 @@ module Puma
|
|
83
77
|
now.strftime("%d/%b/%Y:%H:%M:%S %z"),
|
84
78
|
env[REQUEST_METHOD],
|
85
79
|
env[PATH_INFO],
|
86
|
-
env[QUERY_STRING].empty? ? "" : "
|
80
|
+
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
87
81
|
env["HTTP_VERSION"],
|
88
82
|
status.to_s[0..3],
|
89
83
|
length,
|
90
84
|
now - began_at ]
|
91
85
|
|
86
|
+
write(msg)
|
87
|
+
end
|
88
|
+
|
89
|
+
def write(msg)
|
92
90
|
logger = @logger || env['rack.errors']
|
91
|
+
|
93
92
|
# Standard library logger doesn't support write but it supports << which actually
|
94
93
|
# calls to write on the log device without formatting
|
95
94
|
if logger.respond_to?(:write)
|
data/lib/puma/compat.rb
CHANGED
@@ -6,13 +6,9 @@ class String
|
|
6
6
|
end
|
7
7
|
|
8
8
|
unless method_defined? :byteslice
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def byteslice(*arg)
|
13
|
-
enc = self.encoding
|
14
|
-
self.dup.force_encoding(Encoding::ASCII_8BIT).slice(*arg).force_encoding(enc)
|
15
|
-
end
|
9
|
+
def byteslice(*arg)
|
10
|
+
enc = self.encoding
|
11
|
+
self.dup.force_encoding(Encoding::ASCII_8BIT).slice(*arg).force_encoding(enc)
|
16
12
|
end
|
17
13
|
end
|
18
14
|
end
|
data/lib/puma/configuration.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'puma/rack/builder'
|
2
2
|
require 'puma/plugin'
|
3
|
+
require 'puma/const'
|
3
4
|
|
4
5
|
module Puma
|
5
6
|
|
@@ -12,147 +13,147 @@ module Puma
|
|
12
13
|
DefaultWorkerShutdownTimeout = 30
|
13
14
|
end
|
14
15
|
|
15
|
-
class
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
16
|
+
# A class used for storing "leveled" configuration options.
|
17
|
+
#
|
18
|
+
# In this class any "user" specified options take precedence over any
|
19
|
+
# "file" specified options, take precedence over any "default" options.
|
20
|
+
#
|
21
|
+
# User input is prefered over "defaults":
|
22
|
+
# user_options = { foo: "bar" }
|
23
|
+
# default_options = { foo: "zoo" }
|
24
|
+
# options = UserFileDefaultOptions.new(user_options, default_options)
|
25
|
+
# puts options[:foo]
|
26
|
+
# # => "bar"
|
27
|
+
#
|
28
|
+
# All values can be accessed via `all_of`
|
29
|
+
#
|
30
|
+
# puts options.all_of(:foo)
|
31
|
+
# # => ["bar", "zoo"]
|
32
|
+
#
|
33
|
+
# A "file" option can be set. This config will be prefered over "default" options
|
34
|
+
# but will defer to any available "user" specified options.
|
35
|
+
#
|
36
|
+
# user_options = { foo: "bar" }
|
37
|
+
# default_options = { rackup: "zoo.rb" }
|
38
|
+
# options = UserFileDefaultOptions.new(user_options, default_options)
|
39
|
+
# options.file_options[:rackup] = "sup.rb"
|
40
|
+
# puts options[:rackup]
|
41
|
+
# # => "sup.rb"
|
42
|
+
#
|
43
|
+
# The "default" options can be set via procs. These are resolved during runtime
|
44
|
+
# via calls to `finalize_values`
|
45
|
+
class UserFileDefaultOptions
|
46
|
+
def initialize(user_options, default_options)
|
47
|
+
@user_options = user_options
|
48
|
+
@file_options = {}
|
49
|
+
@default_options = default_options
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_reader :user_options, :file_options, :default_options
|
31
53
|
|
32
54
|
def [](key)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
v = @defaults[key]
|
40
|
-
if v.respond_to? :call
|
41
|
-
v.call
|
42
|
-
else
|
43
|
-
v
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def fetch(key, default=nil)
|
48
|
-
val = self[key]
|
49
|
-
return val if val
|
50
|
-
default
|
51
|
-
end
|
52
|
-
|
53
|
-
attr_reader :cur
|
54
|
-
|
55
|
-
def all_of(key)
|
56
|
-
all = []
|
57
|
-
|
58
|
-
@set.each do |o|
|
59
|
-
if v = o[key]
|
60
|
-
if v.kind_of? Array
|
61
|
-
all += v
|
62
|
-
else
|
63
|
-
all << v
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
all
|
55
|
+
return user_options[key] if user_options.key?(key)
|
56
|
+
return file_options[key] if file_options.key?(key)
|
57
|
+
return default_options[key] if default_options.key?(key)
|
69
58
|
end
|
70
59
|
|
71
|
-
def []=(key,
|
72
|
-
|
60
|
+
def []=(key, value)
|
61
|
+
user_options[key] = value
|
73
62
|
end
|
74
63
|
|
75
|
-
def
|
76
|
-
|
77
|
-
if o.key? key
|
78
|
-
return true
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
@default.key? key
|
64
|
+
def fetch(key, default_value = nil)
|
65
|
+
self[key] || default_value
|
83
66
|
end
|
84
67
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
def flatten
|
92
|
-
options = {}
|
93
|
-
|
94
|
-
@set.each do |o|
|
95
|
-
o.each do |k,v|
|
96
|
-
options[k] ||= v
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
options
|
101
|
-
end
|
68
|
+
def all_of(key)
|
69
|
+
user = user_options[key]
|
70
|
+
file = file_options[key]
|
71
|
+
default = default_options[key]
|
102
72
|
|
103
|
-
|
104
|
-
|
73
|
+
user = [user] unless user.is_a?(Array)
|
74
|
+
file = [file] unless file.is_a?(Array)
|
75
|
+
default = [default] unless default.is_a?(Array)
|
105
76
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
77
|
+
user.compact!
|
78
|
+
file.compact!
|
79
|
+
default.compact!
|
110
80
|
|
111
|
-
|
112
|
-
end
|
81
|
+
user + file + default
|
113
82
|
end
|
114
83
|
|
115
|
-
def
|
116
|
-
@
|
84
|
+
def finalize_values
|
85
|
+
@default_options.each do |k,v|
|
117
86
|
if v.respond_to? :call
|
118
|
-
@
|
87
|
+
@default_options[k] = v.call
|
119
88
|
end
|
120
89
|
end
|
121
90
|
end
|
122
91
|
end
|
123
92
|
|
93
|
+
# The main configuration class of Puma.
|
94
|
+
#
|
95
|
+
# It can be initialized with a set of "user" options and "default" options.
|
96
|
+
# Defaults will be merged with `Configuration.puma_default_options`.
|
97
|
+
#
|
98
|
+
# This class works together with 2 main other classes the `UserFileDefaultOptions`
|
99
|
+
# which stores configuration options in order so the precedence is that user
|
100
|
+
# set configuration wins over "file" based configuration wins over "default"
|
101
|
+
# configuration. These configurations are set via the `DSL` class. This
|
102
|
+
# class powers the Puma config file syntax and does double duty as a configuration
|
103
|
+
# DSL used by the `Puma::CLI` and Puma rack handler.
|
104
|
+
#
|
105
|
+
# It also handles loading plugins.
|
106
|
+
#
|
107
|
+
# > Note: `:port` and `:host` are not valid keys. By they time they make it to the
|
108
|
+
# configuration options they are expected to be incorporated into a `:binds` key.
|
109
|
+
# Under the hood the DSL maps `port` and `host` calls to `:binds`
|
110
|
+
#
|
111
|
+
# config = Configuration.new({}) do |user_config, file_config, default_config|
|
112
|
+
# user_config.port 3003
|
113
|
+
# end
|
114
|
+
# config.load
|
115
|
+
# puts config.options[:port]
|
116
|
+
# # => 3003
|
117
|
+
#
|
118
|
+
# It is expected that `load` is called on the configuration instance after setting
|
119
|
+
# config. This method expands any values in `config_file` and puts them into the
|
120
|
+
# correct configuration option hash.
|
121
|
+
#
|
122
|
+
# Once all configuration is complete it is expected that `clamp` will be called
|
123
|
+
# on the instance. This will expand any procs stored under "default" values. This
|
124
|
+
# is done because an environment variable may have been modified while loading
|
125
|
+
# configuration files.
|
124
126
|
class Configuration
|
125
127
|
include ConfigDefault
|
126
128
|
|
127
|
-
def
|
128
|
-
|
129
|
+
def initialize(user_options={}, default_options = {}, &block)
|
130
|
+
default_options = self.puma_default_options.merge(default_options)
|
129
131
|
|
130
|
-
|
132
|
+
@options = UserFileDefaultOptions.new(user_options, default_options)
|
133
|
+
@plugins = PluginLoader.new
|
134
|
+
@user_dsl = DSL.new(@options.user_options, self)
|
135
|
+
@file_dsl = DSL.new(@options.file_options, self)
|
136
|
+
@default_dsl = DSL.new(@options.default_options, self)
|
131
137
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
def initialize(options={}, &blk)
|
136
|
-
@options = LeveledOptions.new(default_options, options)
|
137
|
-
|
138
|
-
@plugins = PluginLoader.new
|
139
|
-
|
140
|
-
if blk
|
141
|
-
configure(&blk)
|
138
|
+
if block
|
139
|
+
configure(&block)
|
142
140
|
end
|
143
141
|
end
|
144
142
|
|
145
143
|
attr_reader :options, :plugins
|
146
144
|
|
147
|
-
def configure
|
148
|
-
@
|
149
|
-
|
145
|
+
def configure
|
146
|
+
yield @user_dsl, @file_dsl, @default_dsl
|
147
|
+
ensure
|
148
|
+
@user_dsl._offer_plugins
|
149
|
+
@file_dsl._offer_plugins
|
150
|
+
@default_dsl._offer_plugins
|
150
151
|
end
|
151
152
|
|
152
153
|
def initialize_copy(other)
|
153
|
-
@conf
|
154
|
+
@conf = nil
|
154
155
|
@cli_options = nil
|
155
|
-
@options
|
156
|
+
@options = @options.dup
|
156
157
|
end
|
157
158
|
|
158
159
|
def flatten
|
@@ -164,7 +165,7 @@ module Puma
|
|
164
165
|
self
|
165
166
|
end
|
166
167
|
|
167
|
-
def
|
168
|
+
def puma_default_options
|
168
169
|
{
|
169
170
|
:min_threads => 0,
|
170
171
|
:max_threads => 16,
|
@@ -179,37 +180,37 @@ module Puma
|
|
179
180
|
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
180
181
|
:remote_address => :socket,
|
181
182
|
:tag => method(:infer_tag),
|
182
|
-
:environment =>
|
183
|
+
:environment => -> { ENV['RACK_ENV'] || "development" },
|
183
184
|
:rackup => DefaultRackup,
|
184
|
-
:logger => STDOUT
|
185
|
+
:logger => STDOUT,
|
186
|
+
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
187
|
+
:first_data_timeout => Const::FIRST_DATA_TIMEOUT
|
185
188
|
}
|
186
189
|
end
|
187
190
|
|
188
191
|
def load
|
189
|
-
|
192
|
+
config_files.each { |config_file| @file_dsl._load_from(config_file) }
|
190
193
|
|
191
|
-
|
192
|
-
|
193
|
-
File.exist?(f)
|
194
|
-
}
|
194
|
+
@options
|
195
|
+
end
|
195
196
|
|
196
|
-
|
197
|
-
|
198
|
-
files = []
|
199
|
-
end
|
197
|
+
def config_files
|
198
|
+
files = @options.all_of(:config_files)
|
200
199
|
|
201
|
-
files
|
202
|
-
|
200
|
+
return [] if files == ['-']
|
201
|
+
return files if files.any?
|
203
202
|
|
204
|
-
|
203
|
+
first_default_file = %W(config/puma/#{environment_str}.rb config/puma.rb).find do |f|
|
204
|
+
File.exist?(f)
|
205
205
|
end
|
206
|
+
|
207
|
+
[first_default_file]
|
206
208
|
end
|
207
209
|
|
208
210
|
# Call once all configuration (included from rackup files)
|
209
211
|
# is loaded to flesh out any defaults
|
210
212
|
def clamp
|
211
|
-
@options.
|
212
|
-
@options.force_defaults
|
213
|
+
@options.finalize_values
|
213
214
|
end
|
214
215
|
|
215
216
|
# Injects the Configuration object into the env
|
@@ -245,10 +246,12 @@ module Puma
|
|
245
246
|
require 'puma/tcp_logger'
|
246
247
|
|
247
248
|
logger = @options[:logger]
|
248
|
-
|
249
|
+
quiet = !@options[:log_requests]
|
250
|
+
return TCPLogger.new(logger, found, quiet)
|
249
251
|
end
|
250
252
|
|
251
253
|
if @options[:log_requests]
|
254
|
+
require 'puma/commonlogger'
|
252
255
|
logger = @options[:logger]
|
253
256
|
found = CommonLogger.new(found, logger)
|
254
257
|
end
|
@@ -261,6 +264,10 @@ module Puma
|
|
261
264
|
@options[:environment]
|
262
265
|
end
|
263
266
|
|
267
|
+
def environment_str
|
268
|
+
environment.respond_to?(:call) ? environment.call : environment
|
269
|
+
end
|
270
|
+
|
264
271
|
def load_plugin(name)
|
265
272
|
@plugins.create name
|
266
273
|
end
|
@@ -308,17 +315,15 @@ module Puma
|
|
308
315
|
def load_rackup
|
309
316
|
raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
|
310
317
|
|
311
|
-
@options.shift
|
312
|
-
|
313
318
|
rack_app, rack_options = rack_builder.parse_file(rackup)
|
314
|
-
@options.merge!(rack_options)
|
319
|
+
@options.file_options.merge!(rack_options)
|
315
320
|
|
316
321
|
config_ru_binds = []
|
317
322
|
rack_options.each do |k, v|
|
318
323
|
config_ru_binds << v if k.to_s.start_with?("bind")
|
319
324
|
end
|
320
325
|
|
321
|
-
@options[:binds] = config_ru_binds unless config_ru_binds.empty?
|
326
|
+
@options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
|
322
327
|
|
323
328
|
rack_app
|
324
329
|
end
|
@@ -340,7 +345,7 @@ module Puma
|
|
340
345
|
end
|
341
346
|
|
342
347
|
if bytes
|
343
|
-
token = ""
|
348
|
+
token = "".dup
|
344
349
|
bytes.each_byte { |b| token << b.to_s(16) }
|
345
350
|
else
|
346
351
|
token = (0..count).to_a.map { rand(255).to_s(16) }.join
|