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 }