puma 4.3.12 → 6.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 +1729 -521
- data/LICENSE +23 -20
- data/README.md +169 -45
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +63 -26
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +60 -69
- data/docs/fork_worker.md +31 -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 +2 -2
- 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 +84 -128
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +49 -12
- data/ext/puma_http11/http11_parser.c +46 -48
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +3 -3
- data/ext/puma_http11/http11_parser.rl +3 -3
- data/ext/puma_http11/http11_parser_common.rl +2 -2
- data/ext/puma_http11/mini_ssl.c +278 -93
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +6 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +4 -6
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
- data/ext/puma_http11/puma_http11.c +46 -57
- data/lib/puma/app/status.rb +53 -39
- data/lib/puma/binder.rb +237 -121
- data/lib/puma/cli.rb +34 -34
- data/lib/puma/client.rb +172 -98
- data/lib/puma/cluster/worker.rb +180 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +226 -231
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +114 -87
- data/lib/puma/const.rb +139 -95
- data/lib/puma/control_cli.rb +99 -79
- data/lib/puma/detect.rb +33 -2
- data/lib/puma/dsl.rb +516 -110
- data/lib/puma/error_logger.rb +113 -0
- data/lib/puma/events.rb +16 -115
- data/lib/puma/io_buffer.rb +44 -2
- data/lib/puma/jruby_restart.rb +2 -59
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +164 -155
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +36 -19
- data/lib/puma/minissl.rb +230 -55
- data/lib/puma/null_io.rb +18 -1
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +3 -12
- data/lib/puma/rack/builder.rb +7 -11
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +93 -368
- data/lib/puma/request.rb +671 -0
- data/lib/puma/runner.rb +92 -75
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +321 -794
- data/lib/puma/single.rb +20 -74
- data/lib/puma/state_file.rb +45 -8
- data/lib/puma/thread_pool.rb +140 -68
- data/lib/puma/util.rb +21 -4
- data/lib/puma.rb +54 -7
- data/lib/rack/handler/puma.rb +113 -87
- data/tools/{docker/Dockerfile → Dockerfile} +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +33 -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
@@ -3,18 +3,25 @@
|
|
3
3
|
require 'uri'
|
4
4
|
require 'socket'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
require_relative 'const'
|
7
|
+
require_relative 'util'
|
8
|
+
require_relative 'configuration'
|
9
9
|
|
10
10
|
module Puma
|
11
|
+
|
12
|
+
if HAS_SSL
|
13
|
+
require_relative 'minissl'
|
14
|
+
require_relative 'minissl/context_builder'
|
15
|
+
end
|
16
|
+
|
11
17
|
class Binder
|
12
18
|
include Puma::Const
|
13
19
|
|
14
|
-
RACK_VERSION = [1,
|
20
|
+
RACK_VERSION = [1,6].freeze
|
15
21
|
|
16
|
-
def initialize(
|
17
|
-
@
|
22
|
+
def initialize(log_writer, conf = Configuration.new)
|
23
|
+
@log_writer = log_writer
|
24
|
+
@conf = conf
|
18
25
|
@listeners = []
|
19
26
|
@inherited_fds = {}
|
20
27
|
@activated_sockets = {}
|
@@ -22,10 +29,11 @@ module Puma
|
|
22
29
|
|
23
30
|
@proto_env = {
|
24
31
|
"rack.version".freeze => RACK_VERSION,
|
25
|
-
"rack.errors".freeze =>
|
26
|
-
"rack.multithread".freeze =>
|
27
|
-
"rack.multiprocess".freeze =>
|
32
|
+
"rack.errors".freeze => log_writer.stderr,
|
33
|
+
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
34
|
+
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
28
35
|
"rack.run_once".freeze => false,
|
36
|
+
RACK_URL_SCHEME => conf.options[:rack_url_scheme],
|
29
37
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
30
38
|
|
31
39
|
# I'd like to set a default CONTENT_TYPE here but some things
|
@@ -34,7 +42,6 @@ module Puma
|
|
34
42
|
# infer properly.
|
35
43
|
|
36
44
|
"QUERY_STRING".freeze => "",
|
37
|
-
SERVER_PROTOCOL => HTTP_11,
|
38
45
|
SERVER_SOFTWARE => PUMA_SERVER_STRING,
|
39
46
|
GATEWAY_INTERFACE => CGI_VER
|
40
47
|
}
|
@@ -45,6 +52,12 @@ module Puma
|
|
45
52
|
|
46
53
|
attr_reader :ios
|
47
54
|
|
55
|
+
# @version 5.0.0
|
56
|
+
attr_reader :activated_sockets, :envs, :inherited_fds, :listeners, :proto_env, :unix_paths
|
57
|
+
|
58
|
+
# @version 5.0.0
|
59
|
+
attr_writer :ios, :listeners
|
60
|
+
|
48
61
|
def env(sock)
|
49
62
|
@envs.fetch(sock, @proto_env)
|
50
63
|
end
|
@@ -53,80 +66,127 @@ module Puma
|
|
53
66
|
@ios.each { |i| i.close }
|
54
67
|
end
|
55
68
|
|
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
|
-
|
69
|
+
# @!attribute [r] connected_ports
|
70
|
+
# @version 5.0.0
|
71
|
+
def connected_ports
|
72
|
+
t = ios.map { |io| io.addr[1] }; t.uniq!; t
|
73
|
+
end
|
74
|
+
|
75
|
+
# @version 5.0.0
|
76
|
+
def create_inherited_fds(env_hash)
|
77
|
+
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
78
|
+
fd, url = v.split(":", 2)
|
79
|
+
@inherited_fds[url] = fd.to_i
|
80
|
+
end.keys # pass keys back for removal
|
81
|
+
end
|
82
|
+
|
83
|
+
# systemd socket activation.
|
84
|
+
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
85
|
+
# LISTEN_PID = PID of the service process, aka us
|
86
|
+
# @see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
87
|
+
# @version 5.0.0
|
88
|
+
#
|
89
|
+
def create_activated_fds(env_hash)
|
90
|
+
@log_writer.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
|
91
|
+
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
92
|
+
env_hash['LISTEN_FDS'].to_i.times do |index|
|
93
|
+
sock = TCPServer.for_fd(socket_activation_fd(index))
|
94
|
+
key = begin # Try to parse as a path
|
95
|
+
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
96
|
+
rescue ArgumentError # Try to parse as a port/ip
|
97
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
98
|
+
addr = "[#{addr}]" if addr&.include? ':'
|
99
|
+
[:tcp, addr, port]
|
100
|
+
end
|
101
|
+
@activated_sockets[key] = sock
|
102
|
+
@log_writer.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
103
|
+
end
|
104
|
+
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
105
|
+
end
|
106
|
+
|
107
|
+
# Synthesize binds from systemd socket activation
|
108
|
+
#
|
109
|
+
# When systemd socket activation is enabled, it can be tedious to keep the
|
110
|
+
# binds in sync. This method can synthesize any binds based on the received
|
111
|
+
# activated sockets. Any existing matching binds will be respected.
|
112
|
+
#
|
113
|
+
# When only_matching is true in, all binds that do not match an activated
|
114
|
+
# socket is removed in place.
|
115
|
+
#
|
116
|
+
# It's a noop if no activated sockets were received.
|
117
|
+
def synthesize_binds_from_activated_fs(binds, only_matching)
|
118
|
+
return binds unless activated_sockets.any?
|
119
|
+
|
120
|
+
activated_binds = []
|
121
|
+
|
122
|
+
activated_sockets.keys.each do |proto, addr, port|
|
123
|
+
if port
|
124
|
+
tcp_url = "#{proto}://#{addr}:#{port}"
|
125
|
+
ssl_url = "ssl://#{addr}:#{port}"
|
126
|
+
ssl_url_prefix = "#{ssl_url}?"
|
127
|
+
|
128
|
+
existing = binds.find { |bind| bind == tcp_url || bind == ssl_url || bind.start_with?(ssl_url_prefix) }
|
129
|
+
|
130
|
+
activated_binds << (existing || tcp_url)
|
131
|
+
else
|
132
|
+
# TODO: can there be a SSL bind without a port?
|
133
|
+
activated_binds << "#{proto}://#{addr}"
|
81
134
|
end
|
82
135
|
end
|
83
136
|
|
84
|
-
|
85
|
-
|
137
|
+
if only_matching
|
138
|
+
activated_binds
|
139
|
+
else
|
140
|
+
binds | activated_binds
|
86
141
|
end
|
87
142
|
end
|
88
143
|
|
89
|
-
def parse(binds,
|
144
|
+
def parse(binds, log_writer = nil, log_msg = 'Listening')
|
145
|
+
log_writer ||= @log_writer
|
90
146
|
binds.each do |str|
|
91
147
|
uri = URI.parse str
|
92
148
|
case uri.scheme
|
93
149
|
when "tcp"
|
94
150
|
if fd = @inherited_fds.delete(str)
|
95
151
|
io = inherit_tcp_listener uri.host, uri.port, fd
|
96
|
-
|
152
|
+
log_writer.log "* Inherited #{str}"
|
97
153
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
98
154
|
io = inherit_tcp_listener uri.host, uri.port, sock
|
99
|
-
|
155
|
+
log_writer.log "* Activated #{str}"
|
100
156
|
else
|
157
|
+
ios_len = @ios.length
|
101
158
|
params = Util.parse_query uri.query
|
102
159
|
|
103
|
-
|
104
|
-
|
160
|
+
low_latency = params.key?('low_latency') && params['low_latency'] != 'false'
|
161
|
+
backlog = params.fetch('backlog', 1024).to_i
|
105
162
|
|
106
|
-
io = add_tcp_listener uri.host, uri.port,
|
163
|
+
io = add_tcp_listener uri.host, uri.port, low_latency, backlog
|
107
164
|
|
108
|
-
@ios.each do |i|
|
109
|
-
|
110
|
-
|
111
|
-
"[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
|
112
|
-
else
|
113
|
-
i.local_address.ip_unpack.join(':')
|
114
|
-
end
|
115
|
-
|
116
|
-
logger.log "* Listening on tcp://#{addr}"
|
165
|
+
@ios[ios_len..-1].each do |i|
|
166
|
+
addr = loc_addr_str i
|
167
|
+
log_writer.log "* #{log_msg} on http://#{addr}"
|
117
168
|
end
|
118
169
|
end
|
119
170
|
|
120
171
|
@listeners << [str, io] if io
|
121
172
|
when "unix"
|
122
173
|
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
174
|
+
abstract = false
|
175
|
+
if str.start_with? 'unix://@'
|
176
|
+
raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket?
|
177
|
+
abstract = true
|
178
|
+
path = "@#{path}"
|
179
|
+
end
|
123
180
|
|
124
181
|
if fd = @inherited_fds.delete(str)
|
182
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
125
183
|
io = inherit_unix_listener path, fd
|
126
|
-
|
127
|
-
elsif sock = @activated_sockets.delete([ :unix, path ])
|
184
|
+
log_writer.log "* Inherited #{str}"
|
185
|
+
elsif sock = @activated_sockets.delete([ :unix, path ]) ||
|
186
|
+
@activated_sockets.delete([ :unix, File.realdirpath(path) ])
|
187
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
128
188
|
io = inherit_unix_listener path, sock
|
129
|
-
|
189
|
+
log_writer.log "* Activated #{str}"
|
130
190
|
else
|
131
191
|
umask = nil
|
132
192
|
mode = nil
|
@@ -148,36 +208,67 @@ module Puma
|
|
148
208
|
end
|
149
209
|
end
|
150
210
|
|
211
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
151
212
|
io = add_unix_listener path, umask, mode, backlog
|
152
|
-
|
213
|
+
log_writer.log "* #{log_msg} on #{str}"
|
153
214
|
end
|
154
215
|
|
155
216
|
@listeners << [str, io]
|
156
217
|
when "ssl"
|
218
|
+
cert_key = %w[cert key]
|
219
|
+
|
220
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
221
|
+
|
157
222
|
params = Util.parse_query uri.query
|
158
|
-
|
223
|
+
|
224
|
+
# If key and certs are not defined and localhost gem is required.
|
225
|
+
# localhost gem will be used for self signed
|
226
|
+
# Load localhost authority if not loaded.
|
227
|
+
# Ruby 3 `values_at` accepts an array, earlier do not
|
228
|
+
if params.values_at(*cert_key).all? { |v| v.to_s.empty? }
|
229
|
+
ctx = localhost_authority && localhost_authority_context
|
230
|
+
end
|
231
|
+
|
232
|
+
ctx ||=
|
233
|
+
begin
|
234
|
+
# Extract cert_pem and key_pem from options[:store] if present
|
235
|
+
cert_key.each do |v|
|
236
|
+
if params[v]&.start_with?('store:')
|
237
|
+
index = Integer(params.delete(v).split('store:').last)
|
238
|
+
params["#{v}_pem"] = @conf.options[:store][index]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
MiniSSL::ContextBuilder.new(params, @log_writer).context
|
242
|
+
end
|
159
243
|
|
160
244
|
if fd = @inherited_fds.delete(str)
|
161
|
-
|
245
|
+
log_writer.log "* Inherited #{str}"
|
162
246
|
io = inherit_ssl_listener fd, ctx
|
163
247
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
164
248
|
io = inherit_ssl_listener sock, ctx
|
165
|
-
|
249
|
+
log_writer.log "* Activated #{str}"
|
166
250
|
else
|
167
|
-
|
168
|
-
|
251
|
+
ios_len = @ios.length
|
252
|
+
backlog = params.fetch('backlog', 1024).to_i
|
253
|
+
low_latency = params['low_latency'] != 'false'
|
254
|
+
io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog
|
255
|
+
|
256
|
+
@ios[ios_len..-1].each do |i|
|
257
|
+
addr = loc_addr_str i
|
258
|
+
log_writer.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
|
259
|
+
end
|
169
260
|
end
|
170
261
|
|
171
262
|
@listeners << [str, io] if io
|
172
263
|
else
|
173
|
-
|
264
|
+
log_writer.error "Invalid URI: #{str}"
|
174
265
|
end
|
175
266
|
end
|
176
267
|
|
177
268
|
# If we inherited fds but didn't use them (because of a
|
178
269
|
# configuration change), then be sure to close them.
|
179
270
|
@inherited_fds.each do |str, fd|
|
180
|
-
|
271
|
+
log_writer.log "* Closing unused inherited connection: #{str}"
|
181
272
|
|
182
273
|
begin
|
183
274
|
IO.for_fd(fd).close
|
@@ -193,21 +284,35 @@ module Puma
|
|
193
284
|
end
|
194
285
|
|
195
286
|
# Also close any unused activated sockets
|
196
|
-
@activated_sockets.
|
197
|
-
|
198
|
-
|
199
|
-
sock.
|
200
|
-
|
287
|
+
unless @activated_sockets.empty?
|
288
|
+
fds = @ios.map(&:to_i)
|
289
|
+
@activated_sockets.each do |key, sock|
|
290
|
+
next if fds.include? sock.to_i
|
291
|
+
log_writer.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
|
292
|
+
begin
|
293
|
+
sock.close
|
294
|
+
rescue SystemCallError
|
295
|
+
end
|
296
|
+
# We have to unlink a unix socket path that's not being used
|
297
|
+
File.unlink key[1] if key.first == :unix
|
201
298
|
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
299
|
end
|
205
300
|
end
|
206
301
|
|
207
|
-
def
|
208
|
-
|
209
|
-
|
210
|
-
|
302
|
+
def localhost_authority
|
303
|
+
@localhost_authority ||= Localhost::Authority.fetch if defined?(Localhost::Authority) && !Puma::IS_JRUBY
|
304
|
+
end
|
305
|
+
|
306
|
+
def localhost_authority_context
|
307
|
+
return unless localhost_authority
|
308
|
+
|
309
|
+
key_path, crt_path = if [:key_path, :certificate_path].all? { |m| localhost_authority.respond_to?(m) }
|
310
|
+
[localhost_authority.key_path, localhost_authority.certificate_path]
|
311
|
+
else
|
312
|
+
local_certificates_path = File.expand_path("~/.localhost")
|
313
|
+
[File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
|
314
|
+
end
|
315
|
+
MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @log_writer).context
|
211
316
|
end
|
212
317
|
|
213
318
|
# Tell the server to listen on host +host+, port +port+.
|
@@ -226,26 +331,20 @@ module Puma
|
|
226
331
|
end
|
227
332
|
|
228
333
|
host = host[1..-2] if host and host[0..0] == '['
|
229
|
-
|
334
|
+
tcp_server = TCPServer.new(host, port)
|
335
|
+
|
230
336
|
if optimize_for_latency
|
231
|
-
|
337
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
232
338
|
end
|
233
|
-
|
234
|
-
|
235
|
-
@connected_port = s.addr[1]
|
339
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
340
|
+
tcp_server.listen backlog
|
236
341
|
|
237
|
-
@ios <<
|
238
|
-
|
342
|
+
@ios << tcp_server
|
343
|
+
tcp_server
|
239
344
|
end
|
240
345
|
|
241
|
-
attr_reader :connected_port
|
242
|
-
|
243
346
|
def inherit_tcp_listener(host, port, fd)
|
244
|
-
|
245
|
-
s = fd
|
246
|
-
else
|
247
|
-
s = TCPServer.for_fd(fd)
|
248
|
-
end
|
347
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
249
348
|
|
250
349
|
@ios << s
|
251
350
|
s
|
@@ -253,9 +352,10 @@ module Puma
|
|
253
352
|
|
254
353
|
def add_ssl_listener(host, port, ctx,
|
255
354
|
optimize_for_latency=true, backlog=1024)
|
256
|
-
require 'puma/minissl'
|
257
355
|
|
258
|
-
|
356
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
357
|
+
# Puma will try to use local authority context if context is supplied nil
|
358
|
+
ctx ||= localhost_authority_context
|
259
359
|
|
260
360
|
if host == "localhost"
|
261
361
|
loopback_addresses.each do |addr|
|
@@ -272,7 +372,6 @@ module Puma
|
|
272
372
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
273
373
|
s.listen backlog
|
274
374
|
|
275
|
-
|
276
375
|
ssl = MiniSSL::Server.new s, ctx
|
277
376
|
env = @proto_env.dup
|
278
377
|
env[HTTPS_KEY] = HTTPS
|
@@ -283,14 +382,12 @@ module Puma
|
|
283
382
|
end
|
284
383
|
|
285
384
|
def inherit_ssl_listener(fd, ctx)
|
286
|
-
|
287
|
-
|
385
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
386
|
+
# Puma will try to use local authority context if context is supplied nil
|
387
|
+
ctx ||= localhost_authority_context
|
388
|
+
|
389
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
288
390
|
|
289
|
-
if fd.kind_of? TCPServer
|
290
|
-
s = fd
|
291
|
-
else
|
292
|
-
s = TCPServer.for_fd(fd)
|
293
|
-
end
|
294
391
|
ssl = MiniSSL::Server.new(s, ctx)
|
295
392
|
|
296
393
|
env = @proto_env.dup
|
@@ -305,8 +402,6 @@ module Puma
|
|
305
402
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
306
403
|
#
|
307
404
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
308
|
-
@unix_paths << path unless File.exist? path
|
309
|
-
|
310
405
|
# Let anyone connect by default
|
311
406
|
umask ||= 0
|
312
407
|
|
@@ -323,8 +418,7 @@ module Puma
|
|
323
418
|
raise "There is already a server bound to: #{path}"
|
324
419
|
end
|
325
420
|
end
|
326
|
-
|
327
|
-
s = UNIXServer.new(path)
|
421
|
+
s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
|
328
422
|
s.listen backlog
|
329
423
|
@ios << s
|
330
424
|
ensure
|
@@ -343,13 +437,8 @@ module Puma
|
|
343
437
|
end
|
344
438
|
|
345
439
|
def inherit_unix_listener(path, fd)
|
346
|
-
|
440
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
|
347
441
|
|
348
|
-
if fd.kind_of? TCPServer
|
349
|
-
s = fd
|
350
|
-
else
|
351
|
-
s = UNIXServer.for_fd fd
|
352
|
-
end
|
353
442
|
@ios << s
|
354
443
|
|
355
444
|
env = @proto_env.dup
|
@@ -361,25 +450,52 @@ module Puma
|
|
361
450
|
|
362
451
|
def close_listeners
|
363
452
|
@listeners.each do |l, io|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
453
|
+
begin
|
454
|
+
io.close unless io.closed?
|
455
|
+
uri = URI.parse l
|
456
|
+
next unless uri.scheme == 'unix'
|
457
|
+
unix_path = "#{uri.host}#{uri.path}"
|
458
|
+
File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
|
459
|
+
rescue Errno::EBADF
|
460
|
+
end
|
369
461
|
end
|
370
462
|
end
|
371
463
|
|
372
|
-
def
|
373
|
-
@
|
464
|
+
def redirects_for_restart
|
465
|
+
redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
466
|
+
redirects[:close_others] = true
|
467
|
+
redirects
|
374
468
|
end
|
375
469
|
|
376
|
-
|
377
|
-
|
378
|
-
@listeners.
|
379
|
-
|
380
|
-
redirects[io.to_i] = io.to_i
|
470
|
+
# @version 5.0.0
|
471
|
+
def redirects_for_restart_env
|
472
|
+
@listeners.each_with_object({}).with_index do |(listen, memo), i|
|
473
|
+
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
381
474
|
end
|
382
|
-
|
475
|
+
end
|
476
|
+
|
477
|
+
private
|
478
|
+
|
479
|
+
# @!attribute [r] loopback_addresses
|
480
|
+
def loopback_addresses
|
481
|
+
t = Socket.ip_address_list.select do |addrinfo|
|
482
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
483
|
+
end
|
484
|
+
t.map! { |addrinfo| addrinfo.ip_address }; t.uniq!; t
|
485
|
+
end
|
486
|
+
|
487
|
+
def loc_addr_str(io)
|
488
|
+
loc_addr = io.to_io.local_address
|
489
|
+
if loc_addr.ipv6?
|
490
|
+
"[#{loc_addr.ip_unpack[0]}]:#{loc_addr.ip_unpack[1]}"
|
491
|
+
else
|
492
|
+
loc_addr.ip_unpack.join(':')
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
# @version 5.0.0
|
497
|
+
def socket_activation_fd(int)
|
498
|
+
int + 3 # 3 is the magic number you add to follow the SA protocol
|
383
499
|
end
|
384
500
|
end
|
385
501
|
end
|