puma 2.16.0-java → 3.0.0-java

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.

data/lib/puma/client.rb CHANGED
@@ -7,6 +7,7 @@ class IO
7
7
  end
8
8
 
9
9
  require 'puma/detect'
10
+ require 'puma/delegation'
10
11
 
11
12
  if Puma::IS_JRUBY
12
13
  # We have to work around some OpenSSL buffer/io-readiness bugs
@@ -21,6 +22,7 @@ module Puma
21
22
 
22
23
  class Client
23
24
  include Puma::Const
25
+ extend Puma::Delegation
24
26
 
25
27
  def initialize(io, env=nil)
26
28
  @io = io
@@ -57,6 +59,8 @@ module Puma
57
59
 
58
60
  attr_accessor :remote_addr_header
59
61
 
62
+ forward :closed?, :@io
63
+
60
64
  def inspect
61
65
  "#<Puma::Client:0x#{object_id.to_s(16)} @ready=#{@ready.inspect}>"
62
66
  end
data/lib/puma/cluster.rb CHANGED
@@ -2,8 +2,8 @@ require 'puma/runner'
2
2
 
3
3
  module Puma
4
4
  class Cluster < Runner
5
- def initialize(cli)
6
- super cli
5
+ def initialize(cli, events)
6
+ super cli, events
7
7
 
8
8
  @phase = 0
9
9
  @workers = []
@@ -52,9 +52,11 @@ module Puma
52
52
  @options = options
53
53
  @first_term_sent = nil
54
54
  @last_checkin = Time.now
55
+ @last_status = '{}'
56
+ @dead = false
55
57
  end
56
58
 
57
- attr_reader :index, :pid, :phase, :signal, :last_checkin
59
+ attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status
58
60
 
59
61
  def booted?
60
62
  @stage == :booted
@@ -73,8 +75,9 @@ module Puma
73
75
  @dead = true
74
76
  end
75
77
 
76
- def ping!
78
+ def ping!(status)
77
79
  @last_checkin = Time.now
80
+ @last_status = status
78
81
  end
79
82
 
80
83
  def ping_timeout?(which)
@@ -83,10 +86,10 @@ module Puma
83
86
 
84
87
  def term
85
88
  begin
86
- if @first_term_sent && (Time.new - @first_term_sent) > @options[:worker_shutdown_timeout]
89
+ if @first_term_sent && (Time.now - @first_term_sent) > @options[:worker_shutdown_timeout]
87
90
  @signal = "KILL"
88
91
  else
89
- @first_term_sent ||= Time.new
92
+ @first_term_sent ||= Time.now
90
93
  end
91
94
 
92
95
  Process.kill @signal, @pid
@@ -112,12 +115,13 @@ module Puma
112
115
 
113
116
  diff.times do
114
117
  idx = next_worker_index
115
- @options[:before_worker_fork].each { |h| h.call(idx) }
118
+ @launcher.config.run_hooks :before_worker_fork, idx
116
119
 
117
120
  pid = fork { worker(idx, master) }
118
- @cli.debug "Spawned worker: #{pid}"
121
+ debug "Spawned worker: #{pid}"
119
122
  @workers << Worker.new(idx, pid, @phase, @options)
120
- @options[:after_worker_boot].each { |h| h.call }
123
+
124
+ @launcher.config.run_hooks :after_worker_fork, idx
121
125
  end
122
126
 
123
127
  if diff > 0
@@ -220,8 +224,7 @@ module Puma
220
224
 
221
225
  # Invoke any worker boot hooks so they can get
222
226
  # things in shape before booting the app.
223
- hooks = @options[:before_worker_boot]
224
- hooks.each { |h| h.call(index) }
227
+ @launcher.config.run_hooks :before_worker_boot, index
225
228
 
226
229
  server = start_server
227
230
 
@@ -237,11 +240,18 @@ module Puma
237
240
  end
238
241
 
239
242
  Thread.new(@worker_write) do |io|
240
- payload = "p#{Process.pid}\n"
243
+ base_payload = "p#{Process.pid}"
241
244
 
242
245
  while true
243
246
  sleep 5
244
- io << payload
247
+ begin
248
+ b = server.backlog
249
+ r = server.running
250
+ payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r} }\n!
251
+ io << payload
252
+ rescue IOError
253
+ break
254
+ end
245
255
  end
246
256
  end
247
257
 
@@ -249,8 +259,7 @@ module Puma
249
259
 
250
260
  # Invoke any worker shutdown hooks so they can prevent the worker
251
261
  # exiting until any background operations are completed
