forked from tcnksm/go-httpstat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
go18.go
133 lines (111 loc) · 3.35 KB
/
go18.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
129
130
131
132
133
//go:build go1.8
// +build go1.8
package httpstat
import (
"context"
"crypto/tls"
"net/http/httptrace"
"time"
)
// End sets the time when reading the response is done.
// This must be called after reading the response body.
func (r *Result) End() {
// This means the result is empty, and we'll skip
// setting values for contentTransfer and total.
if r.dnsStart.IsZero() {
return
}
r.contentTransfer = time.Since(r.transferStart)
r.total = time.Since(r.dnsStart)
}
// ContentTransfer returns the duration of content transfer time.
// If the request is finished it returns the content transfer time,
// otherwise it returns the duration from the first response byte
// until when the function was called.
func (r *Result) ContentTransfer() time.Duration {
if r.contentTransfer == 0 {
return time.Since(r.serverDone)
}
return r.contentTransfer
}
// Total returns the duration of the total http request.
// If the request is finished it returns the total time,
// otherwise it returns the duration from the DNS lookup
// start time until when the function was called.
func (r *Result) Total() time.Duration {
if r.total == 0 {
return time.Since(r.dnsStart)
}
return r.total
}
// Until returns the duration of the http request until time t.
// Measured from the DNS lookup start time to the given time.
func (r *Result) Until(t time.Time) time.Duration {
return t.Sub(r.dnsStart)
}
func withClientTrace(ctx context.Context, r *Result) context.Context {
return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
DNSStart: func(i httptrace.DNSStartInfo) {
r.dnsStart = time.Now()
},
DNSDone: func(i httptrace.DNSDoneInfo) {
r.DNSLookup = time.Since(r.dnsStart)
r.NameLookup = time.Since(r.dnsStart)
},
ConnectStart: func(_, _ string) {
r.tcpStart = time.Now()
// When connecting to IP (e.g. there's no DNS lookup)
if r.dnsStart.IsZero() {
r.dnsStart = r.tcpStart
}
},
ConnectDone: func(network, addr string, err error) {
r.TCPConnection = time.Since(r.tcpStart)
r.Connect = time.Since(r.dnsStart)
},
TLSHandshakeStart: func() {
r.isTLS = true
r.tlsStart = time.Now()
},
TLSHandshakeDone: func(_ tls.ConnectionState, _ error) {
r.TLSHandshake = time.Since(r.tlsStart)
r.Pretransfer = time.Since(r.dnsStart)
},
GotConn: func(i httptrace.GotConnInfo) {
// Handle when keep alive is used and the connection is reused.
// DNSStart(Done) and ConnectStart(Done) is then skipped.
if i.Reused {
r.isReused = true
}
},
WroteRequest: func(info httptrace.WroteRequestInfo) {
r.serverStart = time.Now()
// When client doesn't use DialContext or using old (before go1.7) `net`
// pakcage, DNS/TCP/TLS hook is not called.
if r.dnsStart.IsZero() && r.tcpStart.IsZero() {
now := r.serverStart
r.dnsStart = now
r.tcpStart = now
}
// When connection is re-used, DNS/TCP/TLS hooks are not called.
if r.isReused {
now := r.serverStart
r.dnsStart = now
r.tcpStart = now
r.tlsStart = now
}
// If no TLS, TLSHandshake is zero and Pretransfer is equal to Connect.
if r.isTLS {
return
}
r.TLSHandshake = time.Duration(0)
r.Pretransfer = r.Connect
},
GotFirstResponseByte: func() {
r.serverDone = time.Now()
r.ServerProcessing = time.Since(r.serverStart)
r.transferStart = time.Now()
r.StartTransfer = time.Since(r.dnsStart)
},
})
}