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.

Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/{History.txt → History.md} +356 -74
  3. data/README.md +143 -227
  4. data/docs/architecture.md +36 -0
  5. data/{DEPLOYMENT.md → docs/deployment.md} +1 -1
  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 +291 -447
  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/http11_parser_common.rl +1 -1
  18. data/ext/puma_http11/io_buffer.c +7 -7
  19. data/ext/puma_http11/mini_ssl.c +67 -6
  20. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +76 -94
  21. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -2
  22. data/ext/puma_http11/puma_http11.c +1 -0
  23. data/lib/puma.rb +13 -5
  24. data/lib/puma/app/status.rb +8 -0
  25. data/lib/puma/binder.rb +46 -21
  26. data/lib/puma/cli.rb +49 -33
  27. data/lib/puma/client.rb +149 -4
  28. data/lib/puma/cluster.rb +55 -13
  29. data/lib/puma/commonlogger.rb +19 -20
  30. data/lib/puma/compat.rb +3 -7
  31. data/lib/puma/configuration.rb +136 -131
  32. data/lib/puma/const.rb +19 -37
  33. data/lib/puma/control_cli.rb +38 -35
  34. data/lib/puma/convenient.rb +3 -3
  35. data/lib/puma/detect.rb +3 -1
  36. data/lib/puma/dsl.rb +86 -57
  37. data/lib/puma/events.rb +17 -13
  38. data/lib/puma/io_buffer.rb +1 -1
  39. data/lib/puma/jruby_restart.rb +0 -1
  40. data/lib/puma/launcher.rb +61 -30
  41. data/lib/puma/minissl.rb +85 -4
  42. data/lib/puma/null_io.rb +6 -13
  43. data/lib/puma/plugin.rb +12 -1
  44. data/lib/puma/plugin/tmp_restart.rb +1 -2
  45. data/lib/puma/rack/builder.rb +3 -0
  46. data/lib/puma/rack/urlmap.rb +9 -8
  47. data/lib/puma/reactor.rb +144 -0
  48. data/lib/puma/runner.rb +27 -1
  49. data/lib/puma/server.rb +135 -33
  50. data/lib/puma/single.rb +17 -3
  51. data/lib/puma/tcp_logger.rb +8 -1
  52. data/lib/puma/thread_pool.rb +70 -20
  53. data/lib/puma/util.rb +1 -5
  54. data/lib/rack/handler/puma.rb +58 -17
  55. data/tools/jungle/README.md +12 -2
  56. data/tools/jungle/init.d/README.md +9 -2
  57. data/tools/jungle/init.d/puma +85 -58
  58. data/tools/jungle/init.d/run-puma +16 -1
  59. data/tools/jungle/rc.d/README.md +74 -0
  60. data/tools/jungle/rc.d/puma +61 -0
  61. data/tools/jungle/rc.d/puma.conf +10 -0
  62. data/tools/jungle/upstart/puma.conf +1 -1
  63. data/tools/trickletest.rb +1 -1
  64. metadata +22 -94
  65. data/Gemfile +0 -13
  66. data/Manifest.txt +0 -78
  67. data/Rakefile +0 -158
  68. data/docs/config.md +0 -0
  69. data/lib/puma/rack/backports/uri/common_18.rb +0 -59
  70. data/lib/puma/rack/backports/uri/common_192.rb +0 -55
  71. 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
 
@@ -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 lambad scope
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
- 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,37 +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
- :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
- files = @options.all_of(:config_files)
192
+ config_files.each { |config_file| @file_dsl._load_from(config_file) }
190
193
 
191
- if files.empty?
192
- imp = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find { |f|
193
- File.exist?(f)
194
- }
194
+ @options
195
+ end
195
196
 
196
- files << imp
197
- elsif files == ["-"]
198
- files = []
199
- end
197
+ def config_files
198
+ files = @options.all_of(:config_files)
200
199
 
201
- files.each do |f|
202
- @options.shift
200
+ return [] if files == ['-']
201
+ return files if files.any?
203
202
 
204
- 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)
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.shift
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
- return TCPLogger.new(logger, found, @options[:log_requests])
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