Skip to content

Commit f16eb70

Browse files
authored
Merge pull request #59 from SourceLabOrg/spp/HttpClientConfigHooks
http client config hooks interface
2 parents 1492a26 + a0633df commit f16eb70

File tree

6 files changed

+554
-83
lines changed

6 files changed

+554
-83
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@
22
The format is based on [Keep a Changelog](http://keepachangelog.com/)
33
and this project adheres to [Semantic Versioning](http://semver.org/).
44

5+
## 3.1.3 (08/11/2021)
6+
- [Issue-55](https://github.com/SourceLabOrg/kafka-connect-client/issues/55) Create new HttpContext for every request.
7+
- [PR-59](https://github.com/SourceLabOrg/kafka-connect-client/pull/59) Adds supportted way to modify the underlying configuration of HttpClient via HttpClientConfigHooks interface.
8+
9+
Usage of these hooks would look like:
10+
11+
```java
12+
// Directly create underlying RestClient and pass your HttpClientConfigHooks implementation.
13+
final RestClient restClient = new HttpClientRestClient(new HttpClientConfigHooks {
14+
// Override methods as needed to modify behavior.
15+
});
16+
17+
// Create KafkaConnectClient, passing configuration and RestClient implementation
18+
final KafkaConnectClient client = new KafkaConnectClient(configuration, restClient);
19+
20+
// Use client as normal...
21+
```
22+
523
## 3.1.2 (07/21/2021)
624

725
- [Issue-54](https://github.com/SourceLabOrg/kafka-connect-client/issues/54) Resolution for issue-54
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright 2018, 2019, 2020, 2021 SourceLab.org https://github.com/SourceLabOrg/kafka-connect-client
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7+
* persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package org.sourcelab.kafka.connect.apiclient.rest;
19+
20+
/**
21+
* Default implementation makes no modifications.
22+
*/
23+
public class DefaultHttpClientConfigHooks implements HttpClientConfigHooks {
24+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/**
2+
* Copyright 2018, 2019, 2020, 2021 SourceLab.org https://github.com/SourceLabOrg/kafka-connect-client
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7+
* persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package org.sourcelab.kafka.connect.apiclient.rest;
19+
20+
import org.apache.http.client.AuthCache;
21+
import org.apache.http.client.CredentialsProvider;
22+
import org.apache.http.client.config.RequestConfig;
23+
import org.apache.http.client.protocol.HttpClientContext;
24+
import org.apache.http.impl.client.BasicAuthCache;
25+
import org.apache.http.impl.client.BasicCredentialsProvider;
26+
import org.apache.http.impl.client.HttpClientBuilder;
27+
import org.sourcelab.kafka.connect.apiclient.Configuration;
28+
29+
/**
30+
* HttpClient configuration hooks.
31+
*
32+
* Provides an interface for modifying how the underlying HttpClient instance is created.
33+
*
34+
* Usage of this would look like:
35+
*
36+
* final RestClient restClient = new HttpClientRestClient(new HttpClientConfigHooks {
37+
* // Override methods as needed to modify behavior.
38+
* });
39+
*
40+
* // Create client, passing configuration and RestClient implementation
41+
* final KafkaConnectClient client = new KafkaConnectClient(configuration, restClient);
42+
*
43+
* // Use client as normal...
44+
*
45+
*/
46+
public interface HttpClientConfigHooks {
47+
/**
48+
* Create HttpClientBuilder instance.
49+
* @param configuration KafkaConnectClient configuration.
50+
* @return HttpClientBuilder instance.
51+
*/
52+
default HttpClientBuilder createHttpClientBuilder(final Configuration configuration) {
53+
return HttpClientBuilder.create();
54+
}
55+
56+
/**
57+
* Create HttpsContextBuilder instance.
58+
* @param configuration KafkaConnectClient configuration.
59+
* @return HttpsContextBuilder instance.
60+
*/
61+
default HttpsContextBuilder createHttpsContextBuilder(final Configuration configuration) {
62+
return new HttpsContextBuilder(configuration);
63+
}
64+
65+
/**
66+
* Create RequestConfig.Builder instance.
67+
* @param configuration KafkaConnectClient configuration.
68+
* @return RequestConfig.Builder instance.
69+
*/
70+
default RequestConfig.Builder createRequestConfigBuilder(final Configuration configuration) {
71+
return RequestConfig.custom();
72+
}
73+
74+
/**
75+
* Create AuthCache instance.
76+
* @param configuration KafkaConnectClient configuration.
77+
* @return AuthCache instance.
78+
*/
79+
default AuthCache createAuthCache(final Configuration configuration) {
80+
return new BasicAuthCache();
81+
}
82+
83+
/**
84+
* Create CredentialsProvider instance.
85+
* @param configuration KafkaConnectClient configuration.
86+
* @return CredentialsProvider instance.
87+
*/
88+
default CredentialsProvider createCredentialsProvider(final Configuration configuration) {
89+
return new BasicCredentialsProvider();
90+
}
91+
92+
/**
93+
* Create HttpClientContext instance.
94+
* @param configuration KafkaConnectClient configuration.
95+
* @return HttpClientContext instance.
96+
*/
97+
default HttpClientContext createHttpClientContext(final Configuration configuration) {
98+
return HttpClientContext.create();
99+
}
100+
101+
/**
102+
* Ability to modify or replace the AuthCache instance after initial configuration has been performed on it.
103+
* @param configuration KafkaConnectClient configuration.
104+
* @return AuthCache instance.
105+
*/
106+
default AuthCache modifyAuthCache(final Configuration configuration, final AuthCache authCache) {
107+
return authCache;
108+
}
109+
110+
/**
111+
* Ability to modify or replace the CredentialsProvider instance after initial configuration has been performed on it.
112+
* @param configuration KafkaConnectClient configuration.
113+
* @return CredentialsProvider instance.
114+
*/
115+
default CredentialsProvider modifyCredentialsProvider(final Configuration configuration, final CredentialsProvider credentialsProvider) {
116+
return credentialsProvider;
117+
}
118+
119+
/**
120+
* Ability to modify or replace the RequestConfig.Builder instance after initial configuration has been performed on it.
121+
* @param configuration KafkaConnectClient configuration.
122+
* @return RequestConfig.Builder instance.
123+
*/
124+
default RequestConfig.Builder modifyRequestConfig(final Configuration configuration, final RequestConfig.Builder builder) {
125+
return builder;
126+
}
127+
128+
/**
129+
* Ability to modify or replace the HttpClientBuilder instance after initial configuration has been performed on it.
130+
* @param configuration KafkaConnectClient configuration.
131+
* @return HttpClientBuilder instance.
132+
*/
133+
default HttpClientBuilder modifyHttpClientBuilder(final Configuration configuration, final HttpClientBuilder builder) {
134+
return builder;
135+
}
136+
137+
/**
138+
* Ability to modify or replace the HttpClientContext instance after initial configuration has been performed on it.
139+
* @param configuration KafkaConnectClient configuration.
140+
* @return HttpClientContext instance.
141+
*/
142+
default HttpClientContext modifyHttpClientContext(final Configuration configuration, final HttpClientContext context) {
143+
return context;
144+
}
145+
}

src/main/java/org/sourcelab/kafka/connect/apiclient/rest/HttpClientRestClient.java

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535
import org.apache.http.client.utils.URIBuilder;
3636
import org.apache.http.entity.StringEntity;
3737
import org.apache.http.impl.auth.BasicScheme;
38-
import org.apache.http.impl.client.BasicAuthCache;
39-
import org.apache.http.impl.client.BasicCredentialsProvider;
4038
import org.apache.http.impl.client.CloseableHttpClient;
4139
import org.apache.http.impl.client.HttpClientBuilder;
4240
import org.apache.http.message.BasicHeader;
@@ -60,6 +58,7 @@
6058
import java.util.Collection;
6159
import java.util.Collections;
6260
import java.util.Map;
61+
import java.util.Objects;
6362
import java.util.concurrent.TimeUnit;
6463

6564
/**
@@ -96,10 +95,24 @@ public class HttpClientRestClient implements RestClient {
9695
*/
9796
private CredentialsProvider credsProvider;
9897

98+
/**
99+
* Provides an interface for modifying how the underlying HttpClient instance is created.
100+
*/
101+
private final HttpClientConfigHooks configHooks;
102+
99103
/**
100104
* Constructor.
101105
*/
102106
public HttpClientRestClient() {
107+
this(new DefaultHttpClientConfigHooks());
108+
}
109+
110+
/**
111+
* Constructor allowing for injecting configuration hooks.
112+
* @param configHooks For hooking/overriding into how the underlying HttpClient is configured.
113+
*/
114+
public HttpClientRestClient(final HttpClientConfigHooks configHooks) {
115+
this.configHooks = configHooks;
103116
}
104117

105118
/**
@@ -113,10 +126,13 @@ public void init(final Configuration configuration) {
113126
this.configuration = configuration;
114127

115128
// Create https context builder utility.
116-
final HttpsContextBuilder httpsContextBuilder = new HttpsContextBuilder(configuration);
129+
final HttpsContextBuilder httpsContextBuilder = configHooks.createHttpsContextBuilder(configuration);
117130

118-
// Setup client builder
119-
final HttpClientBuilder clientBuilder = createHttpClientBuilder();
131+
// Create and setup client builder
132+
HttpClientBuilder clientBuilder = Objects.requireNonNull(
133+
configHooks.createHttpClientBuilder(configuration),
134+
"HttpClientConfigHook::createHttpClientBuilder() must return non-null instance."
135+
);
120136
clientBuilder
121137
// Define timeout
122138
.setConnectionTimeToLive(configuration.getConnectionTimeToLiveInSeconds(), TimeUnit.SECONDS)
@@ -125,15 +141,24 @@ public void init(final Configuration configuration) {
125141
.setSSLSocketFactory(httpsContextBuilder.createSslSocketFactory());
126142

127143
// Define our RequestConfigBuilder
128-
final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
144+
RequestConfig.Builder requestConfigBuilder = Objects.requireNonNull(
145+
configHooks.createRequestConfigBuilder(configuration),
146+
"HttpClientConfigHook::createRequestConfigBuilder() must return non-null instance."
147+
);
129148

130149
requestConfigBuilder.setConnectTimeout(configuration.getRequestTimeoutInSeconds() * 1_000);
131150

132151
// Define our Credentials Provider
133-
credsProvider = new BasicCredentialsProvider();
152+
credsProvider = Objects.requireNonNull(
153+
configHooks.createCredentialsProvider(configuration),
154+
"HttpClientConfigHook::createCredentialsProvider() must return non-null instance."
155+
);
134156

135157
// Define our auth cache
136-
authCache = new BasicAuthCache();
158+
authCache = Objects.requireNonNull(
159+
configHooks.createAuthCache(configuration),
160+
"HttpClientConfigHook::createAuthCache() must return non-null instance."
161+
);
137162

138163
// If we have a configured proxy host
139164
if (configuration.getProxyHost() != null) {
@@ -186,13 +211,31 @@ public void init(final Configuration configuration) {
186211
}
187212
}
188213

214+
// Call Modify hooks
215+
authCache = Objects.requireNonNull(
216+
configHooks.modifyAuthCache(configuration, authCache),
217+
"HttpClientConfigHook::modifyAuthCache() must return non-null instance."
218+
);
219+
credsProvider = Objects.requireNonNull(
220+
configHooks.modifyCredentialsProvider(configuration, credsProvider),
221+
"HttpClientConfigHook::modifyCredentialsProvider() must return non-null instance."
222+
);
223+
requestConfigBuilder = Objects.requireNonNull(
224+
configHooks.modifyRequestConfig(configuration, requestConfigBuilder),
225+
"HttpClientConfigHook::modifyRequestConfig() must return non-null instance."
226+
);
227+
189228
// Attach Credentials provider to client builder.
190229
clientBuilder.setDefaultCredentialsProvider(credsProvider);
191230

192231
// Attach default request config
193232
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
194233

195234
// build http client
235+
clientBuilder = Objects.requireNonNull(
236+
configHooks.modifyHttpClientBuilder(configuration, clientBuilder),
237+
"HttpClientConfigHook::modifyHttpClientBuilder() must return non-null instance."
238+
);
196239
httpClient = clientBuilder.build();
197240
}
198241

@@ -407,12 +450,12 @@ private String constructApiUrl(final String endPoint) {
407450
*/
408451
private HttpClientContext createHttpClientContext() {
409452
// Define our context
410-
final HttpClientContext httpClientContext = HttpClientContext.create();
453+
final HttpClientContext httpClientContext = configHooks.createHttpClientContext(configuration);
411454

412455
// Configure context.
413456
httpClientContext.setAuthCache(authCache);
414457
httpClientContext.setCredentialsProvider(credsProvider);
415458

416-
return httpClientContext;
459+
return configHooks.modifyHttpClientContext(configuration, httpClientContext);
417460
}
418461
}

0 commit comments

Comments
 (0)