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