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.

Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1775 -451
  3. data/LICENSE +23 -20
  4. data/README.md +193 -65
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +59 -21
  7. data/docs/compile_options.md +55 -0
  8. data/docs/deployment.md +69 -58
  9. data/docs/fork_worker.md +31 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +9 -0
  14. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  15. data/{tools → docs}/jungle/rc.d/puma +2 -2
  16. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  17. data/docs/kubernetes.md +66 -0
  18. data/docs/nginx.md +2 -2
  19. data/docs/plugins.md +22 -12
  20. data/docs/rails_dev_mode.md +28 -0
  21. data/docs/restart.md +47 -22
  22. data/docs/signals.md +13 -11
  23. data/docs/stats.md +142 -0
  24. data/docs/systemd.md +94 -120
  25. data/docs/testing_benchmarks_local_files.md +150 -0
  26. data/docs/testing_test_rackup_ci_files.md +36 -0
  27. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  28. data/ext/puma_http11/ext_help.h +1 -1
  29. data/ext/puma_http11/extconf.rb +61 -3
  30. data/ext/puma_http11/http11_parser.c +103 -117
  31. data/ext/puma_http11/http11_parser.h +2 -2
  32. data/ext/puma_http11/http11_parser.java.rl +22 -38
  33. data/ext/puma_http11/http11_parser.rl +3 -3
  34. data/ext/puma_http11/http11_parser_common.rl +6 -6
  35. data/ext/puma_http11/mini_ssl.c +361 -99
  36. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  37. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  38. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
  39. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +248 -92
  40. data/ext/puma_http11/puma_http11.c +49 -57
  41. data/lib/puma/app/status.rb +71 -49
  42. data/lib/puma/binder.rb +242 -150
  43. data/lib/puma/cli.rb +38 -34
  44. data/lib/puma/client.rb +387 -244
  45. data/lib/puma/cluster/worker.rb +180 -0
  46. data/lib/puma/cluster/worker_handle.rb +97 -0
  47. data/lib/puma/cluster.rb +261 -243
  48. data/lib/puma/commonlogger.rb +21 -14
  49. data/lib/puma/configuration.rb +116 -88
  50. data/lib/puma/const.rb +101 -100
  51. data/lib/puma/control_cli.rb +115 -70
  52. data/lib/puma/detect.rb +33 -2
  53. data/lib/puma/dsl.rb +731 -134
  54. data/lib/puma/error_logger.rb +113 -0
  55. data/lib/puma/events.rb +16 -112
  56. data/lib/puma/io_buffer.rb +42 -5
  57. data/lib/puma/jruby_restart.rb +2 -59
  58. data/lib/puma/json_serialization.rb +96 -0
  59. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  60. data/lib/puma/launcher.rb +184 -133
  61. data/lib/puma/log_writer.rb +147 -0
  62. data/lib/puma/minissl/context_builder.rb +92 -0
  63. data/lib/puma/minissl.rb +246 -70
  64. data/lib/puma/null_io.rb +18 -1
  65. data/lib/puma/plugin/systemd.rb +90 -0
  66. data/lib/puma/plugin/tmp_restart.rb +3 -1
  67. data/lib/puma/plugin.rb +7 -13
  68. data/lib/puma/rack/builder.rb +7 -9
  69. data/lib/puma/rack/urlmap.rb +2 -0
  70. data/lib/puma/rack_default.rb +21 -4
  71. data/lib/puma/reactor.rb +85 -316
  72. data/lib/puma/request.rb +665 -0
  73. data/lib/puma/runner.rb +94 -69
  74. data/lib/puma/sd_notify.rb +149 -0
  75. data/lib/puma/server.rb +314 -771
  76. data/lib/puma/single.rb +20 -74
  77. data/lib/puma/state_file.rb +45 -8
  78. data/lib/puma/thread_pool.rb +142 -92
  79. data/lib/puma/util.rb +22 -10
  80. data/lib/puma.rb +60 -5
  81. data/lib/rack/handler/puma.rb +113 -91
  82. data/tools/Dockerfile +16 -0
  83. data/tools/trickletest.rb +0 -1
  84. metadata +54 -32
  85. data/ext/puma_http11/io_buffer.c +0 -155
  86. data/lib/puma/accept_nonblock.rb +0 -23
  87. data/lib/puma/compat.rb +0 -14
  88. data/lib/puma/convenient.rb +0 -25
  89. data/lib/puma/daemon_ext.rb +0 -33
  90. data/lib/puma/delegation.rb +0 -13
  91. data/lib/puma/java_io_buffer.rb +0 -47
  92. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  93. data/lib/puma/tcp_logger.rb +0 -41
  94. data/tools/jungle/README.md +0 -19
  95. data/tools/jungle/init.d/README.md +0 -61
  96. data/tools/jungle/init.d/puma +0 -421
  97. data/tools/jungle/init.d/run-puma +0 -18
  98. data/tools/jungle/upstart/README.md +0 -61
  99. data/tools/jungle/upstart/puma-manager.conf +0 -31
  100. 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
