@@ -17,11 +17,13 @@ package config
1717
1818import (
1919 "bytes"
20+ "context"
2021 "crypto/sha256"
2122 "crypto/tls"
2223 "crypto/x509"
2324 "fmt"
2425 "io/ioutil"
26+ "net"
2527 "net/http"
2628 "net/url"
2729 "strings"
@@ -38,6 +40,12 @@ var DefaultHTTPClientConfig = HTTPClientConfig{
3840 FollowRedirects : true ,
3941}
4042
43+ // defaultHTTPClientOptions holds the default HTTP client options.
44+ var defaultHTTPClientOptions = httpClientOptions {
45+ keepAlivesEnabled : true ,
46+ http2Enabled : true ,
47+ }
48+
4149type closeIdler interface {
4250 CloseIdleConnections ()
4351}
@@ -194,15 +202,50 @@ func (a *BasicAuth) UnmarshalYAML(unmarshal func(interface{}) error) error {
194202 return unmarshal ((* plain )(a ))
195203}
196204
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+
197239// NewClient returns a http.Client using the specified http.RoundTripper.
198240func newClient (rt http.RoundTripper ) * http.Client {
199241 return & http.Client {Transport : rt }
200242}
201243
202244// 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 ... )
206249 if err != nil {
207250 return nil , err
208251 }
@@ -216,29 +259,45 @@ func NewClientFromConfig(cfg HTTPClientConfig, name string, disableKeepAlives, e
216259}
217260
218261// 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+
221283 newRT := func (tlsConfig * tls.Config ) (http.RoundTripper , error ) {
222284 // The only timeout we care about is the configured scrape timeout.
223285 // It is applied on request. So we leave out any timings here.
224286 var rt http.RoundTripper = & http.Transport {
225287 Proxy : http .ProxyURL (cfg .ProxyURL .URL ),
226288 MaxIdleConns : 20000 ,
227289 MaxIdleConnsPerHost : 1000 , // see https://github.com/golang/go/issues/13801
228- DisableKeepAlives : disableKeepAlives ,
290+ DisableKeepAlives : ! opts . keepAlivesEnabled ,
229291 TLSClientConfig : tlsConfig ,
230292 DisableCompression : true ,
231293 // 5 minutes is typically above the maximum sane scrape interval. So we can
232294 // use keepalive for all configurations.
233295 IdleConnTimeout : 5 * time .Minute ,
234296 TLSHandshakeTimeout : 10 * time .Second ,
235297 ExpectContinueTimeout : 1 * time .Second ,
236- DialContext : conntrack .NewDialContextFunc (
237- conntrack .DialWithTracing (),
238- conntrack .DialWithName (name ),
239- ),
298+ DialContext : dialContext ,
240299 }
241- if enableHTTP2 {
300+ if opts . http2Enabled {
242301 // HTTP/2 support is golang has many problematic cornercases where
243302 // dead connections would be kept and used in connection pools.
244303 // https://github.com/golang/go/issues/32388
0 commit comments