puma 3.12.0 → 5.3.1
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 +1413 -439
- data/LICENSE +23 -20
- data/README.md +131 -60
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +24 -19
- data/docs/compile_options.md +19 -0
- data/docs/deployment.md +38 -13
- 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 +20 -10
- data/docs/rails_dev_mode.md +29 -0
- data/docs/restart.md +47 -22
- data/docs/signals.md +7 -6
- data/docs/stats.md +142 -0
- data/docs/systemd.md +48 -70
- data/ext/puma_http11/PumaHttp11Service.java +2 -2
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +27 -0
- data/ext/puma_http11/http11_parser.c +84 -109
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +22 -38
- data/ext/puma_http11/http11_parser.rl +4 -2
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +262 -87
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +89 -106
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +92 -22
- data/ext/puma_http11/puma_http11.c +34 -50
- data/lib/puma/app/status.rb +68 -49
- data/lib/puma/binder.rb +197 -144
- data/lib/puma/cli.rb +17 -15
- data/lib/puma/client.rb +257 -226
- data/lib/puma/cluster/worker.rb +176 -0
- data/lib/puma/cluster/worker_handle.rb +90 -0
- data/lib/puma/cluster.rb +223 -212
- data/lib/puma/commonlogger.rb +4 -2
- data/lib/puma/configuration.rb +58 -51
- data/lib/puma/const.rb +41 -19
- data/lib/puma/control_cli.rb +117 -73
- data/lib/puma/detect.rb +26 -3
- data/lib/puma/dsl.rb +531 -123
- data/lib/puma/error_logger.rb +104 -0
- data/lib/puma/events.rb +57 -31
- data/lib/puma/io_buffer.rb +9 -5
- data/lib/puma/jruby_restart.rb +2 -58
- data/lib/puma/json.rb +96 -0
- data/lib/puma/launcher.rb +182 -70
- data/lib/puma/minissl/context_builder.rb +79 -0
- data/lib/puma/minissl.rb +149 -48
- data/lib/puma/null_io.rb +15 -1
- data/lib/puma/plugin/tmp_restart.rb +2 -0
- data/lib/puma/plugin.rb +8 -12
- data/lib/puma/queue_close.rb +26 -0
- data/lib/puma/rack/builder.rb +4 -5
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +2 -0
- data/lib/puma/reactor.rb +87 -316
- data/lib/puma/request.rb +456 -0
- data/lib/puma/runner.rb +33 -52
- data/lib/puma/server.rb +288 -679
- data/lib/puma/single.rb +13 -67
- data/lib/puma/state_file.rb +10 -3
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +131 -81
- data/lib/puma/util.rb +14 -6
- data/lib/puma.rb +54 -0
- data/lib/rack/handler/puma.rb +8 -6
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +45 -29
- data/ext/puma_http11/io_buffer.c +0 -155
- data/lib/puma/accept_nonblock.rb +0 -23
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/convenient.rb +0 -23
- data/lib/puma/daemon_ext.rb +0 -31
- data/lib/puma/delegation.rb +0 -11
- data/lib/puma/java_io_buffer.rb +0 -45
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
- data/lib/puma/tcp_logger.rb +0 -39
- 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
@@ -1,16 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
require 'socket'
|
3
5
|
|
4
6
|
require 'puma/const'
|
5
7
|
require 'puma/util'
|
8
|
+
require 'puma/configuration'
|
6
9
|
|
7
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
|
+
|
8
26
|
class Binder
|
9
27
|
include Puma::Const
|
10
28
|
|
11
|
-
RACK_VERSION = [1,
|
29
|
+
RACK_VERSION = [1,6].freeze
|
12
30
|
|
13
|
-
def initialize(events)
|
31
|
+
def initialize(events, conf = Configuration.new)
|
14
32
|
@events = events
|
15
33
|
@listeners = []
|
16
34
|
@inherited_fds = {}
|
@@ -20,8 +38,8 @@ module Puma
|
|
20
38
|
@proto_env = {
|
21
39
|
"rack.version".freeze => RACK_VERSION,
|
22
40
|
"rack.errors".freeze => events.stderr,
|
23
|
-
"rack.multithread".freeze =>
|
24
|
-
"rack.multiprocess".freeze =>
|
41
|
+
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
42
|
+
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
25
43
|
"rack.run_once".freeze => false,
|
26
44
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
27
45
|
|
@@ -40,7 +58,13 @@ module Puma
|
|
40
58
|
@ios = []
|
41
59
|
end
|
42
60
|
|
43
|
-
attr_reader :
|
61
|
+
attr_reader :ios
|
62
|
+
|
63
|
+
# @version 5.0.0
|
64
|
+
attr_reader :activated_sockets, :envs, :inherited_fds, :listeners, :proto_env, :unix_paths
|
65
|
+
|
66
|
+
# @version 5.0.0
|
67
|
+
attr_writer :ios, :listeners
|
44
68
|
|
45
69
|
def env(sock)
|
46
70
|
@envs.fetch(sock, @proto_env)
|
@@ -48,76 +72,127 @@ module Puma
|
|
48
72
|
|
49
73
|
def close
|
50
74
|
@ios.each { |i| i.close }
|
51
|
-
@unix_paths.each { |i| File.unlink i }
|
52
75
|
end
|
53
76
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
# @!attribute [r] connected_ports
|
78
|
+
# @version 5.0.0
|
79
|
+
def connected_ports
|
80
|
+
ios.map { |io| io.addr[1] }.uniq
|
81
|
+
end
|
82
|
+
|
83
|
+
# @version 5.0.0
|
84
|
+
def create_inherited_fds(env_hash)
|
85
|
+
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
86
|
+
fd, url = v.split(":", 2)
|
87
|
+
@inherited_fds[url] = fd.to_i
|
88
|
+
end.keys # pass keys back for removal
|
89
|
+
end
|
90
|
+
|
91
|
+
# systemd socket activation.
|
92
|
+
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
93
|
+
# LISTEN_PID = PID of the service process, aka us
|
94
|
+
# @see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
95
|
+
# @version 5.0.0
|
96
|
+
#
|
97
|
+
def create_activated_fds(env_hash)
|
98
|
+
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
99
|
+
env_hash['LISTEN_FDS'].to_i.times do |index|
|
100
|
+
sock = TCPServer.for_fd(socket_activation_fd(index))
|
101
|
+
key = begin # Try to parse as a path
|
102
|
+
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
103
|
+
rescue ArgumentError # Try to parse as a port/ip
|
104
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
105
|
+
addr = "[#{addr}]" if addr =~ /\:/
|
106
|
+
[:tcp, addr, port]
|
107
|
+
end
|
108
|
+
@activated_sockets[key] = sock
|
109
|
+
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
110
|
+
end
|
111
|
+
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
112
|
+
end
|
113
|
+
|
114
|
+
# Synthesize binds from systemd socket activation
|
115
|
+
#
|
116
|
+
# When systemd socket activation is enabled, it can be tedious to keep the
|
117
|
+
# binds in sync. This method can synthesize any binds based on the received
|
118
|
+
# activated sockets. Any existing matching binds will be respected.
|
119
|
+
#
|
120
|
+
# When only_matching is true in, all binds that do not match an activated
|
121
|
+
# socket is removed in place.
|
122
|
+
#
|
123
|
+
# It's a noop if no activated sockets were received.
|
124
|
+
def synthesize_binds_from_activated_fs(binds, only_matching)
|
125
|
+
return binds unless activated_sockets.any?
|
126
|
+
|
127
|
+
activated_binds = []
|
128
|
+
|
129
|
+
activated_sockets.keys.each do |proto, addr, port|
|
130
|
+
if port
|
131
|
+
tcp_url = "#{proto}://#{addr}:#{port}"
|
132
|
+
ssl_url = "ssl://#{addr}:#{port}"
|
133
|
+
ssl_url_prefix = "#{ssl_url}?"
|
134
|
+
|
135
|
+
existing = binds.find { |bind| bind == tcp_url || bind == ssl_url || bind.start_with?(ssl_url_prefix) }
|
136
|
+
|
137
|
+
activated_binds << (existing || tcp_url)
|
138
|
+
else
|
139
|
+
# TODO: can there be a SSL bind without a port?
|
140
|
+
activated_binds << "#{proto}://#{addr}"
|
79
141
|
end
|
80
142
|
end
|
81
143
|
|
82
|
-
|
83
|
-
|
144
|
+
if only_matching
|
145
|
+
activated_binds
|
146
|
+
else
|
147
|
+
binds | activated_binds
|
84
148
|
end
|
85
149
|
end
|
86
150
|
|
87
|
-
def parse(binds, logger)
|
151
|
+
def parse(binds, logger, log_msg = 'Listening')
|
88
152
|
binds.each do |str|
|
89
153
|
uri = URI.parse str
|
90
154
|
case uri.scheme
|
91
155
|
when "tcp"
|
92
156
|
if fd = @inherited_fds.delete(str)
|
93
|
-
logger.log "* Inherited #{str}"
|
94
157
|
io = inherit_tcp_listener uri.host, uri.port, fd
|
158
|
+
logger.log "* Inherited #{str}"
|
95
159
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
96
|
-
logger.log "* Activated #{str}"
|
97
160
|
io = inherit_tcp_listener uri.host, uri.port, sock
|
161
|
+
logger.log "* Activated #{str}"
|
98
162
|
else
|
163
|
+
ios_len = @ios.length
|
99
164
|
params = Util.parse_query uri.query
|
100
165
|
|
101
166
|
opt = params.key?('low_latency')
|
102
167
|
bak = params.fetch('backlog', 1024).to_i
|
103
168
|
|
104
|
-
logger.log "* Listening on #{str}"
|
105
169
|
io = add_tcp_listener uri.host, uri.port, opt, bak
|
170
|
+
|
171
|
+
@ios[ios_len..-1].each do |i|
|
172
|
+
addr = loc_addr_str i
|
173
|
+
logger.log "* #{log_msg} on http://#{addr}"
|
174
|
+
end
|
106
175
|
end
|
107
176
|
|
108
177
|
@listeners << [str, io] if io
|
109
178
|
when "unix"
|
110
179
|
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
180
|
+
abstract = false
|
181
|
+
if str.start_with? 'unix://@'
|
182
|
+
raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket?
|
183
|
+
abstract = true
|
184
|
+
path = "@#{path}"
|
185
|
+
end
|
111
186
|
|
112
187
|
if fd = @inherited_fds.delete(str)
|
113
|
-
|
188
|
+
@unix_paths << path unless abstract
|
114
189
|
io = inherit_unix_listener path, fd
|
190
|
+
logger.log "* Inherited #{str}"
|
115
191
|
elsif sock = @activated_sockets.delete([ :unix, path ])
|
116
|
-
|
192
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
117
193
|
io = inherit_unix_listener path, sock
|
194
|
+
logger.log "* Activated #{str}"
|
118
195
|
else
|
119
|
-
logger.log "* Listening on #{str}"
|
120
|
-
|
121
196
|
umask = nil
|
122
197
|
mode = nil
|
123
198
|
backlog = 1024
|
@@ -138,77 +213,33 @@ module Puma
|
|
138
213
|
end
|
139
214
|
end
|
140
215
|
|
216
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
141
217
|
io = add_unix_listener path, umask, mode, backlog
|
218
|
+
logger.log "* #{log_msg} on #{str}"
|
142
219
|
end
|
143
220
|
|
144
221
|
@listeners << [str, io]
|
145
222
|
when "ssl"
|
146
|
-
params = Util.parse_query uri.query
|
147
|
-
require 'puma/minissl'
|
148
|
-
|
149
|
-
MiniSSL.check
|
150
223
|
|
151
|
-
|
224
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
152
225
|
|
153
|
-
|
154
|
-
|
155
|
-
@events.error "Please specify the Java keystore via 'keystore='"
|
156
|
-
end
|
157
|
-
|
158
|
-
ctx.keystore = params['keystore']
|
159
|
-
|
160
|
-
unless params['keystore-pass']
|
161
|
-
@events.error "Please specify the Java keystore password via 'keystore-pass='"
|
162
|
-
end
|
163
|
-
|
164
|
-
ctx.keystore_pass = params['keystore-pass']
|
165
|
-
ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
|
166
|
-
else
|
167
|
-
unless params['key']
|
168
|
-
@events.error "Please specify the SSL key via 'key='"
|
169
|
-
end
|
170
|
-
|
171
|
-
ctx.key = params['key']
|
172
|
-
|
173
|
-
unless params['cert']
|
174
|
-
@events.error "Please specify the SSL cert via 'cert='"
|
175
|
-
end
|
176
|
-
|
177
|
-
ctx.cert = params['cert']
|
178
|
-
|
179
|
-
if ['peer', 'force_peer'].include?(params['verify_mode'])
|
180
|
-
unless params['ca']
|
181
|
-
@events.error "Please specify the SSL ca via 'ca='"
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
ctx.ca = params['ca'] if params['ca']
|
186
|
-
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
187
|
-
end
|
188
|
-
|
189
|
-
if params['verify_mode']
|
190
|
-
ctx.verify_mode = case params['verify_mode']
|
191
|
-
when "peer"
|
192
|
-
MiniSSL::VERIFY_PEER
|
193
|
-
when "force_peer"
|
194
|
-
MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
195
|
-
when "none"
|
196
|
-
MiniSSL::VERIFY_NONE
|
197
|
-
else
|
198
|
-
@events.error "Please specify a valid verify_mode="
|
199
|
-
MiniSSL::VERIFY_NONE
|
200
|
-
end
|
201
|
-
end
|
226
|
+
params = Util.parse_query uri.query
|
227
|
+
ctx = MiniSSL::ContextBuilder.new(params, @events).context
|
202
228
|
|
203
229
|
if fd = @inherited_fds.delete(str)
|
204
230
|
logger.log "* Inherited #{str}"
|
205
231
|
io = inherit_ssl_listener fd, ctx
|
206
232
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
207
|
-
logger.log "* Activated #{str}"
|
208
233
|
io = inherit_ssl_listener sock, ctx
|
234
|
+
logger.log "* Activated #{str}"
|
209
235
|
else
|
210
|
-
|
236
|
+
ios_len = @ios.length
|
211
237
|
io = add_ssl_listener uri.host, uri.port, ctx
|
238
|
+
|
239
|
+
@ios[ios_len..-1].each do |i|
|
240
|
+
addr = loc_addr_str i
|
241
|
+
logger.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
|
242
|
+
end
|
212
243
|
end
|
213
244
|
|
214
245
|
@listeners << [str, io] if io
|
@@ -236,23 +267,21 @@ module Puma
|
|
236
267
|
end
|
237
268
|
|
238
269
|
# Also close any unused activated sockets
|
239
|
-
@activated_sockets.
|
240
|
-
|
241
|
-
|
242
|
-
sock.
|
243
|
-
|
270
|
+
unless @activated_sockets.empty?
|
271
|
+
fds = @ios.map(&:to_i)
|
272
|
+
@activated_sockets.each do |key, sock|
|
273
|
+
next if fds.include? sock.to_i
|
274
|
+
logger.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
|
275
|
+
begin
|
276
|
+
sock.close
|
277
|
+
rescue SystemCallError
|
278
|
+
end
|
279
|
+
# We have to unlink a unix socket path that's not being used
|
280
|
+
File.unlink key[1] if key.first == :unix
|
244
281
|
end
|
245
|
-
# We have to unlink a unix socket path that's not being used
|
246
|
-
File.unlink key[1] if key[0] == :unix
|
247
282
|
end
|
248
283
|
end
|
249
284
|
|
250
|
-
def loopback_addresses
|
251
|
-
Socket.ip_address_list.select do |addrinfo|
|
252
|
-
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
253
|
-
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
254
|
-
end
|
255
|
-
|
256
285
|
# Tell the server to listen on host +host+, port +port+.
|
257
286
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
258
287
|
# will be optimized for latency over throughput.
|
@@ -269,26 +298,19 @@ module Puma
|
|
269
298
|
end
|
270
299
|
|
271
300
|
host = host[1..-2] if host and host[0..0] == '['
|
272
|
-
|
301
|
+
tcp_server = TCPServer.new(host, port)
|
273
302
|
if optimize_for_latency
|
274
|
-
|
303
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
275
304
|
end
|
276
|
-
|
277
|
-
|
278
|
-
@connected_port = s.addr[1]
|
305
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
306
|
+
tcp_server.listen backlog
|
279
307
|
|
280
|
-
@ios <<
|
281
|
-
|
308
|
+
@ios << tcp_server
|
309
|
+
tcp_server
|
282
310
|
end
|
283
311
|
|
284
|
-
attr_reader :connected_port
|
285
|
-
|
286
312
|
def inherit_tcp_listener(host, port, fd)
|
287
|
-
|
288
|
-
s = fd
|
289
|
-
else
|
290
|
-
s = TCPServer.for_fd(fd)
|
291
|
-
end
|
313
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
292
314
|
|
293
315
|
@ios << s
|
294
316
|
s
|
@@ -296,9 +318,8 @@ module Puma
|
|
296
318
|
|
297
319
|
def add_ssl_listener(host, port, ctx,
|
298
320
|
optimize_for_latency=true, backlog=1024)
|
299
|
-
require 'puma/minissl'
|
300
321
|
|
301
|
-
|
322
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
302
323
|
|
303
324
|
if host == "localhost"
|
304
325
|
loopback_addresses.each do |addr|
|
@@ -315,7 +336,6 @@ module Puma
|
|
315
336
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
316
337
|
s.listen backlog
|
317
338
|
|
318
|
-
|
319
339
|
ssl = MiniSSL::Server.new s, ctx
|
320
340
|
env = @proto_env.dup
|
321
341
|
env[HTTPS_KEY] = HTTPS
|
@@ -326,14 +346,10 @@ module Puma
|
|
326
346
|
end
|
327
347
|
|
328
348
|
def inherit_ssl_listener(fd, ctx)
|
329
|
-
|
330
|
-
|
349
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
350
|
+
|
351
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
331
352
|
|
332
|
-
if fd.kind_of? TCPServer
|
333
|
-
s = fd
|
334
|
-
else
|
335
|
-
s = TCPServer.for_fd(fd)
|
336
|
-
end
|
337
353
|
ssl = MiniSSL::Server.new(s, ctx)
|
338
354
|
|
339
355
|
env = @proto_env.dup
|
@@ -348,8 +364,6 @@ module Puma
|
|
348
364
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
349
365
|
#
|
350
366
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
351
|
-
@unix_paths << path
|
352
|
-
|
353
367
|
# Let anyone connect by default
|
354
368
|
umask ||= 0
|
355
369
|
|
@@ -366,8 +380,7 @@ module Puma
|
|
366
380
|
raise "There is already a server bound to: #{path}"
|
367
381
|
end
|
368
382
|
end
|
369
|
-
|
370
|
-
s = UNIXServer.new(path)
|
383
|
+
s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
|
371
384
|
s.listen backlog
|
372
385
|
@ios << s
|
373
386
|
ensure
|
@@ -386,13 +399,8 @@ module Puma
|
|
386
399
|
end
|
387
400
|
|
388
401
|
def inherit_unix_listener(path, fd)
|
389
|
-
|
402
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
|
390
403
|
|
391
|
-
if fd.kind_of? TCPServer
|
392
|
-
s = fd
|
393
|
-
else
|
394
|
-
s = UNIXServer.for_fd fd
|
395
|
-
end
|
396
404
|
@ios << s
|
397
405
|
|
398
406
|
env = @proto_env.dup
|
@@ -402,5 +410,50 @@ module Puma
|
|
402
410
|
s
|
403
411
|
end
|
404
412
|
|
413
|
+
def close_listeners
|
414
|
+
@listeners.each do |l, io|
|
415
|
+
io.close unless io.closed?
|
416
|
+
uri = URI.parse l
|
417
|
+
next unless uri.scheme == 'unix'
|
418
|
+
unix_path = "#{uri.host}#{uri.path}"
|
419
|
+
File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def redirects_for_restart
|
424
|
+
redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
425
|
+
redirects[:close_others] = true
|
426
|
+
redirects
|
427
|
+
end
|
428
|
+
|
429
|
+
# @version 5.0.0
|
430
|
+
def redirects_for_restart_env
|
431
|
+
@listeners.each_with_object({}).with_index do |(listen, memo), i|
|
432
|
+
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
private
|
437
|
+
|
438
|
+
# @!attribute [r] loopback_addresses
|
439
|
+
def loopback_addresses
|
440
|
+
Socket.ip_address_list.select do |addrinfo|
|
441
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
442
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
443
|
+
end
|
444
|
+
|
445
|
+
def loc_addr_str(io)
|
446
|
+
loc_addr = io.to_io.local_address
|
447
|
+
if loc_addr.ipv6?
|
448
|
+
"[#{loc_addr.ip_unpack[0]}]:#{loc_addr.ip_unpack[1]}"
|
449
|
+
else
|
450
|
+
loc_addr.ip_unpack.join(':')
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
# @version 5.0.0
|
455
|
+
def socket_activation_fd(int)
|
456
|
+
int + 3 # 3 is the magic number you add to follow the SA protocol
|
457
|
+
end
|
405
458
|
end
|
406
459
|
end
|
data/lib/puma/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'optparse'
|
2
4
|
require 'uri'
|
3
5
|
|
@@ -78,7 +80,7 @@ module Puma
|
|
78
80
|
@launcher.run
|
79
81
|
end
|
80
82
|
|
81
|
-
|
83
|
+
private
|
82
84
|
def unsupported(str)
|
83
85
|
@events.error(str)
|
84
86
|
raise UnsupportedOption
|
@@ -102,6 +104,10 @@ module Puma
|
|
102
104
|
user_config.bind arg
|
103
105
|
end
|
104
106
|
|
107
|
+
o.on "--bind-to-activated-sockets [only]", "Bind to all activated sockets" do |arg|
|
108
|
+
user_config.bind_to_activated_sockets(arg || true)
|
109
|
+
end
|
110
|
+
|
105
111
|
o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
|
106
112
|
file_config.load arg
|
107
113
|
end
|
@@ -110,21 +116,11 @@ module Puma
|
|
110
116
|
configure_control_url(arg)
|
111
117
|
end
|
112
118
|
|
113
|
-
# alias --control-url for backwards-compatibility
|
114
|
-
o.on "--control URL", "DEPRECATED alias for --control-url" do |arg|
|
115
|
-
configure_control_url(arg)
|
116
|
-
end
|
117
|
-
|
118
119
|
o.on "--control-token TOKEN",
|
119
120
|
"The token to use as authentication for the control server" do |arg|
|
120
121
|
@control_options[:auth_token] = arg
|
121
122
|
end
|
122
123
|
|
123
|
-
o.on "-d", "--daemon", "Daemonize the server into the background" do
|
124
|
-
user_config.daemonize
|
125
|
-
user_config.quiet
|
126
|
-
end
|
127
|
-
|
128
124
|
o.on "--debug", "Log lowlevel debugging information" do
|
129
125
|
user_config.debug
|
130
126
|
end
|
@@ -138,6 +134,12 @@ module Puma
|
|
138
134
|
user_config.environment arg
|
139
135
|
end
|
140
136
|
|
137
|
+
o.on "-f", "--fork-worker=[REQUESTS]", OptionParser::DecimalInteger,
|
138
|
+
"Fork new workers from existing worker. Cluster mode only",
|
139
|
+
"Auto-refork after REQUESTS (default 1000)" do |*args|
|
140
|
+
user_config.fork_worker(*args.compact)
|
141
|
+
end
|
142
|
+
|
141
143
|
o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
|
142
144
|
$LOAD_PATH.unshift(*arg.split(':'))
|
143
145
|
end
|
@@ -159,6 +161,10 @@ module Puma
|
|
159
161
|
user_config.prune_bundler
|
160
162
|
end
|
161
163
|
|
164
|
+
o.on "--extra-runtime-dependencies GEM1,GEM2", "Defines any extra needed gems when using --prune-bundler" do |arg|
|
165
|
+
user_config.extra_runtime_dependencies arg.split(',')
|
166
|
+
end
|
167
|
+
|
162
168
|
o.on "-q", "--quiet", "Do not log requests internally (default true)" do
|
163
169
|
user_config.quiet
|
164
170
|
end
|
@@ -186,10 +192,6 @@ module Puma
|
|
186
192
|
end
|
187
193
|
end
|
188
194
|
|
189
|
-
o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
|
190
|
-
user_config.tcp_mode!
|
191
|
-
end
|
192
|
-
|
193
195
|
o.on "--early-hints", "Enable early hints support" do
|
194
196
|
user_config.early_hints
|
195
197
|
end
|