-
Notifications
You must be signed in to change notification settings - Fork 16
/
http_transport.go
100 lines (79 loc) · 2.71 KB
/
http_transport.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
package sdk_golang
import (
"context"
"crypto/tls"
"net"
"net/http"
"strings"
"github.com/openziti/sdk-golang/ziti"
"github.com/openziti/sdk-golang/ziti/edge"
cmap "github.com/orcaman/concurrent-map/v2"
)
// NewHttpClient returns a http.Client that can be used exactly as any other http.Client but will route requests
// over a Ziti network using the host name as the Ziti service name. Supplying a tlsConfig is possible to connect
// to HTTPS services, but for it to be successful, the Ziti service name MUST be in the servers URI SANs.
func NewHttpClient(ctx ziti.Context, tlsConfig *tls.Config) *http.Client {
return &http.Client{
Transport: NewZitiTransport(ctx, tlsConfig),
}
}
// ZitiTransport wraps the default http.RoundTripper implementation with Ziti edge.Conn pooling
type ZitiTransport struct {
http.Transport
connByAddr cmap.ConcurrentMap[string, edge.Conn]
Context ziti.Context
TlsConfig *tls.Config
}
// NewZitiTransport returns a new http.Transport that routes HTTP requests and response over a
// Ziti network.
func NewZitiTransport(ctx ziti.Context, clientTlsConfig *tls.Config) *ZitiTransport {
zitiTransport := &ZitiTransport{
connByAddr: cmap.New[edge.Conn](),
TlsConfig: clientTlsConfig,
Context: ctx,
}
zitiTransport.Transport = http.Transport{
DialContext: zitiTransport.DialContext,
DialTLSContext: zitiTransport.DialTLSContext,
}
return zitiTransport
}
// urlToServiceName removes ports from host names that internal standard GoLang capabilities may have added.
func urlToServiceName(addr string) string {
return strings.Split(addr, ":")[0]
}
// getConn returns an edge.Conn that can act as a net.Conn, but is pooled by service name.
func (transport *ZitiTransport) getConn(addr string) (edge.Conn, error) {
var err error
edgeConn := transport.connByAddr.Upsert(addr, nil, func(_ bool, existingConn edge.Conn, _ edge.Conn) edge.Conn {
if existingConn == nil || existingConn.IsClosed() {
var newConn edge.Conn
serviceName := urlToServiceName(addr)
if err != nil {
return nil
}
newConn, err = transport.Context.Dial(serviceName)
return newConn
}
return existingConn
})
return edgeConn, err
}
func (transport *ZitiTransport) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
edgeConn, err := transport.getConn(addr)
return edgeConn, err
}
func (transport *ZitiTransport) DialTLSContext(ctx context.Context, network, addr string) (net.Conn, error) {
edgeConn, err := transport.getConn(addr)
if err != nil {
return nil, err
}
tlsConn := tls.Client(edgeConn, transport.TlsConfig)
if err := tlsConn.Handshake(); err != nil {
if edgeConn != nil {
_ = edgeConn.Close()
}
return nil, err
}
return edgeConn, err
}