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