diff --git a/cliclient/cliclient.go b/cliclient/cliclient.go index e8fba559..79dd2aa8 100644 --- a/cliclient/cliclient.go +++ b/cliclient/cliclient.go @@ -1,9 +1,13 @@ package cliclient import ( + "context" "errors" "fmt" + "net" + "net/http" "strings" + "time" errorsPkg "github.com/pkg/errors" "github.com/rancher/cli/config" @@ -181,10 +185,11 @@ func createClientOpts(config *config.ServerConfig) *clientbase.ClientOpts { } options := &clientbase.ClientOpts{ - URL: serverURL, - AccessKey: config.AccessKey, - SecretKey: config.SecretKey, - CACerts: config.CACerts, + HTTPClient: DefaultHTTPClient(), + URL: serverURL, + AccessKey: config.AccessKey, + SecretKey: config.SecretKey, + CACerts: config.CACerts, } return options } @@ -203,3 +208,23 @@ func CheckProject(s string) []string { return clustProj } + +// DefaultHTTPClient makes http.Client including http.Transport, +// with default values (for example: proxy) and custom timeouts. +// See: https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/ +func DefaultHTTPClient() *http.Client { + transport := http.DefaultTransport.(*http.Transport).Clone() + transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + dialer := &net.Dialer{ + Timeout: 5 * time.Second, + KeepAlive: 30 * time.Second, + } + return dialer.DialContext(ctx, network, addr) + } + transport.ResponseHeaderTimeout = 10 * time.Second + + return &http.Client{ + Transport: transport, + Timeout: 20 * time.Second, + } +} diff --git a/cmd/kubectl_token.go b/cmd/kubectl_token.go index dfd8ece8..814c7b28 100644 --- a/cmd/kubectl_token.go +++ b/cmd/kubectl_token.go @@ -22,6 +22,7 @@ import ( "strings" "time" + "github.com/rancher/cli/cliclient" "github.com/rancher/cli/config" apiv3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3" managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3" @@ -639,10 +640,9 @@ func getClient(skipVerify bool, caCerts string) (*http.Client, error) { return nil, err } - // clone the DefaultTransport to get the default values - transport := http.DefaultTransport.(*http.Transport).Clone() - transport.TLSClientConfig = tlsConfig - return &http.Client{Transport: transport}, nil + client := cliclient.DefaultHTTPClient() + client.Transport.(*http.Transport).TLSClientConfig = tlsConfig + return client, nil } func getTLSConfig(skipVerify bool, caCerts string) (*tls.Config, error) { diff --git a/cmd/login.go b/cmd/login.go index d7af6ff6..9f24abf6 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -260,10 +260,8 @@ func getCertFromServer(ctx *cli.Context, cf *config.ServerConfig) (*cliclient.Ma req.SetBasicAuth(cf.AccessKey, cf.SecretKey) - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - client := &http.Client{Transport: tr} + client := cliclient.DefaultHTTPClient() + client.Transport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} res, err := client.Do(req) if err != nil { diff --git a/cmd/ssh.go b/cmd/ssh.go index 53a0d859..060fd504 100644 --- a/cmd/ssh.go +++ b/cmd/ssh.go @@ -170,7 +170,7 @@ func getSSHKey(c *cliclient.MasterClient, link, nodeName string) ([]byte, string req.SetBasicAuth(c.UserConfig.AccessKey, c.UserConfig.SecretKey) req.Header.Add("Accept-Encoding", "zip") - client := &http.Client{} + client := cliclient.DefaultHTTPClient() if c.UserConfig.CACerts != "" { roots := x509.NewCertPool() @@ -178,12 +178,9 @@ func getSSHKey(c *cliclient.MasterClient, link, nodeName string) ([]byte, string if !ok { return []byte{}, "", err } - tr := &http.Transport{ - TLSClientConfig: &tls.Config{ - RootCAs: roots, - }, + client.Transport.(*http.Transport).TLSClientConfig = &tls.Config{ + RootCAs: roots, } - client.Transport = tr } resp, err := client.Do(req)