/
http_trace.go
110 lines (97 loc) · 3.01 KB
/
http_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
/*
Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
Use of this source code is governed by a MIT license that can be found in the LICENSE file.
*/
package webutil
import (
"crypto/tls"
"net/http"
"net/http/httptrace"
"time"
)
// WithClientHTTPTrace adds the http client trace to the request.
func WithClientHTTPTrace(req *http.Request, trace *HTTPTrace) *http.Request {
return req.WithContext(httptrace.WithClientTrace(req.Context(), trace.Trace()))
}
// HTTPTrace is timing information for the full http call.
type HTTPTrace struct {
Start time.Time `json:"start"`
GetConn time.Time `json:"getConn"`
GotConn time.Time `json:"gotConn"`
PutIdleConn time.Time `json:"putIdleConn"`
DNSStart time.Time `json:"dnsStart"`
DNSDone time.Time `json:"dnsDone"`
ConnectStart time.Time `json:"connectStart"`
ConnectDone time.Time `json:"connectDone"`
TLSHandshakeStart time.Time `json:"tlsHandshakeStart"`
TLSHandshakeDone time.Time `json:"tlsHandshakeDone"`
WroteHeaders time.Time `json:"wroteHeaders"`
WroteRequest time.Time `json:"wroteRequest"`
GotFirstResponseByte time.Time `json:"gotFirstResponseByte"`
DNSElapsed time.Duration `json:"dnsElapsed"`
TLSHandshakeElapsed time.Duration `json:"tlsHandshakeElapsed"`
DialElapsed time.Duration `json:"dialElapsed"`
RequestElapsed time.Duration `json:"requestElapsed"`
ServerElapsed time.Duration `json:"severElapsed"`
}
// Trace returns the trace binder.
func (ht *HTTPTrace) Trace() *httptrace.ClientTrace {
now := func() time.Time {
return time.Now().UTC()
}
ht.Start = now()
return &httptrace.ClientTrace{
GetConn: func(_ string) {
ht.GetConn = now()
},
GotConn: func(_ httptrace.GotConnInfo) {
ht.GotConn = now()
},
PutIdleConn: func(_ error) {
ht.PutIdleConn = now()
},
GotFirstResponseByte: func() {
ht.GotFirstResponseByte = now()
ht.ServerElapsed = ht.GotFirstResponseByte.Sub(ht.WroteRequest)
},
DNSStart: func(_ httptrace.DNSStartInfo) {
ht.DNSStart = now()
},
DNSDone: func(_ httptrace.DNSDoneInfo) {
ht.DNSDone = now()
ht.DNSElapsed = ht.DNSDone.Sub(ht.DNSStart)
},
ConnectStart: func(_, _ string) {
ht.ConnectStart = now()
},
ConnectDone: func(_, _ string, _ error) {
ht.ConnectDone = now()
ht.DialElapsed = ht.ConnectDone.Sub(ht.ConnectStart)
},
TLSHandshakeStart: func() {
ht.TLSHandshakeStart = now()
},
TLSHandshakeDone: func(_ tls.ConnectionState, _ error) {
ht.TLSHandshakeDone = now()
ht.TLSHandshakeElapsed = ht.TLSHandshakeDone.Sub(ht.TLSHandshakeStart)
},
WroteHeaders: func() {
ht.WroteHeaders = now()
},
WroteRequest: func(_ httptrace.WroteRequestInfo) {
ht.WroteRequest = now()
if !ht.ConnectDone.IsZero() {
ht.RequestElapsed = ht.WroteRequest.Sub(ht.ConnectDone)
return
}
if !ht.GetConn.IsZero() {
ht.RequestElapsed = ht.WroteRequest.Sub(ht.GetConn)
return
}
if !ht.GotConn.IsZero() {
ht.RequestElapsed = ht.WroteRequest.Sub(ht.GotConn)
return
}
},
}
}