categraf/pkg/httpx/client.go

70 lines
2.0 KiB
Go

// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.
package httpx
import (
"log"
"net/http"
"sync"
"time"
)
// ResetClient wraps (http.Client).Do and resets the underlying connections at the
// configured interval
type ResetClient struct {
httpClientFactory func() *http.Client
resetInterval time.Duration
mu sync.RWMutex
httpClient *http.Client
lastReset time.Time
}
// NewResetClient returns an initialized Client resetting connections at the passed resetInterval ("0"
// means that no reset is performed).
// The underlying http.Client used will be created using the passed http client factory.
func NewResetClient(resetInterval time.Duration, httpClientFactory func() *http.Client) *ResetClient {
return &ResetClient{
httpClientFactory: httpClientFactory,
resetInterval: resetInterval,
httpClient: httpClientFactory(),
lastReset: time.Now(),
}
}
// Do wraps (http.Client).Do. Thread safe.
func (c *ResetClient) Do(req *http.Request) (*http.Response, error) {
c.checkReset()
c.mu.RLock()
httpClient := c.httpClient
c.mu.RUnlock()
return httpClient.Do(req)
}
// checkReset checks whether a client reset should be performed, and performs it
// if so
func (c *ResetClient) checkReset() {
if c.resetInterval == 0 {
return
}
c.mu.Lock()
defer c.mu.Unlock()
if time.Since(c.lastReset) < c.resetInterval {
return
}
log.Println("W! Resetting HTTP client's connections")
c.lastReset = time.Now()
// Close idle connections on underlying client. Safe to do while other goroutines use the client.
// This is a best effort: if other goroutine(s) are currently using the client,
// the related open connection(s) will remain open until the client is GC'ed
c.httpClient.CloseIdleConnections()
c.httpClient = c.httpClientFactory()
}