Skip to content

Commit 544ad9f

Browse files
authored
Revert "feature: Update config, not params, during builder phase" (#736)
1 parent 935b86c commit 544ad9f

File tree

9 files changed

+441
-262
lines changed

9 files changed

+441
-262
lines changed

conjure-go-client/httpclient/client_builder.go

+107-74
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/palantir/conjure-go-runtime/v2/conjure-go-client/httpclient/internal"
2626
"github.com/palantir/conjure-go-runtime/v2/conjure-go-client/httpclient/internal/refreshingclient"
2727
"github.com/palantir/pkg/bytesbuffers"
28+
"github.com/palantir/pkg/metrics"
2829
"github.com/palantir/pkg/refreshable"
2930
werror "github.com/palantir/witchcraft-go-error"
3031
)
@@ -54,6 +55,7 @@ var (
5455
type clientBuilder struct {
5556
HTTP *httpClientBuilder
5657

58+
URIs refreshable.StringSlice
5759
URIScorerBuilder func([]string) internal.URIScoringMiddleware
5860

5961
// If false, NewClient() will return an error when URIs.Current() is empty.
@@ -63,15 +65,19 @@ type clientBuilder struct {
6365
ErrorDecoder ErrorDecoder
6466

6567
BytesBufferPool bytesbuffers.Pool
68+
MaxAttempts refreshable.IntPtr
69+
RetryParams refreshingclient.RefreshableRetryParams
6670
}
6771

6872
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
7581
MetricsTagProviders []TagsProvider
7682

7783
// These middleware options are not refreshed anywhere because they are not in ClientConfig,
@@ -81,69 +87,57 @@ type httpClientBuilder struct {
8187
DisableTraceHeaders bool
8288
}
8389

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) {
8591
for _, p := range params {
8692
if p == nil {
8793
continue
8894
}
8995
if err := p.applyHTTPClient(b); err != nil {
90-
return nil, nil, err
96+
return nil, err
9197
}
9298
}
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()})
10599

106100
var tlsProvider refreshingclient.TLSProvider
107101
if b.TLSConfig != nil {
108102
tlsProvider = refreshingclient.NewStaticTLSConfigProvider(b.TLSConfig)
109103
} else {
110-
refreshableProvider, err := refreshingclient.NewRefreshableTLSConfig(ctx, transportParams.TLS())
104+
refreshableProvider, err := refreshingclient.NewRefreshableTLSConfig(ctx, b.TransportParams.TLS())
111105
if err != nil {
112-
return nil, nil, err
106+
return nil, err
113107
}
114108
tlsProvider = refreshableProvider
115109
}
116110

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))
121115
if !b.DisableRecovery {
122116
transport = wrapTransport(transport, recoveryMiddleware{})
123117
}
124118
transport = wrapTransport(transport, b.Middlewares...)
125119

126-
client := refreshingclient.NewRefreshableHTTPClient(transport, validParams.Timeout())
127-
return client, validParams, nil
120+
return refreshingclient.NewRefreshableHTTPClient(transport, b.Timeout), nil
128121
}
129122

130123
// NewClient returns a configured client ready for use.
131124
// We apply "sane defaults" before applying the provided params.
132125
func NewClient(params ...ClientParam) (Client, error) {
133-
return NewClientFromRefreshableConfig(context.TODO(), nil, params...)
126+
b := newClientBuilder()
127+
return newClient(context.TODO(), b, params...)
134128
}
135129

136130
// NewClientFromRefreshableConfig returns a configured client ready for use.
137131
// We apply "sane defaults" before applying the provided params.
138132
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
142136
}
143-
return newClient(ctx, config, b, nil, params...)
137+
return newClient(ctx, b, params...)
144138
}
145139

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) {
147141
for _, p := range params {
148142
if p == nil {
149143
continue
@@ -152,6 +146,12 @@ func newClient(ctx context.Context, config RefreshableClientConfig, b *clientBui
152146
return nil, err
153147
}
154148
}
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+
}
155155

156156
var edm Middleware
157157
if b.ErrorDecoder != nil {
@@ -161,40 +161,27 @@ func newClient(ctx context.Context, config RefreshableClientConfig, b *clientBui
161161
middleware := b.HTTP.Middlewares
162162
b.HTTP.Middlewares = nil
163163

164-
httpClient, validParams, err := b.HTTP.Build(ctx, config, reloadErrorSubmitter)
164+
httpClient, err := b.HTTP.Build(ctx)
165165
if err != nil {
166166
return nil, err
167167
}
168168

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-
177169
var recovery Middleware
178170
if !b.HTTP.DisableRecovery {
179171
recovery = recoveryMiddleware{}
180172
}
181-
uriScorer := internal.NewRefreshableURIScoringMiddleware(validParams.URIs(), func(uris []string) internal.URIScoringMiddleware {
173+
uriScorer := internal.NewRefreshableURIScoringMiddleware(b.URIs, func(uris []string) internal.URIScoringMiddleware {
182174
if b.URIScorerBuilder == nil {
183175
return internal.NewBalancedURIScoringMiddleware(uris, func() int64 { return time.Now().UnixNano() })
184176
}
185177
return b.URIScorerBuilder(uris)
186178
})
187-
188-
middleware = append(middleware,
189-
newAuthTokenMiddlewareFromRefreshable(validParams.APIToken()),
190-
newBasicAuthMiddlewareFromRefreshable(validParams.BasicAuth()))
191-
192179
return &clientImpl{
193-
serviceName: validParams.ServiceName(),
180+
serviceName: b.HTTP.ServiceName,
194181
client: httpClient,
195182
uriScorer: uriScorer,
196-
maxAttempts: validParams.MaxAttempts(),
197-
backoffOptions: validParams.Retry(),
183+
maxAttempts: b.MaxAttempts,
184+
backoffOptions: b.RetryParams,
198185
middlewares: middleware,
199186
errorDecoderMiddleware: edm,
200187
recoveryMiddleware: recovery,
@@ -205,7 +192,8 @@ func newClient(ctx context.Context, config RefreshableClientConfig, b *clientBui
205192
// NewHTTPClient returns a configured http client ready for use.
206193
// We apply "sane defaults" before applying the provided params.
207194
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...)
209197
if err != nil {
210198
return nil, err
211199
}
@@ -217,16 +205,57 @@ type RefreshableHTTPClient = refreshingclient.RefreshableHTTPClient
217205

218206
// NewHTTPClientFromRefreshableConfig returns a configured http client ready for use.
219207
// 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().
223208
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+
}
226256
}
227257

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 {
230259
refreshingParams, err := refreshable.NewMapValidatingRefreshable(config, func(i interface{}) (interface{}, error) {
231260
p, err := newValidatedClientParamsFromConfig(ctx, i.(ClientConfig))
232261
if reloadErrorSubmitter != nil {
@@ -235,21 +264,25 @@ func newRefreshingValidatedClientParams(ctx context.Context, config RefreshableC
235264
return p, err
236265
})
237266
if err != nil {
238-
return nil, err
267+
return err
239268
}
240269
validParams := refreshingclient.NewRefreshingValidatedClientParams(refreshingParams)
241-
return validParams, nil
242-
}
243270

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
255288
}

0 commit comments

Comments
 (0)