github.com/hellofresh/janus@v0.0.0-20230925145208-ce8de8183c67/pkg/proxy/transport/transport.go (about)

     1  package transport
     2  
     3  import (
     4  	"crypto/tls"
     5  	"fmt"
     6  	"net"
     7  	"net/http"
     8  	"strings"
     9  	"time"
    10  
    11  	"golang.org/x/net/http2"
    12  )
    13  
    14  const (
    15  	// DefaultDialTimeout when connecting to a backend server.
    16  	DefaultDialTimeout = 30 * time.Second
    17  
    18  	// DefaultIdleConnsPerHost the default value set for http.Transport.MaxIdleConnsPerHost.
    19  	DefaultIdleConnsPerHost = 64
    20  
    21  	// DefaultIdleConnTimeout is the default value for the the maximum amount of time an idle
    22  	// (keep-alive) connection will remain idle before closing itself.
    23  	DefaultIdleConnTimeout = 90 * time.Second
    24  )
    25  
    26  type transport struct {
    27  	// Same as net/http.Transport.MaxIdleConnsPerHost, but the default
    28  	// is 64. This value supports scenarios with relatively few remote
    29  	// hosts. When the routing table contains different hosts in the
    30  	// range of hundreds, it is recommended to set this options to a
    31  	// lower value.
    32  	idleConnectionsPerHost int
    33  	insecureSkipVerify     bool
    34  	dialTimeout            time.Duration
    35  	responseHeaderTimeout  time.Duration
    36  	idleConnTimeout        time.Duration
    37  	idleConnPurgeTicker    *time.Ticker
    38  }
    39  
    40  func (t transport) hash() string {
    41  	return strings.Join([]string{
    42  		fmt.Sprintf("idleConnectionsPerHost:%v", t.idleConnectionsPerHost),
    43  		fmt.Sprintf("insecureSkipVerify:%v", t.insecureSkipVerify),
    44  		fmt.Sprintf("dialTimeout:%v", t.dialTimeout),
    45  		fmt.Sprintf("responseHeaderTimeout:%v", t.responseHeaderTimeout),
    46  		fmt.Sprintf("idleConnTimeout:%v", t.idleConnTimeout),
    47  	}, ";")
    48  }
    49  
    50  var registryInstance *registry
    51  
    52  func init() {
    53  	registryInstance = newRegistry()
    54  }
    55  
    56  // New creates a new instance of Transport with the given params
    57  func New(opts ...Option) *http.Transport {
    58  	t := transport{}
    59  
    60  	for _, opt := range opts {
    61  		opt(&t)
    62  	}
    63  
    64  	if t.dialTimeout <= 0 {
    65  		t.dialTimeout = DefaultDialTimeout
    66  	}
    67  
    68  	if t.idleConnectionsPerHost <= 0 {
    69  		t.idleConnectionsPerHost = DefaultIdleConnsPerHost
    70  	}
    71  
    72  	if t.idleConnTimeout == 0 {
    73  		t.idleConnTimeout = DefaultIdleConnTimeout
    74  	}
    75  
    76  	// let's try to get the cached transport from registry, since there is no need to create lots of
    77  	// transports with the same configuration
    78  	hash := t.hash()
    79  	if tr, ok := registryInstance.get(hash); ok {
    80  		return tr
    81  	}
    82  
    83  	tr := &http.Transport{
    84  		Proxy: http.ProxyFromEnvironment,
    85  		DialContext: (&net.Dialer{
    86  			Timeout:   t.dialTimeout,
    87  			KeepAlive: 30 * time.Second,
    88  			DualStack: true,
    89  		}).DialContext,
    90  		MaxIdleConns:          100,
    91  		IdleConnTimeout:       t.idleConnTimeout,
    92  		TLSHandshakeTimeout:   10 * time.Second,
    93  		ExpectContinueTimeout: 1 * time.Second,
    94  		ResponseHeaderTimeout: t.responseHeaderTimeout,
    95  		MaxIdleConnsPerHost:   t.idleConnectionsPerHost,
    96  		TLSClientConfig:       &tls.Config{InsecureSkipVerify: t.insecureSkipVerify},
    97  	}
    98  
    99  	http2.ConfigureTransport(tr)
   100  
   101  	// Create a channel that listens to idleConnPurgeTicker to periodically purge idle connections
   102  	if t.idleConnPurgeTicker != nil {
   103  		go func(transport *http.Transport) {
   104  			for {
   105  				select {
   106  				case <-t.idleConnPurgeTicker.C:
   107  					// This is a hack to prevent (stale) keep-alive connections from being used
   108  					// Toggling DisableKeepAlives flushes all idle keep-alive connections
   109  					transport.DisableKeepAlives = true
   110  					transport.CloseIdleConnections()
   111  					transport.DisableKeepAlives = false
   112  				}
   113  			}
   114  		}(tr)
   115  	}
   116  
   117  	// save newly created transport in registry, to try to reuse it in the future
   118  	registryInstance.put(hash, tr)
   119  
   120  	return tr
   121  }