-
Notifications
You must be signed in to change notification settings - Fork 0
/
trace.go
128 lines (119 loc) · 3.09 KB
/
trace.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package request
import (
"context"
"crypto/tls"
"net/http/httptrace"
"time"
)
// TraceTimings represents the timings of the request
type TraceTimings struct {
ConnGet time.Time
ConnGot time.Time
PutIdleConn time.Time
DNSStart time.Time
DNSDone time.Time
ConnectStart time.Time
ConnectDone time.Time
TLSHandshakeStart time.Time
TLSHandshakeDone time.Time
FirstByte time.Time
Wait100Continue time.Time
WroteHeaders time.Time
WroteRequest time.Time
}
// TraceConnect represents a connection trace
type TraceConnect struct {
Network string
Address string
Error error
Time time.Time
}
// TLSHandshake represents a TLS handshake
type TLSHandshake struct {
State tls.ConnectionState
Error error
}
// TraceInfo represents the trace information
type TraceInfo struct {
Timings TraceTimings
GetConnHost string
GotConn httptrace.GotConnInfo
DNSDone httptrace.DNSDoneInfo
DNSStart httptrace.DNSStartInfo
WroteRequest httptrace.WroteRequestInfo
PutIdleError error
ConnectStart []TraceConnect
ConnectDone []TraceConnect
}
// Hooks returns a httptrace.ClientTrace with the trace hooks
func (s *TraceInfo) hooks() *httptrace.ClientTrace {
t := &httptrace.ClientTrace{
GetConn: func(hostPort string) {
s.Timings.ConnGet = time.Now()
s.GetConnHost = hostPort
},
GotConn: func(info httptrace.GotConnInfo) {
s.Timings.ConnGot = time.Now()
s.GotConn = info
},
PutIdleConn: func(err error) {
s.Timings.PutIdleConn = time.Now()
s.PutIdleError = err
},
GotFirstResponseByte: func() {
s.Timings.FirstByte = time.Now()
},
Got100Continue: func() {
s.Timings.Wait100Continue = time.Now()
},
DNSStart: func(info httptrace.DNSStartInfo) {
s.Timings.DNSStart = time.Now()
s.DNSStart = info
},
DNSDone: func(info httptrace.DNSDoneInfo) {
s.Timings.DNSDone = time.Now()
s.DNSDone = info
},
ConnectStart: func(network, addr string) {
s.ConnectStart = append(s.ConnectStart, TraceConnect{
Network: network,
Address: addr,
Time: time.Now(),
})
if s.Timings.ConnectStart.IsZero() {
s.Timings.ConnectStart = time.Now()
}
},
ConnectDone: func(network, addr string, err error) {
s.ConnectDone = append(s.ConnectDone, TraceConnect{
Network: network,
Address: addr,
Error: err,
Time: time.Now(),
})
s.Timings.ConnectDone = time.Now()
},
TLSHandshakeStart: func() {
s.Timings.TLSHandshakeStart = time.Now()
},
TLSHandshakeDone: func(_ tls.ConnectionState, err error) {
s.Timings.TLSHandshakeDone = time.Now()
},
Wait100Continue: func() {
s.Timings.Wait100Continue = time.Now()
},
WroteHeaders: func() {
s.Timings.WroteHeaders = time.Now()
},
WroteRequest: func(info httptrace.WroteRequestInfo) {
s.Timings.WroteRequest = time.Now()
},
}
return t
}
// createTraceContext creates a new context with a trace hook
func createTraceContext(ctx context.Context) (info *TraceInfo, traceCtx context.Context) {
info = &TraceInfo{}
traceCtx = httptrace.WithClientTrace(ctx, info.hooks())
return
}