puma 3.5.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.

Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/{History.txt → History.md} +318 -75
  3. data/README.md +143 -227
  4. data/docs/architecture.md +36 -0
  5. data/{DEPLOYMENT.md → docs/deployment.md} +0 -0
  6. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  7. data/docs/images/puma-connection-flow.png +0 -0
  8. data/docs/images/puma-general-arch.png +0 -0
  9. data/docs/plugins.md +28 -0
  10. data/docs/restart.md +39 -0
  11. data/docs/signals.md +56 -3
  12. data/docs/systemd.md +124 -22
  13. data/ext/puma_http11/extconf.rb +2 -0
  14. data/ext/puma_http11/http11_parser.c +85 -84
  15. data/ext/puma_http11/http11_parser.h +1 -0
  16. data/ext/puma_http11/http11_parser.rl +10 -9
  17. data/ext/puma_http11/io_buffer.c +7 -7
  18. data/ext/puma_http11/mini_ssl.c +67 -6
  19. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +13 -16
  20. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -2
  21. data/ext/puma_http11/puma_http11.c +1 -0
  22. data/lib/puma.rb +13 -5
  23. data/lib/puma/app/status.rb +8 -0
  24. data/lib/puma/binder.rb +22 -17
  25. data/lib/puma/cli.rb +49 -33
  26. data/lib/puma/client.rb +149 -4
  27. data/lib/puma/cluster.rb +54 -12
  28. data/lib/puma/commonlogger.rb +19 -20
  29. data/lib/puma/compat.rb +3 -7
  30. data/lib/puma/configuration.rb +133 -130
  31. data/lib/puma/const.rb +19 -37
  32. data/lib/puma/control_cli.rb +38 -35
  33. data/lib/puma/convenient.rb +3 -3
  34. data/lib/puma/detect.rb +3 -1
  35. data/lib/puma/dsl.rb +80 -58
  36. data/lib/puma/events.rb +6 -8
  37. data/lib/puma/io_buffer.rb +1 -1
  38. data/lib/puma/jruby_restart.rb +0 -1
  39. data/lib/puma/launcher.rb +61 -30
  40. data/lib/puma/minissl.rb +85 -4
  41. data/lib/puma/null_io.rb +6 -13
  42. data/lib/puma/plugin.rb +12 -1
  43. data/lib/puma/plugin/tmp_restart.rb +1 -2
  44. data/lib/puma/rack/builder.rb +3 -0
  45. data/lib/puma/rack/urlmap.rb +9 -8
  46. data/lib/puma/reactor.rb +135 -0
  47. data/lib/puma/runner.rb +27 -1
  48. data/lib/puma/server.rb +134 -32
  49. data/lib/puma/single.rb +17 -3
  50. data/lib/puma/thread_pool.rb +67 -20
  51. data/lib/puma/util.rb +1 -5
  52. data/lib/rack/handler/puma.rb +58 -17
  53. data/tools/jungle/README.md +12 -2
  54. data/tools/jungle/init.d/README.md +9 -2
  55. data/tools/jungle/init.d/puma +32 -62
  56. data/tools/jungle/init.d/run-puma +5 -1
  57. data/tools/jungle/rc.d/README.md +74 -0
  58. data/tools/jungle/rc.d/puma +61 -0
  59. data/tools/jungle/rc.d/puma.conf +10 -0
  60. data/tools/trickletest.rb +1 -1
  61. metadata +22 -92
  62. data/Gemfile +0 -13
  63. data/Manifest.txt +0 -77
  64. data/Rakefile +0 -158
  65. data/lib/puma/rack/backports/uri/common_18.rb +0 -59
  66. data/lib/puma/rack/backports/uri/common_192.rb +0 -55
  67. data/puma.gemspec +0 -52
@@ -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.waitall
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 + 5
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 = "puma: cluster worker #{index}: #{master}"
210
- title << " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
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 5
291
+ sleep WORKER_CHECK_INTERVAL
257
292
  begin
258
- b = server.backlog
259
- r = server.running
260
- payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r} }\n!
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
 
@@ -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
- start_control
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, 5)
488
+ res = IO.select([read], nil, nil, WORKER_CHECK_INTERVAL)
447
489
 
448
490
  if res
449
491
  req = read.read_nonblock(1)
@@ -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
- logger = @logger || env['rack.errors']
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["REQUEST_METHOD"],
58
- env["PATH_INFO"],
59
- env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
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
- PATH_INFO = 'PATH_INFO'.freeze
65
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
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? ? "" : "?"+env[QUERY_STRING],
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)
@@ -6,13 +6,9 @@ class String
6
6
  end
7
7
 
8
8
  unless method_defined? :byteslice
9
- if RUBY_VERSION < '1.9'
10
- alias_method :byteslice, :[]
11
- else
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
@@ -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 LeveledOptions
16
- def initialize(default_options, user_options)
17
- @cur = user_options
18
- @set = [@cur]
19
- @defaults = default_options.dup
20
- end
21
-
22
- def initialize_copy(other)
23
- @set = @set.map { |o| o.dup }
24
- @cur = @set.last
25
- end
26
-
27
- def shift
28
- @cur = {}
29
- @set << @cur
30
- end
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
- @set.each do |o|
34
- if o.key? key
35
- return o[key]
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, val)
72
- @cur[key] = val
60
+ def []=(key, value)
61
+ user_options[key] = value
73
62
  end
