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