@@ -17,11 +17,13 @@ package config
17
17
18
18
import (
19
19
"bytes"
20
+ "context"
20
21
"crypto/sha256"
21
22
"crypto/tls"
22
23
"crypto/x509"
23
24
"fmt"
24
25
"io/ioutil"
26
+ "net"
25
27
"net/http"
26
28
"net/url"
27
29
"strings"
@@ -38,6 +40,12 @@ var DefaultHTTPClientConfig = HTTPClientConfig{
38
40
FollowRedirects : true ,
39
41
}
40
42
43
+ // defaultHTTPClientOptions holds the default HTTP client options.
44
+ var defaultHTTPClientOptions = httpClientOptions {
45
+ keepAlivesEnabled : true ,
46
+ http2Enabled : true ,
47
+ }
48
+
41
49
type closeIdler interface {
42
50
CloseIdleConnections ()
43
51
}
@@ -194,15 +202,50 @@ func (a *BasicAuth) UnmarshalYAML(unmarshal func(interface{}) error) error {
194
202
return unmarshal ((* plain )(a ))
195
203
}
196
204
205
+ // DialContextFunc defines the signature of the DialContext() function implemented
206
+ // by net.Dialer.
207
+ type DialContextFunc func (context.Context , string , string ) (net.Conn , error )
208
+
209
+ type httpClientOptions struct {
210
+ dialContextFunc DialContextFunc
211
+ keepAlivesEnabled bool
212
+ http2Enabled bool
213
+ }
214
+
215
+ // HTTPClientOption defines an option that can be applied to the HTTP client.
216
+ type HTTPClientOption func (options * httpClientOptions )
217
+
218
+ // WithDialContextFunc allows you to override func gets used for the actual dialing. The default is `net.Dialer.DialContext`.
219
+ func WithDialContextFunc (fn DialContextFunc ) HTTPClientOption {
220
+ return func (opts * httpClientOptions ) {
221
+ opts .dialContextFunc = fn
222
+ }
223
+ }
224
+
225
+ // WithKeepAlivesDisabled allows to disable HTTP keepalive.
226
+ func WithKeepAlivesDisabled () HTTPClientOption {
227
+ return func (opts * httpClientOptions ) {
228
+ opts .keepAlivesEnabled = false
229
+ }
230
+ }
231
+
232
+ // WithHTTP2Disabled allows to disable HTTP2.
233
+ func WithHTTP2Disabled () HTTPClientOption {
234
+ return func (opts * httpClientOptions ) {
235
+ opts .http2Enabled = false
236
+ }
237
+ }
238
+
197
239
// NewClient returns a http.Client using the specified http.RoundTripper.
198
240
func newClient (rt http.RoundTripper ) * http.Client {
199
241
return & http.Client {Transport : rt }
200
242
}
201
243
202
244
// NewClientFromConfig returns a new HTTP client configured for the
203
- // given config.HTTPClientConfig. The name is used as go-conntrack metric label.
204
- func NewClientFromConfig (cfg HTTPClientConfig , name string , disableKeepAlives , enableHTTP2 bool ) (* http.Client , error ) {
205
- rt , err := NewRoundTripperFromConfig (cfg , name , disableKeepAlives , enableHTTP2 )
245
+ // given config.HTTPClientConfig and config.HTTPClientOption.
246
+ // The name is used as go-conntrack metric label.
247
+ func NewClientFromConfig (cfg HTTPClientConfig , name string , optFuncs ... HTTPClientOption ) (* http.Client , error ) {
248
+ rt , err := NewRoundTripperFromConfig (cfg , name , optFuncs ... )
206
249
if err != nil {
207
250
return nil , err
208
251
}
@@ -216,29 +259,45 @@ func NewClientFromConfig(cfg HTTPClientConfig, name string, disableKeepAlives, e
216
259
}
217
260
218
261
// NewRoundTripperFromConfig returns a new HTTP RoundTripper configured for the
219
- // given config.HTTPClientConfig. The name is used as go-conntrack metric label.
220
- func NewRoundTripperFromConfig (cfg HTTPClientConfig , name string , disableKeepAlives , enableHTTP2 bool ) (http.RoundTripper , error ) {
262
+ // given config.HTTPClientConfig and config.HTTPClientOption.
263
+ // The name is used as go-conntrack metric label.
264
+ func NewRoundTripperFromConfig (cfg HTTPClientConfig , name string , optFuncs ... HTTPClientOption ) (http.RoundTripper , error ) {
265
+ opts := defaultHTTPClientOptions
266
+ for _ , f := range optFuncs {
267
+ f (& opts )
268
+ }
269
+
270
+ var dialContext func (ctx context.Context , network , addr string ) (net.Conn , error )
271
+
272
+ if opts .dialContextFunc != nil {
273
+ dialContext = conntrack .NewDialContextFunc (
274
+ conntrack .DialWithDialContextFunc ((func (context.Context , string , string ) (net.Conn , error ))(opts .dialContextFunc )),
275
+ conntrack .DialWithTracing (),
276
+ conntrack .DialWithName (name ))
277
+ } else {
278
+ dialContext = conntrack .NewDialContextFunc (
279
+ conntrack .DialWithTracing (),
280
+ conntrack .DialWithName (name ))
281
+ }
282
+
221
283
newRT := func (tlsConfig * tls.Config ) (http.RoundTripper , error ) {
222
284
// The only timeout we care about is the configured scrape timeout.
223
285
// It is applied on request. So we leave out any timings here.
224
286
var rt http.RoundTripper = & http.Transport {
225
287
Proxy : http .ProxyURL (cfg .ProxyURL .URL ),
226
288
MaxIdleConns : 20000 ,
227
289
MaxIdleConnsPerHost : 1000 , // see https://github.com/golang/go/issues/13801
228
- DisableKeepAlives : disableKeepAlives ,
290
+ DisableKeepAlives : ! opts . keepAlivesEnabled ,
229
291
TLSClientConfig : tlsConfig ,
230
292
DisableCompression : true ,
231
293
// 5 minutes is typically above the maximum sane scrape interval. So we can
232
294
// use keepalive for all configurations.
233
295
IdleConnTimeout : 5 * time .Minute ,
234
296
TLSHandshakeTimeout : 10 * time .Second ,
235
297
ExpectContinueTimeout : 1 * time .Second ,
236
- DialContext : conntrack .NewDialContextFunc (
237
- conntrack .DialWithTracing (),
238
- conntrack .DialWithName (name ),
239
- ),
298
+ DialContext : dialContext ,
240
299
}
241
- if enableHTTP2 {
300
+ if opts . http2Enabled {
242
301
// HTTP/2 support is golang has many problematic cornercases where
243
302
// dead connections would be kept and used in connection pools.
244
303
// https://github.com/golang/go/issues/32388
0 commit comments