pkg/http/client.go (76 lines of code) (raw):

package http import ( "crypto/tls" "net/http" "time" "golang.org/x/net/http2" ) type ClientOpts struct { // Close controls whether the client closes the connection after each request. Close bool // Timeout is the timeout for the http client and the http request. Timeout time.Duration // InsecureSkipVerify controls whether the client verifies the server's certificate chain and host name. InsecureSkipVerify bool // IdleConnTimeout is the maximum amount of time an idle (keep-alive) connection // will remain idle before closing itself. IdleConnTimeout time.Duration // ResponseHeaderTimeout is the amount of time to wait for a server's response headers // after fully writing the request (including its body, if any). ResponseHeaderTimeout time.Duration // MaxIdleConns controls the maximum number of idle (keep-alive) connections across all hosts. MaxIdleConns int // MaxIdleConnsPerHost, if non-zero, controls the maximum idle (keep-alive) per host. MaxIdleConnsPerHost int // MaxConnsPerHost, if non-zero, controls the maximum connections per host. MaxConnsPerHost int // TLSHandshakeTimeout specifies the maximum amount of time to // wait for a TLS handshake. Zero means no timeout. TLSHandshakeTimeout time.Duration // DisableHTTP2 controls whether the client disables HTTP/2 support. DisableHTTP2 bool // DisableKeepAlives controls whether the client disables HTTP keep-alives. DisableKeepAlives bool } func (c ClientOpts) WithDefaults() ClientOpts { if c.Timeout == 0 { c.Timeout = 10 * time.Second } if c.IdleConnTimeout == 0 { c.IdleConnTimeout = 1 * time.Minute } if c.ResponseHeaderTimeout == 0 { c.ResponseHeaderTimeout = 10 * time.Second } if c.MaxIdleConns == 0 { c.MaxIdleConns = 100 } if c.MaxIdleConnsPerHost == 0 { c.MaxIdleConnsPerHost = 5 } if c.MaxConnsPerHost == 0 { c.MaxConnsPerHost = 5 } if c.TLSHandshakeTimeout == 0 { c.TLSHandshakeTimeout = 10 * time.Second } return c } func NewClient(opts ClientOpts) *http.Client { opts = opts.WithDefaults() t := http.DefaultTransport.(*http.Transport).Clone() t.MaxIdleConns = opts.MaxIdleConns t.MaxConnsPerHost = opts.MaxConnsPerHost t.MaxIdleConnsPerHost = opts.MaxIdleConnsPerHost t.ResponseHeaderTimeout = opts.ResponseHeaderTimeout t.IdleConnTimeout = opts.IdleConnTimeout if t.TLSClientConfig == nil { t.TLSClientConfig = &tls.Config{} } t.TLSClientConfig.InsecureSkipVerify = opts.InsecureSkipVerify t.TLSHandshakeTimeout = opts.TLSHandshakeTimeout t.DisableKeepAlives = opts.DisableKeepAlives if http2Transport, err := http2.ConfigureTransports(t); err == nil { // if the connection has been idle for 10 seconds, send a ping frame for a health check http2Transport.ReadIdleTimeout = 10 * time.Second // if there's no response to the ping within 2 seconds, close the connection http2Transport.PingTimeout = 2 * time.Second http2Transport.TLSClientConfig = &tls.Config{ InsecureSkipVerify: opts.InsecureSkipVerify, } } if opts.DisableHTTP2 { t.ForceAttemptHTTP2 = false t.TLSNextProto = make(map[string]func(authority string, c *tls.Conn) http.RoundTripper) t.TLSClientConfig = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify} t.TLSClientConfig.NextProtos = []string{"http/1.1"} } return &http.Client{ Timeout: opts.Timeout, Transport: t, } }