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/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 || File.exist?(path)
|
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
|