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