blob: 7cffe2a5faf3f274dea798fcf1a6d353c5af342d [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
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 "sync"
24 "time"
25
26 utilnet "k8s.io/apimachinery/pkg/util/net"
27)
28
29// TlsTransportCache caches TLS http.RoundTrippers different configurations. The
30// same RoundTripper will be returned for configs with identical TLS options If
31// the config has no custom TLS options, http.DefaultTransport is returned.
32type tlsTransportCache struct {
33 mu sync.Mutex
34 transports map[tlsCacheKey]*http.Transport
35}
36
37const idleConnsPerHost = 25
38
39var tlsCache = &tlsTransportCache{transports: make(map[tlsCacheKey]*http.Transport)}
40
41type tlsCacheKey struct {
42 insecure bool
43 caData string
44 certData string
45 keyData string
46 getCert string
47 serverName string
48 dial string
49}
50
51func (t tlsCacheKey) String() string {
52 keyText := "<none>"
53 if len(t.keyData) > 0 {
54 keyText = "<redacted>"
55 }
56 return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, getCert: %s, serverName:%s, dial:%s", t.insecure, t.caData, t.certData, keyText, t.getCert, t.serverName, t.dial)
57}
58
59func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
60 key, err := tlsConfigKey(config)
61 if err != nil {
62 return nil, err
63 }
64
65 // Ensure we only create a single transport for the given TLS options
66 c.mu.Lock()
67 defer c.mu.Unlock()
68
69 // See if we already have a custom transport for this config
70 if t, ok := c.transports[key]; ok {
71 return t, nil
72 }
73
74 // Get the TLS options for this client config
75 tlsConfig, err := TLSConfigFor(config)
76 if err != nil {
77 return nil, err
78 }
79 // The options didn't require a custom TLS config
80 if tlsConfig == nil && config.Dial == nil {
81 return http.DefaultTransport, nil
82 }
83
84 dial := config.Dial
85 if dial == nil {
86 dial = (&net.Dialer{
87 Timeout: 30 * time.Second,
88 KeepAlive: 30 * time.Second,
89 }).DialContext
90 }
91 // Cache a single transport for these options
92 c.transports[key] = utilnet.SetTransportDefaults(&http.Transport{
93 Proxy: http.ProxyFromEnvironment,
94 TLSHandshakeTimeout: 10 * time.Second,
95 TLSClientConfig: tlsConfig,
96 MaxIdleConnsPerHost: idleConnsPerHost,
97 DialContext: dial,
98 })
99 return c.transports[key], nil
100}
101
102// tlsConfigKey returns a unique key for tls.Config objects returned from TLSConfigFor
103func tlsConfigKey(c *Config) (tlsCacheKey, error) {
104 // Make sure ca/key/cert content is loaded
105 if err := loadTLSFiles(c); err != nil {
106 return tlsCacheKey{}, err
107 }
108 return tlsCacheKey{
109 insecure: c.TLS.Insecure,
110 caData: string(c.TLS.CAData),
111 certData: string(c.TLS.CertData),
112 keyData: string(c.TLS.KeyData),
113 getCert: fmt.Sprintf("%p", c.TLS.GetCert),
114 serverName: c.TLS.ServerName,
115 dial: fmt.Sprintf("%p", c.Dial),
116 }, nil
117}