github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/http/dial_dnscache.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package http
    19  
    20  import (
    21  	"context"
    22  	"net"
    23  	"time"
    24  )
    25  
    26  // LookupHost is a function to make custom lookupHost for optional cached DNS requests
    27  type LookupHost func(ctx context.Context, host string) (addrs []string, err error)
    28  
    29  // DialContextWithLookupHost is a helper function which returns `net.DialContext` function.
    30  // It randomly fetches an IP via custom LookupHost function and dials it by the given dial
    31  // function. LookupHost may implement an internal DNS caching implementation, lookupHost
    32  // input if nil then net.DefaultResolver.LookupHost is used.
    33  //
    34  // It dials one by one and returns first connected `net.Conn`.
    35  // If it fails to dial all IPs from cache it returns first error. If no baseDialFunc
    36  // is given, it sets default dial function.
    37  //
    38  // You can use returned dial function for `http.Transport.DialContext`.
    39  //
    40  // In this function, it uses functions from `rand` package. To make it really random,
    41  // you MUST call `rand.Seed` and change the value from the default in your application
    42  func DialContextWithLookupHost(lookupHost LookupHost, baseDialCtx DialContext) DialContext {
    43  	if lookupHost == nil {
    44  		lookupHost = net.DefaultResolver.LookupHost
    45  	}
    46  
    47  	if baseDialCtx == nil {
    48  		// This is same as which `http.DefaultTransport` uses.
    49  		baseDialCtx = (&net.Dialer{
    50  			Timeout:   30 * time.Second,
    51  			KeepAlive: 30 * time.Second,
    52  		}).DialContext
    53  	}
    54  
    55  	return func(ctx context.Context, network, addr string) (conn net.Conn, err error) {
    56  		host, port, err := net.SplitHostPort(addr)
    57  		if err != nil {
    58  			return nil, err
    59  		}
    60  
    61  		if net.ParseIP(host) != nil {
    62  			// For IP only setups there is no need for DNS lookups.
    63  			return baseDialCtx(ctx, "tcp", addr)
    64  		}
    65  
    66  		ips, err := lookupHost(ctx, host)
    67  		if err != nil {
    68  			return nil, err
    69  		}
    70  
    71  		for _, ip := range ips {
    72  			conn, err = baseDialCtx(ctx, "tcp", net.JoinHostPort(ip, port))
    73  			if err == nil {
    74  				break
    75  			}
    76  		}
    77  
    78  		return
    79  	}
    80  }