puma 4.3.12 → 5.6.6
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 +1511 -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 +3 -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 +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 +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 +476 -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 +28 -23
- 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
|
|