k8s.io/client-go@v0.22.2/transport/cache.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package transport 18 19 import ( 20 "fmt" 21 "net" 22 "net/http" 23 "strings" 24 "sync" 25 "time" 26 27 utilnet "k8s.io/apimachinery/pkg/util/net" 28 "k8s.io/apimachinery/pkg/util/wait" 29 ) 30 31 // TlsTransportCache caches TLS http.RoundTrippers different configurations. The 32 // same RoundTripper will be returned for configs with identical TLS options If 33 // the config has no custom TLS options, http.DefaultTransport is returned. 34 type tlsTransportCache struct { 35 mu sync.Mutex 36 transports map[tlsCacheKey]*http.Transport 37 } 38 39 const idleConnsPerHost = 25 40 41 var tlsCache = &tlsTransportCache{transports: make(map[tlsCacheKey]*http.Transport)} 42 43 type tlsCacheKey struct { 44 insecure bool 45 caData string 46 certData string 47 keyData string `datapolicy:"security-key"` 48 certFile string 49 keyFile string 50 serverName string 51 nextProtos string 52 disableCompression bool 53 } 54 55 func (t tlsCacheKey) String() string { 56 keyText := "<none>" 57 if len(t.keyData) > 0 { 58 keyText = "<redacted>" 59 } 60 return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, serverName:%s, disableCompression:%t", t.insecure, t.caData, t.certData, keyText, t.serverName, t.disableCompression) 61 } 62 63 func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) { 64 key, canCache, err := tlsConfigKey(config) 65 if err != nil { 66 return nil, err 67 } 68 69 if canCache { 70 // Ensure we only create a single transport for the given TLS options 71 c.mu.Lock() 72 defer c.mu.Unlock() 73 74 // See if we already have a custom transport for this config 75 if t, ok := c.transports[key]; ok { 76 return t, nil 77 } 78 } 79 80 // Get the TLS options for this client config 81 tlsConfig, err := TLSConfigFor(config) 82 if err != nil { 83 return nil, err 84 } 85 // The options didn't require a custom TLS config 86 if tlsConfig == nil && config.Dial == nil && config.Proxy == nil { 87 return http.DefaultTransport, nil 88 } 89 90 dial := config.Dial 91 if dial == nil { 92 dial = (&net.Dialer{ 93 Timeout: 30 * time.Second, 94 KeepAlive: 30 * time.Second, 95 }).DialContext 96 } 97 98 // If we use are reloading files, we need to handle certificate rotation properly 99 // TODO(jackkleeman): We can also add rotation here when config.HasCertCallback() is true 100 if config.TLS.ReloadTLSFiles { 101 dynamicCertDialer := certRotatingDialer(tlsConfig.GetClientCertificate, dial) 102 tlsConfig.GetClientCertificate = dynamicCertDialer.GetClientCertificate 103 dial = dynamicCertDialer.connDialer.DialContext 104 go dynamicCertDialer.Run(wait.NeverStop) 105 } 106 107 proxy := http.ProxyFromEnvironment 108 if config.Proxy != nil { 109 proxy = config.Proxy 110 } 111 112 transport := utilnet.SetTransportDefaults(&http.Transport{ 113 Proxy: proxy, 114 TLSHandshakeTimeout: 10 * time.Second, 115 TLSClientConfig: tlsConfig, 116 MaxIdleConnsPerHost: idleConnsPerHost, 117 DialContext: dial, 118 DisableCompression: config.DisableCompression, 119 }) 120 121 if canCache { 122 // Cache a single transport for these options 123 c.transports[key] = transport 124 } 125 126 return transport, nil 127 } 128 129 // tlsConfigKey returns a unique key for tls.Config objects returned from TLSConfigFor 130 func tlsConfigKey(c *Config) (tlsCacheKey, bool, error) { 131 // Make sure ca/key/cert content is loaded 132 if err := loadTLSFiles(c); err != nil { 133 return tlsCacheKey{}, false, err 134 } 135 136 if c.TLS.GetCert != nil || c.Dial != nil || c.Proxy != nil { 137 // cannot determine equality for functions 138 return tlsCacheKey{}, false, nil 139 } 140 141 k := tlsCacheKey{ 142 insecure: c.TLS.Insecure, 143 caData: string(c.TLS.CAData), 144 serverName: c.TLS.ServerName, 145 nextProtos: strings.Join(c.TLS.NextProtos, ","), 146 disableCompression: c.DisableCompression, 147 } 148 149 if c.TLS.ReloadTLSFiles { 150 k.certFile = c.TLS.CertFile 151 k.keyFile = c.TLS.KeyFile 152 } else { 153 k.certData = string(c.TLS.CertData) 154 k.keyData = string(c.TLS.KeyData) 155 } 156 157 return k, true, nil 158 }