puma 3.12.6 → 6.3.0
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 +1806 -451
- data/LICENSE +23 -20
- data/README.md +217 -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/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 +389 -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 +244 -150
- data/lib/puma/cli.rb +38 -34
- data/lib/puma/client.rb +388 -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 +154 -104
- data/lib/puma/control_cli.rb +115 -70
- data/lib/puma/detect.rb +33 -2
- data/lib/puma/dsl.rb +764 -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 +93 -0
- data/lib/puma/minissl.rb +263 -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 +9 -11
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +21 -4
- data/lib/puma/reactor.rb +93 -315
- data/lib/puma/request.rb +671 -0
- data/lib/puma/runner.rb +94 -69
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +327 -772
- data/lib/puma/single.rb +20 -74
- data/lib/puma/state_file.rb +45 -8
- data/lib/puma/thread_pool.rb +146 -92
- data/lib/puma/util.rb +22 -10
- data/lib/puma.rb +60 -5
- data/lib/rack/handler/puma.rb +116 -90
- 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/{tools → docs}/jungle/rc.d/puma.conf +0 -0
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,7 +42,6 @@ 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
|
}
|
@@ -42,7 +50,13 @@ module Puma
|
|
42
50
|
@ios = []
|
43
51
|
end
|
44
52
|
|
45
|
-
attr_reader :
|
53
|
+
attr_reader :ios
|
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
|
46
60
|
|
47
61
|
def env(sock)
|
48
62
|
@envs.fetch(sock, @proto_env)
|
@@ -50,73 +64,129 @@ module Puma
|
|
50
64
|
|
51
65
|
def close
|
52
66
|
@ios.each { |i| i.close }
|
53
|
-
@unix_paths.each { |i| File.unlink i }
|
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]
|
81
100
|
end
|
101
|
+
@activated_sockets[key] = sock
|
102
|
+
@log_writer.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
82
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 = []
|
83
121
|
|
84
|
-
|
85
|
-
|
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}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
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
|
162
|
+
|
163
|
+
io = add_tcp_listener uri.host, uri.port, low_latency, backlog
|
105
164
|
|
106
|
-
|
107
|
-
|
165
|
+
@ios[ios_len..-1].each do |i|
|
166
|
+
addr = loc_addr_str i
|
167
|
+
log_writer.log "* #{log_msg} on http://#{addr}"
|
168
|
+
end
|
108
169
|
end
|
109
170
|
|
110
171
|
@listeners << [str, io] if io
|
111
172
|
when "unix"
|
112
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
|
113
180
|
|
114
181
|
if fd = @inherited_fds.delete(str)
|
182
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
115
183
|
io = inherit_unix_listener path, fd
|
116
|
-
|
117
|
-
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)
|
118
188
|
io = inherit_unix_listener path, sock
|
119
|
-
|
189
|
+
log_writer.log "* Activated #{str}"
|
120
190
|
else
|
121
191
|
umask = nil
|
122
192
|
mode = nil
|
@@ -138,90 +208,67 @@ module Puma
|
|
138
208
|
end
|
139
209
|
end
|
140
210
|
|
211
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
141
212
|
io = add_unix_listener path, umask, mode, backlog
|
142
|
-
|
213
|
+
log_writer.log "* #{log_msg} on #{str}"
|
143
214
|
end
|
144
215
|
|
145
216
|
@listeners << [str, io]
|
146
217
|
when "ssl"
|
147
|
-
|
148
|
-
require 'puma/minissl'
|
149
|
-
|
150
|
-
MiniSSL.check
|
218
|
+
cert_key = %w[cert key]
|
151
219
|
|
152
|
-
|
220
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
153
221
|
|
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
|
222
|
+
params = Util.parse_query uri.query
|
177
223
|
|
178
|
-
|
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
|
179
231
|
|
180
|
-
|
181
|
-
|
182
|
-
|
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
|
183
240
|
end
|
241
|
+
MiniSSL::ContextBuilder.new(params, @log_writer).context
|
184
242
|
end
|
185
243
|
|
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
244
|
if fd = @inherited_fds.delete(str)
|
205
|
-
|
245
|
+
log_writer.log "* Inherited #{str}"
|
206
246
|
io = inherit_ssl_listener fd, ctx
|
207
247
|
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
208
248
|
io = inherit_ssl_listener sock, ctx
|
209
|
-
|
249
|
+
log_writer.log "* Activated #{str}"
|
210
250
|
else
|
211
|
-
|
212
|
-
|
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
|
213
260
|
end
|
214
261
|
|
215
262
|
@listeners << [str, io] if io
|
216
263
|
else
|
217
|
-
|
264
|
+
log_writer.error "Invalid URI: #{str}"
|
218
265
|
end
|
219
266
|
end
|
220
267
|
|
221
268
|
# If we inherited fds but didn't use them (because of a
|
222
269
|
# configuration change), then be sure to close them.
|
223
270
|
@inherited_fds.each do |str, fd|
|
224
|
-
|
271
|
+
log_writer.log "* Closing unused inherited connection: #{str}"
|
225
272
|
|
226
273
|
begin
|
227
274
|
IO.for_fd(fd).close
|
@@ -237,21 +284,35 @@ module Puma
|
|
237
284
|
end
|
238
285
|
|
239
286
|
# Also close any unused activated sockets
|
240
|
-
@activated_sockets.
|
241
|
-
|
242
|
-
|
243
|
-
sock.
|
244
|
-
|
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
|
245
298
|
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
299
|
end
|
249
300
|
end
|
250
301
|
|
251
|
-
def
|
252
|
-
|
253
|
-
|
254
|
-
|
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
|
255
316
|
end
|
256
317
|
|
257
318
|
# Tell the server to listen on host +host+, port +port+.
|
@@ -270,26 +331,20 @@ module Puma
|
|
270
331
|
end
|
271
332
|
|
272
333
|
host = host[1..-2] if host and host[0..0] == '['
|
273
|
-
|
334
|
+
tcp_server = TCPServer.new(host, port)
|
335
|
+
|
274
336
|
if optimize_for_latency
|
275
|
-
|
337
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
276
338
|
end
|
277
|
-
|
278
|
-
|
279
|
-
@connected_port = s.addr[1]
|
339
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
340
|
+
tcp_server.listen backlog
|
280
341
|
|
281
|
-
@ios <<
|
282
|
-
|
342
|
+
@ios << tcp_server
|
343
|
+
tcp_server
|
283
344
|
end
|
284
345
|
|
285
|
-
attr_reader :connected_port
|
286
|
-
|
287
346
|
def inherit_tcp_listener(host, port, fd)
|
288
|
-
|
289
|
-
s = fd
|
290
|
-
else
|
291
|
-
s = TCPServer.for_fd(fd)
|
292
|
-
end
|
347
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
293
348
|
|
294
349
|
@ios << s
|
295
350
|
s
|
@@ -297,9 +352,10 @@ module Puma
|
|
297
352
|
|
298
353
|
def add_ssl_listener(host, port, ctx,
|
299
354
|
optimize_for_latency=true, backlog=1024)
|
300
|
-
require 'puma/minissl'
|
301
355
|
|
302
|
-
|
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
|
303
359
|
|
304
360
|
if host == "localhost"
|
305
361
|
loopback_addresses.each do |addr|
|
@@ -316,7 +372,6 @@ module Puma
|
|
316
372
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
317
373
|
s.listen backlog
|
318
374
|
|
319
|
-
|
320
375
|
ssl = MiniSSL::Server.new s, ctx
|
321
376
|
env = @proto_env.dup
|
322
377
|
env[HTTPS_KEY] = HTTPS
|
@@ -327,14 +382,12 @@ module Puma
|
|
327
382
|
end
|
328
383
|
|
329
384
|
def inherit_ssl_listener(fd, ctx)
|
330
|
-
|
331
|
-
|
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)
|
332
390
|
|
333
|
-
if fd.kind_of? TCPServer
|
334
|
-
s = fd
|
335
|
-
else
|
336
|
-
s = TCPServer.for_fd(fd)
|
337
|
-
end
|
338
391
|
ssl = MiniSSL::Server.new(s, ctx)
|
339
392
|
|
340
393
|
env = @proto_env.dup
|
@@ -349,8 +402,6 @@ module Puma
|
|
349
402
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
350
403
|
#
|
351
404
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
352
|
-
@unix_paths << path
|
353
|
-
|
354
405
|
# Let anyone connect by default
|
355
406
|
umask ||= 0
|
356
407
|
|
@@ -367,8 +418,7 @@ module Puma
|
|
367
418
|
raise "There is already a server bound to: #{path}"
|
368
419
|
end
|
369
420
|
end
|
370
|
-
|
371
|
-
s = UNIXServer.new(path)
|
421
|
+
s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
|
372
422
|
s.listen backlog
|
373
423
|
@ios << s
|
374
424
|
ensure
|
@@ -387,13 +437,8 @@ module Puma
|
|
387
437
|
end
|
388
438
|
|
389
439
|
def inherit_unix_listener(path, fd)
|
390
|
-
|
440
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
|
391
441
|
|
392
|
-
if fd.kind_of? TCPServer
|
393
|
-
s = fd
|
394
|
-
else
|
395
|
-
s = UNIXServer.for_fd fd
|
396
|
-
end
|
397
442
|
@ios << s
|
398
443
|
|
399
444
|
env = @proto_env.dup
|
@@ -403,5 +448,54 @@ module Puma
|
|
403
448
|
s
|
404
449
|
end
|
405
450
|
|
451
|
+
def close_listeners
|
452
|
+
@listeners.each do |l, io|
|
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
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
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
|
468
|
+
end
|
469
|
+
|
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]}"
|
474
|
+
end
|
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
|
499
|
+
end
|
406
500
|
end
|
407
501
|
end
|