...
1 package ldcomponents
2
3 import (
4 "fmt"
5 "net/http"
6 "net/url"
7 "os"
8 "strings"
9 "time"
10
11 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
12 "github.com/launchdarkly/go-server-sdk/v6/internal"
13 "github.com/launchdarkly/go-server-sdk/v6/ldhttp"
14 "github.com/launchdarkly/go-server-sdk/v6/subsystems"
15 )
16
17
18
19 const DefaultConnectTimeout = 3 * time.Second
20
21
22
23
24
25
26
27
28
29
30
31
32 type HTTPConfigurationBuilder struct {
33 inited bool
34 connectTimeout time.Duration
35 httpClientFactory func() *http.Client
36 httpOptions []ldhttp.TransportOption
37 proxyURL string
38 userAgent string
39 wrapperIdentifier string
40 customHeaders map[string]string
41 }
42
43
44
45
46
47
48
49
50 func HTTPConfiguration() *HTTPConfigurationBuilder {
51 return &HTTPConfigurationBuilder{}
52 }
53
54 func (b *HTTPConfigurationBuilder) checkValid() bool {
55 if b == nil {
56 internal.LogErrorNilPointerMethod("HTTPConfigurationBuilder")
57 return false
58 }
59 if !b.inited {
60 b.connectTimeout = DefaultConnectTimeout
61 b.customHeaders = make(map[string]string)
62 b.inited = true
63 }
64 return true
65 }
66
67
68
69
70
71 func (b *HTTPConfigurationBuilder) CACert(certData []byte) *HTTPConfigurationBuilder {
72 if b.checkValid() {
73 b.httpOptions = append(b.httpOptions, ldhttp.CACertOption(certData))
74 }
75 return b
76 }
77
78
79
80
81
82
83 func (b *HTTPConfigurationBuilder) CACertFile(filePath string) *HTTPConfigurationBuilder {
84 if b.checkValid() {
85 b.httpOptions = append(b.httpOptions, ldhttp.CACertFileOption(filePath))
86 }
87 return b
88 }
89
90
91
92
93
94
95
96
97
98
99
100 func (b *HTTPConfigurationBuilder) ConnectTimeout(connectTimeout time.Duration) *HTTPConfigurationBuilder {
101 if b.checkValid() {
102 if connectTimeout <= 0 {
103 b.connectTimeout = DefaultConnectTimeout
104 } else {
105 b.connectTimeout = connectTimeout
106 }
107 }
108 return b
109 }
110
111
112
113
114
115
116
117
118 func (b *HTTPConfigurationBuilder) HTTPClientFactory(httpClientFactory func() *http.Client) *HTTPConfigurationBuilder {
119 if b.checkValid() {
120 b.httpClientFactory = httpClientFactory
121 }
122 return b
123 }
124
125
126
127
128
129
130
131
132 func (b *HTTPConfigurationBuilder) ProxyURL(proxyURL string) *HTTPConfigurationBuilder {
133 if b.checkValid() {
134 b.proxyURL = proxyURL
135 }
136 return b
137 }
138
139
140
141
142
143
144
145
146
147 func (b *HTTPConfigurationBuilder) Header(key string, value string) *HTTPConfigurationBuilder {
148 if b.checkValid() {
149 b.customHeaders[key] = value
150 }
151 return b
152 }
153
154
155 func (b *HTTPConfigurationBuilder) UserAgent(userAgent string) *HTTPConfigurationBuilder {
156 if b.checkValid() {
157 b.userAgent = userAgent
158 }
159 return b
160 }
161
162
163
164
165
166 func (b *HTTPConfigurationBuilder) Wrapper(wrapperName, wrapperVersion string) *HTTPConfigurationBuilder {
167 if b.checkValid() {
168 if wrapperName == "" || wrapperVersion == "" {
169 b.wrapperIdentifier = wrapperName
170 } else {
171 b.wrapperIdentifier = fmt.Sprintf("%s/%s", wrapperName, wrapperVersion)
172 }
173 }
174 return b
175 }
176
177
178 func (b *HTTPConfigurationBuilder) DescribeConfiguration(context subsystems.ClientContext) ldvalue.Value {
179 if !b.checkValid() {
180 defaults := HTTPConfigurationBuilder{}
181 return defaults.DescribeConfiguration(context)
182 }
183 builder := ldvalue.ObjectBuild()
184
185 builder.Set("connectTimeoutMillis", durationToMillisValue(b.connectTimeout))
186 builder.Set("socketTimeoutMillis", durationToMillisValue(b.connectTimeout))
187
188 builder.SetBool("usingProxy", b.isProxyEnabled())
189
190 return builder.Build()
191 }
192
193 func (b *HTTPConfigurationBuilder) isProxyEnabled() bool {
194
195
196
197 if os.Getenv("HTTP_PROXY") != "" {
198 return true
199 }
200 if b.httpClientFactory != nil {
201 return false
202 }
203 if b.proxyURL != "" {
204 return true
205 }
206 return false
207 }
208
209
210 func (b *HTTPConfigurationBuilder) Build(
211 clientContext subsystems.ClientContext,
212 ) (subsystems.HTTPConfiguration, error) {
213 if !b.checkValid() {
214 defaults := HTTPConfigurationBuilder{}
215 return defaults.Build(clientContext)
216 }
217
218 headers := make(http.Header)
219 headers.Set("Authorization", clientContext.GetSDKKey())
220 userAgent := "GoClient/" + internal.SDKVersion
221 if b.userAgent != "" {
222 userAgent = userAgent + " " + b.userAgent
223 }
224 headers.Set("User-Agent", userAgent)
225 if b.wrapperIdentifier != "" {
226 headers.Add("X-LaunchDarkly-Wrapper", b.wrapperIdentifier)
227 }
228 if tagsHeaderValue := buildTagsHeaderValue(clientContext); tagsHeaderValue != "" {
229 headers.Add("X-LaunchDarkly-Tags", tagsHeaderValue)
230 }
231
232
233
234 for key, value := range b.customHeaders {
235 headers.Set(key, value)
236 }
237
238 transportOpts := b.httpOptions
239
240 if b.proxyURL != "" {
241 u, err := url.Parse(b.proxyURL)
242 if err != nil {
243 return subsystems.HTTPConfiguration{}, err
244 }
245 transportOpts = append(transportOpts, ldhttp.ProxyOption(*u))
246 }
247
248 clientFactory := b.httpClientFactory
249 if clientFactory == nil {
250 connectTimeout := b.connectTimeout
251 if connectTimeout <= 0 {
252 connectTimeout = DefaultConnectTimeout
253 }
254 transportOpts = append(transportOpts, ldhttp.ConnectTimeoutOption(connectTimeout))
255 transport, _, err := ldhttp.NewHTTPTransport(transportOpts...)
256 if err != nil {
257 return subsystems.HTTPConfiguration{}, err
258 }
259 clientFactory = func() *http.Client {
260 return &http.Client{
261 Timeout: b.connectTimeout,
262 Transport: transport,
263 }
264 }
265 }
266
267 return subsystems.HTTPConfiguration{
268 DefaultHeaders: headers,
269 CreateHTTPClient: clientFactory,
270 }, nil
271 }
272
273 func buildTagsHeaderValue(clientContext subsystems.ClientContext) string {
274 var parts []string
275 if value := clientContext.GetApplicationInfo().ApplicationID; value != "" {
276 parts = append(parts, fmt.Sprintf("application-id/%s", value))
277 }
278 if value := clientContext.GetApplicationInfo().ApplicationVersion; value != "" {
279 parts = append(parts, fmt.Sprintf("application-version/%s", value))
280 }
281 return strings.Join(parts, " ")
282 }
283
View as plain text