puma 3.8.2 → 4.0.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 +5 -5
- data/History.md +157 -0
- data/README.md +155 -225
- data/docs/architecture.md +37 -0
- data/{DEPLOYMENT.md → docs/deployment.md} +24 -4
- 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/plugins.md +28 -0
- data/docs/restart.md +41 -0
- data/docs/signals.md +56 -3
- data/docs/systemd.md +130 -37
- data/ext/puma_http11/PumaHttp11Service.java +2 -0
- data/ext/puma_http11/http11_parser.c +84 -84
- data/ext/puma_http11/http11_parser.rl +9 -9
- data/ext/puma_http11/mini_ssl.c +51 -9
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +13 -16
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +26 -6
- data/lib/puma.rb +8 -0
- data/lib/puma/app/status.rb +9 -0
- data/lib/puma/binder.rb +31 -18
- data/lib/puma/cli.rb +22 -7
- data/lib/puma/client.rb +67 -18
- data/lib/puma/cluster.rb +64 -19
- data/lib/puma/commonlogger.rb +2 -0
- data/lib/puma/configuration.rb +22 -14
- data/lib/puma/const.rb +13 -2
- data/lib/puma/control_cli.rb +26 -14
- data/lib/puma/convenient.rb +2 -0
- data/lib/puma/daemon_ext.rb +2 -0
- data/lib/puma/delegation.rb +2 -0
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +91 -12
- data/lib/puma/events.rb +3 -2
- data/lib/puma/io_buffer.rb +3 -6
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/launcher.rb +51 -30
- data/lib/puma/minissl.rb +79 -28
- data/lib/puma/null_io.rb +2 -0
- data/lib/puma/plugin.rb +2 -0
- data/lib/puma/plugin/tmp_restart.rb +0 -1
- data/lib/puma/rack/builder.rb +2 -1
- data/lib/puma/reactor.rb +218 -30
- data/lib/puma/runner.rb +17 -4
- data/lib/puma/server.rb +113 -49
- data/lib/puma/single.rb +16 -5
- data/lib/puma/state_file.rb +2 -0
- data/lib/puma/tcp_logger.rb +2 -0
- data/lib/puma/thread_pool.rb +59 -6
- data/lib/puma/util.rb +2 -6
- data/lib/rack/handler/puma.rb +13 -2
- data/tools/jungle/README.md +12 -2
- data/tools/jungle/init.d/README.md +2 -0
- data/tools/jungle/init.d/puma +7 -7
- data/tools/jungle/init.d/run-puma +1 -1
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- data/tools/trickletest.rb +1 -1
- metadata +25 -87
- data/.github/issue_template.md +0 -20
- data/Gemfile +0 -12
- data/Manifest.txt +0 -78
- data/Rakefile +0 -158
- data/Release.md +0 -9
- data/gemfiles/2.1-Gemfile +0 -12
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/java_io_buffer.rb +0 -45
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
- data/puma.gemspec +0 -52
data/lib/puma/cluster.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/runner'
|
2
4
|
require 'puma/util'
|
3
5
|
require 'puma/plugin'
|
@@ -5,9 +7,18 @@ require 'puma/plugin'
|
|
5
7
|
require 'time'
|
6
8
|
|
7
9
|
module Puma
|
10
|
+
# This class is instantiated by the `Puma::Launcher` and used
|
11
|
+
# to boot and serve a Ruby application when puma "workers" are needed
|
12
|
+
# i.e. when using multi-processes. For example `$ puma -w 5`
|
13
|
+
#
|
14
|
+
# At the core of this class is running an instance of `Puma::Server` which
|
15
|
+
# gets created via the `start_server` method from the `Puma::Runner` class
|
16
|
+
# that this inherits from.
|
17
|
+
#
|
18
|
+
# An instance of this class will spawn the number of processes passed in
|
19
|
+
# via the `spawn_workers` method call. Each worker will have it's own
|
20
|
+
# instance of a `Puma::Server`.
|
8
21
|
class Cluster < Runner
|
9
|
-
WORKER_CHECK_INTERVAL = 5
|
10
|
-
|
11
22
|
def initialize(cli, events)
|
12
23
|
super cli, events
|
13
24
|
|
@@ -24,7 +35,35 @@ module Puma
|
|
24
35
|
@workers.each { |x| x.term }
|
25
36
|
|
26
37
|
begin
|
27
|
-
|
38
|
+
if RUBY_VERSION < '2.6'
|
39
|
+
@workers.each do |w|
|
40
|
+
begin
|
41
|
+
Process.waitpid(w.pid)
|
42
|
+
rescue Errno::ECHILD
|
43
|
+
# child is already terminated
|
44
|
+
end
|
45
|
+
end
|
46
|
+
else
|
47
|
+
# below code is for a bug in Ruby 2.6+, above waitpid call hangs
|
48
|
+
t_st = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
49
|
+
pids = @workers.map(&:pid)
|
50
|
+
loop do
|
51
|
+
pids.reject! do |w_pid|
|
52
|
+
begin
|
53
|
+
if Process.waitpid(w_pid, Process::WNOHANG)
|
54
|
+
log " worker status: #{$?}"
|
55
|
+
true
|
56
|
+
end
|
57
|
+
rescue Errno::ECHILD
|
58
|
+
true # child is already terminated
|
59
|
+
end
|
60
|
+
end
|
61
|
+
break if pids.empty?
|
62
|
+
sleep 0.5
|
63
|
+
end
|
64
|
+
t_end = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
65
|
+
log format(" worker shutdown time: %6.2f", t_end - t_st)
|
66
|
+
end
|
28
67
|
rescue Interrupt
|
29
68
|
log "! Cancelled waiting for workers"
|
30
69
|
end
|
@@ -170,7 +209,7 @@ module Puma
|
|
170
209
|
def check_workers(force=false)
|
171
210
|
return if !force && @next_check && @next_check >= Time.now
|
172
211
|
|
173
|
-
@next_check = Time.now + WORKER_CHECK_INTERVAL
|
212
|
+
@next_check = Time.now + Const::WORKER_CHECK_INTERVAL
|
174
213
|
|
175
214
|
any = false
|
176
215
|
|
@@ -187,14 +226,9 @@ module Puma
|
|
187
226
|
# during this loop by giving the kernel time to kill them.
|
188
227
|
sleep 1 if any
|
189
228
|
|
190
|
-
|
191
|
-
pid = Process.waitpid(-1, Process::WNOHANG)
|
192
|
-
break unless pid
|
229
|
+
@workers.reject! { |w| Process.waitpid(w.pid, Process::WNOHANG) }
|
193
230
|
|
194
|
-
|
195
|
-
end
|
196
|
-
|
197
|
-
@workers.delete_if(&:dead?)
|
231
|
+
@workers.reject!(&:dead?)
|
198
232
|
|
199
233
|
cull_workers
|
200
234
|
spawn_workers
|
@@ -224,12 +258,13 @@ module Puma
|
|
224
258
|
begin
|
225
259
|
@wakeup.write "!" unless @wakeup.closed?
|
226
260
|
rescue SystemCallError, IOError
|
261
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
227
262
|
end
|
228
263
|
end
|
229
264
|
|
230
265
|
def worker(index, master)
|
231
|
-
title
|
232
|
-
title
|
266
|
+
title = "puma: cluster worker #{index}: #{master}"
|
267
|
+
title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
|
233
268
|
$0 = title
|
234
269
|
|
235
270
|
Signal.trap "SIGINT", "IGNORE"
|
@@ -267,6 +302,7 @@ module Puma
|
|
267
302
|
begin
|
268
303
|
@worker_write << "b#{Process.pid}\n"
|
269
304
|
rescue SystemCallError, IOError
|
305
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
270
306
|
STDERR.puts "Master seems to have exited, exiting."
|
271
307
|
return
|
272
308
|
end
|
@@ -275,13 +311,16 @@ module Puma
|
|
275
311
|
base_payload = "p#{Process.pid}"
|
276
312
|
|
277
313
|
while true
|
278
|
-
sleep WORKER_CHECK_INTERVAL
|
314
|
+
sleep Const::WORKER_CHECK_INTERVAL
|
279
315
|
begin
|
280
|
-
b = server.backlog
|
281
|
-
r = server.running
|
282
|
-
|
316
|
+
b = server.backlog || 0
|
317
|
+
r = server.running || 0
|
318
|
+
t = server.pool_capacity || 0
|
319
|
+
m = server.max_threads || 0
|
320
|
+
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m} }\n!
|
283
321
|
io << payload
|
284
322
|
rescue IOError
|
323
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
285
324
|
break
|
286
325
|
end
|
287
326
|
end
|
@@ -337,7 +376,7 @@ module Puma
|
|
337
376
|
def stats
|
338
377
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
339
378
|
booted_worker_count = @workers.count { |w| w.booted? }
|
340
|
-
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(",") + ']'
|
379
|
+
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(",") + ']'
|
341
380
|
%Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }!
|
342
381
|
end
|
343
382
|
|
@@ -372,7 +411,13 @@ module Puma
|
|
372
411
|
log "Early termination of worker"
|
373
412
|
exit! 0
|
374
413
|
else
|
414
|
+
@launcher.close_binder_listeners
|
415
|
+
|
416
|
+
stop_workers
|
375
417
|
stop
|
418
|
+
|
419
|
+
raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
|
420
|
+
exit 0 # Clean exit, workers were stopped
|
376
421
|
end
|
377
422
|
end
|
378
423
|
end
|
@@ -466,7 +511,7 @@ module Puma
|
|
466
511
|
|
467
512
|
force_check = false
|
468
513
|
|
469
|
-
res = IO.select([read], nil, nil, WORKER_CHECK_INTERVAL)
|
514
|
+
res = IO.select([read], nil, nil, Const::WORKER_CHECK_INTERVAL)
|
470
515
|
|
471
516
|
if res
|
472
517
|
req = read.read_nonblock(1)
|
data/lib/puma/commonlogger.rb
CHANGED
data/lib/puma/configuration.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/rack/builder'
|
2
4
|
require 'puma/plugin'
|
3
5
|
require 'puma/const'
|
@@ -180,30 +182,32 @@ module Puma
|
|
180
182
|
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
181
183
|
:remote_address => :socket,
|
182
184
|
:tag => method(:infer_tag),
|
183
|
-
:environment => ->{ ENV['RACK_ENV'] || "development" },
|
185
|
+
:environment => -> { ENV['RACK_ENV'] || "development" },
|
184
186
|
:rackup => DefaultRackup,
|
185
187
|
:logger => STDOUT,
|
186
|
-
:persistent_timeout => Const::PERSISTENT_TIMEOUT
|
188
|
+
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
189
|
+
:first_data_timeout => Const::FIRST_DATA_TIMEOUT,
|
190
|
+
:raise_exception_on_sigterm => true
|
187
191
|
}
|
188
192
|
end
|
189
193
|
|
190
194
|
def load
|
195
|
+
config_files.each { |config_file| @file_dsl._load_from(config_file) }
|
196
|
+
|
197
|
+
@options
|
198
|
+
end
|
199
|
+
|
200
|
+
def config_files
|
191
201
|
files = @options.all_of(:config_files)
|
192
202
|
|
193
|
-
if files
|
194
|
-
|
195
|
-
File.exist?(f)
|
196
|
-
}
|
203
|
+
return [] if files == ['-']
|
204
|
+
return files if files.any?
|
197
205
|
|
198
|
-
|
199
|
-
|
200
|
-
files = []
|
206
|
+
first_default_file = %W(config/puma/#{environment_str}.rb config/puma.rb).find do |f|
|
207
|
+
File.exist?(f)
|
201
208
|
end
|
202
209
|
|
203
|
-
|
204
|
-
@file_dsl._load_from(f)
|
205
|
-
end
|
206
|
-
@options
|
210
|
+
[first_default_file]
|
207
211
|
end
|
208
212
|
|
209
213
|
# Call once all configuration (included from rackup files)
|
@@ -263,6 +267,10 @@ module Puma
|
|
263
267
|
@options[:environment]
|
264
268
|
end
|
265
269
|
|
270
|
+
def environment_str
|
271
|
+
environment.respond_to?(:call) ? environment.call : environment
|
272
|
+
end
|
273
|
+
|
266
274
|
def load_plugin(name)
|
267
275
|
@plugins.create name
|
268
276
|
end
|
@@ -340,7 +348,7 @@ module Puma
|
|
340
348
|
end
|
341
349
|
|
342
350
|
if bytes
|
343
|
-
token = ""
|
351
|
+
token = "".dup
|
344
352
|
bytes.each_byte { |b| token << b.to_s(16) }
|
345
353
|
else
|
346
354
|
token = (0..count).to_a.map { rand(255).to_s(16) }.join
|
data/lib/puma/const.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
module Puma
|
3
5
|
class UnsupportedOption < RuntimeError
|
4
6
|
end
|
@@ -53,6 +55,8 @@ module Puma
|
|
53
55
|
415 => 'Unsupported Media Type',
|
54
56
|
416 => 'Range Not Satisfiable',
|
55
57
|
417 => 'Expectation Failed',
|
58
|
+
418 => 'I\'m A Teapot',
|
59
|
+
421 => 'Misdirected Request',
|
56
60
|
422 => 'Unprocessable Entity',
|
57
61
|
423 => 'Locked',
|
58
62
|
424 => 'Failed Dependency',
|
@@ -60,6 +64,7 @@ module Puma
|
|
60
64
|
428 => 'Precondition Required',
|
61
65
|
429 => 'Too Many Requests',
|
62
66
|
431 => 'Request Header Fields Too Large',
|
67
|
+
451 => 'Unavailable For Legal Reasons',
|
63
68
|
500 => 'Internal Server Error',
|
64
69
|
501 => 'Not Implemented',
|
65
70
|
502 => 'Bad Gateway',
|
@@ -95,8 +100,8 @@ module Puma
|
|
95
100
|
# too taxing on performance.
|
96
101
|
module Const
|
97
102
|
|
98
|
-
PUMA_VERSION = VERSION = "
|
99
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "4.0.0".freeze
|
104
|
+
CODE_NAME = "4 Fast 4 Furious".freeze
|
100
105
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
101
106
|
|
102
107
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
@@ -220,5 +225,11 @@ module Puma
|
|
220
225
|
HIJACK_P = "rack.hijack?".freeze
|
221
226
|
HIJACK = "rack.hijack".freeze
|
222
227
|
HIJACK_IO = "rack.hijack_io".freeze
|
228
|
+
|
229
|
+
EARLY_HINTS = "rack.early_hints".freeze
|
230
|
+
|
231
|
+
# Mininum interval to checks worker health
|
232
|
+
WORKER_CHECK_INTERVAL = 5
|
233
|
+
|
223
234
|
end
|
224
235
|
end
|
data/lib/puma/control_cli.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'optparse'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
4
|
+
require_relative 'state_file'
|
5
|
+
require_relative 'const'
|
6
|
+
require_relative 'detect'
|
7
|
+
require_relative 'configuration'
|
6
8
|
require 'uri'
|
7
9
|
require 'socket'
|
8
10
|
|
9
11
|
module Puma
|
10
12
|
class ControlCLI
|
11
13
|
|
12
|
-
COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory}
|
14
|
+
COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory gc gc-stats}
|
13
15
|
|
14
16
|
def initialize(argv, stdout=STDOUT, stderr=STDERR)
|
15
17
|
@state = nil
|
@@ -69,6 +71,7 @@ module Puma
|
|
69
71
|
end
|
70
72
|
|
71
73
|
opts.order!(argv) { |a| opts.terminate a }
|
74
|
+
opts.parse!
|
72
75
|
|
73
76
|
@command = argv.shift
|
74
77
|
|
@@ -128,7 +131,7 @@ module Puma
|
|
128
131
|
uri = URI.parse @control_url
|
129
132
|
|
130
133
|
# create server object by scheme
|
131
|
-
|
134
|
+
server = case uri.scheme
|
132
135
|
when "tcp"
|
133
136
|
TCPSocket.new uri.host, uri.port
|
134
137
|
when "unix"
|
@@ -146,9 +149,9 @@ module Puma
|
|
146
149
|
url = url + "?token=#{@control_auth_token}"
|
147
150
|
end
|
148
151
|
|
149
|
-
|
152
|
+
server << "GET #{url} HTTP/1.0\r\n\r\n"
|
150
153
|
|
151
|
-
unless data =
|
154
|
+
unless data = server.read
|
152
155
|
raise "Server closed connection before responding"
|
153
156
|
end
|
154
157
|
|
@@ -169,10 +172,10 @@ module Puma
|
|
169
172
|
end
|
170
173
|
|
171
174
|
message "Command #{@command} sent success"
|
172
|
-
message response.last if @command == "stats"
|
175
|
+
message response.last if @command == "stats" || @command == "gc-stats"
|
173
176
|
end
|
174
|
-
|
175
|
-
|
177
|
+
ensure
|
178
|
+
server.close if server && !server.closed?
|
176
179
|
end
|
177
180
|
|
178
181
|
def send_signal
|
@@ -203,8 +206,17 @@ module Puma
|
|
203
206
|
when "phased-restart"
|
204
207
|
Process.kill "SIGUSR1", @pid
|
205
208
|
|
209
|
+
when "status"
|
210
|
+
begin
|
211
|
+
Process.kill 0, @pid
|
212
|
+
puts "Puma is started"
|
213
|
+
rescue Errno::ESRCH
|
214
|
+
raise "Puma is not running"
|
215
|
+
end
|
216
|
+
|
217
|
+
return
|
218
|
+
|
206
219
|
else
|
207
|
-
message "Puma is started"
|
208
220
|
return
|
209
221
|
end
|
210
222
|
|
@@ -220,7 +232,7 @@ module Puma
|
|
220
232
|
end
|
221
233
|
|
222
234
|
def run
|
223
|
-
start if @command == "start"
|
235
|
+
return start if @command == "start"
|
224
236
|
|
225
237
|
prepare_configuration
|
226
238
|
|
@@ -245,7 +257,7 @@ module Puma
|
|
245
257
|
run_args += ["-S", @state] if @state
|
246
258
|
run_args += ["-q"] if @quiet
|
247
259
|
run_args += ["--pidfile", @pidfile] if @pidfile
|
248
|
-
run_args += ["--control", @control_url] if @control_url
|
260
|
+
run_args += ["--control-url", @control_url] if @control_url
|
249
261
|
run_args += ["--control-token", @control_auth_token] if @control_auth_token
|
250
262
|
run_args += ["-C", @config_file] if @config_file
|
251
263
|
|
data/lib/puma/convenient.rb
CHANGED
data/lib/puma/daemon_ext.rb
CHANGED
data/lib/puma/delegation.rb
CHANGED
data/lib/puma/detect.rb
CHANGED
data/lib/puma/dsl.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'puma/const'
|
4
|
+
|
1
5
|
module Puma
|
2
6
|
# The methods that are available for use inside the config file.
|
3
7
|
# These same methods are used in Puma cli and the rack handler
|
@@ -55,6 +59,14 @@ module Puma
|
|
55
59
|
@plugins.clear
|
56
60
|
end
|
57
61
|
|
62
|
+
def set_default_host(host)
|
63
|
+
@options[:default_host] = host
|
64
|
+
end
|
65
|
+
|
66
|
+
def default_host
|
67
|
+
@options[:default_host] || Configuration::DefaultTCPHost
|
68
|
+
end
|
69
|
+
|
58
70
|
def inject(&blk)
|
59
71
|
instance_eval(&blk)
|
60
72
|
end
|
@@ -93,7 +105,12 @@ module Puma
|
|
93
105
|
end
|
94
106
|
|
95
107
|
if opts[:no_token]
|
96
|
-
|
108
|
+
# We need to use 'none' rather than :none because this value will be
|
109
|
+
# passed on to an instance of OptionParser, which doesn't support
|
110
|
+
# symbols as option values.
|
111
|
+
#
|
112
|
+
# See: https://github.com/puma/puma/issues/1193#issuecomment-305995488
|
113
|
+
auth_token = 'none'
|
97
114
|
else
|
98
115
|
auth_token = opts[:auth_token]
|
99
116
|
auth_token ||= Configuration.random_token
|
@@ -110,18 +127,35 @@ module Puma
|
|
110
127
|
@options[:config_files] << file
|
111
128
|
end
|
112
129
|
|
113
|
-
#
|
114
|
-
# protocols.
|
130
|
+
# Adds a binding for the server to +url+. tcp://, unix://, and ssl:// are the only accepted
|
131
|
+
# protocols. Use query parameters within the url to specify options.
|
132
|
+
#
|
133
|
+
# @note multiple urls can be bound to, calling `bind` does not overwrite previous bindings.
|
134
|
+
#
|
135
|
+
# @example Explicitly the socket backlog depth (default is 1024)
|
136
|
+
# bind('unix:///var/run/puma.sock?backlog=2048')
|
115
137
|
#
|
138
|
+
# @example Set up ssl cert
|
139
|
+
# bind('ssl://127.0.0.1:9292?key=key.key&cert=cert.pem')
|
140
|
+
#
|
141
|
+
# @example Prefer low-latency over higher throughput (via `Socket::TCP_NODELAY`)
|
142
|
+
# bind('tcp://0.0.0.0:9292?low_latency=true')
|
143
|
+
#
|
144
|
+
# @example Set socket permissions
|
145
|
+
# bind('unix:///var/run/puma.sock?umask=0111')
|
116
146
|
def bind(url)
|
117
147
|
@options[:binds] ||= []
|
118
148
|
@options[:binds] << url
|
119
149
|
end
|
120
150
|
|
151
|
+
def clear_binds!
|
152
|
+
@options[:binds] = []
|
153
|
+
end
|
154
|
+
|
121
155
|
# Define the TCP port to bind to. Use +bind+ for more advanced options.
|
122
156
|
#
|
123
157
|
def port(port, host=nil)
|
124
|
-
host ||=
|
158
|
+
host ||= default_host
|
125
159
|
bind "tcp://#{host}:#{port}"
|
126
160
|
end
|
127
161
|
|
@@ -129,7 +163,13 @@ module Puma
|
|
129
163
|
# them
|
130
164
|
#
|
131
165
|
def persistent_timeout(seconds)
|
132
|
-
@options[:persistent_timeout] = seconds
|
166
|
+
@options[:persistent_timeout] = Integer(seconds)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Define how long the tcp socket stays open, if no data has been received
|
170
|
+
#
|
171
|
+
def first_data_timeout(seconds)
|
172
|
+
@options[:first_data_timeout] = Integer(seconds)
|
133
173
|
end
|
134
174
|
|
135
175
|
# Work around leaky apps that leave garbage in Thread locals
|
@@ -146,7 +186,7 @@ module Puma
|
|
146
186
|
end
|
147
187
|
|
148
188
|
# When shutting down, drain the accept socket of pending
|
149
|
-
# connections and
|
189
|
+
# connections and process them. This loops over the accept
|
150
190
|
# socket until there are no more read events and then stops
|
151
191
|
# looking and waits for the requests to finish.
|
152
192
|
def drain_on_shutdown(which=true)
|
@@ -231,6 +271,10 @@ module Puma
|
|
231
271
|
@options[:mode] = :tcp
|
232
272
|
end
|
233
273
|
|
274
|
+
def early_hints(answer=true)
|
275
|
+
@options[:early_hints] = answer
|
276
|
+
end
|
277
|
+
|
234
278
|
# Redirect STDOUT and STDERR to files specified.
|
235
279
|
def stdout_redirect(stdout=nil, stderr=nil, append=false)
|
236
280
|
@options[:redirect_stdout] = stdout
|
@@ -258,12 +302,15 @@ module Puma
|
|
258
302
|
|
259
303
|
def ssl_bind(host, port, opts)
|
260
304
|
verify = opts.fetch(:verify_mode, 'none')
|
305
|
+
no_tlsv1 = opts.fetch(:no_tlsv1, 'false')
|
306
|
+
ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
|
261
307
|
|
262
308
|
if defined?(JRUBY_VERSION)
|
263
309
|
keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
|
264
|
-
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}"
|
310
|
+
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}#{ca_additions}"
|
265
311
|
else
|
266
|
-
|
312
|
+
ssl_cipher_filter = "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" if opts[:ssl_cipher_filter]
|
313
|
+
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}#{ssl_cipher_filter}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}#{ca_additions}"
|
267
314
|
end
|
268
315
|
end
|
269
316
|
|
@@ -338,6 +385,21 @@ module Puma
|
|
338
385
|
|
339
386
|
alias_method :after_worker_boot, :after_worker_fork
|
340
387
|
|
388
|
+
# Code to run out-of-band when the worker is idle.
|
389
|
+
# These hooks run immediately after a request has finished
|
390
|
+
# processing and there are no busy threads on the worker.
|
391
|
+
# The worker doesn't accept new requests until this code finishes.
|
392
|
+
#
|
393
|
+
# This hook is useful for running out-of-band garbage collection
|
394
|
+
# or scheduling asynchronous tasks to execute after a response.
|
395
|
+
#
|
396
|
+
# This can be called multiple times to add hooks.
|
397
|
+
#
|
398
|
+
def out_of_band(&block)
|
399
|
+
@options[:out_of_band] ||= []
|
400
|
+
@options[:out_of_band] << block
|
401
|
+
end
|
402
|
+
|
341
403
|
# The directory to operate out of.
|
342
404
|
def directory(dir)
|
343
405
|
@options[:directory] = dir.to_s
|
@@ -387,6 +449,16 @@ module Puma
|
|
387
449
|
@options[:prune_bundler] = answer
|
388
450
|
end
|
389
451
|
|
452
|
+
# In environments where SIGTERM is something expected, instructing
|
453
|
+
# puma to shutdown gracefully ( for example in Kubernetes, where
|
454
|
+
# rolling restart is guaranteed usually on infrastructure level )
|
455
|
+
# SignalException should not be raised for SIGTERM
|
456
|
+
#
|
457
|
+
# When set to false, if puma process receives SIGTERM, it won't raise SignalException
|
458
|
+
def raise_exception_on_sigterm(answer=true)
|
459
|
+
@options[:raise_exception_on_sigterm] = answer
|
460
|
+
end
|
461
|
+
|
390
462
|
# Additional text to display in process listing
|
391
463
|
def tag(string)
|
392
464
|
@options[:tag] = string.to_s
|
@@ -397,17 +469,24 @@ module Puma
|
|
397
469
|
# that have not checked in within the given +timeout+.
|
398
470
|
# This mitigates hung processes. Default value is 60 seconds.
|
399
471
|
def worker_timeout(timeout)
|
400
|
-
|
472
|
+
timeout = Integer(timeout)
|
473
|
+
min = Const::WORKER_CHECK_INTERVAL
|
474
|
+
|
475
|
+
if timeout <= min
|
476
|
+
raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
|
477
|
+
end
|
478
|
+
|
479
|
+
@options[:worker_timeout] = Integer(timeout)
|
401
480
|
end
|
402
481
|
|
403
482
|
# *Cluster mode only* Set the timeout for workers to boot
|
404
483
|
def worker_boot_timeout(timeout)
|
405
|
-
@options[:worker_boot_timeout] = timeout
|
484
|
+
@options[:worker_boot_timeout] = Integer(timeout)
|
406
485
|
end
|
407
486
|
|
408
487
|
# *Cluster mode only* Set the timeout for worker shutdown
|
409
488
|
def worker_shutdown_timeout(timeout)
|
410
|
-
@options[:worker_shutdown_timeout] = timeout
|
489
|
+
@options[:worker_shutdown_timeout] = Integer(timeout)
|
411
490
|
end
|
412
491
|
|
413
492
|
# When set to true (the default), workers accept all requests
|
@@ -466,7 +545,7 @@ module Puma
|
|
466
545
|
when Hash
|
467
546
|
if hdr = val[:header]
|
468
547
|
@options[:remote_address] = :header
|
469
|
-
@options[:remote_address_header] = "HTTP_" + hdr.upcase.
|
548
|
+
@options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
|
470
549
|
else
|
471
550
|
raise "Invalid value for set_remote_address - #{val.inspect}"
|
472
551
|
end
|