252
- hooks = @options[:before_worker_shutdown]
253
- hooks.each { |h| h.call(index) }
262
+ @launcher.config.run_hooks :before_worker_shutdown, index
254
263
  ensure
255
264
  @worker_write << "t#{Process.pid}\n" rescue nil
256
265
  @worker_write.close
@@ -297,13 +306,47 @@ module Puma
297
306
  def stats
298
307
  old_worker_count = @workers.count { |w| w.phase != @phase }
299
308
  booted_worker_count = @workers.count { |w| w.booted? }
300
- %Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count} }!
309
+ 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(",") + ']'
310
+ %Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }!
301
311
  end
302
312
 
303
313
  def preload?
304
314
  @options[:preload_app]
305
315
  end
306
316
 
317
+ # We do this in a separate method to keep the lambad scope
318
+ # of the signals handlers as small as possible.
319
+ def setup_signals
320
+ Signal.trap "SIGCHLD" do
321
+ wakeup!
322
+ end
323
+
324
+ Signal.trap "TTIN" do
325
+ @options[:workers] += 1
326
+ wakeup!
327
+ end
328
+
329
+ Signal.trap "TTOU" do
330
+ @options[:workers] -= 1 if @options[:workers] >= 2
331
+ @workers.last.term
332
+ wakeup!
333
+ end
334
+
335
+ master_pid = Process.pid
336
+
337
+ Signal.trap "SIGTERM" do
338
+ # The worker installs their own SIGTERM when booted.
339
+ # Until then, this is run by the worker and the worker
340
+ # should just exit if they get it.
341
+ if Process.pid != master_pid
342
+ log "Early termination of worker"
343
+ exit! 0
344
+ else
345
+ stop
346
+ end
347
+ end
348
+ end
349
+
307
350
  def run
308
351
  @status = :run
309
352
 
@@ -333,44 +376,17 @@ module Puma
333
376
  else
334
377
  log "* Phased restart available"
335
378
 
336
- unless @cli.config.app_configured?
379
+ unless @launcher.config.app_configured?
337
380
  error "No application configured, nothing to run"
338
381
  exit 1
339
382
  end
340
383
 
341
- @cli.binder.parse @options[:binds], self
384
+ @launcher.binder.parse @options[:binds], self
342
385
  end
343
386
 
344
387
  read, @wakeup = Puma::Util.pipe
345
388
 
346
- Signal.trap "SIGCHLD" do
347
- wakeup!
348
- end
349
-
350
- Signal.trap "TTIN" do
351
- @options[:workers] += 1
352
- wakeup!
353
- end
354
-
355
- Signal.trap "TTOU" do
356
- @options[:workers] -= 1 if @options[:workers] >= 2
357
- @workers.last.term
358
- wakeup!
359
- end
360
-
361
- master_pid = Process.pid
362
-
363
- Signal.trap "SIGTERM" do
364
- # The worker installs their own SIGTERM when booted.
365
- # Until then, this is run by the worker and the worker
366
- # should just exit if they get it.
367
- if Process.pid != master_pid
368
- log "Early termination of worker"
369
- exit! 0
370
- else
371
- stop
372
- end
373
- end
389
+ setup_signals
374
390
 
375
391
  # Used by the workers to detect if the master process dies.
376
392
  # If select says that @check_pipe is ready, it's because the
@@ -390,12 +406,11 @@ module Puma
390
406
 
391
407
  start_control
392
408
 
393
- @cli.write_state
409
+ @launcher.write_state
394
410
 
395
411
  @master_read, @worker_write = read, @wakeup
396
412
 
397
- hooks = @options[:before_fork]
398
- hooks.each { |h| h.call }
413
+ @launcher.config.run_hooks :before_fork, nil
399
414
 
400
415
  spawn_workers
401
416
 
@@ -403,7 +418,7 @@ module Puma
403
418
  stop
404
419
  end
405
420
 
406
- @cli.events.fire_on_booted!
421
+ @launcher.events.fire_on_booted!
407
422
 
408
423
  begin
409
424
  while @status == :run
@@ -417,7 +432,8 @@ module Puma
417
432
 
418
433
  next if !req || req == "!"
419
434
 
420
- pid = read.gets.to_i
435
+ result = read.gets
436
+ pid = result.to_i
421
437
 
422
438
  if w = @workers.find { |x| x.pid == pid }
423
439
  case req
@@ -429,7 +445,7 @@ module Puma
429
445
  w.dead!
430
446
  force_check = true
431
447
  when "p"
432
- w.ping!
448
+ w.ping!(result.sub(/^\d+/,'').chomp)
433
449
  end
434
450
  else
435
451
  log "! Out-of-sync worker list, no #{pid} worker"
