class Puma::Binder
def parse(binds, log_writer = nil, log_msg = 'Listening')
def parse(binds, log_writer = nil, log_msg = 'Listening') log_writer ||= @log_writer binds.each do |str| uri = URI.parse str case uri.scheme when "tcp" if fd = @inherited_fds.delete(str) io = inherit_tcp_listener uri.host, uri.port, fd log_writer.log "* Inherited #{str}" elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ]) io = inherit_tcp_listener uri.host, uri.port, sock log_writer.log "* Activated #{str}" else ios_len = @ios.length params = Util.parse_query uri.query low_latency = params.key?('low_latency') && params['low_latency'] != 'false' backlog = params.fetch('backlog', 1024).to_i io = add_tcp_listener uri.host, uri.port, low_latency, backlog @ios[ios_len..-1].each do |i| addr = loc_addr_str i log_writer.log "* #{log_msg} on http://#{addr}" end end @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 || File.exist?(path) io = inherit_unix_listener path, fd log_writer.log "* Inherited #{str}" elsif sock = @activated_sockets.delete([ :unix, path ]) || @activated_sockets.delete([ :unix, File.realdirpath(path) ]) @unix_paths << path unless abstract || File.exist?(path) io = inherit_unix_listener path, sock log_writer.log "* Activated #{str}" else umask = nil mode = nil backlog = 1024 if uri.query params = Util.parse_query uri.query if u = params['umask'] # Use Integer() to respect the 0 prefix as octal umask = Integer(u) end if u = params['mode'] mode = Integer('0'+u) end if u = params['backlog'] backlog = Integer(u) end end @unix_paths << path unless abstract || File.exist?(path) io = add_unix_listener path, umask, mode, backlog log_writer.log "* #{log_msg} on #{str}" end @listeners << [str, io] when "ssl" cert_key = %w[cert key] raise "Puma compiled without SSL support" unless HAS_SSL params = Util.parse_query uri.query # If key and certs are not defined and localhost gem is required. # localhost gem will be used for self signed # Load localhost authority if not loaded. # Ruby 3 `values_at` accepts an array, earlier do not if params.values_at(*cert_key).all? { |v| v.to_s.empty? } ctx = localhost_authority && localhost_authority_context end ctx ||= begin # Extract cert_pem and key_pem from options[:store] if present cert_key.each do |v| if params[v]&.start_with?('store:') index = Integer(params.delete(v).split('store:').last) params["#{v}_pem"] = @conf.options[:store][index] end end MiniSSL::ContextBuilder.new(params, @log_writer).context end if fd = @inherited_fds.delete(str) log_writer.log "* Inherited #{str}" io = inherit_ssl_listener fd, ctx elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ]) io = inherit_ssl_listener sock, ctx log_writer.log "* Activated #{str}" else ios_len = @ios.length backlog = params.fetch('backlog', 1024).to_i low_latency = params['low_latency'] != 'false' io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog @ios[ios_len..-1].each do |i| addr = loc_addr_str i log_writer.log "* #{log_msg} on ssl://#{addr}?#{uri.query}" end end @listeners << [str, io] if io else log_writer.error "Invalid URI: #{str}" end end # If we inherited fds but didn't use them (because of a # configuration change), then be sure to close them. @inherited_fds.each do |str, fd| log_writer.log "* Closing unused inherited connection: #{str}" begin IO.for_fd(fd).close rescue SystemCallError end # We have to unlink a unix socket path that's not being used uri = URI.parse str if uri.scheme == "unix" path = "#{uri.host}#{uri.path}" File.unlink path end end # Also close any unused activated sockets unless @activated_sockets.empty? fds = @ios.map(&:to_i) @activated_sockets.each do |key, sock| next if fds.include? sock.to_i log_writer.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}" begin sock.close rescue SystemCallError end # We have to unlink a unix socket path that's not being used File.unlink key[1] if key.first == :unix end end end