mirror of
https://github.com/discourse/discourse.git
synced 2026-08-06 06:24:48 +08:00
Ruby 3.4 shipped support for happy eyeballs in `Socket.tcp` and `TCPSocket`. However, our `FinalDestination::HTTP` wrapper was performing a DNS lookup and passing IP addresses one at a time when opening the socket. That meant that we didn't benefit from the new Ruby feature in most Discourse features. This commit factors the strategy. Now, `FinalDestination::HTTP` encodes the DNS result and passes it to the underlying implementation as a fake hostname string. A patch to `Addrinfo` detects this fake hostname and returns the given IPs instead of performing its own lookup. For this Addrinfo patch to work, we also had to patch `TCPSocket` so that it uses the ruby-based `Socket.tcp` rather than its native C socket-opening code. The result is that we now get the benefit of the native Ruby 'Happy Eyeballs' support for concurrent ipv4 and ipv6 connections. All this patching of low-level ruby classes is not ideal, but there is no native way to control name resolution in `Net::HTTP` or its dependencies.
39 lines
1.1 KiB
Ruby
Vendored
39 lines
1.1 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
class FinalDestination
|
|
# FinalDestination resolves hostnames to allowed IPs, then encodes them in a
|
|
# pipe-separated format to be read by our patched versions of TCPSocket and
|
|
# Addrinfo in `freedom_patches/final_destination_connect.rb`.
|
|
# This module exists to encode/decode that format.
|
|
module Connector
|
|
TOKEN_SUFFIX = ".final-destination.invalid"
|
|
|
|
class << self
|
|
def encode(host, ips)
|
|
"#{host}|#{ips.join(",")}#{TOKEN_SUFFIX}"
|
|
end
|
|
|
|
def token?(name)
|
|
name.is_a?(String) && name.end_with?(TOKEN_SUFFIX)
|
|
end
|
|
|
|
def addresses(token)
|
|
token.delete_suffix(TOKEN_SUFFIX).rpartition("|").last.split(",")
|
|
end
|
|
|
|
def addresses_for_family(ips, family)
|
|
wanted =
|
|
case family
|
|
when Integer
|
|
family
|
|
when :ipv6, "ipv6", :INET6, "INET6", "AF_INET6"
|
|
Socket::AF_INET6
|
|
when :ipv4, "ipv4", :INET, "INET", "AF_INET"
|
|
Socket::AF_INET
|
|
end
|
|
return ips unless wanted
|
|
ips.select { |ip| Addrinfo.ip(ip).afamily == wanted }
|
|
end
|
|
end
|
|
end
|
|
end
|