Replace usage of http.DefaultClient and http.DefaultTransport (#104135)
Remove usage of http.DefaultClient and http.DefaultTransport Part of grafana/data-sources#484
This commit is contained in:
@@ -2270,6 +2270,9 @@ github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.263.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.267.0/go.mod h1:OuwS4c/JYgn0rr/w5zhJBpLo4gKm/vw15RsfpYAvK9Q=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.269.1/go.mod h1:yv2KbO4mlr9WuDK2f+2gHAMTwwLmLuqaEnrPXTRU+OI=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.275.0/go.mod h1:mO9LJqdXDh5JpO/xIdPAeg5LdThgQ06Y/SLpXDWKw2c=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.277.0 h1:VDU2F4Y5NeRS//ejctdZtsAshrGaEdbtW33FsK0EQss=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.277.0/go.mod h1:mAUWg68w5+1f5TLDqagIr8sWr1RT9h7ufJl5NMcWJAU=
|
||||
github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173/go.mod h1:goSDiy3jtC2cp8wjpPZdUHRENcoSUHae1/Px/MDfddA=
|
||||
github.com/grafana/grafana/apps/advisor v0.0.0-20250220154326-6e5de80ef295/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c=
|
||||
github.com/grafana/grafana/apps/advisor v0.0.0-20250506052906-7a2fc797fb4a/go.mod h1:xOL9buMMbQg+3m0jPfrza4/5iwe4EBrnur/aJGAA1pM=
|
||||
|
||||
@@ -16,20 +16,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func setClientWithoutRedirectFollow(t *testing.T) {
|
||||
t.Helper()
|
||||
old := http.DefaultClient
|
||||
http.DefaultClient = &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
http.DefaultClient = old
|
||||
})
|
||||
}
|
||||
|
||||
func TestOAuthLogin_Redirect(t *testing.T) {
|
||||
type testCase struct {
|
||||
desc string
|
||||
@@ -79,7 +65,9 @@ func TestOAuthLogin_Redirect(t *testing.T) {
|
||||
})
|
||||
|
||||
// we need to prevent the http.Client from following redirects
|
||||
setClientWithoutRedirectFollow(t)
|
||||
server.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
res, err := server.Send(server.NewGetRequest("/login/generic_oauth"))
|
||||
require.NoError(t, err)
|
||||
@@ -155,7 +143,9 @@ func TestOAuthLogin_AuthorizationCode(t *testing.T) {
|
||||
})
|
||||
|
||||
// we need to prevent the http.Client from following redirects
|
||||
setClientWithoutRedirectFollow(t)
|
||||
server.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
res, err := server.Send(server.NewGetRequest("/login/generic_oauth?code=code"))
|
||||
require.NoError(t, err)
|
||||
@@ -198,8 +188,9 @@ func TestOAuthLogin_Error(t *testing.T) {
|
||||
hs.log = log.NewNopLogger()
|
||||
hs.SecretsService = fakes.NewFakeSecretsService()
|
||||
})
|
||||
|
||||
setClientWithoutRedirectFollow(t)
|
||||
server.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
res, err := server.Send(server.NewGetRequest("/login/azuread?error=someerror"))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/httpclient"
|
||||
)
|
||||
|
||||
type ManifestInfo struct {
|
||||
@@ -36,6 +37,7 @@ type EntryPointInfo struct {
|
||||
var (
|
||||
entryPointAssetsCacheMu sync.RWMutex // guard entryPointAssetsCache
|
||||
entryPointAssetsCache *dtos.EntryPointAssets // TODO: get rid of global state
|
||||
httpClient = httpclient.New()
|
||||
)
|
||||
|
||||
func GetWebAssets(ctx context.Context, cfg *setting.Cfg, license licensing.Licensing) (*dtos.EntryPointAssets, error) {
|
||||
@@ -88,7 +90,7 @@ func readWebAssetsFromCDN(ctx context.Context, baseURL string) (*dtos.EntryPoint
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := http.DefaultClient.Do(req)
|
||||
response, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -26,6 +28,23 @@ import (
|
||||
|
||||
const grafanaAPI = "https://grafana.com/api"
|
||||
|
||||
var httpClient = http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: func(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) {
|
||||
return dialer.DialContext
|
||||
}(&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}),
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
// GrafanaCom implements the sub-command "grafana-com".
|
||||
func GrafanaCom(c *cli.Context) error {
|
||||
bucketStr := c.String("src-bucket")
|
||||
@@ -330,7 +349,7 @@ func postRequest(cfg packaging.PublishConfig, pth string, body any, descr string
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed posting to %s (%s): %s", u, descr, err)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/Azure/azure-storage-blob-go/azblob"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/util/httpclient"
|
||||
)
|
||||
|
||||
type AzureBlobUploader struct {
|
||||
@@ -175,7 +176,7 @@ func (c *StorageClient) transport() http.RoundTripper {
|
||||
if c.Transport != nil {
|
||||
return c.Transport
|
||||
}
|
||||
return http.DefaultTransport
|
||||
return httpclient.NewHTTPTransport()
|
||||
}
|
||||
|
||||
func NewStorageClient(account, accessKey string) *StorageClient {
|
||||
|
||||
@@ -82,7 +82,7 @@ func New(cfg *setting.Cfg, validator validations.DataSourceRequestURLValidator,
|
||||
})
|
||||
}
|
||||
|
||||
// newConntrackRoundTripper takes a http.DefaultTransport and adds the Conntrack Dialer
|
||||
// newConntrackRoundTripper takes a http.Transport and adds the Conntrack Dialer
|
||||
// so we can instrument outbound connections
|
||||
func newConntrackRoundTripper(name string, transport *http.Transport) *http.Transport {
|
||||
transport.DialContext = conntrack.NewDialContextFunc(
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util/httpclient"
|
||||
)
|
||||
|
||||
var errBytesLimitExceeded = fmt.Errorf("bytes limit exceeded")
|
||||
@@ -17,10 +19,10 @@ type ByteLimitedTransport struct {
|
||||
}
|
||||
|
||||
// NewByteLimitedTransport creates a new ByteLimitedTransport with the specified transport and byte limit.
|
||||
// If transport is nil, http.DefaultTransport will be used.
|
||||
// If transport is nil, a new http.Transport modeled after http.DefaultTransport will be used.
|
||||
func NewByteLimitedTransport(transport http.RoundTripper, limit int64) *ByteLimitedTransport {
|
||||
if transport == nil {
|
||||
transport = http.DefaultTransport
|
||||
transport = httpclient.NewHTTPTransport()
|
||||
}
|
||||
return &ByteLimitedTransport{
|
||||
Transport: transport,
|
||||
|
||||
@@ -47,7 +47,8 @@ func TestNewByteLimitedTransport(t *testing.T) {
|
||||
assert.Equal(t, int64(0), blt.Bytes)
|
||||
|
||||
if tt.transport == nil {
|
||||
assert.Equal(t, http.DefaultTransport, blt.Transport)
|
||||
assert.NotNil(t, blt.Transport)
|
||||
assert.NotEqual(t, http.DefaultTransport, blt.Transport)
|
||||
} else {
|
||||
assert.Equal(t, tt.transport, blt.Transport)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets"
|
||||
"github.com/grafana/grafana/pkg/util/httpclient"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,7 +37,7 @@ const (
|
||||
|
||||
func init() {
|
||||
// Create a size-limited writer that will cancel the context if size is exceeded
|
||||
limitedTransport := NewByteLimitedTransport(http.DefaultTransport, maxOperationBytes)
|
||||
limitedTransport := NewByteLimitedTransport(httpclient.NewHTTPTransport(), maxOperationBytes)
|
||||
httpClient := githttp.NewClient(&http.Client{
|
||||
Transport: limitedTransport,
|
||||
})
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/models/usertoken"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/httpclient"
|
||||
)
|
||||
|
||||
type gcomLogoutRequest struct {
|
||||
@@ -48,7 +49,7 @@ func (s *GComSSOService) LogoutHook(ctx context.Context, user identity.Requester
|
||||
hReq.Header.Add("Content-Type", "application/json")
|
||||
hReq.Header.Add("Authorization", "Bearer "+s.cfg.GrafanaComSSOAPIToken)
|
||||
|
||||
c := http.DefaultClient
|
||||
c := httpclient.New()
|
||||
resp, err := c.Do(hReq)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to send request", "error", err)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/centrifugal/centrifuge"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/util/httpclient"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/live/managedstream"
|
||||
)
|
||||
@@ -63,24 +64,25 @@ func postTestData() {
|
||||
}
|
||||
jsonData, _ := json.Marshal(d)
|
||||
log.Println(string(jsonData))
|
||||
httpClient := httpclient.New()
|
||||
|
||||
req, _ := http.NewRequest("POST", "http://localhost:3000/api/live/pipeline/push/stream/json/auto", bytes.NewReader(jsonData))
|
||||
req.Header.Set("Authorization", "Bearer "+os.Getenv("GF_TOKEN"))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
req, _ = http.NewRequest("POST", "http://localhost:3000/api/live/push/pipeline/push/stream/json/tip", bytes.NewReader(jsonData))
|
||||
req.Header.Set("Authorization", "Bearer "+os.Getenv("GF_TOKEN"))
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
resp, err = httpClient.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
req, _ = http.NewRequest("POST", "http://localhost:3000/api/live/pipeline/push/stream/json/exact", bytes.NewReader(jsonData))
|
||||
req.Header.Set("Authorization", "Bearer "+os.Getenv("GF_TOKEN"))
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
resp, err = httpClient.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/client"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||
"github.com/grafana/grafana/pkg/util/httpclient"
|
||||
amclient "github.com/prometheus/alertmanager/api/v2/client"
|
||||
)
|
||||
|
||||
@@ -37,7 +38,7 @@ func NewAlertmanager(cfg *AlertmanagerConfig, metrics *metrics.RemoteAlertmanage
|
||||
c := &http.Client{Transport: &MimirAuthRoundTripper{
|
||||
TenantID: cfg.TenantID,
|
||||
Password: cfg.Password,
|
||||
Next: http.DefaultTransport,
|
||||
Next: httpclient.NewHTTPTransport(),
|
||||
}}
|
||||
|
||||
tc := client.NewTimedClient(c, metrics.RequestLatency)
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/client"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||
"github.com/grafana/grafana/pkg/util/httpclient"
|
||||
)
|
||||
|
||||
// MimirClient contains all the methods to query the migration critical endpoints of Mimir instance, it's an interface to allow multiple implementations.
|
||||
@@ -86,7 +87,7 @@ func New(cfg *Config, metrics *metrics.RemoteAlertmanager, tracer tracing.Tracer
|
||||
rt := &MimirAuthRoundTripper{
|
||||
TenantID: cfg.TenantID,
|
||||
Password: cfg.Password,
|
||||
Next: http.DefaultTransport,
|
||||
Next: httpclient.NewHTTPTransport(),
|
||||
}
|
||||
|
||||
c := &http.Client{
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/grafana/grafana/pkg/util/httpclient"
|
||||
"github.com/prometheus/alertmanager/api/v2/models"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
config_util "github.com/prometheus/common/config"
|
||||
@@ -216,7 +217,7 @@ func newAlertMetrics(r prometheus.Registerer, queueCap int, queueLen, alertmanag
|
||||
|
||||
func do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
client = httpclient.New()
|
||||
}
|
||||
return client.Do(req.WithContext(ctx))
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package cloudwatch
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
@@ -324,14 +323,7 @@ func (ds *DataSource) newSession(region string) (*session.Session, error) {
|
||||
// only update the transport to try to avoid the issue mentioned here https://github.com/grafana/grafana/issues/46365
|
||||
// also, 'sess' is cached and reused, so the first time it might have the transport not set, the following uses it will
|
||||
if sess.Config.HTTPClient.Transport == nil {
|
||||
// following go standard library logic (https://pkg.go.dev/net/http#Client), if no Transport is provided,
|
||||
// then we use http.DefaultTransport
|
||||
defTransport, ok := http.DefaultTransport.(*http.Transport)
|
||||
if !ok {
|
||||
// this should not happen but validating just in case
|
||||
return nil, errors.New("default http client transport is not of type http.Transport")
|
||||
}
|
||||
sess.Config.HTTPClient.Transport = defTransport.Clone()
|
||||
sess.Config.HTTPClient.Transport = httpclient.NewHTTPTransport()
|
||||
}
|
||||
err = proxy.New(ds.ProxyOpts).ConfigureSecureSocksHTTPProxy(sess.Config.HTTPClient.Transport.(*http.Transport))
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package httpclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// New creates a new http.Client.
|
||||
func New() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: NewHTTPTransport(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewHTTPTransport returns a new HTTP Transport, based off the definition in
|
||||
// the stdlib http.DefaultTransport. It's not a clone, because that would return
|
||||
// any mutations of http.DefaultTransport from other code at the time of the call.
|
||||
// Any plugin that needs a default http transport should use this function.
|
||||
func NewHTTPTransport() http.RoundTripper {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: func(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) {
|
||||
return dialer.DialContext
|
||||
}(&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}),
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util/httpclient"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
@@ -24,6 +25,7 @@ type Server struct {
|
||||
Mux *web.Mux
|
||||
RouteRegister routing.RouteRegister
|
||||
TestServer *httptest.Server
|
||||
HttpClient *http.Client
|
||||
}
|
||||
|
||||
// NewServer starts and returns a new server.
|
||||
@@ -50,6 +52,7 @@ func NewServer(t testing.TB, routeRegister routing.RouteRegister) *Server {
|
||||
RouteRegister: routeRegister,
|
||||
Mux: m,
|
||||
TestServer: testServer,
|
||||
HttpClient: httpclient.New(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +84,7 @@ func (s *Server) NewRequest(method string, target string, body io.Reader) *http.
|
||||
|
||||
// Send sends a HTTP request to the test server and returns an HTTP response.
|
||||
func (s *Server) Send(req *http.Request) (*http.Response, error) {
|
||||
return http.DefaultClient.Do(req)
|
||||
return s.HttpClient.Do(req)
|
||||
}
|
||||
|
||||
// SendJSON sets the Content-Type header to application/json and sends
|
||||
|
||||
@@ -2,15 +2,34 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var httpClient = http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: func(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) {
|
||||
return dialer.DialContext
|
||||
}(&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}),
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
type publisher struct {
|
||||
apiKey string
|
||||
apiURI string
|
||||
@@ -264,7 +283,7 @@ func (p *publisher) postRequest(url string, obj any, desc string) error {
|
||||
req.Header.Add("Authorization", "Bearer "+p.apiKey)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
res, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user