74
63
 
75
- def key?(key)
76
- @set.each do |o|
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 merge!(o)
86
- o.each do |k,v|
87
- @cur[k]= v
88
- end
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
- def explain
104
- indent = ""
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
- @set.each do |o|
107
- o.keys.sort.each do |k|
108
- puts "#{indent}#{k}: #{o[k].inspect}"
109
- end
77
+ user.compact!
78
+ file.compact!
79
+ default.compact!
110
80
 
111
- indent = " #{indent}"
112
- end
81
+ user + file + default
113
82
  end
114
83
 
115
- def force_defaults
116
- @defaults.each do |k,v|
84
+ def finalize_values
85
+ @default_options.each do |k,v|
117
86
  if v.respond_to? :call
118
- @defaults[k] = v.call
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 self.from_file(path)
128
- cfg = new
129
+ def initialize(user_options={}, default_options = {}, &block)
130
+ default_options = self.puma_default_options.merge(default_options)
129
131
 
130
- DSL.new(cfg.options, cfg)._load_from path
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
- return cfg
133
- end
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(&blk)
148
- @options.shift
149
- DSL.new(@options, self)._run(&blk)
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 = nil
154
+ @conf = nil
154
155
  @cli_options = nil
155
- @options = @options.dup
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 default_options
168
+ def puma_default_options
168
169
  {
169
170
  :min_threads => 0,
170
171
  :max_threads => 16,
@@ -179,38 +180,37 @@ module Puma
179
180
  :worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
180
181
  :remote_address => :socket,
181
182
  :tag => method(:infer_tag),
182
- :environment => lambda { ENV['RACK_ENV'] || "development" },
183
+ :environment => -> { ENV['RACK_ENV'] || "development" },
183
184
  :rackup => DefaultRackup,
184
185
  :logger => STDOUT,
185
- :persistent_timeout => Const::PERSISTENT_TIMEOUT
186
+ :persistent_timeout => Const::PERSISTENT_TIMEOUT,
187
+ :first_data_timeout => Const::FIRST_DATA_TIMEOUT
186
188
  }
187
189
  end
188
190
 
189
191
  def load
190
- files = @options.all_of(:config_files)
192
+ config_files.each { |config_file| @file_dsl._load_from(config_file) }
191
193
 
192
- if files.empty?
193
- imp = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find { |f|
194
- File.exist?(f)
195
- }
194
+ @options
195
+ end
196
196
 
197
- files << imp
198
- elsif files == ["-"]
199
- files = []
200
- end
197
+ def config_files
198
+ files = @options.all_of(:config_files)
201
199
 
202
- files.each do |f|
203
- @options.shift
200
+ return [] if files == ['-']
201
+ return files if files.any?
204
202
 
205
- DSL.load @options, self, f
203
+ first_default_file = %W(config/puma/#{environment_str}.rb config/puma.rb).find do |f|
204
+ File.exist?(f)
206
205
  end
206
+
207
+ [first_default_file]
207
208
  end
208
209
 
209
210
  # Call once all configuration (included from rackup files)
210
211
  # is loaded to flesh out any defaults
211
212
  def clamp
212
- @options.shift
213
- @options.force_defaults
213
+ @options.finalize_values
214
214
  end
215
215
 
216
216
  # Injects the Configuration object into the env
@@ -251,6 +251,7 @@ module Puma
251
251
  end
252
252
 
253
253
  if @options[:log_requests]
254
+ require 'puma/commonlogger'
254
255
  logger = @options[:logger]
255
256
  found = CommonLogger.new(found, logger)
256
257
  end
@@ -263,6 +264,10 @@ module Puma
263
264
  @options[:environment]
264
265
  end
265
266
 
267
+ def environment_str
268
+ environment.respond_to?(:call) ? environment.call : environment
269
+ end
270
+
266
271
  def load_plugin(name)
267
272
  @plugins.create name
268
273
  end
@@ -310,17 +315,15 @@ module Puma
310
315
  def load_rackup
311
316
  raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
312
317
 
313
- @options.shift
314
-
315
318
  rack_app, rack_options = rack_builder.parse_file(rackup)
316
- @options.merge!(rack_options)
319
+ @options.file_options.merge!(rack_options)
317
320
 
318
321
  config_ru_binds = []
319
322
  rack_options.each do |k, v|
320
323
  config_ru_binds << v if k.to_s.start_with?("bind")
321
324
  end
322
325
 
323
- @options[:binds] = config_ru_binds unless config_ru_binds.empty?
326
+ @options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
324
327
 
325
328
  rack_app
326
329
  end
@@ -342,7 +345,7 @@ module Puma
342
345
  end
343
346
 
344
347
  if bytes
345
- token = ""
348
+ token = "".dup
346
349
  bytes.each_byte { |b| token << b.to_s(16) }
347
350
  else
348
351
  token = (0..count).to_a.map { rand(255).to_s(16) }.join