From c4424beffc1f653635d36231fa3549beeb8c4a14 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Tue, 2 Mar 2021 17:49:25 -0600 Subject: [PATCH] Support Linux's abstract sockets Closes #2526 --- lib/puma.rb | 14 ++++++++++++++ lib/puma/binder.rb | 27 ++++++++++++++++----------- lib/puma/control_cli.rb | 3 ++- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/lib/puma.rb b/lib/puma.rb index 2e74228d7d..c9a75088d8 100644 --- a/lib/puma.rb +++ b/lib/puma.rb @@ -39,6 +39,20 @@ def self.ssl? HAS_SSL end + def self.abstract_unix_socket? + @abstract_unix ||= + if HAS_UNIX_SOCKET + begin + ::UNIXServer.new("\0puma.temp.unix").close + true + rescue ArgumentError # darwin + false + end + else + false + end + end + # @!attribute [rw] stats_object= def self.stats_object=(val) @get_stats = val diff --git a/lib/puma/binder.rb b/lib/puma/binder.rb index 13fbec06ec..bfaa6c0541 100644 --- a/lib/puma/binder.rb +++ b/lib/puma/binder.rb @@ -177,11 +177,19 @@ def parse(binds, logger, log_msg = 'Listening') @listeners << [str, io] if io when "unix" path = "#{uri.host}#{uri.path}".gsub("%20", " ") + abstract = false + if str.start_with? 'unix://@' + raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket? + abstract = true + path = "@#{path}" + end if fd = @inherited_fds.delete(str) + @unix_paths << path unless abstract io = inherit_unix_listener path, fd logger.log "* Inherited #{str}" elsif sock = @activated_sockets.delete([ :unix, path ]) + @unix_paths << path unless abstract || File.exist?(path) io = inherit_unix_listener path, sock logger.log "* Activated #{str}" else @@ -205,6 +213,7 @@ def parse(binds, logger, log_msg = 'Listening') end end + @unix_paths << path unless abstract || File.exist?(path) io = add_unix_listener path, umask, mode, backlog logger.log "* #{log_msg} on #{str}" end @@ -355,8 +364,6 @@ def inherit_ssl_listener(fd, ctx) # Tell the server to listen on +path+ as a UNIX domain socket. # def add_unix_listener(path, umask=nil, mode=nil, backlog=1024) - @unix_paths << path unless File.exist? path - # Let anyone connect by default umask ||= 0 @@ -374,7 +381,7 @@ def add_unix_listener(path, umask=nil, mode=nil, backlog=1024) end end - s = UNIXServer.new(path) + s = UNIXServer.new path.sub(/\A@/, "\0") s.listen backlog @ios << s ensure @@ -393,8 +400,6 @@ def add_unix_listener(path, umask=nil, mode=nil, backlog=1024) end def inherit_unix_listener(path, fd) - @unix_paths << path unless File.exist? path - s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd) @ios << s @@ -407,24 +412,24 @@ def inherit_unix_listener(path, fd) end def close_listeners - listeners.each do |l, io| - io.close unless io.closed? # Ruby 2.2 issue - uri = URI.parse(l) + @listeners.each do |l, io| + io.close unless io.closed? + uri = URI.parse l next unless uri.scheme == 'unix' unix_path = "#{uri.host}#{uri.path}" - File.unlink unix_path if unix_paths.include? unix_path + File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path) end end def redirects_for_restart - redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h + redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h redirects[:close_others] = true redirects end # @version 5.0.0 def redirects_for_restart_env - listeners.each_with_object({}).with_index do |(listen, memo), i| + @listeners.each_with_object({}).with_index do |(listen, memo), i| memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}" end end diff --git a/lib/puma/control_cli.rb b/lib/puma/control_cli.rb index cc874acc57..22f370cec7 100644 --- a/lib/puma/control_cli.rb +++ b/lib/puma/control_cli.rb @@ -176,7 +176,8 @@ def send_request when 'tcp' TCPSocket.new uri.host, uri.port when 'unix' - UNIXSocket.new "#{uri.host}#{uri.path}" + UNIXSocket.new(@control_url.start_with?('unix://@') ? + "\0#{uri.host}#{uri.path}" : "#{uri.host}#{uri.path}") else raise "Invalid scheme: #{uri.scheme}" end