-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtransport.go
More file actions
128 lines (107 loc) · 3.21 KB
/
transport.go
File metadata and controls
128 lines (107 loc) · 3.21 KB
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 httpsig
import (
"net/http"
"strings"
"zvelo.io/httpsig/digest"
opentracing "github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/opentracing/opentracing-go/log"
)
// These are the default values for TransportOptions
const (
DefaultDigestAlgo = digest.SHA256
DefaultHeaderType = SignatureHeader
)
type transport struct {
keyID string
key interface{}
algo Algorithm
digestAlgo digest.Algorithm
headerType HeaderType
tracer func() opentracing.Tracer
}
func defaultTransport() *transport {
return &transport{
digestAlgo: DefaultDigestAlgo,
headerType: DefaultHeaderType,
tracer: opentracing.GlobalTracer,
}
}
// A TransportOption is an option that can be used with Algorithm.Transport
type TransportOption func(*transport)
// WithAuthorizationHeader causes the Transport to use the Authorization header
// for HTTP signatures instead of the Signature header
func WithAuthorizationHeader() TransportOption {
return func(t *transport) {
t.headerType = AuthorizationHeader
}
}
// WithDigestAlgorithm causes the Transport to use the digest algorithm val when
// generating HTTP Digest headers
func WithDigestAlgorithm(val digest.Algorithm) TransportOption {
if val == digest.Unknown {
val = DefaultDigestAlgo
}
return func(t *transport) {
t.digestAlgo = val
}
}
// WithTracer sets the tracer to be used by the Transport
func WithTracer(tracer opentracing.Tracer) TransportOption {
if tracer == nil {
tracer = opentracing.GlobalTracer()
}
return func(t *transport) {
t.tracer = func() opentracing.Tracer {
return tracer
}
}
}
func (t transport) startSpan(req *http.Request, operationName string, opts ...opentracing.StartSpanOption) (opentracing.Span, *http.Request) {
var span opentracing.Span
if parentSpan := opentracing.SpanFromContext(req.Context()); parentSpan != nil {
opts = append(opts, opentracing.ChildOf(parentSpan.Context()))
span = t.tracer().StartSpan(operationName, opts...)
} else {
span = t.tracer().StartSpan(operationName, opts...)
}
return span, req.WithContext(opentracing.ContextWithSpan(req.Context(), span))
}
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
// startSpan clones req per RoundTripper contract
span, req := t.startSpan(req, strings.ToUpper(req.URL.Scheme)+" "+req.Method)
defer span.Finish()
ext.SpanKindRPCClient.Set(span)
ext.HTTPMethod.Set(span, req.Method)
ext.HTTPUrl.Set(span, req.URL.String())
ext.Component.Set(span, "httpsig")
if err := t.headerType.Set(t.algo, t.keyID, t.key, req, t.digestAlgo); err != nil {
span.LogFields(
log.String("event", "error"),
log.Error(err),
)
return nil, err
}
resp, err := http.DefaultTransport.RoundTrip(req)
if err != nil {
span.LogFields(
log.String("event", "error"),
log.Error(err),
)
return nil, err
}
ext.HTTPStatusCode.Set(span, uint16(resp.StatusCode))
return resp, nil
}
// Transport returns an http.RoundTripper that sets HTTP signatures on outgoing
// requests
func (a Algorithm) Transport(keyID string, key interface{}, opts ...TransportOption) http.RoundTripper {
t := defaultTransport()
for _, opt := range opts {
opt(t)
}
t.keyID = keyID
t.key = key
t.algo = a
return t
}