-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathhttp.go
107 lines (84 loc) · 2.79 KB
/
http.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
package clienttiming
import (
"context"
"net/http"
"strconv"
"strings"
"github.com/mitchellh/go-server-timing"
)
// KeySource is the key in the metric in which the source name will be stored
const KeySource = "source"
// Transport returns a server-timing instrumented round tripper for the current context
func (t *Timer) Transport(ctx context.Context, opts ...Option) http.RoundTripper {
tr := &transport{
Timer: *t,
timing: servertiming.FromContext(ctx),
}
// apply extra options on the timer copy
for _, opt := range opts {
opt(&tr.Timer)
}
return tr
}
// Client returns a server-timing instrumented http timer for the current context
func (t *Timer) Client(ctx context.Context, opts ...Option) *http.Client {
return &http.Client{Transport: t.Transport(ctx, opts...)}
}
type transport struct {
Timer
// timing is the timing header
timing *servertiming.Header
}
// RoundTrip implements the http.RoundTripper interface
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
// Start the metrics for the get
metric := t.timing.NewMetric(t.metric(req)).WithDesc(t.desc(req))
if metric.Extra == nil {
metric.Extra = make(map[string]string)
}
// Add the timer name as a source to the metric
if t.name != "" {
metric.Extra[KeySource] = t.name
}
// Start the metric before the round trip
metric.Start()
// Run the inner round trip
resp, err := t.inner.RoundTrip(req)
// Stop the metric after get
metric.Stop()
// Update the metric with the response and error of the request
t.update(metric, resp, err)
// In case of round trip error, return it
if err != nil {
return nil, err
}
// Insert the timing headers from the response to the current headers
InsertMetrics(t.timing, resp.Header)
return resp, err
}
// InsertMetrics inserts to servertiming header metrics from an HTTP header of another request
// They are prepended since they happened before the metrics of the header itself
func InsertMetrics(h *servertiming.Header, headers http.Header) {
more, err := servertiming.ParseHeader(headers.Get(servertiming.HeaderKey))
if err != nil {
return
}
h.Metrics = append(more.Metrics, h.Metrics...)
}
// DefaultMetric set the metric name as the request host
func DefaultMetric(req *http.Request) string {
// remove colons since they are not a legal character for name
return strings.Replace(req.Host, ":", "/", -1)
}
// DefaultDesc set the metric description as the request method and path
func DefaultDesc(req *http.Request) string {
return req.Method + " " + req.URL.Path
}
// DefaultUpdate sets status code in metric if there was no error, otherwise it sets the error text.
func DefaultUpdate(m *servertiming.Metric, resp *http.Response, err error) {
if err != nil {
m.Extra["error"] = err.Error()
} else {
m.Extra["code"] = strconv.Itoa(resp.StatusCode)
}
}