puma 4.3.12 → 5.6.4
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 +1461 -524
- data/LICENSE +23 -20
- data/README.md +120 -36
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +63 -26
- data/docs/compile_options.md +21 -0
- data/docs/deployment.md +60 -69
- data/docs/fork_worker.md +33 -0
- data/docs/jungle/README.md +9 -0
- data/{tools → docs}/jungle/rc.d/README.md +1 -1
- data/{tools → docs}/jungle/rc.d/puma +2 -2
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +46 -23
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +85 -128
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +38 -9
- data/ext/puma_http11/http11_parser.c +45 -47
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +204 -86
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +3 -5
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +105 -61
- data/ext/puma_http11/puma_http11.c +32 -51
- data/lib/puma/app/status.rb +47 -36
- data/lib/puma/binder.rb +225 -106
- data/lib/puma/cli.rb +24 -18
- data/lib/puma/client.rb +104 -76
- data/lib/puma/cluster/worker.rb +173 -0
- data/lib/puma/cluster/worker_handle.rb +94 -0
- data/lib/puma/cluster.rb +212 -220
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +58 -49
- data/lib/puma/const.rb +13 -6
- data/lib/puma/control_cli.rb +93 -76
- data/lib/puma/detect.rb +29 -2
- data/lib/puma/dsl.rb +364 -96
- data/lib/puma/error_logger.rb +104 -0
- data/lib/puma/events.rb +55 -34
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher.rb +117 -46
- data/lib/puma/minissl/context_builder.rb +14 -9
- data/lib/puma/minissl.rb +128 -46
- data/lib/puma/null_io.rb +13 -1
- data/lib/puma/plugin.rb +3 -12
- data/lib/puma/queue_close.rb +26 -0
- data/lib/puma/rack/builder.rb +1 -5
- data/lib/puma/reactor.rb +85 -369
- data/lib/puma/request.rb +472 -0
- data/lib/puma/runner.rb +46 -61
- data/lib/puma/server.rb +290 -763
- data/lib/puma/single.rb +9 -65
- data/lib/puma/state_file.rb +47 -8
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +125 -57
- data/lib/puma/util.rb +20 -1
- data/lib/puma.rb +46 -0
- data/lib/rack/handler/puma.rb +2 -3
- data/tools/{docker/Dockerfile → Dockerfile} +1 -1
- metadata +26 -22
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/accept_nonblock.rb +0 -29
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
- data/tools/jungle/upstart/README.md +0 -61
- data/tools/jungle/upstart/puma-manager.conf +0 -31
- data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/launcher.rb
CHANGED
@@ -15,6 +15,7 @@ module Puma
|
|
15
15
|
# It is responsible for either launching a cluster of Puma workers or a single
|
16
16
|
# puma server.
|
17
17
|
class Launcher
|
18
|
+
# @deprecated 6.0.0
|
18
19
|
KEYS_NOT_TO_PERSIST_IN_STATE = [
|
19
20
|
:logger, :lowlevel_error_handler,
|
20
21
|
:before_worker_shutdown, :before_worker_boot, :before_worker_fork,
|
@@ -47,8 +48,9 @@ module Puma
|
|
47
48
|
@original_argv = @argv.dup
|
48
49
|
@config = conf
|
49
50
|
|
50
|
-
@binder = Binder.new(@events)
|
51
|
-
@binder.
|
51
|
+
@binder = Binder.new(@events, conf)
|
52
|
+
@binder.create_inherited_fds(ENV).each { |k| ENV.delete k }
|
53
|
+
@binder.create_activated_fds(ENV).each { |k| ENV.delete k }
|
52
54
|
|
53
55
|
@environment = conf.environment
|
54
56
|
|
@@ -57,6 +59,13 @@ module Puma
|
|
57
59
|
|
58
60
|
@config.load
|
59
61
|
|
62
|
+
if @config.options[:bind_to_activated_sockets]
|
63
|
+
@config.options[:binds] = @binder.synthesize_binds_from_activated_fs(
|
64
|
+
@config.options[:binds],
|
65
|
+
@config.options[:bind_to_activated_sockets] == 'only'
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
60
69
|
@options = @config.options
|
61
70
|
@config.clamp
|
62
71
|
|
@@ -65,14 +74,10 @@ module Puma
|
|
65
74
|
|
66
75
|
generate_restart_data
|
67
76
|
|
68
|
-
if clustered? && !
|
77
|
+
if clustered? && !Puma.forkable?
|
69
78
|
unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
|
70
79
|
end
|
71
80
|
|
72
|
-
if @options[:daemon] && Puma.windows?
|
73
|
-
unsupported 'daemon mode not supported on Windows'
|
74
|
-
end
|
75
|
-
|
76
81
|
Dir.chdir(@restart_dir)
|
77
82
|
|
78
83
|
prune_bundler if prune_bundler?
|
@@ -90,6 +95,8 @@ module Puma
|
|
90
95
|
Puma.stats_object = @runner
|
91
96
|
|
92
97
|
@status = :run
|
98
|
+
|
99
|
+
log_config if ENV['PUMA_LOG_CONFIG']
|
93
100
|
end
|
94
101
|
|
95
102
|
attr_reader :binder, :events, :config, :options, :restart_dir
|
@@ -105,6 +112,7 @@ module Puma
|
|
105
112
|
write_pid
|
106
113
|
|
107
114
|
path = @options[:state]
|
115
|
+
permission = @options[:state_permission]
|
108
116
|
return unless path
|
109
117
|
|
110
118
|
require 'puma/state_file'
|
@@ -113,8 +121,9 @@ module Puma
|
|
113
121
|
sf.pid = Process.pid
|
114
122
|
sf.control_url = @options[:control_url]
|
115
123
|
sf.control_auth_token = @options[:control_auth_token]
|
124
|
+
sf.running_from = File.expand_path('.')
|
116
125
|
|
117
|
-
sf.save path
|
126
|
+
sf.save path, permission
|
118
127
|
end
|
119
128
|
|
120
129
|
# Delete the configured pidfile
|
@@ -169,29 +178,34 @@ module Puma
|
|
169
178
|
|
170
179
|
setup_signals
|
171
180
|
set_process_title
|
181
|
+
integrate_with_systemd
|
172
182
|
@runner.run
|
173
183
|
|
174
184
|
case @status
|
175
185
|
when :halt
|
176
186
|
log "* Stopping immediately!"
|
187
|
+
@runner.stop_control
|
177
188
|
when :run, :stop
|
178
189
|
graceful_stop
|
179
190
|
when :restart
|
180
191
|
log "* Restarting..."
|
181
192
|
ENV.replace(previous_env)
|
182
|
-
@runner.
|
193
|
+
@runner.stop_control
|
183
194
|
restart!
|
184
195
|
when :exit
|
185
196
|
# nothing
|
186
197
|
end
|
187
|
-
@
|
198
|
+
close_binder_listeners unless @status == :restart
|
188
199
|
end
|
189
200
|
|
190
|
-
# Return
|
191
|
-
|
192
|
-
|
201
|
+
# Return all tcp ports the launcher may be using, TCP or SSL
|
202
|
+
# @!attribute [r] connected_ports
|
203
|
+
# @version 5.0.0
|
204
|
+
def connected_ports
|
205
|
+
@binder.connected_ports
|
193
206
|
end
|
194
207
|
|
208
|
+
# @!attribute [r] restart_args
|
195
209
|
def restart_args
|
196
210
|
cmd = @options[:restart_cmd]
|
197
211
|
if cmd
|
@@ -202,7 +216,25 @@ module Puma
|
|
202
216
|
end
|
203
217
|
|
204
218
|
def close_binder_listeners
|
219
|
+
@runner.close_control_listeners
|
205
220
|
@binder.close_listeners
|
221
|
+
unless @status == :restart
|
222
|
+
log "=== puma shutdown: #{Time.now} ==="
|
223
|
+
log "- Goodbye!"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# @!attribute [r] thread_status
|
228
|
+
# @version 5.0.0
|
229
|
+
def thread_status
|
230
|
+
Thread.list.each do |thread|
|
231
|
+
name = "Thread: TID-#{thread.object_id.to_s(36)}"
|
232
|
+
name += " #{thread['label']}" if thread['label']
|
233
|
+
name += " #{thread.name}" if thread.respond_to?(:name) && thread.name
|
234
|
+
backtrace = thread.backtrace || ["<no backtrace available>"]
|
235
|
+
|
236
|
+
yield name, backtrace
|
237
|
+
end
|
206
238
|
end
|
207
239
|
|
208
240
|
private
|
@@ -212,11 +244,10 @@ module Puma
|
|
212
244
|
def write_pid
|
213
245
|
path = @options[:pidfile]
|
214
246
|
return unless path
|
215
|
-
|
216
|
-
File.
|
217
|
-
cur = Process.pid
|
247
|
+
cur_pid = Process.pid
|
248
|
+
File.write path, cur_pid, mode: 'wb:UTF-8'
|
218
249
|
at_exit do
|
219
|
-
delete_pidfile if
|
250
|
+
delete_pidfile if cur_pid == Process.pid
|
220
251
|
end
|
221
252
|
end
|
222
253
|
|
@@ -225,7 +256,8 @@ module Puma
|
|
225
256
|
end
|
226
257
|
|
227
258
|
def restart!
|
228
|
-
@
|
259
|
+
@events.fire_on_restart!
|
260
|
+
@config.run_hooks :on_restart, self, @events
|
229
261
|
|
230
262
|
if Puma.jruby?
|
231
263
|
close_binder_listeners
|
@@ -241,21 +273,20 @@ module Puma
|
|
241
273
|
else
|
242
274
|
argv = restart_args
|
243
275
|
Dir.chdir(@restart_dir)
|
276
|
+
ENV.update(@binder.redirects_for_restart_env)
|
244
277
|
argv += [@binder.redirects_for_restart]
|
245
278
|
Kernel.exec(*argv)
|
246
279
|
end
|
247
280
|
end
|
248
281
|
|
249
|
-
|
282
|
+
# @!attribute [r] files_to_require_after_prune
|
283
|
+
def files_to_require_after_prune
|
250
284
|
puma = spec_for_gem("puma")
|
251
285
|
|
252
|
-
|
253
|
-
"#{d.name}:#{spec_for_gem(d.name).version}"
|
254
|
-
end
|
255
|
-
|
256
|
-
[deps, require_paths_for_gem(puma) + extra_runtime_deps_directories]
|
286
|
+
require_paths_for_gem(puma) + extra_runtime_deps_directories
|
257
287
|
end
|
258
288
|
|
289
|
+
# @!attribute [r] extra_runtime_deps_directories
|
259
290
|
def extra_runtime_deps_directories
|
260
291
|
Array(@options[:extra_runtime_dependencies]).map do |d_name|
|
261
292
|
if (spec = spec_for_gem(d_name))
|
@@ -267,6 +298,7 @@ module Puma
|
|
267
298
|
end.flatten.compact
|
268
299
|
end
|
269
300
|
|
301
|
+
# @!attribute [r] puma_wild_location
|
270
302
|
def puma_wild_location
|
271
303
|
puma = spec_for_gem("puma")
|
272
304
|
dirs = require_paths_for_gem(puma)
|
@@ -275,6 +307,7 @@ module Puma
|
|
275
307
|
end
|
276
308
|
|
277
309
|
def prune_bundler
|
310
|
+
return if ENV['PUMA_BUNDLER_PRUNED']
|
278
311
|
return unless defined?(Bundler)
|
279
312
|
require_rubygems_min_version!(Gem::Version.new("2.2"), "prune_bundler")
|
280
313
|
unless puma_wild_location
|
@@ -282,20 +315,48 @@ module Puma
|
|
282
315
|
return
|
283
316
|
end
|
284
317
|
|
285
|
-
|
318
|
+
dirs = files_to_require_after_prune
|
286
319
|
|
287
320
|
log '* Pruning Bundler environment'
|
288
321
|
home = ENV['GEM_HOME']
|
289
|
-
Bundler.
|
322
|
+
bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
|
323
|
+
bundle_app_config = Bundler.original_env['BUNDLE_APP_CONFIG']
|
324
|
+
with_unbundled_env do
|
290
325
|
ENV['GEM_HOME'] = home
|
326
|
+
ENV['BUNDLE_GEMFILE'] = bundle_gemfile
|
291
327
|
ENV['PUMA_BUNDLER_PRUNED'] = '1'
|
292
|
-
|
328
|
+
ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
|
329
|
+
args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':')] + @original_argv
|
293
330
|
# Ruby 2.0+ defaults to true which breaks socket activation
|
294
331
|
args += [{:close_others => false}]
|
295
332
|
Kernel.exec(*args)
|
296
333
|
end
|
297
334
|
end
|
298
335
|
|
336
|
+
#
|
337
|
+
# Puma's systemd integration allows Puma to inform systemd:
|
338
|
+
# 1. when it has successfully started
|
339
|
+
# 2. when it is starting shutdown
|
340
|
+
# 3. periodically for a liveness check with a watchdog thread
|
341
|
+
#
|
342
|
+
|
343
|
+
def integrate_with_systemd
|
344
|
+
return unless ENV["NOTIFY_SOCKET"]
|
345
|
+
|
346
|
+
begin
|
347
|
+
require 'puma/systemd'
|
348
|
+
rescue LoadError
|
349
|
+
log "Systemd integration failed. It looks like you're trying to use systemd notify but don't have sd_notify gem installed"
|
350
|
+
return
|
351
|
+
end
|
352
|
+
|
353
|
+
log "* Enabling systemd notification integration"
|
354
|
+
|
355
|
+
systemd = Systemd.new(@events)
|
356
|
+
systemd.hook_events
|
357
|
+
systemd.start_watchdog
|
358
|
+
end
|
359
|
+
|
299
360
|
def spec_for_gem(gem_name)
|
300
361
|
Bundler.rubygems.loaded_specs(gem_name)
|
301
362
|
end
|
@@ -318,30 +379,15 @@ module Puma
|
|
318
379
|
end
|
319
380
|
|
320
381
|
def graceful_stop
|
382
|
+
@events.fire_on_stopped!
|
321
383
|
@runner.stop_blocked
|
322
|
-
log "=== puma shutdown: #{Time.now} ==="
|
323
|
-
log "- Goodbye!"
|
324
|
-
end
|
325
|
-
|
326
|
-
def log_thread_status
|
327
|
-
Thread.list.each do |thread|
|
328
|
-
log "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
|
329
|
-
logstr = "Thread: TID-#{thread.object_id.to_s(36)}"
|
330
|
-
logstr += " #{thread.name}" if thread.respond_to?(:name)
|
331
|
-
log logstr
|
332
|
-
|
333
|
-
if thread.backtrace
|
334
|
-
log thread.backtrace.join("\n")
|
335
|
-
else
|
336
|
-
log "<no backtrace available>"
|
337
|
-
end
|
338
|
-
end
|
339
384
|
end
|
340
385
|
|
341
386
|
def set_process_title
|
342
387
|
Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
|
343
388
|
end
|
344
389
|
|
390
|
+
# @!attribute [r] title
|
345
391
|
def title
|
346
392
|
buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
|
347
393
|
buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
|
@@ -353,6 +399,7 @@ module Puma
|
|
353
399
|
ENV['RACK_ENV'] = environment
|
354
400
|
end
|
355
401
|
|
402
|
+
# @!attribute [r] environment
|
356
403
|
def environment
|
357
404
|
@environment
|
358
405
|
end
|
@@ -456,8 +503,13 @@ module Puma
|
|
456
503
|
end
|
457
504
|
|
458
505
|
begin
|
459
|
-
|
460
|
-
|
506
|
+
unless Puma.jruby? # INFO in use by JVM already
|
507
|
+
Signal.trap "SIGINFO" do
|
508
|
+
thread_status do |name, backtrace|
|
509
|
+
@events.log name
|
510
|
+
@events.log backtrace.map { |bt| " #{bt}" }
|
511
|
+
end
|
512
|
+
end
|
461
513
|
end
|
462
514
|
rescue Exception
|
463
515
|
# Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
|
@@ -471,5 +523,24 @@ module Puma
|
|
471
523
|
raise "#{feature} is not supported on your version of RubyGems. " \
|
472
524
|
"You must have RubyGems #{min_version}+ to use this feature."
|
473
525
|
end
|
526
|
+
|
527
|
+
# @version 5.0.0
|
528
|
+
def with_unbundled_env
|
529
|
+
bundler_ver = Gem::Version.new(Bundler::VERSION)
|
530
|
+
if bundler_ver < Gem::Version.new('2.1.0')
|
531
|
+
Bundler.with_clean_env { yield }
|
532
|
+
else
|
533
|
+
Bundler.with_unbundled_env { yield }
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
def log_config
|
538
|
+
log "Configuration:"
|
539
|
+
|
540
|
+
@config.final_options
|
541
|
+
.each { |config_key, value| log "- #{config_key}: #{value}" }
|
542
|
+
|
543
|
+
log "\n"
|
544
|
+
end
|
474
545
|
end
|
475
546
|
end
|
@@ -2,9 +2,6 @@ module Puma
|
|
2
2
|
module MiniSSL
|
3
3
|
class ContextBuilder
|
4
4
|
def initialize(params, events)
|
5
|
-
require 'puma/minissl'
|
6
|
-
MiniSSL.check
|
7
|
-
|
8
5
|
@params = params
|
9
6
|
@events = events
|
10
7
|
end
|
@@ -26,17 +23,19 @@ module Puma
|
|
26
23
|
ctx.keystore_pass = params['keystore-pass']
|
27
24
|
ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
|
28
25
|
else
|
29
|
-
|
30
|
-
events.error "Please specify the SSL key via 'key='"
|
26
|
+
if params['key'].nil? && params['key_pem'].nil?
|
27
|
+
events.error "Please specify the SSL key via 'key=' or 'key_pem='"
|
31
28
|
end
|
32
29
|
|
33
|
-
ctx.key = params['key']
|
30
|
+
ctx.key = params['key'] if params['key']
|
31
|
+
ctx.key_pem = params['key_pem'] if params['key_pem']
|
34
32
|
|
35
|
-
|
36
|
-
events.error "Please specify the SSL cert via 'cert='"
|
33
|
+
if params['cert'].nil? && params['cert_pem'].nil?
|
34
|
+
events.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
|
37
35
|
end
|
38
36
|
|
39
|
-
ctx.cert = params['cert']
|
37
|
+
ctx.cert = params['cert'] if params['cert']
|
38
|
+
ctx.cert_pem = params['cert_pem'] if params['cert_pem']
|
40
39
|
|
41
40
|
if ['peer', 'force_peer'].include?(params['verify_mode'])
|
42
41
|
unless params['ca']
|
@@ -65,6 +64,12 @@ module Puma
|
|
65
64
|
end
|
66
65
|
end
|
67
66
|
|
67
|
+
if params['verification_flags']
|
68
|
+
ctx.verification_flags = params['verification_flags'].split(',').
|
69
|
+
map { |flag| MiniSSL::VERIFICATION_FLAGS.fetch(flag) }.
|
70
|
+
inject { |sum, flag| sum ? sum | flag : flag }
|
71
|
+
end
|
72
|
+
|
68
73
|
ctx
|
69
74
|
end
|
70
75
|
|