11package http
22
33import (
4+ "context"
45 "crypto/tls"
6+ "fmt"
7+ "net"
58 "net/http"
69 "time"
10+
11+ "k8s.io/klog/v2"
712)
813
14+ // ImmediateFallbackDialContext creates a DialContext function that tries connection
15+ // attempts sequentially in the order returned by DNS, without the 300ms Happy Eyeballs
16+ // delay. This respects DNS server ordering while eliminating the racing delay.
17+ func ImmediateFallbackDialContext (ctx context.Context , network , address string ) (net.Conn , error ) {
18+ // Split the address into host and port
19+ host , port , err := net .SplitHostPort (address )
20+ if err != nil {
21+ return nil , err
22+ }
23+
24+ klog .V (4 ).InfoS ("Resolving DNS for connection" , "host" , host , "port" , port , "network" , network )
25+
26+ // Resolve all IP addresses for the host
27+ ips , err := net .DefaultResolver .LookupIP (ctx , "ip" , host )
28+ if err != nil {
29+ klog .V (2 ).ErrorS (err , "DNS resolution failed" , "host" , host )
30+ return nil , err
31+ }
32+
33+ if len (ips ) == 0 {
34+ err := fmt .Errorf ("no IP addresses found for host %s" , host )
35+ klog .V (2 ).ErrorS (err , "DNS resolution returned no addresses" , "host" , host )
36+ return nil , err
37+ }
38+
39+ klog .V (4 ).InfoS ("DNS resolution complete" , "host" , host , "addressCount" , len (ips ))
40+
41+ dialer := & net.Dialer {
42+ Timeout : 30 * time .Second ,
43+ KeepAlive : 30 * time .Second ,
44+ }
45+
46+ // Try each address sequentially in the order DNS returned them
47+ var lastErr error
48+ for i , ip := range ips {
49+ // Determine address type and dial network
50+ var addrType , dialNetwork string
51+ if ip .To4 () != nil {
52+ addrType = "IPv4"
53+ dialNetwork = network
54+ if network == "tcp" {
55+ dialNetwork = "tcp4"
56+ }
57+ } else {
58+ addrType = "IPv6"
59+ dialNetwork = network
60+ if network == "tcp" {
61+ dialNetwork = "tcp6"
62+ }
63+ }
64+
65+ target := net .JoinHostPort (ip .String (), port )
66+ klog .V (2 ).InfoS ("Attempting connection" , "host" , host , "type" , addrType ,
67+ "address" , ip .String (), "port" , port , "attempt" , i + 1 , "of" , len (ips ))
68+
69+ conn , err := dialer .DialContext (ctx , dialNetwork , target )
70+ if err == nil {
71+ klog .InfoS ("Successfully connected" , "host" , host , "type" , addrType ,
72+ "address" , ip .String (), "port" , port )
73+ return conn , nil
74+ }
75+ klog .V (2 ).ErrorS (err , "Connection failed" , "host" , host , "type" , addrType ,
76+ "address" , ip .String (), "port" , port , "attempt" , i + 1 , "of" , len (ips ))
77+ lastErr = err
78+ }
79+
80+ klog .ErrorS (lastErr , "All connection attempts failed" , "host" , host , "totalAttempts" , len (ips ))
81+ return nil , lastErr
82+ }
83+
84+ // ConfigureDefaultTransport configures http.DefaultTransport to use ImmediateFallbackDialContext.
85+ // This affects all HTTP clients that use the default transport, including the containers/image
86+ // library used for pulling from registries. Returns an error if DefaultTransport is not *http.Transport.
87+ func ConfigureDefaultTransport () error {
88+ transport , ok := http .DefaultTransport .(* http.Transport )
89+ if ! ok {
90+ return fmt .Errorf ("http.DefaultTransport is not *http.Transport, cannot configure custom dialer" )
91+ }
92+ transport .DialContext = ImmediateFallbackDialContext
93+ return nil
94+ }
95+
996func BuildHTTPClient (cpw * CertPoolWatcher ) (* http.Client , error ) {
1097 httpClient := & http.Client {Timeout : 10 * time .Second }
1198
@@ -14,13 +101,16 @@ func BuildHTTPClient(cpw *CertPoolWatcher) (*http.Client, error) {
14101 return nil , err
15102 }
16103
17- tlsConfig := & tls.Config {
104+ // Clone the default transport to inherit custom dialer and other defaults
105+ transport , ok := http .DefaultTransport .(* http.Transport )
106+ if ! ok {
107+ return nil , fmt .Errorf ("http.DefaultTransport is not *http.Transport, cannot build HTTP client" )
108+ }
109+ tlsTransport := transport .Clone ()
110+ tlsTransport .TLSClientConfig = & tls.Config {
18111 RootCAs : pool ,
19112 MinVersion : tls .VersionTLS12 ,
20113 }
21- tlsTransport := & http.Transport {
22- TLSClientConfig : tlsConfig ,
23- }
24114 httpClient .Transport = tlsTransport
25115
26116 return httpClient , nil
0 commit comments