-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathhttpclient.go
More file actions
142 lines (122 loc) · 4.17 KB
/
Copy pathhttpclient.go
File metadata and controls
142 lines (122 loc) · 4.17 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package api
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net"
"net/http"
"time"
)
// We must our own http.Client which adds the authorization header in all requests sent to Humio.
// We use the approach described here: https://github.com/shurcooL/graphql/issues/28#issuecomment-464713908
type headerTransport struct {
base http.RoundTripper
headers map[string]string
}
// newUnixSocketProxyDialer creates a dialer that connects through a unix socket proxy
func newUnixSocketProxyDialer(socketPath string, fallbackDialer func(ctx context.Context, network, addr string) (net.Conn, error)) func(ctx context.Context, network, addr string) (net.Conn, error) {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
// Connect directly to the unix socket
// The proxy is expected to handle the connection transparently
conn, err := net.Dial("unix", socketPath)
if err != nil {
return nil, fmt.Errorf("failed to connect to unix socket proxy: %w", err)
}
return conn, nil
}
}
func NewHttpTransport(config Config) *http.Transport {
dialContext := config.DialContext
if dialContext == nil {
dialContext = (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext
}
// If a unix socket proxy is specified, wrap the dialer to use it
if config.UnixSocketProxy != "" {
dialContext = newUnixSocketProxyDialer(config.UnixSocketProxy, dialContext)
}
if config.Insecure {
// Return HTTP transport where we skip certificate verification
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: config.Insecure, // #nosec G402
},
}
}
if len(config.CACertificatePEM) > 0 {
// Create a certificate pool and return a HTTP transport with the specified specified CA certificate.
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM([]byte(config.CACertificatePEM))
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
InsecureSkipVerify: config.Insecure, // #nosec G402
},
}
}
// Return a regular default HTTP client
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}
// NewHTTPClientWithHeaders returns a *http.Client that attaches a defined set of Headers to all requests.
func (c *Client) newHTTPClientWithHeaders(headers map[string]string) *http.Client {
return &http.Client{
Transport: &headerTransport{
base: c.httpTransport,
headers: headers,
},
Timeout: 30 * time.Second,
}
}
func (h *headerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req2 := CloneRequest(req)
for key, val := range h.headers {
req2.Header.Set(key, val)
}
return h.base.RoundTrip(req2)
}
// CloneRequest and CloneHeader copied from https://github.com/kubernetes/apimachinery/blob/a76b7114b20a2e56fd698bba815b1e2c82ec4bff/pkg/util/net/http.go#L469-L491
// CloneRequest creates a shallow copy of the request along with a deep copy of the Headers.
func CloneRequest(req *http.Request) *http.Request {
r := new(http.Request)
// shallow clone
*r = *req
// deep copy headers
r.Header = CloneHeader(req.Header)
return r
}
// CloneHeader creates a deep copy of an http.Header.
func CloneHeader(in http.Header) http.Header {
out := make(http.Header, len(in))
for key, values := range in {
newValues := make([]string, len(values))
copy(newValues, values)
out[key] = newValues
}
return out
}