@@ -25,6 +25,7 @@ import (
25
25
"github.com/palantir/conjure-go-runtime/v2/conjure-go-client/httpclient/internal"
26
26
"github.com/palantir/conjure-go-runtime/v2/conjure-go-client/httpclient/internal/refreshingclient"
27
27
"github.com/palantir/pkg/bytesbuffers"
28
+ "github.com/palantir/pkg/metrics"
28
29
"github.com/palantir/pkg/refreshable"
29
30
werror "github.com/palantir/witchcraft-go-error"
30
31
)
54
55
type clientBuilder struct {
55
56
HTTP * httpClientBuilder
56
57
58
+ URIs refreshable.StringSlice
57
59
URIScorerBuilder func ([]string ) internal.URIScoringMiddleware
58
60
59
61
// If false, NewClient() will return an error when URIs.Current() is empty.
@@ -63,15 +65,19 @@ type clientBuilder struct {
63
65
ErrorDecoder ErrorDecoder
64
66
65
67
BytesBufferPool bytesbuffers.Pool
68
+ MaxAttempts refreshable.IntPtr
69
+ RetryParams refreshingclient.RefreshableRetryParams
66
70
}
67
71
68
72
type httpClientBuilder struct {
69
- // A set of functions that modify the ClientConfig before it is used to build the client.
70
- // Populated by client params and applied using Map on the underlying refreshable config.
71
- ConfigOverride []func (c * ClientConfig )
72
-
73
- TLSConfig * tls.Config // Optionally set by WithTLSConfig(). If unset, config.Security is used.
74
- Middlewares []Middleware
73
+ ServiceName refreshable.String
74
+ Timeout refreshable.Duration
75
+ DialerParams refreshingclient.RefreshableDialerParams
76
+ TLSConfig * tls.Config // If unset, config in TransportParams will be used.
77
+ TransportParams refreshingclient.RefreshableTransportParams
78
+ Middlewares []Middleware
79
+
80
+ DisableMetrics refreshable.Bool
75
81
MetricsTagProviders []TagsProvider
76
82
77
83
// These middleware options are not refreshed anywhere because they are not in ClientConfig,
@@ -81,69 +87,57 @@ type httpClientBuilder struct {
81
87
DisableTraceHeaders bool
82
88
}
83
89
84
- func (b * httpClientBuilder ) Build (ctx context.Context , config RefreshableClientConfig , reloadErrorSubmitter func ( error ), params ... HTTPClientParam ) (RefreshableHTTPClient , refreshingclient. RefreshableValidatedClientParams , error ) {
90
+ func (b * httpClientBuilder ) Build (ctx context.Context , params ... HTTPClientParam ) (RefreshableHTTPClient , error ) {
85
91
for _ , p := range params {
86
92
if p == nil {
87
93
continue
88
94
}
89
95
if err := p .applyHTTPClient (b ); err != nil {
90
- return nil , nil , err
96
+ return nil , err
91
97
}
92
98
}
93
- finalConfig := newRefreshingClientConfigWithConfigOverrides (config , b .ConfigOverride ... )
94
-
95
- validParams , err := newRefreshingValidatedClientParams (ctx , finalConfig , reloadErrorSubmitter )
96
- if err != nil {
97
- return nil , nil , err
98
- }
99
-
100
- // Build the transport with a refreshable dialer and tls config.
101
- // Each component attempts to rebuild as infrequently as possible.
102
- serviceName := validParams .ServiceName ()
103
- transportParams := validParams .Transport ()
104
- metricsTagProviders := append (b .MetricsTagProviders , refreshableTagsProvider {validParams .MetricsTags ()})
105
99
106
100
var tlsProvider refreshingclient.TLSProvider
107
101
if b .TLSConfig != nil {
108
102
tlsProvider = refreshingclient .NewStaticTLSConfigProvider (b .TLSConfig )
109
103
} else {
110
- refreshableProvider , err := refreshingclient .NewRefreshableTLSConfig (ctx , transportParams .TLS ())
104
+ refreshableProvider , err := refreshingclient .NewRefreshableTLSConfig (ctx , b . TransportParams .TLS ())
111
105
if err != nil {
112
- return nil , nil , err
106
+ return nil , err
113
107
}
114
108
tlsProvider = refreshableProvider
115
109
}
116
110
117
- dialer := refreshingclient .NewRefreshableDialer (ctx , validParams . Dialer () )
118
- transport := refreshingclient .NewRefreshableTransport (ctx , transportParams , tlsProvider , dialer )
119
- transport = wrapTransport (transport , newMetricsMiddleware (serviceName , metricsTagProviders , validParams .DisableMetrics () ))
120
- transport = wrapTransport (transport , newTraceMiddleware (serviceName , b .DisableRequestSpan , b .DisableTraceHeaders ))
111
+ dialer := refreshingclient .NewRefreshableDialer (ctx , b . DialerParams )
112
+ transport := refreshingclient .NewRefreshableTransport (ctx , b . TransportParams , tlsProvider , dialer )
113
+ transport = wrapTransport (transport , newMetricsMiddleware (b . ServiceName , b . MetricsTagProviders , b .DisableMetrics ))
114
+ transport = wrapTransport (transport , newTraceMiddleware (b . ServiceName , b .DisableRequestSpan , b .DisableTraceHeaders ))
121
115
if ! b .DisableRecovery {
122
116
transport = wrapTransport (transport , recoveryMiddleware {})
123
117
}
124
118
transport = wrapTransport (transport , b .Middlewares ... )
125
119
126
- client := refreshingclient .NewRefreshableHTTPClient (transport , validParams .Timeout ())
127
- return client , validParams , nil
120
+ return refreshingclient .NewRefreshableHTTPClient (transport , b .Timeout ), nil
128
121
}
129
122
130
123
// NewClient returns a configured client ready for use.
131
124
// We apply "sane defaults" before applying the provided params.
132
125
func NewClient (params ... ClientParam ) (Client , error ) {
133
- return NewClientFromRefreshableConfig (context .TODO (), nil , params ... )
126
+ b := newClientBuilder ()
127
+ return newClient (context .TODO (), b , params ... )
134
128
}
135
129
136
130
// NewClientFromRefreshableConfig returns a configured client ready for use.
137
131
// We apply "sane defaults" before applying the provided params.
138
132
func NewClientFromRefreshableConfig (ctx context.Context , config RefreshableClientConfig , params ... ClientParam ) (Client , error ) {
139
- b := & clientBuilder {
140
- HTTP : & httpClientBuilder {},
141
- ErrorDecoder : restErrorDecoder {},
133
+ b := newClientBuilder ()
134
+ if err := newClientBuilderFromRefreshableConfig ( ctx , config , b , nil ); err != nil {
135
+ return nil , err
142
136
}
143
- return newClient (ctx , config , b , nil , params ... )
137
+ return newClient (ctx , b , params ... )
144
138
}
145
139
146
- func newClient (ctx context.Context , config RefreshableClientConfig , b * clientBuilder , reloadErrorSubmitter func ( error ) , params ... ClientParam ) (Client , error ) {
140
+ func newClient (ctx context.Context , b * clientBuilder , params ... ClientParam ) (Client , error ) {
147
141
for _ , p := range params {
148
142
if p == nil {
149
143
continue
@@ -152,6 +146,12 @@ func newClient(ctx context.Context, config RefreshableClientConfig, b *clientBui
152
146
return nil , err
153
147
}
154
148
}
149
+ if b .URIs == nil {
150
+ return nil , werror .ErrorWithContextParams (ctx , "httpclient URLs must be set in configuration or by constructor param" , werror .SafeParam ("serviceName" , b .HTTP .ServiceName .CurrentString ()))
151
+ }
152
+ if ! b .AllowEmptyURIs && len (b .URIs .CurrentStringSlice ()) == 0 {
153
+ return nil , werror .WrapWithContextParams (ctx , ErrEmptyURIs , "" , werror .SafeParam ("serviceName" , b .HTTP .ServiceName .CurrentString ()))
154
+ }
155
155
156
156
var edm Middleware
157
157
if b .ErrorDecoder != nil {
@@ -161,40 +161,27 @@ func newClient(ctx context.Context, config RefreshableClientConfig, b *clientBui
161
161
middleware := b .HTTP .Middlewares
162
162
b .HTTP .Middlewares = nil
163
163
164
- httpClient , validParams , err := b .HTTP .Build (ctx , config , reloadErrorSubmitter )
164
+ httpClient , err := b .HTTP .Build (ctx )
165
165
if err != nil {
166
166
return nil , err
167
167
}
168
168
169
- if ! b .AllowEmptyURIs {
170
- // Validate that the URIs are not empty.
171
- if curr := validParams .CurrentValidatedClientParams (); len (curr .URIs ) == 0 {
172
- return nil , werror .ErrorWithContextParams (ctx , "httpclient URIs must be set in configuration or by constructor param" ,
173
- werror .SafeParam ("serviceName" , curr .ServiceName ))
174
- }
175
- }
176
-
177
169
var recovery Middleware
178
170
if ! b .HTTP .DisableRecovery {
179
171
recovery = recoveryMiddleware {}
180
172
}
181
- uriScorer := internal .NewRefreshableURIScoringMiddleware (validParams .URIs () , func (uris []string ) internal.URIScoringMiddleware {
173
+ uriScorer := internal .NewRefreshableURIScoringMiddleware (b .URIs , func (uris []string ) internal.URIScoringMiddleware {
182
174
if b .URIScorerBuilder == nil {
183
175
return internal .NewBalancedURIScoringMiddleware (uris , func () int64 { return time .Now ().UnixNano () })
184
176
}
185
177
return b .URIScorerBuilder (uris )
186
178
})
187
-
188
- middleware = append (middleware ,
189
- newAuthTokenMiddlewareFromRefreshable (validParams .APIToken ()),
190
- newBasicAuthMiddlewareFromRefreshable (validParams .BasicAuth ()))
191
-
192
179
return & clientImpl {
193
- serviceName : validParams . ServiceName () ,
180
+ serviceName : b . HTTP . ServiceName ,
194
181
client : httpClient ,
195
182
uriScorer : uriScorer ,
196
- maxAttempts : validParams .MaxAttempts () ,
197
- backoffOptions : validParams . Retry () ,
183
+ maxAttempts : b .MaxAttempts ,
184
+ backoffOptions : b . RetryParams ,
198
185
middlewares : middleware ,
199
186
errorDecoderMiddleware : edm ,
200
187
recoveryMiddleware : recovery ,
@@ -205,7 +192,8 @@ func newClient(ctx context.Context, config RefreshableClientConfig, b *clientBui
205
192
// NewHTTPClient returns a configured http client ready for use.
206
193
// We apply "sane defaults" before applying the provided params.
207
194
func NewHTTPClient (params ... HTTPClientParam ) (* http.Client , error ) {
208
- provider , err := NewHTTPClientFromRefreshableConfig (context .TODO (), nil , params ... )
195
+ b := newClientBuilder ()
196
+ provider , err := b .HTTP .Build (context .TODO (), params ... )
209
197
if err != nil {
210
198
return nil , err
211
199
}
@@ -217,16 +205,57 @@ type RefreshableHTTPClient = refreshingclient.RefreshableHTTPClient
217
205
218
206
// NewHTTPClientFromRefreshableConfig returns a configured http client ready for use.
219
207
// We apply "sane defaults" before applying the provided params.
220
- //
221
- // The RefreshableClientConfig is not accepted as a client param because there must be exactly one
222
- // subscription used to build the ValidatedClientParams in Build().
223
208
func NewHTTPClientFromRefreshableConfig (ctx context.Context , config RefreshableClientConfig , params ... HTTPClientParam ) (RefreshableHTTPClient , error ) {
224
- client , _ , err := new (httpClientBuilder ).Build (ctx , config , nil , params ... )
225
- return client , err
209
+ b := newClientBuilder ()
210
+ if err := newClientBuilderFromRefreshableConfig (ctx , config , b , nil ); err != nil {
211
+ return nil , err
212
+ }
213
+ return b .HTTP .Build (ctx , params ... )
214
+ }
215
+
216
+ func newClientBuilder () * clientBuilder {
217
+ return & clientBuilder {
218
+ HTTP : & httpClientBuilder {
219
+ ServiceName : refreshable .NewString (refreshable .NewDefaultRefreshable ("" )),
220
+ Timeout : refreshable .NewDuration (refreshable .NewDefaultRefreshable (defaultHTTPTimeout )),
221
+ DialerParams : refreshingclient .NewRefreshingDialerParams (refreshable .NewDefaultRefreshable (refreshingclient.DialerParams {
222
+ DialTimeout : defaultDialTimeout ,
223
+ KeepAlive : defaultKeepAlive ,
224
+ SocksProxyURL : nil ,
225
+ })),
226
+ TransportParams : refreshingclient .NewRefreshingTransportParams (refreshable .NewDefaultRefreshable (refreshingclient.TransportParams {
227
+ MaxIdleConns : defaultMaxIdleConns ,
228
+ MaxIdleConnsPerHost : defaultMaxIdleConnsPerHost ,
229
+ DisableHTTP2 : false ,
230
+ DisableKeepAlives : false ,
231
+ IdleConnTimeout : defaultIdleConnTimeout ,
232
+ ExpectContinueTimeout : defaultExpectContinueTimeout ,
233
+ ResponseHeaderTimeout : 0 ,
234
+ TLSHandshakeTimeout : defaultTLSHandshakeTimeout ,
235
+ HTTPProxyURL : nil ,
236
+ ProxyFromEnvironment : true ,
237
+ HTTP2ReadIdleTimeout : defaultHTTP2ReadIdleTimeout ,
238
+ HTTP2PingTimeout : defaultHTTP2PingTimeout ,
239
+ })),
240
+ Middlewares : nil ,
241
+ DisableMetrics : refreshable .NewBool (refreshable .NewDefaultRefreshable (false )),
242
+ MetricsTagProviders : nil ,
243
+ DisableRecovery : false ,
244
+ DisableRequestSpan : false ,
245
+ DisableTraceHeaders : false ,
246
+ },
247
+ URIs : nil ,
248
+ BytesBufferPool : nil ,
249
+ ErrorDecoder : restErrorDecoder {},
250
+ MaxAttempts : nil ,
251
+ RetryParams : refreshingclient .NewRefreshingRetryParams (refreshable .NewDefaultRefreshable (refreshingclient.RetryParams {
252
+ InitialBackoff : defaultInitialBackoff ,
253
+ MaxBackoff : defaultMaxBackoff ,
254
+ })),
255
+ }
226
256
}
227
257
228
- // Map the final config to a set of validated client params used to build the dialer, retrier, tls config, and transport.
229
- func newRefreshingValidatedClientParams (ctx context.Context , config RefreshableClientConfig , reloadErrorSubmitter func (error )) (refreshingclient.RefreshableValidatedClientParams , error ) {
258
+ func newClientBuilderFromRefreshableConfig (ctx context.Context , config RefreshableClientConfig , b * clientBuilder , reloadErrorSubmitter func (error )) error {
230
259
refreshingParams , err := refreshable .NewMapValidatingRefreshable (config , func (i interface {}) (interface {}, error ) {
231
260
p , err := newValidatedClientParamsFromConfig (ctx , i .(ClientConfig ))
232
261
if reloadErrorSubmitter != nil {
@@ -235,21 +264,25 @@ func newRefreshingValidatedClientParams(ctx context.Context, config RefreshableC
235
264
return p , err
236
265
})
237
266
if err != nil {
238
- return nil , err
267
+ return err
239
268
}
240
269
validParams := refreshingclient .NewRefreshingValidatedClientParams (refreshingParams )
241
- return validParams , nil
242
- }
243
270
244
- func newRefreshingClientConfigWithConfigOverrides (config RefreshableClientConfig , configOverride ... func (c * ClientConfig )) RefreshableClientConfig {
245
- // Build the final config refreshable by applying the overrides.
246
- if config == nil {
247
- config = NewRefreshingClientConfig (refreshable .NewDefaultRefreshable (ClientConfig {}))
248
- }
249
- return NewRefreshingClientConfig (config .MapClientConfig (func (c ClientConfig ) interface {} {
250
- for _ , o := range configOverride {
251
- o (& c )
252
- }
253
- return c
254
- }))
271
+ b .HTTP .ServiceName = validParams .ServiceName ()
272
+ b .HTTP .DialerParams = validParams .Dialer ()
273
+ b .HTTP .TransportParams = validParams .Transport ()
274
+ b .HTTP .Timeout = validParams .Timeout ()
275
+ b .HTTP .DisableMetrics = validParams .DisableMetrics ()
276
+ b .HTTP .MetricsTagProviders = append (b .HTTP .MetricsTagProviders ,
277
+ TagsProviderFunc (func (* http.Request , * http.Response , error ) metrics.Tags {
278
+ return validParams .CurrentValidatedClientParams ().MetricsTags
279
+ }))
280
+ b .HTTP .Middlewares = append (b .HTTP .Middlewares ,
281
+ newAuthTokenMiddlewareFromRefreshable (validParams .APIToken ()),
282
+ newBasicAuthMiddlewareFromRefreshable (validParams .BasicAuth ()))
283
+
284
+ b .URIs = validParams .URIs ()
285
+ b .MaxAttempts = validParams .MaxAttempts ()
286
+ b .RetryParams = validParams .Retry ()
287
+ return nil
255
288
}
0 commit comments