- require 'puma/const'
7
- require 'puma/util'
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,3].freeze
20
+ RACK_VERSION = [1,6].freeze
14
21
 
15
- def initialize(events)
16
- @events = events
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 => events.stderr,
25
- "rack.multithread".freeze => true,
26
- "rack.multiprocess".freeze => false,
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 :listeners, :ios
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
- def import_from_env
57
- remove = []
58
-
59
- ENV.each do |k,v|
60
- if k =~ /PUMA_INHERIT_\d+/
61
- fd, url = v.split(":", 2)
62
- @inherited_fds[url] = fd.to_i
63
- remove << k
64
- elsif k == 'LISTEN_FDS' && ENV['LISTEN_PID'].to_i == $$
65
- v.to_i.times do |num|
66
- fd = num + 3
67
- sock = TCPServer.for_fd(fd)
68
- begin
69
- key = [ :unix, Socket.unpack_sockaddr_un(sock.getsockname) ]
70
- rescue ArgumentError
71
- port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
72
- if addr =~ /\:/
73
- addr = "[#{addr}]"
74
- end
75
- key = [ :tcp, addr, port ]
76
- end
77
- @activated_sockets[key] = sock
78
- @events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
79
- end
80
- remove << k << 'LISTEN_PID'
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
- remove.each do |k|
85
- ENV.delete k
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, logger)
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
- logger.log "* Inherited #{str}"
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
- logger.log "* Activated #{str}"
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
- opt = params.key?('low_latency')
104
- bak = params.fetch('backlog', 1024).to_i
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, opt, bak
107
- logger.log "* Listening on #{str}"
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
- logger.log "* Inherited #{str}"
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
- logger.log "* Activated #{str}"
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
- logger.log "* Listening on #{str}"
214
+ log_writer.log "* #{log_msg} on #{str}"
143
215
  end
144
216
 
145
217
  @listeners << [str, io]
146
218
  when "ssl"
147
- params = Util.parse_query uri.query
148
- require 'puma/minissl'
149
-
150
- MiniSSL.check
219
+ cert_key = %w[cert key]
151
220
 
152
- ctx = MiniSSL::Context.new
221
+ raise "Puma compiled without SSL support" unless HAS_SSL
153
222
 
154
- if defined?(JRUBY_VERSION)
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
- ctx.cert = params['cert']
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
- if ['peer', 'force_peer'].include?(params['verify_mode'])
181
- unless params['ca']
182
- @events.error "Please specify the SSL ca via 'ca='"
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
- logger.log "* Inherited #{str}"
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
- logger.log "* Activated #{str}"
250
+ log_writer.log "* Activated #{str}"
210
251
  else
211
- io = add_ssl_listener uri.host, uri.port, ctx
212
- logger.log "* Listening on #{str}"
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
- logger.error "Invalid URI: #{str}"
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
- logger.log "* Closing unused inherited connection: #{str}"
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.each do |key, sock|
241
- logger.log "* Closing unused activated socket: #{key.join ':'}"
242
- begin
243
- sock.close
244
- rescue SystemCallError
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 loopback_addresses
252
- Socket.ip_address_list.select do |addrinfo|
253
- addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
254
- end.map { |addrinfo| addrinfo.ip_address }.uniq
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
- s = TCPServer.new(host, port)
335
+ tcp_server = TCPServer.new(host, port)
336
+
274
337
  if optimize_for_latency
275
- s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
338
+ tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
276
339
  end
277
- s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
278
- s.listen backlog
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 << s
282
- s
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
- if fd.kind_of? TCPServer
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
- MiniSSL.check
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
- require 'puma/minissl'
331
- MiniSSL.check
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
- @unix_paths << path
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