@@ -1,4 +1,5 @@
1
1
  require 'puma/rack/builder'
2
+ require 'puma/plugin'
2
3
 
3
4
  module Puma
4
5
 
@@ -11,30 +12,139 @@ module Puma
11
12
  DefaultWorkerShutdownTimeout = 30
12
13
  end
13
14
 
15
+ class LeveledOptions
16
+ def initialize(default={})
17
+ @cur = {}
18
+ @set = [@cur]
19
+ @defaults = default.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
31
+
32
+ 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
+ attr_reader :cur
48
+
49
+ def all_of(key)
50
+ all = []
51
+
52
+ @set.each do |o|
53
+ if v = o[key]
54
+ if v.kind_of? Array
55
+ all += v
56
+ else
57
+ all << v
58
+ end
59
+ end
60
+ end
61
+
62
+ all
63
+ end
64
+
65
+ def []=(key, val)
66
+ @cur[key] = val
67
+ end
68
+
69
+ def key?(key)
70
+ @set.each do |o|
71
+ if o.key? key
72
+ return true
73
+ end
74
+ end
75
+
76
+ @default.key? key
77
+ end
78
+
79
+ def merge!(o)
80
+ o.each do |k,v|
81
+ @cur[k]= v
82
+ end
83
+ end
84
+
85
+ def flatten
86
+ options = {}
87
+
88
+ @set.each do |o|
89
+ o.each do |k,v|
90
+ options[k] ||= v
91
+ end
92
+ end
93
+
94
+ options
95
+ end
96
+
97
+ def explain
98
+ indent = ""
99
+
100
+ @set.each do |o|
101
+ o.keys.sort.each do |k|
102
+ puts "#{indent}#{k}: #{o[k].inspect}"
103
+ end
104
+
105
+ indent = " #{indent}"
106
+ end
107
+ end
108
+
109
+ def force_defaults
110
+ @defaults.each do |k,v|
111
+ if v.respond_to? :call
112
+ @defaults[k] = v.call
113
+ end
114
+ end
115
+ end
116
+ end
117
+
14
118
  class Configuration
15
119
  include ConfigDefault
16
120
 
17
- def initialize(options)
18
- @cli_options = options
121
+ def self.from_file(path)
122
+ cfg = new
19
123
 
20
- @conf = {}
21
- @conf[:mode] ||= :http
22
- @conf[:binds] ||= []
23
- @conf[:on_restart] ||= []
24
- @conf[:before_fork] ||= []
25
- @conf[:before_worker_shutdown] ||= []
26
- @conf[:before_worker_boot] ||= []
27
- @conf[:before_worker_fork] ||= []
28
- @conf[:after_worker_boot] ||= []
29
- @conf[:worker_timeout] ||= DefaultWorkerTimeout
30
- @conf[:worker_boot_timeout] ||= @conf[:worker_timeout]
31
- @conf[:worker_shutdown_timeout] ||= DefaultWorkerShutdownTimeout
32
- @conf[:remote_address] ||= :socket
124
+ DSL.new(cfg.options, cfg)._load_from path
33
125
 
34
- @options = {}
126
+ return cfg
127
+ end
128
+
129
+ def initialize(options={}, &blk)
130
+ @options = LeveledOptions.new(default_options)
131
+ @plugins = PluginLoader.new
132
+
133
+ # options.each do |k,v|
134
+ # @options[k] = v
135
+ # end
136
+
137
+ if blk
138
+ configure(&blk)
139
+ end
35
140
  end
36
141
 
37
- attr_reader :options
142
+ attr_reader :options, :plugins
143
+
144
+ def configure(&blk)
145
+ @options.shift
146
+ DSL.new(@options, self)._run(&blk)
147
+ end
38
148
 
39
149
  def initialize_copy(other)
40
150
  @conf = nil
@@ -42,31 +152,61 @@ module Puma
42
152
  @options = @options.dup
43
153
  end
44
154
 
155
+ def flatten
156
+ dup.flatten!
157
+ end
158
+
159
+ def flatten!
160
+ @options = @options.flatten
161
+ self
162
+ end
163
+
45
164
  def default_options
46
165
  {
47
166
  :min_threads => 0,
48
167
  :max_threads => 16,
49
- :quiet => false,
168
+ :log_requests => false,
50
169
  :debug => false,
51
- :binds => [],
170
+ :binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
52
171
  :workers => 0,
53
172
  :daemon => false,
173
+ :mode => :http,
174
+ :worker_timeout => DefaultWorkerTimeout,
175
+ :worker_boot_timeout => DefaultWorkerTimeout,
176
+ :worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
177
+ :remote_address => :socket,
178
+ :tag => method(:infer_tag),
179
+ :environment => lambda { ENV['RACK_ENV'] || "development" },
180
+ :rackup => DefaultRackup,
181
+ :logger => STDOUT
54
182
  }
