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/binder.rb
CHANGED
@@ -5,16 +5,32 @@ require 'socket'
|
|
5
5
|
|
6
6
|
require 'puma/const'
|
7
7
|
require 'puma/util'
|
8
|
-
require 'puma/
|
8
|
+
require 'puma/configuration'
|
9
9
|
|
10
10
|
module Puma
|
11
|
+
|
12
|
+
if HAS_SSL
|
13
|
+
require 'puma/minissl'
|
14
|
+
require 'puma/minissl/context_builder'
|
15
|
+
|
16
|
+
# Odd bug in 'pure Ruby' nio4r version 2.5.2, which installs with Ruby 2.3.
|
17
|
+
# NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
|
18
|
+
# The bug was that it did not require openssl.
|
19
|
+
# @todo remove when Ruby 2.3 support is dropped
|
20
|
+
#
|
21
|
+
if windows? && RbConfig::CONFIG['ruby_version'] == '2.3.0'
|
22
|
+
require 'openssl'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
11
26
|
class Binder
|
12
27
|
include Puma::Const
|
13
28
|
|
14
|
-
RACK_VERSION = [1,
|
29
|
+
RACK_VERSION = [1,6].freeze
|
15
30
|
|
16
|
-
def initialize(events)
|
31
|
+
def initialize(events, conf = Configuration.new)
|
17
32
|
@events = events
|
33
|
+
@conf = conf
|
18
34
|
@listeners = []
|
19
35
|
@inherited_fds = {}
|
20
36
|
@activated_sockets = {}
|
@@ -23,9 +39,10 @@ module Puma
|
|
23
39
|
@proto_env = {
|
24
40
|
"rack.version".freeze => RACK_VERSION,
|
25
41
|
"rack.errors".freeze => events.stderr,
|
26
|
-
"rack.multithread".freeze =>
|
27
|
-
"rack.multiprocess".freeze =>
|
42
|
+
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
43
|
+
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
28
44
|
"rack.run_once".freeze => false,
|
45
|
+
RACK_URL_SCHEME => conf.options[:rack_url_scheme],
|
29
46
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
30
47
|
|
31
48
|
# I'd like to set a default CONTENT_TYPE here but some things
|
@@ -41,10 +58,17 @@ module Puma
|
|
41
58
|
|
42
59
|
@envs = {}
|
43
60
|
@ios = []
|
61
|
+
localhost_authority
|
44
62
|
end
|
45
63
|
|
46
64
|
attr_reader :ios
|
47
65
|
|
66
|
+
# @version 5.0.0
|
67
|
+
attr_reader :activated_sockets, :envs, :inherited_fds, :listeners, :proto_env, :unix_paths
|
68
|
+
|
69
|
+
# @version 5.0.0
|
70
|
+
attr_writer :ios, :listeners
|
71
|
+
|
48
72
|
def env(sock)
|
49
73
|
@envs.fetch(sock, @proto_env)
|
50
74
|
end
|
@@ -53,40 +77,82 @@ module Puma
|
|
53
77
|
@ios.each { |i| i.close }
|
54
78
|
end
|
55
79
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
80
|
+
# @!attribute [r] connected_ports
|
81
|
+
# @version 5.0.0
|
82
|
+
def connected_ports
|
83
|
+
ios.map { |io| io.addr[1] }.uniq
|
84
|
+
end
|
85
|
+
|
86
|
+
# @version 5.0.0
|
87
|
+
def create_inherited_fds(env_hash)
|
88
|
+
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
89
|
+
fd, url = v.split(":", 2)
|
90
|
+
@inherited_fds[url] = fd.to_i
|
91
|
+
end.keys # pass keys back for removal
|
92
|
+
end
|
93
|
+
|
94
|
+
# systemd socket activation.
|
95
|
+
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
96
|
+
# LISTEN_PID = PID of the service process, aka us
|
97
|
+
# @see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
98
|
+
# @version 5.0.0
|
99
|
+
#
|
100
|
+
def create_activated_fds(env_hash)
|
101
|
+
@events.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
|
102
|
+
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
103
|
+
env_hash['LISTEN_FDS'].to_i.times do |index|
|
104
|
+
sock = TCPServer.for_fd(socket_activation_fd(index))
|
105
|
+
key = begin # Try to parse as a path
|
106
|
+
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
107
|
+
rescue ArgumentError # Try to parse as a port/ip
|
108
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
109
|
+
addr = "[#{addr}]" if addr =~ /\:/
|
110
|
+
[:tcp, addr, port]
|
111
|
+
end
|
112
|
+
@activated_sockets[key] = sock
|
113
|
+
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
114
|
+
end
|
115
|
+
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
116
|
+
end
|
117
|
+
|
118
|
+
# Synthesize binds from systemd socket activation
|
119
|
+
#
|
120
|
+
# When systemd socket activation is enabled, it can be tedious to keep the
|
121
|
+
# binds in sync. This method can synthesize any binds based on the received
|
122
|
+
# activated sockets. Any existing matching binds will be respected.
|
123
|
+
#
|
124
|
+
# When only_matching is true in, all binds that do not match an activated
|
125
|
+
# socket is removed in place.
|
126
|
+
#
|
127
|
+
# It's a noop if no activated sockets were received.
|
128
|
+
def synthesize_binds_from_activated_fs(binds, only_matching)
|
129
|
+
return binds unless activated_sockets.any?
|
130
|
+
|
131
|
+
activated_binds = []
|
132
|
+
|
133
|
+
activated_sockets.keys.each do |proto, addr, port|
|
134
|
+
if port
|
135
|
+
tcp_url = "#{proto}://#{addr}:#{port}"
|
136
|
+
ssl_url = "ssl://#{addr}:#{port}"
|
137
|
+
ssl_url_prefix = "#{ssl_url}?"
|
138
|
+
|
139
|
+
existing = binds.find { |bind| bind == tcp_url || bind == ssl_url || bind.start_with?(ssl_url_prefix) }
|
140
|
+
|
141
|
+
activated_binds << (existing || tcp_url)
|
142
|
+
else
|
143
|
+
# TODO: can there be a SSL bind without a port?
|
144
|
+
activated_binds << "#{proto}://#{addr}"
|
81
145
|
end
|
82
146
|
end
|
83
147
|
|
84
|
-
|
85
|
-
|
148
|
+
if only_matching
|
149
|
+
activated_binds
|
150
|
+
else
|
151
|
+
binds | activated_binds
|
86
152
|
end
|
87
153
|
end
|
88
154
|
|
89
|
-
def parse(binds, logger)
|
155
|
+
def parse(binds, logger, log_msg = 'Listening')
|
90
156
|
binds.each do |str|
|
91
157
|
uri = URI.parse str
|
92
158
|
case uri.scheme
|
@@ -98,33 +164,37 @@ module Puma
|
|
98
164
|
io = inherit_tcp_listener uri.host, uri.port, sock
|
99
165
|
logger.log "* Activated #{str}"
|
100
166
|
else
|
167
|
+
ios_len = @ios.length
|
101
168
|
params = Util.parse_query uri.query
|
102
169
|
|
103
|
-
opt = params.key?('low_latency')
|
104
|
-
|
105
|
-
|
106
|
-
io = add_tcp_listener uri.host, uri.port, opt, bak
|
170
|
+
opt = params.key?('low_latency') && params['low_latency'] != 'false'
|
171
|
+
backlog = params.fetch('backlog', 1024).to_i
|
107
172
|
|
108
|
-
|
109
|
-
next unless TCPServer === i
|
110
|
-
addr = if i.local_address.ipv6?
|
111
|
-
"[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
|
112
|
-
else
|
113
|
-
i.local_address.ip_unpack.join(':')
|
114
|
-
end
|
173
|
+
io = add_tcp_listener uri.host, uri.port, opt, backlog
|
115
174
|
|
116
|
-
|
175
|
+
@ios[ios_len..-1].each do |i|
|
176
|
+
addr = loc_addr_str i
|
177
|
+
logger.log "* #{log_msg} on http://#{addr}"
|
117
178
|
end
|
118
179
|
end
|
119
180
|
|
120
181
|
@listeners << [str, io] if io
|
121
182
|
when "unix"
|
122
183
|
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
184
|
+
abstract = false
|
185
|
+
if str.start_with? 'unix://@'
|
186
|
+
raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket?
|
187
|
+
abstract = true
|
188
|
+
path = "@#{path}"
|
189
|
+
end
|
123
190
|
|
124
191
|
if fd = @inherited_fds.delete(str)
|
192
|
+
@unix_paths << path unless abstract
|
125
193
|
io = inherit_unix_listener path, fd
|
126
194
|
logger.log "* Inherited #{str}"
|
127
|
-
elsif sock = @activated_sockets.delete([ :unix, path ])
|
195
|
+
elsif sock = @activated_sockets.delete([ :unix, path ]) ||
|
196
|
+
@activated_sockets.delete([ :unix, File.realdirpath(path) ])
|
197
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
128
198
|
io = inherit_unix_listener path, sock
|
129
199
|
logger.log "* Activated #{str}"
|
130
200
|
else
|
@@ -148,14 +218,36 @@ module Puma
|
|
148
218
|
end
|
149
219
|
end
|
150
220
|
|
221
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
151
222
|
io = add_unix_listener path, umask, mode, backlog
|
152
|
-
logger.log "*
|
223
|
+
logger.log "* #{log_msg} on #{str}"
|
153
224
|
end
|
154
225
|
|
155
226
|
@listeners << [str, io]
|
156
227
|
when "ssl"
|
228
|
+
|
229
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
230
|
+
|
157
231
|
params = Util.parse_query uri.query
|
158
|
-
|
232
|
+
|
233
|
+
# If key and certs are not defined and localhost gem is required.
|
234
|
+
# localhost gem will be used for self signed
|
235
|
+
# Load localhost authority if not loaded.
|
236
|
+
if params.values_at('cert', 'key').all? { |v| v.to_s.empty? }
|
237
|
+
ctx = localhost_authority && localhost_authority_context
|
238
|
+
end
|
239
|
+
|
240
|
+
ctx ||=
|
241
|
+
begin
|
242
|
+
# Extract cert_pem and key_pem from options[:store] if present
|
243
|
+
['cert', 'key'].each do |v|
|
244
|
+
if params[v] && params[v].start_with?('store:')
|
245
|
+
index = Integer(params.delete(v).split('store:').last)
|
246
|
+
params["#{v}_pem"] = @conf.options[:store][index]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
MiniSSL::ContextBuilder.new(params, @events).context
|
250
|
+
end
|
159
251
|
|
160
252
|
if fd = @inherited_fds.delete(str)
|
161
253
|
logger.log "* Inherited #{str}"
|
@@ -164,8 +256,14 @@ module Puma
|
|
164
256
|
io = inherit_ssl_listener sock, ctx
|
165
257
|
logger.log "* Activated #{str}"
|
166
258
|
else
|
167
|
-
|
168
|
-
|
259
|
+
ios_len = @ios.length
|
260
|
+
backlog = params.fetch('backlog', 1024).to_i
|
261
|
+
io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog
|
262
|
+
|
263
|
+
@ios[ios_len..-1].each do |i|
|
264
|
+
addr = loc_addr_str i
|
265
|
+
logger.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
|
266
|
+
end
|
169
267
|
end
|
170
268
|
|
171
269
|
@listeners << [str, io] if io
|
@@ -193,21 +291,35 @@ module Puma
|
|
193
291
|
end
|
194
292
|
|
195
293
|
# Also close any unused activated sockets
|
196
|
-
@activated_sockets.
|
197
|
-
|
198
|
-
|
199
|
-
sock.
|
200
|
-
|
294
|
+
unless @activated_sockets.empty?
|
295
|
+
fds = @ios.map(&:to_i)
|
296
|
+
@activated_sockets.each do |key, sock|
|
297
|
+
next if fds.include? sock.to_i
|
298
|
+
logger.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
|
299
|
+
begin
|
300
|
+
sock.close
|
301
|
+
rescue SystemCallError
|
302
|
+
end
|
303
|
+
# We have to unlink a unix socket path that's not being used
|
304
|
+
File.unlink key[1] if key.first == :unix
|
201
305
|
end
|
202
|
-
# We have to unlink a unix socket path that's not being used
|
203
|
-
File.unlink key[1] if key[0] == :unix
|
204
306
|
end
|
205
307
|
end
|
206
308
|
|
207
|
-
def
|
208
|
-
|
209
|
-
|
210
|
-
|
309
|
+
def localhost_authority
|
310
|
+
@localhost_authority ||= Localhost::Authority.fetch if defined?(Localhost::Authority) && !Puma::IS_JRUBY
|
311
|
+
end
|
312
|
+
|
313
|
+
def localhost_authority_context
|
314
|
+
return unless localhost_authority
|
315
|
+
|
316
|
+
key_path, crt_path = if [:key_path, :certificate_path].all? { |m| localhost_authority.respond_to?(m) }
|
317
|
+
[localhost_authority.key_path, localhost_authority.certificate_path]
|
318
|
+
else
|
319
|
+
local_certificates_path = File.expand_path("~/.localhost")
|
320
|
+
[File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
|
321
|
+
end
|
322
|
+
MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @events).context
|
211
323
|
end
|
212
324
|
|
213
325
|
# Tell the server to listen on host +host+, port +port+.
|
@@ -226,26 +338,20 @@ module Puma
|
|
226
338
|
end
|
227
339
|
|
228
340
|
host = host[1..-2] if host and host[0..0] == '['
|
229
|
-
|
341
|
+
tcp_server = TCPServer.new(host, port)
|
342
|
+
|
230
343
|
if optimize_for_latency
|
231
|
-
|
344
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
232
345
|
end
|
233
|
-
|
234
|
-
|
235
|
-
@connected_port = s.addr[1]
|
346
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
347
|
+
tcp_server.listen backlog
|
236
348
|
|
237
|
-
@ios <<
|
238
|
-
|
349
|
+
@ios << tcp_server
|
350
|
+
tcp_server
|
239
351
|
end
|
240
352
|
|
241
|
-
attr_reader :connected_port
|
242
|
-
|
243
353
|
def inherit_tcp_listener(host, port, fd)
|
244
|
-
|
245
|
-
s = fd
|
246
|
-
else
|
247
|
-
s = TCPServer.for_fd(fd)
|
248
|
-
end
|
354
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
249
355
|
|
250
356
|
@ios << s
|
251
357
|
s
|
@@ -253,9 +359,10 @@ module Puma
|
|
253
359
|
|
254
360
|
def add_ssl_listener(host, port, ctx,
|
255
361
|
optimize_for_latency=true, backlog=1024)
|
256
|
-
require 'puma/minissl'
|
257
362
|
|
258
|
-
|
363
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
364
|
+
# Puma will try to use local authority context if context is supplied nil
|
365
|
+
ctx ||= localhost_authority_context
|
259
366
|
|
260
367
|
if host == "localhost"
|
261
368
|
loopback_addresses.each do |addr|
|
@@ -272,7 +379,6 @@ module Puma
|
|
272
379
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
273
380
|
s.listen backlog
|
274
381
|
|
275
|
-
|
276
382
|
ssl = MiniSSL::Server.new s, ctx
|
277
383
|
env = @proto_env.dup
|
278
384
|
env[HTTPS_KEY] = HTTPS
|
@@ -283,14 +389,12 @@ module Puma
|
|
283
389
|
end
|
284
390
|
|
285
391
|
def inherit_ssl_listener(fd, ctx)
|
286
|
-
|
287
|
-
|
392
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
393
|
+
# Puma will try to use local authority context if context is supplied nil
|
394
|
+
ctx ||= localhost_authority_context
|
395
|
+
|
396
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
288
397
|
|
289
|
-
if fd.kind_of? TCPServer
|
290
|
-
s = fd
|
291
|
-
else
|
292
|
-
s = TCPServer.for_fd(fd)
|
293
|
-
end
|
294
398
|
ssl = MiniSSL::Server.new(s, ctx)
|
295
399
|
|
296
400
|
env = @proto_env.dup
|
@@ -305,8 +409,6 @@ module Puma
|
|
305
409
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
306
410
|
#
|
307
411
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
308
|
-
@unix_paths << path unless File.exist? path
|
309
|
-
|
310
412
|
# Let anyone connect by default
|
311
413
|
umask ||= 0
|
312
414
|
|
@@ -323,8 +425,7 @@ module Puma
|
|
323
425
|
raise "There is already a server bound to: #{path}"
|
324
426
|
end
|
325
427
|
end
|
326
|
-
|
327
|
-
s = UNIXServer.new(path)
|
428
|
+
s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
|
328
429
|
s.listen backlog
|
329
430
|
@ios << s
|
330
431
|
ensure
|
@@ -343,13 +444,8 @@ module Puma
|
|
343
444
|
end
|
344
445
|
|
345
446
|
def inherit_unix_listener(path, fd)
|
346
|
-
|
447
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
|
347
448
|
|
348
|
-
if fd.kind_of? TCPServer
|
349
|
-
s = fd
|
350
|
-
else
|
351
|
-
s = UNIXServer.for_fd fd
|
352
|
-
end
|
353
449
|
@ios << s
|
354
450
|
|
355
451
|
env = @proto_env.dup
|
@@ -361,25 +457,48 @@ module Puma
|
|
361
457
|
|
362
458
|
def close_listeners
|
363
459
|
@listeners.each do |l, io|
|
364
|
-
io.close
|
365
|
-
uri = URI.parse
|
460
|
+
io.close unless io.closed?
|
461
|
+
uri = URI.parse l
|
366
462
|
next unless uri.scheme == 'unix'
|
367
463
|
unix_path = "#{uri.host}#{uri.path}"
|
368
|
-
File.unlink unix_path if @unix_paths.include? unix_path
|
464
|
+
File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
|
369
465
|
end
|
370
466
|
end
|
371
467
|
|
372
|
-
def
|
373
|
-
@
|
468
|
+
def redirects_for_restart
|
469
|
+
redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
470
|
+
redirects[:close_others] = true
|
471
|
+
redirects
|
374
472
|
end
|
375
473
|
|
376
|
-
|
377
|
-
|
378
|
-
@listeners.
|
379
|
-
|
380
|
-
redirects[io.to_i] = io.to_i
|
474
|
+
# @version 5.0.0
|
475
|
+
def redirects_for_restart_env
|
476
|
+
@listeners.each_with_object({}).with_index do |(listen, memo), i|
|
477
|
+
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
381
478
|
end
|
382
|
-
|
479
|
+
end
|
480
|
+
|
481
|
+
private
|
482
|
+
|
483
|
+
# @!attribute [r] loopback_addresses
|
484
|
+
def loopback_addresses
|
485
|
+
Socket.ip_address_list.select do |addrinfo|
|
486
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
487
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
488
|
+
end
|
489
|
+
|
490
|
+
def loc_addr_str(io)
|
491
|
+
loc_addr = io.to_io.local_address
|
492
|
+
if loc_addr.ipv6?
|
493
|
+
"[#{loc_addr.ip_unpack[0]}]:#{loc_addr.ip_unpack[1]}"
|
494
|
+
else
|
495
|
+
loc_addr.ip_unpack.join(':')
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
# @version 5.0.0
|
500
|
+
def socket_activation_fd(int)
|
501
|
+
int + 3 # 3 is the magic number you add to follow the SA protocol
|
383
502
|
end
|
384
503
|
end
|
385
504
|
end
|
data/lib/puma/cli.rb
CHANGED
@@ -11,16 +11,17 @@ require 'puma/events'
|
|
11
11
|
|
12
12
|
module Puma
|
13
13
|
class << self
|
14
|
-
# The CLI exports
|
15
|
-
# apps to pick it up. An app
|
16
|
-
#
|
17
|
-
#
|
14
|
+
# The CLI exports a Puma::Configuration instance here to allow
|
15
|
+
# apps to pick it up. An app must load this object conditionally
|
16
|
+
# because it is not set if the app is launched via any mechanism
|
17
|
+
# other than the CLI class.
|
18
18
|
attr_accessor :cli_config
|
19
19
|
end
|
20
20
|
|
21
21
|
# Handles invoke a Puma::Server in a command line style.
|
22
22
|
#
|
23
23
|
class CLI
|
24
|
+
# @deprecated 6.0.0
|
24
25
|
KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
|
25
26
|
|
26
27
|
# Create a new CLI object using +argv+ as the command line
|
@@ -80,7 +81,7 @@ module Puma
|
|
80
81
|
@launcher.run
|
81
82
|
end
|
82
83
|
|
83
|
-
|
84
|
+
private
|
84
85
|
def unsupported(str)
|
85
86
|
@events.error(str)
|
86
87
|
raise UnsupportedOption
|
@@ -104,16 +105,20 @@ module Puma
|
|
104
105
|
user_config.bind arg
|
105
106
|
end
|
106
107
|
|
108
|
+
o.on "--bind-to-activated-sockets [only]", "Bind to all activated sockets" do |arg|
|
109
|
+
user_config.bind_to_activated_sockets(arg || true)
|
110
|
+
end
|
111
|
+
|
107
112
|
o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
|
108
113
|
file_config.load arg
|
109
114
|
end
|
110
115
|
|
111
|
-
|
112
|
-
|
116
|
+
# Identical to supplying --config "-", but more semantic
|
117
|
+
o.on "--no-config", "Prevent Puma from searching for a config file" do |arg|
|
118
|
+
file_config.load "-"
|
113
119
|
end
|
114
120
|
|
115
|
-
|
116
|
-
o.on "--control URL", "DEPRECATED alias for --control-url" do |arg|
|
121
|
+
o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
|
117
122
|
configure_control_url(arg)
|
118
123
|
end
|
119
124
|
|
@@ -122,11 +127,6 @@ module Puma
|
|
122
127
|
@control_options[:auth_token] = arg
|
123
128
|
end
|
124
129
|
|
125
|
-
o.on "-d", "--daemon", "Daemonize the server into the background" do
|
126
|
-
user_config.daemonize
|
127
|
-
user_config.quiet
|
128
|
-
end
|
129
|
-
|
130
130
|
o.on "--debug", "Log lowlevel debugging information" do
|
131
131
|
user_config.debug
|
132
132
|
end
|
@@ -140,6 +140,12 @@ module Puma
|
|
140
140
|
user_config.environment arg
|
141
141
|
end
|
142
142
|
|
143
|
+
o.on "-f", "--fork-worker=[REQUESTS]", OptionParser::DecimalInteger,
|
144
|
+
"Fork new workers from existing worker. Cluster mode only",
|
145
|
+
"Auto-refork after REQUESTS (default 1000)" do |*args|
|
146
|
+
user_config.fork_worker(*args.compact)
|
147
|
+
end
|
148
|
+
|
143
149
|
o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
|
144
150
|
$LOAD_PATH.unshift(*arg.split(':'))
|
145
151
|
end
|
@@ -179,6 +185,10 @@ module Puma
|
|
179
185
|
user_config.restart_command cmd
|
180
186
|
end
|
181
187
|
|
188
|
+
o.on "-s", "--silent", "Do not log prompt messages other than errors" do
|
189
|
+
@events = Events.new NullIO.new, $stderr
|
190
|
+
end
|
191
|
+
|
182
192
|
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
183
193
|
user_config.state_path arg
|
184
194
|
end
|
@@ -192,10 +202,6 @@ module Puma
|
|
192
202
|
end
|
193
203
|
end
|
194
204
|
|
195
|
-
o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
|
196
|
-
user_config.tcp_mode!
|
197
|
-
end
|
198
|
-
|
199
205
|
o.on "--early-hints", "Enable early hints support" do
|
200
206
|
user_config.early_hints
|
201
207
|
end
|