puma 4.3.12 → 5.6.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +1526 -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/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -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 +44 -10
- 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/http11_parser_common.rl +0 -0
- data/ext/puma_http11/mini_ssl.c +225 -89
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +5 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +3 -5
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +109 -67
- data/ext/puma_http11/puma_http11.c +32 -51
- data/lib/puma/app/status.rb +50 -36
- data/lib/puma/binder.rb +225 -106
- data/lib/puma/cli.rb +24 -18
- data/lib/puma/client.rb +146 -84
- 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 +22 -7
- data/lib/puma/control_cli.rb +99 -76
- data/lib/puma/detect.rb +29 -2
- data/lib/puma/dsl.rb +368 -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 +128 -46
- data/lib/puma/minissl/context_builder.rb +14 -9
- data/lib/puma/minissl.rb +137 -50
- data/lib/puma/null_io.rb +18 -1
- data/lib/puma/plugin/tmp_restart.rb +0 -0
- 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/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +0 -0
- data/lib/puma/reactor.rb +85 -369
- data/lib/puma/request.rb +489 -0
- data/lib/puma/runner.rb +46 -61
- data/lib/puma/server.rb +292 -763
- data/lib/puma/single.rb +9 -65
- data/lib/puma/state_file.rb +48 -8
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +125 -57
- data/lib/puma/util.rb +32 -4
- data/lib/puma.rb +48 -0
- data/lib/rack/handler/puma.rb +2 -3
- data/lib/rack/version_restriction.rb +15 -0
- data/tools/{docker/Dockerfile → Dockerfile} +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +29 -24
- 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
|
@@ -150,6 +159,17 @@ module Puma
|
|
150
159
|
true
|
151
160
|
end
|
152
161
|
|
162
|
+
# Begin a refork if supported
|
163
|
+
def refork
|
164
|
+
if clustered? && @runner.respond_to?(:fork_worker!) && @options[:fork_worker]
|
165
|
+
@runner.fork_worker!
|
166
|
+
true
|
167
|
+
else
|
168
|
+
log "* refork called but not available."
|
169
|
+
false
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
153
173
|
# Run the server. This blocks until the server is stopped
|
154
174
|
def run
|
155
175
|
previous_env =
|
@@ -169,29 +189,34 @@ module Puma
|
|
169
189
|
|
170
190
|
setup_signals
|
171
191
|
set_process_title
|
192
|
+
integrate_with_systemd
|
172
193
|
@runner.run
|
173
194
|
|
174
195
|
case @status
|
175
196
|
when :halt
|
176
197
|
log "* Stopping immediately!"
|
198
|
+
@runner.stop_control
|
177
199
|
when :run, :stop
|
178
200
|
graceful_stop
|
179
201
|
when :restart
|
180
202
|
log "* Restarting..."
|
181
203
|
ENV.replace(previous_env)
|
182
|
-
@runner.
|
204
|
+
@runner.stop_control
|
183
205
|
restart!
|
184
206
|
when :exit
|
185
207
|
# nothing
|
186
208
|
end
|
187
|
-
@
|
209
|
+
close_binder_listeners unless @status == :restart
|
188
210
|
end
|
189
211
|
|
190
|
-
# Return
|
191
|
-
|
192
|
-
|
212
|
+
# Return all tcp ports the launcher may be using, TCP or SSL
|
213
|
+
# @!attribute [r] connected_ports
|
214
|
+
# @version 5.0.0
|
215
|
+
def connected_ports
|
216
|
+
@binder.connected_ports
|
193
217
|
end
|
194
218
|
|
219
|
+
# @!attribute [r] restart_args
|
195
220
|
def restart_args
|
196
221
|
cmd = @options[:restart_cmd]
|
197
222
|
if cmd
|
@@ -202,7 +227,25 @@ module Puma
|
|
202
227
|
end
|
203
228
|
|
204
229
|
def close_binder_listeners
|
230
|
+
@runner.close_control_listeners
|
205
231
|
@binder.close_listeners
|
232
|
+
unless @status == :restart
|
233
|
+
log "=== puma shutdown: #{Time.now} ==="
|
234
|
+
log "- Goodbye!"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# @!attribute [r] thread_status
|
239
|
+
# @version 5.0.0
|
240
|
+
def thread_status
|
241
|
+
Thread.list.each do |thread|
|
242
|
+
name = "Thread: TID-#{thread.object_id.to_s(36)}"
|
243
|
+
name += " #{thread['label']}" if thread['label']
|
244
|
+
name += " #{thread.name}" if thread.respond_to?(:name) && thread.name
|
245
|
+
backtrace = thread.backtrace || ["<no backtrace available>"]
|
246
|
+
|
247
|
+
yield name, backtrace
|
248
|
+
end
|
206
249
|
end
|
207
250
|
|
208
251
|
private
|
@@ -212,11 +255,10 @@ module Puma
|
|
212
255
|
def write_pid
|
213
256
|
path = @options[:pidfile]
|
214
257
|
return unless path
|
215
|
-
|
216
|
-
File.
|
217
|
-
cur = Process.pid
|
258
|
+
cur_pid = Process.pid
|
259
|
+
File.write path, cur_pid, mode: 'wb:UTF-8'
|
218
260
|
at_exit do
|
219
|
-
delete_pidfile if
|
261
|
+
delete_pidfile if cur_pid == Process.pid
|
220
262
|
end
|
221
263
|
end
|
222
264
|
|
@@ -225,7 +267,8 @@ module Puma
|
|
225
267
|
end
|
226
268
|
|
227
269
|
def restart!
|
228
|
-
@
|
270
|
+
@events.fire_on_restart!
|
271
|
+
@config.run_hooks :on_restart, self, @events
|
229
272
|
|
230
273
|
if Puma.jruby?
|
231
274
|
close_binder_listeners
|
@@ -241,21 +284,20 @@ module Puma
|
|
241
284
|
else
|
242
285
|
argv = restart_args
|
243
286
|
Dir.chdir(@restart_dir)
|
287
|
+
ENV.update(@binder.redirects_for_restart_env)
|
244
288
|
argv += [@binder.redirects_for_restart]
|
245
289
|
Kernel.exec(*argv)
|
246
290
|
end
|
247
291
|
end
|
248
292
|
|
249
|
-
|
293
|
+
# @!attribute [r] files_to_require_after_prune
|
294
|
+
def files_to_require_after_prune
|
250
295
|
puma = spec_for_gem("puma")
|
251
296
|
|
252
|
-
|
253
|
-
"#{d.name}:#{spec_for_gem(d.name).version}"
|
254
|
-
end
|
255
|
-
|
256
|
-
[deps, require_paths_for_gem(puma) + extra_runtime_deps_directories]
|
297
|
+
require_paths_for_gem(puma) + extra_runtime_deps_directories
|
257
298
|
end
|
258
299
|
|
300
|
+
# @!attribute [r] extra_runtime_deps_directories
|
259
301
|
def extra_runtime_deps_directories
|
260
302
|
Array(@options[:extra_runtime_dependencies]).map do |d_name|
|
261
303
|
if (spec = spec_for_gem(d_name))
|
@@ -267,6 +309,7 @@ module Puma
|
|
267
309
|
end.flatten.compact
|
268
310
|
end
|
269
311
|
|
312
|
+
# @!attribute [r] puma_wild_location
|
270
313
|
def puma_wild_location
|
271
314
|
puma = spec_for_gem("puma")
|
272
315
|
dirs = require_paths_for_gem(puma)
|
@@ -275,6 +318,7 @@ module Puma
|
|
275
318
|
end
|
276
319
|
|
277
320
|
def prune_bundler
|
321
|
+
return if ENV['PUMA_BUNDLER_PRUNED']
|
278
322
|
return unless defined?(Bundler)
|
279
323
|
require_rubygems_min_version!(Gem::Version.new("2.2"), "prune_bundler")
|
280
324
|
unless puma_wild_location
|
@@ -282,20 +326,48 @@ module Puma
|
|
282
326
|
return
|
283
327
|
end
|
284
328
|
|
285
|
-
|
329
|
+
dirs = files_to_require_after_prune
|
286
330
|
|
287
331
|
log '* Pruning Bundler environment'
|
288
332
|
home = ENV['GEM_HOME']
|
289
|
-
Bundler.
|
333
|
+
bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
|
334
|
+
bundle_app_config = Bundler.original_env['BUNDLE_APP_CONFIG']
|
335
|
+
with_unbundled_env do
|
290
336
|
ENV['GEM_HOME'] = home
|
337
|
+
ENV['BUNDLE_GEMFILE'] = bundle_gemfile
|
291
338
|
ENV['PUMA_BUNDLER_PRUNED'] = '1'
|
292
|
-
|
339
|
+
ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
|
340
|
+
args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':')] + @original_argv
|
293
341
|
# Ruby 2.0+ defaults to true which breaks socket activation
|
294
342
|
args += [{:close_others => false}]
|
295
343
|
Kernel.exec(*args)
|
296
344
|
end
|
297
345
|
end
|
298
346
|
|
347
|
+
#
|
348
|
+
# Puma's systemd integration allows Puma to inform systemd:
|
349
|
+
# 1. when it has successfully started
|
350
|
+
# 2. when it is starting shutdown
|
351
|
+
# 3. periodically for a liveness check with a watchdog thread
|
352
|
+
#
|
353
|
+
|
354
|
+
def integrate_with_systemd
|
355
|
+
return unless ENV["NOTIFY_SOCKET"]
|
356
|
+
|
357
|
+
begin
|
358
|
+
require 'puma/systemd'
|
359
|
+
rescue LoadError
|
360
|
+
log "Systemd integration failed. It looks like you're trying to use systemd notify but don't have sd_notify gem installed"
|
361
|
+
return
|
362
|
+
end
|
363
|
+
|
364
|
+
log "* Enabling systemd notification integration"
|
365
|
+
|
366
|
+
systemd = Systemd.new(@events)
|
367
|
+
systemd.hook_events
|
368
|
+
systemd.start_watchdog
|
369
|
+
end
|
370
|
+
|
299
371
|
def spec_for_gem(gem_name)
|
300
372
|
Bundler.rubygems.loaded_specs(gem_name)
|
301
373
|
end
|
@@ -318,30 +390,15 @@ module Puma
|
|
318
390
|
end
|
319
391
|
|
320
392
|
def graceful_stop
|
393
|
+
@events.fire_on_stopped!
|
321
394
|
@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
395
|
end
|
340
396
|
|
341
397
|
def set_process_title
|
342
398
|
Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
|
343
399
|
end
|
344
400
|
|
401
|
+
# @!attribute [r] title
|
345
402
|
def title
|
346
403
|
buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
|
347
404
|
buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
|
@@ -353,6 +410,7 @@ module Puma
|
|
353
410
|
ENV['RACK_ENV'] = environment
|
354
411
|
end
|
355
412
|
|
413
|
+
# @!attribute [r] environment
|
356
414
|
def environment
|
357
415
|
@environment
|
358
416
|
end
|
@@ -456,8 +514,13 @@ module Puma
|
|
456
514
|
end
|
457
515
|
|
458
516
|
begin
|
459
|
-
|
460
|
-
|
517
|
+
unless Puma.jruby? # INFO in use by JVM already
|
518
|
+
Signal.trap "SIGINFO" do
|
519
|
+
thread_status do |name, backtrace|
|
520
|
+
@events.log name
|
521
|
+
@events.log backtrace.map { |bt| " #{bt}" }
|
522
|
+
end
|
523
|
+
end
|
461
524
|
end
|
462
525
|
rescue Exception
|
463
526
|
# Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
|
@@ -471,5 +534,24 @@ module Puma
|
|
471
534
|
raise "#{feature} is not supported on your version of RubyGems. " \
|
472
535
|
"You must have RubyGems #{min_version}+ to use this feature."
|
473
536
|
end
|
537
|
+
|
538
|
+
# @version 5.0.0
|
539
|
+
def with_unbundled_env
|
540
|
+
bundler_ver = Gem::Version.new(Bundler::VERSION)
|
541
|
+
if bundler_ver < Gem::Version.new('2.1.0')
|
542
|
+
Bundler.with_clean_env { yield }
|
543
|
+
else
|
544
|
+
Bundler.with_unbundled_env { yield }
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
def log_config
|
549
|
+
log "Configuration:"
|
550
|
+
|
551
|
+
@config.final_options
|
552
|
+
.each { |config_key, value| log "- #{config_key}: #{value}" }
|
553
|
+
|
554
|
+
log "\n"
|
555
|
+
end
|
474
556
|
end
|
475
557
|
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
|
|