55
183
  end
56
184
 
57
185
  def load
58
- @conf.merge! @cli_options
59
- DSL.load(@conf, @cli_options[:config_file])
186
+ files = @options.all_of(:config_files)
187
+
188
+ if files.empty?
189
+ imp = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find { |f|
190
+ File.exist?(f)
191
+ }
60
192
 
61
- # Load the options in the right priority
62
- #
63
- @options.merge! default_options
64
- @options.merge! @conf
65
- @options.merge! @cli_options
193
+ files << imp
194
+ elsif files == ["-"]
195
+ files = []
196
+ end
66
197
 
67
- setup_binds
68
- setup_control
69
- @options[:tag] ||= infer_tag
198
+ files.each do |f|
199
+ @options.shift
200
+
201
+ DSL.load @options, self, f
202
+ end
203
+ end
204
+
205
+ # Call once all configuration (included from rackup files)
206
+ # is loaded to flesh out any defaults
207
+ def clamp
208
+ @options.shift
209
+ @options.force_defaults
70
210
  end
71
211
 
72
212
  # Injects the Configuration object into the env
@@ -89,7 +229,7 @@ module Puma
89
229
  end
90
230
 
91
231
  def rackup
92
- @options[:rackup] || DefaultRackup
232
+ @options[:rackup]
93
233
  end
94
234
 
95
235
  # Load the specified rackup file, pull options from
@@ -101,18 +241,31 @@ module Puma
101
241
  if @options[:mode] == :tcp
102
242
  require 'puma/tcp_logger'
103
243
 
104
- logger = @options[:logger] || STDOUT
105
- return TCPLogger.new(logger, found, @options[:quiet])
244
+ logger = @options[:logger]
245
+ return TCPLogger.new(logger, found, @options[:log_requests])
106
246
  end
107
247
 
108
- if !@options[:quiet] and @options[:environment] == "development"
109
- logger = @options[:logger] || STDOUT
248
+ if @options[:log_requests]
249
+ logger = @options[:logger]
110
250
  found = CommonLogger.new(found, logger)
111
251
  end
112
252
 
113
253
  ConfigMiddleware.new(self, found)
114
254
  end
115
255
 
256
+ # Return which environment we're running in
257
+ def environment
258
+ @options[:environment]
259
+ end
260
+
261
+ def load_plugin(name)
262
+ @plugins.create name
263
+ end
264
+
265
+ def run_hooks(key, arg)
266
+ @options.all_of(key).each { |b| b.call arg }
267
+ end
268
+
116
269
  def self.temp_path
117
270
  require 'tmpdir'
118
271
 
@@ -152,6 +305,8 @@ module Puma
152
305
  def load_rackup
153
306
  raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
154
307
 
308
+ @options.shift
309
+
155
310
  rack_app, rack_options = rack_builder.parse_file(rackup)
156
311
  @options.merge!(rack_options)
157
312
 
@@ -159,37 +314,13 @@ module Puma
159
314
  rack_options.each do |k, v|
160
315
  config_ru_binds << v if k.to_s.start_with?("bind")
161
316
  end
317
+
162
318
  @options[:binds] = config_ru_binds unless config_ru_binds.empty?
163
319
 
164
320
  rack_app
165
321
  end
166
322
 
167
- def setup_binds
168
- # Rakeup default option support
169
- host = @options[:Host]
170
- if host
171
- port = @options[:Port] || DefaultTCPPort
172
- @options[:binds] << "tcp://#{host}:#{port}"
173
- end
174
-
175
- if @options[:binds].empty?
176
- @options[:binds] << "tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"
177
- end
178
-
179
- @options[:binds].uniq!
180
- end
181
-
182
- def setup_control
183
- if @options[:control_url] == 'auto'
184
- path = Configuration.temp_path
185
- @options[:control_url] = "unix://#{path}"
186
- @options[:control_url_temp] = path
187
- end
188
-
189
- setup_random_token unless @options[:control_auth_token]
190
- end
191
-
192
- def setup_random_token
323
+ def self.random_token
193
324
  begin
194
325
  require 'openssl'
195
326
  rescue LoadError
@@ -212,7 +343,7 @@ module Puma
212
343
  token = (0..count).to_a.map { rand(255).to_s(16) }.join
213
344
  end
214
345
 
215
- @options[:control_auth_token] = token
346
+ return token
216
347
  end
217
348
  end
218
349
  end