Repository navigation
http-client not picking the Configuration dynamically. #189
Description
Hello Team,
Let us assume a case we are trying to connect to openstack with SSL Configuration in a loop i.e if not able to authenticate retry again until authentication is not successful, as for SSL Configuration we need to load up the certificate. So I will say two threads are running parallelly
- Trying to update keystore dynamically. Thread 1
- Trying to authenticate user to openstack over SSL/TSL Thread 2.
Thread 2 loads the certificate from the keystore before the thread 1 update the certificate in keystore. So Thread 2 Continuously picking the updated certificate even though before each authentication request we are creating the OSClientV3 and Config object in loop you can see the below snippet which is running in a loop.
`Config cfg = Config.newConfig().withConnectionTimeout(100000).withReadTimeout(100000);
System.out.println("sslContext= "+sslContext);
cfg.withSSLContext(sslContext);
OSFactory.enableHttpLoggingFilter(true);
V3 cb = OSFactory.builderV3().withConfig(cfg).endpoint(url).credentials(user, password,
Identifier.byName(userDomain));
if (projectDomain != null && !projectDomain.trim().isEmpty()) {
if (tenant != null && !tenant.trim().isEmpty()) {
/* project scoped auth */
cb.scopeToProject(Identifier.byName(tenant), Identifier.byName(projectDomain));
} else {
/* domain scoped auth */
cb.scopeToDomain(Identifier.byName(projectDomain));
}
}
OSClientV3 os = cb.authenticate();`
Root cause of this issue which I assume that we are creating singleton object Of Client in Factory class regardless the Config object changed or not
CloseableHttpClient getClient(Config config) { if (client == null) { synchronized (this) { if (client == null) { client = buildClient(config); } } } return client; }
But if we see the implementation of jersey connector it takes care of it i.e is config object changed or not. Please see the code in jersey code base
` private static LoadingCache<Config, Client> CACHE = CacheBuilder.newBuilder()
.expireAfterAccess(20, TimeUnit.MINUTES)
.build(new CacheLoader<Config, Client>() {
@OverRide
public Client load(Config config) throws Exception {
return buildClientFromConfig(config);
}
});
/**
* Creates or Returns a Client
*
* @param config the configuration to use for the given client
* @return the client
*/
static Client create(Config config) {
try {
return CACHE.get(config);
} catch (ExecutionException e) {
throw new ConnectionException("Issue creating Jersey 2 Client: " + e.getMessage(), 0, e);
}
}`
At last what are real world scenario...
- If someone wants to update the certificate and do not want to restart the service.
- If someone wants to connect with two different version of certificate in same app or service then always first Certificate will gets it precedence
Please share your thoughts and if possible if there is any workaround please comment.
Thank You,
Activity