88 "io"
99 "maps"
1010 "net/http"
11+ "net/http/httptrace"
1112 "slices"
1213 "strings"
1314
@@ -169,9 +170,10 @@ func responseHeadersFromNetHeader(h http.Header) (map[string]string, map[string]
169170}
170171
171172type httpClient struct {
172- client * safeurl.WrappedClient
173- config HTTPClientConfig
174- lggr logger.Logger
173+ client * safeurl.WrappedClient
174+ config HTTPClientConfig
175+ lggr logger.Logger
176+ metrics * httpClientMetrics
175177}
176178
177179// NewHTTPClient creates a new NewHTTPClient
@@ -194,12 +196,21 @@ func NewHTTPClient(config HTTPClientConfig, lggr logger.Logger) (HTTPClient, err
194196 SetBlockedIPs (config .BlockedIPs ... ).
195197 SetBlockedIPsCIDR (config .BlockedIPsCIDR ... ).
196198 SetCheckRedirect (disableRedirects ).
199+ SetTransport (
200+ http .DefaultTransport .(* http.Transport ),
201+ ).
197202 Build ()
198203
204+ metrics , err := newHTTPClientMetrics ()
205+ if err != nil {
206+ return nil , err
207+ }
208+
199209 return & httpClient {
200- config : config ,
201- client : safeurl .Client (safeConfig ),
202- lggr : lggr ,
210+ config : config ,
211+ client : safeurl .Client (safeConfig ),
212+ lggr : lggr ,
213+ metrics : metrics ,
203214 }, nil
204215}
205216
@@ -297,8 +308,13 @@ func (c *httpClient) Send(ctx context.Context, req HTTPRequest) (*HTTPResponse,
297308 timeoutCtx , cancel := context .WithTimeout (ctx , to )
298309 defer cancel ()
299310
311+ requestStart := time .Now ()
312+ trace , traceState := newClientTrace (ctx , req .Method , requestStart , c .metrics )
313+ timeoutCtx = httptrace .WithClientTrace (timeoutCtx , trace )
314+
300315 r , err := http .NewRequestWithContext (timeoutCtx , req .Method , req .URL , bytes .NewBuffer (req .Body ))
301316 if err != nil {
317+ c .metrics .recordTotal (ctx , req .Method , 0 , false , false , time .Since (requestStart ))
302318 return nil , err
303319 }
304320 for k , values := range requestToNetHeader (req ) {
@@ -309,6 +325,7 @@ func (c *httpClient) Send(ctx context.Context, req HTTPRequest) (*HTTPResponse,
309325
310326 resp , err := c .client .Do (r )
311327 if err != nil {
328+ c .metrics .recordTotal (ctx , req .Method , 0 , false , traceState .connReused .Load (), time .Since (requestStart ))
312329 if isBlockedRequest (err ) {
313330 c .lggr .Warnw ("HTTP request blocked" , "err" , err )
314331 return nil , fmt .Errorf ("%w: %w" , ErrBlockedRequest , err )
@@ -324,10 +341,13 @@ func (c *httpClient) Send(ctx context.Context, req HTTPRequest) (*HTTPResponse,
324341 reader := http .MaxBytesReader (nil , resp .Body , int64 (n ))
325342 body , err := io .ReadAll (reader )
326343 if err != nil {
344+ c .metrics .recordTotal (ctx , req .Method , resp .StatusCode , false , traceState .connReused .Load (), time .Since (requestStart ))
327345 c .lggr .Errorw ("failed to read HTTP response body" , "err" , err )
328346 return nil , errors .Join (err , ErrHTTPRead )
329347 }
330348
349+ c .metrics .recordTotal (ctx , req .Method , resp .StatusCode , true , traceState .connReused .Load (), time .Since (requestStart ))
350+
331351 headers , multiHeaders := responseHeadersFromNetHeader (resp .Header )
332352 c .lggr .Debugw ("received HTTP response" , "statusCode" , resp .StatusCode )
333353 return & HTTPResponse {
0 commit comments