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 }