Skip to content

Commit 8e992ab

Browse files
authored
kms: add Config.Transport field for custom http.RoundTripper (#43)
This commit adds the `Config.Transport` field that allows users to specify a custom `http.RoundTripper`. However, users have to create and set a TLS config on their `http.RoundTripper` implementation. `NewClient` does not use the `APIKey` or `TLS` field to try to modify the `Transport` because: - The `Transport` might be a custom type and not neccessarily a `*http.Transport`. Hence, we cannot inject a TLS config. - Even if it is a `*http.Transport`, we would have to modify its `TLSClientConfig` field which may not be expected and can lead to surprising side effects. Signed-off-by: Andreas Auernhammer <github@aead.dev>
1 parent 1ab28c5 commit 8e992ab

File tree

1 file changed

+53
-34
lines changed

1 file changed

+53
-34
lines changed

kms/client.go

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,27 @@ type Config struct {
5050
// If no API key is set, either a TLS.Certificates
5151
// or TLS.GetClientCertificate must be present.
5252
TLS *tls.Config
53+
54+
// Optional custom Transport specifying the mechanism
55+
// by which individual HTTP requests are made. If empty,
56+
// a default transport is used.
57+
//
58+
// A custom Transport cannot be used in combination with
59+
// an APIKey or TLS config. It's the users responsibility
60+
// to set a TLS configuration on the custom Transport.
61+
Transport http.RoundTripper
5362
}
5463

5564
// NewClient returns a new Client with the given configuration.
5665
func NewClient(conf *Config) (*Client, error) {
57-
if conf.APIKey == nil && (conf.TLS == nil || (len(conf.TLS.Certificates) == 0 && conf.TLS.GetClientCertificate == nil)) {
58-
return nil, errors.New("kms: invalid config: no API key or TLS client certificate provided")
66+
if conf.Transport != nil && (conf.APIKey != nil || conf.TLS != nil) {
67+
// We must not modify the TLSClientConfig of a custom Transport (even if we type-assert that it's *http.Transport).
68+
// Hence, we cannot allow a custom Transport in combination with a APIKey or custom TLS config. Users that want
69+
// to use a custom Transport have to create a proper TLS config themselves.
70+
return nil, errors.New("kms: invalid config: cannot use custom Transport with APIKey or TLS config")
71+
}
72+
if conf.APIKey == nil && (conf.TLS == nil || (len(conf.TLS.Certificates) == 0 && conf.TLS.GetClientCertificate == nil)) && conf.Transport == nil {
73+
return nil, errors.New("kms: invalid config: no API key, TLS client certificate or custom Transport provided")
5974
}
6075
if conf.APIKey != nil && conf.TLS != nil && len(conf.TLS.Certificates) > 0 {
6176
return nil, errors.New("kms: invalid config: 'APIKey' and 'TLS.Certificates' are present")
@@ -64,24 +79,41 @@ func NewClient(conf *Config) (*Client, error) {
6479
return nil, errors.New("kms: invalid config: 'APIKey' and 'TLS.GetClientCertificate' are present")
6580
}
6681

67-
tlsConf := conf.TLS
68-
if conf.APIKey != nil {
69-
cert, err := GenerateCertificate(conf.APIKey, nil)
70-
if err != nil {
71-
return nil, err
72-
}
73-
74-
// ensure that the TLS configuration is not nil and
75-
// the TLS configuration is cloned to avoid
76-
// modifying the original TLS configuration.
77-
if tlsConf == nil {
78-
tlsConf = &tls.Config{}
79-
} else {
80-
tlsConf = tlsConf.Clone()
82+
transport := conf.Transport
83+
if transport == nil {
84+
tlsConf := conf.TLS
85+
if conf.APIKey != nil {
86+
cert, err := GenerateCertificate(conf.APIKey, nil)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
// ensure that the TLS configuration is not nil and
92+
// the TLS configuration is cloned to avoid
93+
// modifying the original TLS configuration.
94+
if tlsConf == nil {
95+
tlsConf = &tls.Config{}
96+
} else {
97+
tlsConf = tlsConf.Clone()
98+
}
99+
100+
tlsConf.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
101+
return &cert, nil
102+
}
81103
}
82-
83-
tlsConf.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
84-
return &cert, nil
104+
transport = &http.Transport{
105+
Proxy: http.ProxyFromEnvironment,
106+
DialContext: (&net.Dialer{
107+
Timeout: 30 * time.Second,
108+
KeepAlive: 30 * time.Second,
109+
DualStack: true,
110+
}).DialContext,
111+
ForceAttemptHTTP2: true,
112+
MaxIdleConnsPerHost: 50,
113+
IdleConnTimeout: 90 * time.Second,
114+
TLSHandshakeTimeout: 10 * time.Second,
115+
ExpectContinueTimeout: 1 * time.Second,
116+
TLSClientConfig: tlsConf,
85117
}
86118
}
87119

@@ -97,21 +129,8 @@ func NewClient(conf *Config) (*Client, error) {
97129
}
98130

99131
lb := &https.LoadBalancer{
100-
Hosts: hosts,
101-
RoundTripper: &http.Transport{
102-
Proxy: http.ProxyFromEnvironment,
103-
DialContext: (&net.Dialer{
104-
Timeout: 30 * time.Second,
105-
KeepAlive: 30 * time.Second,
106-
DualStack: true,
107-
}).DialContext,
108-
ForceAttemptHTTP2: true,
109-
MaxIdleConnsPerHost: 50,
110-
IdleConnTimeout: 90 * time.Second,
111-
TLSHandshakeTimeout: 10 * time.Second,
112-
ExpectContinueTimeout: 1 * time.Second,
113-
TLSClientConfig: tlsConf,
114-
},
132+
Hosts: hosts,
133+
RoundTripper: transport,
115134
}
116135
return &Client{
117136
direct: http.Client{Transport: lb.RoundTripper},

0 commit comments

Comments
 (0)