puma 4.1.1 → 4.2.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 +4 -4
- data/History.md +31 -7
- data/README.md +5 -16
- data/ext/puma_http11/http11_parser.c +37 -62
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/lib/puma.rb +6 -0
- data/lib/puma/app/status.rb +29 -28
- data/lib/puma/binder.rb +32 -10
- data/lib/puma/cli.rb +4 -0
- data/lib/puma/client.rb +191 -203
- data/lib/puma/cluster.rb +14 -12
- data/lib/puma/const.rb +15 -18
- data/lib/puma/control_cli.rb +11 -2
- data/lib/puma/dsl.rb +16 -0
- data/lib/puma/launcher.rb +90 -46
- data/lib/puma/plugin.rb +5 -2
- data/lib/puma/reactor.rb +5 -4
- data/lib/puma/runner.rb +3 -3
- data/lib/puma/server.rb +11 -5
- data/lib/puma/single.rb +1 -0
- data/lib/puma/thread_pool.rb +9 -31
- data/lib/rack/handler/puma.rb +0 -2
- data/tools/docker/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +4 -3
- data/lib/puma/daemon_ext.rb +0 -33
data/lib/puma/cluster.rb
CHANGED
@@ -74,7 +74,6 @@ module Puma
|
|
74
74
|
@started_at = Time.now
|
75
75
|
@last_checkin = Time.now
|
76
76
|
@last_status = '{}'
|
77
|
-
@dead = false
|
78
77
|
@term = false
|
79
78
|
end
|
80
79
|
|
@@ -89,14 +88,6 @@ module Puma
|
|
89
88
|
@stage = :booted
|
90
89
|
end
|
91
90
|
|
92
|
-
def dead?
|
93
|
-
@dead
|
94
|
-
end
|
95
|
-
|
96
|
-
def dead!
|
97
|
-
@dead = true
|
98
|
-
end
|
99
|
-
|
100
91
|
def term?
|
101
92
|
@term
|
102
93
|
end
|
@@ -225,8 +216,10 @@ module Puma
|
|
225
216
|
log "- Stopping #{w.pid} for phased upgrade..."
|
226
217
|
end
|
227
218
|
|
228
|
-
w.term
|
229
|
-
|
219
|
+
unless w.term?
|
220
|
+
w.term
|
221
|
+
log "- #{w.signal} sent to #{w.pid}..."
|
222
|
+
end
|
230
223
|
end
|
231
224
|
end
|
232
225
|
end
|
@@ -253,6 +246,7 @@ module Puma
|
|
253
246
|
@suicide_pipe.close
|
254
247
|
|
255
248
|
Thread.new do
|
249
|
+
Puma.set_thread_name "worker check pipe"
|
256
250
|
IO.select [@check_pipe]
|
257
251
|
log "! Detected parent died, dying"
|
258
252
|
exit! 1
|
@@ -275,6 +269,7 @@ module Puma
|
|
275
269
|
server = start_server
|
276
270
|
|
277
271
|
Signal.trap "SIGTERM" do
|
272
|
+
@worker_write << "e#{Process.pid}\n" rescue nil
|
278
273
|
server.stop
|
279
274
|
end
|
280
275
|
|
@@ -287,6 +282,7 @@ module Puma
|
|
287
282
|
end
|
288
283
|
|
289
284
|
Thread.new(@worker_write) do |io|
|
285
|
+
Puma.set_thread_name "stat payload"
|
290
286
|
base_payload = "p#{Process.pid}"
|
291
287
|
|
292
288
|
while true
|
@@ -352,6 +348,8 @@ module Puma
|
|
352
348
|
Dir.chdir dir
|
353
349
|
end
|
354
350
|
|
351
|
+
# Inside of a child process, this will return all zeroes, as @workers is only populated in
|
352
|
+
# the master process.
|
355
353
|
def stats
|
356
354
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
357
355
|
booted_worker_count = @workers.count { |w| w.booted? }
|
@@ -506,8 +504,11 @@ module Puma
|
|
506
504
|
w.boot!
|
507
505
|
log "- Worker #{w.index} (pid: #{pid}) booted, phase: #{w.phase}"
|
508
506
|
force_check = true
|
507
|
+
when "e"
|
508
|
+
# external term, see worker method, Signal.trap "SIGTERM"
|
509
|
+
w.instance_variable_set :@term, true
|
509
510
|
when "t"
|
510
|
-
w.
|
511
|
+
w.term unless w.term?
|
511
512
|
force_check = true
|
512
513
|
when "p"
|
513
514
|
w.ping!(result.sub(/^\d+/,'').chomp)
|
@@ -528,6 +529,7 @@ module Puma
|
|
528
529
|
@suicide_pipe.close
|
529
530
|
read.close
|
530
531
|
@wakeup.close
|
532
|
+
@launcher.close_binder_unix_paths
|
531
533
|
end
|
532
534
|
end
|
533
535
|
|
data/lib/puma/const.rb
CHANGED
@@ -100,8 +100,8 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "4.
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "4.2.0".freeze
|
104
|
+
CODE_NAME = "Distant Airhorns".freeze
|
105
105
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
106
106
|
|
107
107
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
@@ -122,27 +122,24 @@ module Puma
|
|
122
122
|
REQUEST_URI= 'REQUEST_URI'.freeze
|
123
123
|
REQUEST_PATH = 'REQUEST_PATH'.freeze
|
124
124
|
QUERY_STRING = 'QUERY_STRING'.freeze
|
125
|
+
CONTENT_LENGTH = "CONTENT_LENGTH".freeze
|
125
126
|
|
126
127
|
PATH_INFO = 'PATH_INFO'.freeze
|
127
128
|
|
128
129
|
PUMA_TMP_BASE = "puma".freeze
|
129
130
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze
|
143
|
-
|
144
|
-
# A common header for indicating the server is too busy. Not used yet.
|
145
|
-
ERROR_503_RESPONSE = "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
131
|
+
ERROR_RESPONSE = {
|
132
|
+
# Indicate that we couldn't parse the request
|
133
|
+
400 => "HTTP/1.1 400 Bad Request\r\n\r\n".freeze,
|
134
|
+
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
|
135
|
+
404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND".freeze,
|
136
|
+
# The standard empty 408 response for requests that timed out.
|
137
|
+
408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze,
|
138
|
+
# Indicate that there was an internal error, obviously.
|
139
|
+
500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze,
|
140
|
+
# A common header for indicating the server is too busy. Not used yet.
|
141
|
+
503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
142
|
+
}
|
146
143
|
|
147
144
|
# The basic max request size we'll try to read.
|
148
145
|
CHUNK_SIZE = 16 * 1024
|
data/lib/puma/control_cli.rb
CHANGED
@@ -22,6 +22,7 @@ module Puma
|
|
22
22
|
@control_auth_token = nil
|
23
23
|
@config_file = nil
|
24
24
|
@command = nil
|
25
|
+
@environment = ENV['RACK_ENV'] || "development"
|
25
26
|
|
26
27
|
@argv = argv.dup
|
27
28
|
@stdout = stdout
|
@@ -59,6 +60,11 @@ module Puma
|
|
59
60
|
@config_file = arg
|
60
61
|
end
|
61
62
|
|
63
|
+
o.on "-e", "--environment ENVIRONMENT",
|
64
|
+
"The environment to run the Rack app on (default development)" do |arg|
|
65
|
+
@environment = arg
|
66
|
+
end
|
67
|
+
|
62
68
|
o.on_tail("-H", "--help", "Show this message") do
|
63
69
|
@stdout.puts o
|
64
70
|
exit
|
@@ -76,8 +82,10 @@ module Puma
|
|
76
82
|
@command = argv.shift
|
77
83
|
|
78
84
|
unless @config_file == '-'
|
79
|
-
if @config_file.nil?
|
80
|
-
@config_file =
|
85
|
+
if @config_file.nil?
|
86
|
+
@config_file = %W(config/puma/#{@environment}.rb config/puma.rb).find do |f|
|
87
|
+
File.exist?(f)
|
88
|
+
end
|
81
89
|
end
|
82
90
|
|
83
91
|
if @config_file
|
@@ -258,6 +266,7 @@ module Puma
|
|
258
266
|
run_args += ["--control-url", @control_url] if @control_url
|
259
267
|
run_args += ["--control-token", @control_auth_token] if @control_auth_token
|
260
268
|
run_args += ["-C", @config_file] if @config_file
|
269
|
+
run_args += ["-e", @environment] if @environment
|
261
270
|
|
262
271
|
events = Puma::Events.new @stdout, @stderr
|
263
272
|
|
data/lib/puma/dsl.rb
CHANGED
@@ -584,6 +584,7 @@ module Puma
|
|
584
584
|
# dictates.
|
585
585
|
#
|
586
586
|
# @note This is incompatible with +preload_app!+.
|
587
|
+
# @note This is only supported for RubyGems 2.2+
|
587
588
|
def prune_bundler(answer=true)
|
588
589
|
@options[:prune_bundler] = answer
|
589
590
|
end
|
@@ -601,6 +602,21 @@ module Puma
|
|
601
602
|
@options[:raise_exception_on_sigterm] = answer
|
602
603
|
end
|
603
604
|
|
605
|
+
# When using prune_bundler, if extra runtime dependencies need to be loaded to
|
606
|
+
# initialize your app, then this setting can be used.
|
607
|
+
#
|
608
|
+
# Before bundler is pruned, the gem names supplied will be looked up in the bundler
|
609
|
+
# context and then loaded again after bundler is pruned.
|
610
|
+
# Only applies if prune_bundler is used.
|
611
|
+
#
|
612
|
+
# @example
|
613
|
+
# extra_runtime_dependencies ['gem_name_1', 'gem_name_2']
|
614
|
+
# @example
|
615
|
+
# extra_runtime_dependencies ['puma_worker_killer']
|
616
|
+
def extra_runtime_dependencies(answer = [])
|
617
|
+
@options[:extra_runtime_dependencies] = Array(answer)
|
618
|
+
end
|
619
|
+
|
604
620
|
# Additional text to display in process listing.
|
605
621
|
#
|
606
622
|
# If you do not specify a tag, Puma will infer it. If you do not want Puma
|
data/lib/puma/launcher.rb
CHANGED
@@ -2,12 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'puma/events'
|
4
4
|
require 'puma/detect'
|
5
|
-
|
6
5
|
require 'puma/cluster'
|
7
6
|
require 'puma/single'
|
8
|
-
|
9
7
|
require 'puma/const'
|
10
|
-
|
11
8
|
require 'puma/binder'
|
12
9
|
|
13
10
|
module Puma
|
@@ -126,19 +123,6 @@ module Puma
|
|
126
123
|
File.unlink(path) if path && File.exist?(path)
|
127
124
|
end
|
128
125
|
|
129
|
-
# If configured, write the pid of the current process out
|
130
|
-
# to a file.
|
131
|
-
def write_pid
|
132
|
-
path = @options[:pidfile]
|
133
|
-
return unless path
|
134
|
-
|
135
|
-
File.open(path, 'w') { |f| f.puts Process.pid }
|
136
|
-
cur = Process.pid
|
137
|
-
at_exit do
|
138
|
-
delete_pidfile if cur == Process.pid
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
126
|
# Begin async shutdown of the server
|
143
127
|
def halt
|
144
128
|
@status = :halt
|
@@ -217,16 +201,28 @@ module Puma
|
|
217
201
|
end
|
218
202
|
|
219
203
|
def close_binder_listeners
|
220
|
-
@binder.
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
end
|
204
|
+
@binder.close_listeners
|
205
|
+
end
|
206
|
+
|
207
|
+
def close_binder_unix_paths
|
208
|
+
@binder.close_unix_paths
|
226
209
|
end
|
227
210
|
|
228
211
|
private
|
229
212
|
|
213
|
+
# If configured, write the pid of the current process out
|
214
|
+
# to a file.
|
215
|
+
def write_pid
|
216
|
+
path = @options[:pidfile]
|
217
|
+
return unless path
|
218
|
+
|
219
|
+
File.open(path, 'w') { |f| f.puts Process.pid }
|
220
|
+
cur = Process.pid
|
221
|
+
at_exit do
|
222
|
+
delete_pidfile if cur == Process.pid
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
230
226
|
def reload_worker_directory
|
231
227
|
@runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
|
232
228
|
end
|
@@ -246,48 +242,71 @@ module Puma
|
|
246
242
|
Dir.chdir(@restart_dir)
|
247
243
|
Kernel.exec(*argv)
|
248
244
|
else
|
249
|
-
redirects = {:close_others => true}
|
250
|
-
@binder.listeners.each_with_index do |(l, io), i|
|
251
|
-
ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
|
252
|
-
redirects[io.to_i] = io.to_i
|
253
|
-
end
|
254
|
-
|
255
245
|
argv = restart_args
|
256
246
|
Dir.chdir(@restart_dir)
|
257
|
-
argv += [
|
247
|
+
argv += [@binder.redirects_for_restart]
|
258
248
|
Kernel.exec(*argv)
|
259
249
|
end
|
260
250
|
end
|
261
251
|
|
262
|
-
def
|
263
|
-
|
264
|
-
|
265
|
-
|
252
|
+
def dependencies_and_files_to_require_after_prune
|
253
|
+
puma = spec_for_gem("puma")
|
254
|
+
|
255
|
+
deps = puma.runtime_dependencies.map do |d|
|
256
|
+
"#{d.name}:#{spec_for_gem(d.name).version}"
|
257
|
+
end
|
258
|
+
|
259
|
+
[deps, require_paths_for_gem(puma) + extra_runtime_deps_directories]
|
260
|
+
end
|
261
|
+
|
262
|
+
def extra_runtime_deps_directories
|
263
|
+
Array(@options[:extra_runtime_dependencies]).map do |d_name|
|
264
|
+
if (spec = spec_for_gem(d_name))
|
265
|
+
require_paths_for_gem(spec)
|
266
|
+
else
|
267
|
+
log "* Could not load extra dependency: #{d_name}"
|
268
|
+
nil
|
269
|
+
end
|
270
|
+
end.flatten.compact
|
271
|
+
end
|
272
|
+
|
273
|
+
def puma_wild_location
|
274
|
+
puma = spec_for_gem("puma")
|
275
|
+
dirs = require_paths_for_gem(puma)
|
266
276
|
puma_lib_dir = dirs.detect { |x| File.exist? File.join(x, '../bin/puma-wild') }
|
277
|
+
File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
|
278
|
+
end
|
267
279
|
|
268
|
-
|
280
|
+
def prune_bundler
|
281
|
+
return unless defined?(Bundler)
|
282
|
+
require_rubygems_min_version!(Gem::Version.new("2.2"), "prune_bundler")
|
283
|
+
unless puma_wild_location
|
269
284
|
log "! Unable to prune Bundler environment, continuing"
|
270
285
|
return
|
271
286
|
end
|
272
287
|
|
273
|
-
deps =
|
274
|
-
spec = Bundler.rubygems.loaded_specs(d.name)
|
275
|
-
"#{d.name}:#{spec.version.to_s}"
|
276
|
-
end
|
288
|
+
deps, dirs = dependencies_and_files_to_require_after_prune
|
277
289
|
|
278
290
|
log '* Pruning Bundler environment'
|
279
291
|
home = ENV['GEM_HOME']
|
280
292
|
Bundler.with_clean_env do
|
281
293
|
ENV['GEM_HOME'] = home
|
282
294
|
ENV['PUMA_BUNDLER_PRUNED'] = '1'
|
283
|
-
|
284
|
-
args = [Gem.ruby, wild, '-I', dirs.join(':'), deps.join(',')] + @original_argv
|
295
|
+
args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':'), deps.join(',')] + @original_argv
|
285
296
|
# Ruby 2.0+ defaults to true which breaks socket activation
|
286
297
|
args += [{:close_others => false}]
|
287
298
|
Kernel.exec(*args)
|
288
299
|
end
|
289
300
|
end
|
290
301
|
|
302
|
+
def spec_for_gem(gem_name)
|
303
|
+
Bundler.rubygems.loaded_specs(gem_name)
|
304
|
+
end
|
305
|
+
|
306
|
+
def require_paths_for_gem(gem_spec)
|
307
|
+
gem_spec.full_require_paths
|
308
|
+
end
|
309
|
+
|
291
310
|
def log(str)
|
292
311
|
@events.log str
|
293
312
|
end
|
@@ -307,6 +326,21 @@ module Puma
|
|
307
326
|
log "- Goodbye!"
|
308
327
|
end
|
309
328
|
|
329
|
+
def log_thread_status
|
330
|
+
Thread.list.each do |thread|
|
331
|
+
log "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
|
332
|
+
logstr = "Thread: TID-#{thread.object_id.to_s(36)}"
|
333
|
+
logstr += " #{thread.name}" if thread.respond_to?(:name)
|
334
|
+
log logstr
|
335
|
+
|
336
|
+
if thread.backtrace
|
337
|
+
log thread.backtrace.join("\n")
|
338
|
+
else
|
339
|
+
log "<no backtrace available>"
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
310
344
|
def set_process_title
|
311
345
|
Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
|
312
346
|
end
|
@@ -406,12 +440,6 @@ module Puma
|
|
406
440
|
|
407
441
|
begin
|
408
442
|
Signal.trap "SIGINT" do
|
409
|
-
if Puma.jruby?
|
410
|
-
@status = :exit
|
411
|
-
graceful_stop
|
412
|
-
exit
|
413
|
-
end
|
414
|
-
|
415
443
|
stop
|
416
444
|
end
|
417
445
|
rescue Exception
|
@@ -429,6 +457,22 @@ module Puma
|
|
429
457
|
rescue Exception
|
430
458
|
log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
|
431
459
|
end
|
460
|
+
|
461
|
+
begin
|
462
|
+
Signal.trap "SIGINFO" do
|
463
|
+
log_thread_status
|
464
|
+
end
|
465
|
+
rescue Exception
|
466
|
+
# Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
|
467
|
+
# to see this constantly on Linux.
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
def require_rubygems_min_version!(min_version, feature)
|
472
|
+
return if min_version <= Gem::Version.new(Gem::VERSION)
|
473
|
+
|
474
|
+
raise "#{feature} is not supported on your version of RubyGems. " \
|
475
|
+
"You must have RubyGems #{min_version}+ to use this feature."
|
432
476
|
end
|
433
477
|
end
|
434
478
|
end
|
data/lib/puma/plugin.rb
CHANGED
data/lib/puma/reactor.rb
CHANGED
@@ -225,7 +225,7 @@ module Puma
|
|
225
225
|
# will be flooding them with errors when persistent connections
|
226
226
|
# are closed.
|
227
227
|
rescue ConnectionError
|
228
|
-
c.
|
228
|
+
c.write_error(500)
|
229
229
|
c.close
|
230
230
|
|
231
231
|
clear_monitor mon
|
@@ -252,7 +252,7 @@ module Puma
|
|
252
252
|
rescue HttpParserError => e
|
253
253
|
@server.lowlevel_error(e, c.env)
|
254
254
|
|
255
|
-
c.
|
255
|
+
c.write_error(400)
|
256
256
|
c.close
|
257
257
|
|
258
258
|
clear_monitor mon
|
@@ -261,7 +261,7 @@ module Puma
|
|
261
261
|
rescue StandardError => e
|
262
262
|
@server.lowlevel_error(e, c.env)
|
263
263
|
|
264
|
-
c.
|
264
|
+
c.write_error(500)
|
265
265
|
c.close
|
266
266
|
|
267
267
|
clear_monitor mon
|
@@ -277,7 +277,7 @@ module Puma
|
|
277
277
|
while @timeouts.first.value.timeout_at < now
|
278
278
|
mon = @timeouts.shift
|
279
279
|
c = mon.value
|
280
|
-
c.
|
280
|
+
c.write_error(408) if c.in_data_phase
|
281
281
|
c.close
|
282
282
|
|
283
283
|
clear_monitor mon
|
@@ -307,6 +307,7 @@ module Puma
|
|
307
307
|
|
308
308
|
def run_in_thread
|
309
309
|
@thread = Thread.new do
|
310
|
+
Puma.set_thread_name "reactor"
|
310
311
|
begin
|
311
312
|
run_internal
|
312
313
|
rescue StandardError => e
|