1//go:build integration && perftest 2// +build integration,perftest 3 4package main 5 6import ( 7 "context" 8 "crypto/tls" 9 "net/http/httptrace" 10 "time" 11 12 "github.com/aws/aws-sdk-go/aws/request" 13) 14 15type RequestTrace struct { 16 ID int64 17 context.Context 18 19 start, finish time.Time 20 21 errs Errors 22 attempts []RequestAttempt 23 24 curAttempt RequestAttempt 25} 26 27func NewRequestTrace(ctx context.Context, id int64) *RequestTrace { 28 rt := &RequestTrace{ 29 ID: id, 30 start: time.Now(), 31 attempts: []RequestAttempt{}, 32 } 33 34 trace := &httptrace.ClientTrace{ 35 GetConn: rt.getConn, 36 GotConn: rt.gotConn, 37 PutIdleConn: rt.putIdleConn, 38 GotFirstResponseByte: rt.gotFirstResponseByte, 39 Got100Continue: rt.got100Continue, 40 DNSStart: rt.dnsStart, 41 DNSDone: rt.dnsDone, 42 ConnectStart: rt.connectStart, 43 ConnectDone: rt.connectDone, 44 TLSHandshakeStart: rt.tlsHandshakeStart, 45 TLSHandshakeDone: rt.tlsHandshakeDone, 46 WroteHeaders: rt.wroteHeaders, 47 Wait100Continue: rt.wait100Continue, 48 WroteRequest: rt.wroteRequest, 49 } 50 51 rt.Context = httptrace.WithClientTrace(ctx, trace) 52 53 return rt 54} 55 56func (rt *RequestTrace) AppendError(err error) { 57 rt.errs = append(rt.errs, err) 58} 59func (rt *RequestTrace) OnCompleteAttempt(r *request.Request) { 60 rt.curAttempt.Start = r.AttemptTime 61 rt.curAttempt.Finish = time.Now() 62 rt.curAttempt.Err = r.Error 63 64 rt.attempts = append(rt.attempts, rt.curAttempt) 65 rt.curAttempt = RequestAttempt{} 66} 67func (rt *RequestTrace) OnSendAttempt(r *request.Request) { 68 rt.curAttempt.SendStart = time.Now() 69} 70func (rt *RequestTrace) OnCompleteRequest(r *request.Request) {} 71func (rt *RequestTrace) RequestDone() { 72 rt.finish = time.Now() 73 // Last attempt includes reading the response body 74 rt.attempts[len(rt.attempts)-1].Finish = rt.finish 75} 76 77func (rt *RequestTrace) Err() error { 78 return rt.errs 79} 80func (rt *RequestTrace) TotalLatency() time.Duration { 81 return rt.finish.Sub(rt.start) 82} 83func (rt *RequestTrace) Attempts() []RequestAttempt { 84 return rt.attempts 85} 86func (rt *RequestTrace) Retries() int { 87 return len(rt.attempts) - 1 88} 89 90func (rt *RequestTrace) getConn(hostPort string) {} 91func (rt *RequestTrace) gotConn(info httptrace.GotConnInfo) { 92 rt.curAttempt.Reused = info.Reused 93} 94func (rt *RequestTrace) putIdleConn(err error) {} 95func (rt *RequestTrace) gotFirstResponseByte() { 96 rt.curAttempt.FirstResponseByte = time.Now() 97} 98func (rt *RequestTrace) got100Continue() {} 99func (rt *RequestTrace) dnsStart(info httptrace.DNSStartInfo) { 100 rt.curAttempt.DNSStart = time.Now() 101} 102func (rt *RequestTrace) dnsDone(info httptrace.DNSDoneInfo) { 103 rt.curAttempt.DNSDone = time.Now() 104} 105func (rt *RequestTrace) connectStart(network, addr string) { 106 rt.curAttempt.ConnectStart = time.Now() 107} 108func (rt *RequestTrace) connectDone(network, addr string, err error) { 109 rt.curAttempt.ConnectDone = time.Now() 110} 111func (rt *RequestTrace) tlsHandshakeStart() { 112 rt.curAttempt.TLSHandshakeStart = time.Now() 113} 114func (rt *RequestTrace) tlsHandshakeDone(state tls.ConnectionState, err error) { 115 rt.curAttempt.TLSHandshakeDone = time.Now() 116} 117func (rt *RequestTrace) wroteHeaders() {} 118func (rt *RequestTrace) wait100Continue() {} 119func (rt *RequestTrace) wroteRequest(info httptrace.WroteRequestInfo) { 120 rt.curAttempt.RequestWritten = time.Now() 121} 122 123type RequestAttempt struct { 124 Start, Finish time.Time 125 SendStart time.Time 126 Err error 127 128 Reused bool 129 130 DNSStart, DNSDone time.Time 131 ConnectStart, ConnectDone time.Time 132 TLSHandshakeStart, TLSHandshakeDone time.Time 133 RequestWritten time.Time 134 FirstResponseByte time.Time 135} 136