1 // Copyright (c) 2015-2021 Jeevanandam M (jeeva@myjeeva.com), All rights reserved. 2 // resty source code and usage is governed by a MIT style 3 // license that can be found in the LICENSE file. 4 5 package resty 6 7 import ( 8 "context" 9 "crypto/tls" 10 "net" 11 "net/http/httptrace" 12 "time" 13 ) 14 15 //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ 16 // TraceInfo struct 17 //_______________________________________________________________________ 18 19 // TraceInfo struct is used provide request trace info such as DNS lookup 20 // duration, Connection obtain duration, Server processing duration, etc. 21 // 22 // Since v2.0.0 23 type TraceInfo struct { 24 // DNSLookup is a duration that transport took to perform 25 // DNS lookup. 26 DNSLookup time.Duration 27 28 // ConnTime is a duration that took to obtain a successful connection. 29 ConnTime time.Duration 30 31 // TCPConnTime is a duration that took to obtain the TCP connection. 32 TCPConnTime time.Duration 33 34 // TLSHandshake is a duration that TLS handshake took place. 35 TLSHandshake time.Duration 36 37 // ServerTime is a duration that server took to respond first byte. 38 ServerTime time.Duration 39 40 // ResponseTime is a duration since first response byte from server to 41 // request completion. 42 ResponseTime time.Duration 43 44 // TotalTime is a duration that total request took end-to-end. 45 TotalTime time.Duration 46 47 // IsConnReused is whether this connection has been previously 48 // used for another HTTP request. 49 IsConnReused bool 50 51 // IsConnWasIdle is whether this connection was obtained from an 52 // idle pool. 53 IsConnWasIdle bool 54 55 // ConnIdleTime is a duration how long the connection was previously 56 // idle, if IsConnWasIdle is true. 57 ConnIdleTime time.Duration 58 59 // RequestAttempt is to represent the request attempt made during a Resty 60 // request execution flow, including retry count. 61 RequestAttempt int 62 63 // RemoteAddr returns the remote network address. 64 RemoteAddr net.Addr 65 } 66 67 //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ 68 // ClientTrace struct and its methods 69 //_______________________________________________________________________ 70 71 // tracer struct maps the `httptrace.ClientTrace` hooks into Fields 72 // with same naming for easy understanding. Plus additional insights 73 // Request. 74 type clientTrace struct { 75 getConn time.Time 76 dnsStart time.Time 77 dnsDone time.Time 78 connectDone time.Time 79 tlsHandshakeStart time.Time 80 tlsHandshakeDone time.Time 81 gotConn time.Time 82 gotFirstResponseByte time.Time 83 endTime time.Time 84 gotConnInfo httptrace.GotConnInfo 85 } 86 87 //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ 88 // Trace unexported methods 89 //_______________________________________________________________________ 90 91 func (t *clientTrace) createContext(ctx context.Context) context.Context { 92 return httptrace.WithClientTrace( 93 ctx, 94 &httptrace.ClientTrace{ 95 DNSStart: func(_ httptrace.DNSStartInfo) { 96 t.dnsStart = time.Now() 97 }, 98 DNSDone: func(_ httptrace.DNSDoneInfo) { 99 t.dnsDone = time.Now() 100 }, 101 ConnectStart: func(_, _ string) { 102 if t.dnsDone.IsZero() { 103 t.dnsDone = time.Now() 104 } 105 if t.dnsStart.IsZero() { 106 t.dnsStart = t.dnsDone 107 } 108 }, 109 ConnectDone: func(net, addr string, err error) { 110 t.connectDone = time.Now() 111 }, 112 GetConn: func(_ string) { 113 t.getConn = time.Now() 114 }, 115 GotConn: func(ci httptrace.GotConnInfo) { 116 t.gotConn = time.Now() 117 t.gotConnInfo = ci 118 }, 119 GotFirstResponseByte: func() { 120 t.gotFirstResponseByte = time.Now() 121 }, 122 TLSHandshakeStart: func() { 123 t.tlsHandshakeStart = time.Now() 124 }, 125 TLSHandshakeDone: func(_ tls.ConnectionState, _ error) { 126 t.tlsHandshakeDone = time.Now() 127 }, 128 }, 129 ) 130 } 131