github.com/blend/go-sdk@v1.20240719.1/webutil/http_trace.go (about)

     1  /*
     2  
     3  Copyright (c) 2024 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package webutil
     9  
    10  import (
    11  	"crypto/tls"
    12  	"net/http"
    13  	"net/http/httptrace"
    14  	"time"
    15  )
    16  
    17  // WithClientHTTPTrace adds the http client trace to the request.
    18  func WithClientHTTPTrace(req *http.Request, trace *HTTPTrace) *http.Request {
    19  	return req.WithContext(httptrace.WithClientTrace(req.Context(), trace.Trace()))
    20  }
    21  
    22  // HTTPTrace is timing information for the full http call.
    23  type HTTPTrace struct {
    24  	Start       time.Time `json:"start"`
    25  	GetConn     time.Time `json:"getConn"`
    26  	GotConn     time.Time `json:"gotConn"`
    27  	PutIdleConn time.Time `json:"putIdleConn"`
    28  
    29  	DNSStart time.Time `json:"dnsStart"`
    30  	DNSDone  time.Time `json:"dnsDone"`
    31  
    32  	ConnectStart time.Time `json:"connectStart"`
    33  	ConnectDone  time.Time `json:"connectDone"`
    34  
    35  	TLSHandshakeStart time.Time `json:"tlsHandshakeStart"`
    36  	TLSHandshakeDone  time.Time `json:"tlsHandshakeDone"`
    37  
    38  	WroteHeaders         time.Time `json:"wroteHeaders"`
    39  	WroteRequest         time.Time `json:"wroteRequest"`
    40  	GotFirstResponseByte time.Time `json:"gotFirstResponseByte"`
    41  
    42  	DNSElapsed          time.Duration `json:"dnsElapsed"`
    43  	TLSHandshakeElapsed time.Duration `json:"tlsHandshakeElapsed"`
    44  	DialElapsed         time.Duration `json:"dialElapsed"`
    45  	RequestElapsed      time.Duration `json:"requestElapsed"`
    46  	ServerElapsed       time.Duration `json:"severElapsed"`
    47  }
    48  
    49  // Trace returns the trace binder.
    50  func (ht *HTTPTrace) Trace() *httptrace.ClientTrace {
    51  	now := func() time.Time {
    52  		return time.Now().UTC()
    53  	}
    54  	ht.Start = now()
    55  	return &httptrace.ClientTrace{
    56  		GetConn: func(_ string) {
    57  			ht.GetConn = now()
    58  		},
    59  		GotConn: func(_ httptrace.GotConnInfo) {
    60  			ht.GotConn = now()
    61  		},
    62  		PutIdleConn: func(_ error) {
    63  			ht.PutIdleConn = now()
    64  		},
    65  		GotFirstResponseByte: func() {
    66  			ht.GotFirstResponseByte = now()
    67  			ht.ServerElapsed = ht.GotFirstResponseByte.Sub(ht.WroteRequest)
    68  		},
    69  		DNSStart: func(_ httptrace.DNSStartInfo) {
    70  			ht.DNSStart = now()
    71  		},
    72  		DNSDone: func(_ httptrace.DNSDoneInfo) {
    73  			ht.DNSDone = now()
    74  			ht.DNSElapsed = ht.DNSDone.Sub(ht.DNSStart)
    75  		},
    76  		ConnectStart: func(_, _ string) {
    77  			ht.ConnectStart = now()
    78  		},
    79  		ConnectDone: func(_, _ string, _ error) {
    80  			ht.ConnectDone = now()
    81  			ht.DialElapsed = ht.ConnectDone.Sub(ht.ConnectStart)
    82  		},
    83  		TLSHandshakeStart: func() {
    84  			ht.TLSHandshakeStart = now()
    85  		},
    86  		TLSHandshakeDone: func(_ tls.ConnectionState, _ error) {
    87  			ht.TLSHandshakeDone = now()
    88  			ht.TLSHandshakeElapsed = ht.TLSHandshakeDone.Sub(ht.TLSHandshakeStart)
    89  		},
    90  		WroteHeaders: func() {
    91  			ht.WroteHeaders = now()
    92  		},
    93  		WroteRequest: func(_ httptrace.WroteRequestInfo) {
    94  			ht.WroteRequest = now()
    95  
    96  			if !ht.ConnectDone.IsZero() {
    97  				ht.RequestElapsed = ht.WroteRequest.Sub(ht.ConnectDone)
    98  				return
    99  			}
   100  			if !ht.GetConn.IsZero() {
   101  				ht.RequestElapsed = ht.WroteRequest.Sub(ht.GetConn)
   102  				return
   103  			}
   104  			if !ht.GotConn.IsZero() {
   105  				ht.RequestElapsed = ht.WroteRequest.Sub(ht.GotConn)
   106  				return
   107  			}
   108  		},
   109  	}
   110  }