Rest Template release Connection from Pool - spring

I have rest template config similar to the following. I am trying to release a connection from the pool if I get a status code that does not equal 2XX (long story but need this code). Is here a way I can get the connection Manager and release a specific connection?
#Bean
public RestTemplate restTemplate() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(100);
connectionManager.setDefaultMaxPerRoute(20);
RequestConfig requestConfig = RequestConfig
.custom()
.setConnectionRequestTimeout(5000) // timeout to get connection from pool
.setSocketTimeout(5000) // standard connection timeout
.setConnectTimeout(5000) // standard connection timeout
.build();
HttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(requestFactory);
}
Looking for a way to accomplish something similar to the following
if(!httpStatusCode.substr(1).equals("2")) {
restTemplate.getConnectionPool().relase().thisConnection();
}
enter code here

Related

How to set connection-timeout and socket-timeout with feign +http client?

#Bean
public ApacheHttpClient client() {
RequestConfig config = RequestConfig.custom().setConnectTimeout(30)
.setSocketTimeout(30).build();
CloseableHttpClient httpClientObject = HttpClientBuilder.create()
.setDefaultRequestConfig(config)
.build();
return new ApacheHttpClient(httpClientObject);
}
#Autowired
public HTTPRequestClient(Decoder decoder, Encoder encoder, ApacheHttpClient client) {
this.client = client;
setHttpRequestAdaptor(Feign.builder()
.client(client)
.encoder(encoder)
.decoder(decoder)
.logLevel(feign.Logger.Level.FULL)
.target(Target.EmptyTarget.create(HTTPRequestAdaptor.class)));
}
Connection timeout and socket time out is not working. Help required what properties work with feign http client? I have been using
feign.httpclient.enabled=true
feign.httpclient.connection-timeout=2
but it didn't worked.
You can add a .options(new Request.Options(30, 30))
to the feign builder
If that fails then maybe add the entire class to the quesion. The this.client = client part of code looks a bit odd .

HttpClient have both SSL and Proxy authentication configured?

I have two pieces of code using HttpClient,
First part in case that the end point requires SSL
Second is proxy connection with basic authentication
My question Is how can I make this code conditional so in cases i have SSL + Proxy or SSL only
I have hard time figuring out how to set the default credentials for example after I created the client using the client in the SSL part
.setDefaultCredentialsProvider(credsProvider)
This part is how I create the Client when I need SSL
CloseableHttpClient client = null;
if(conf.isUseSslConfig()) {
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File(conf.getTrustStoreLocation()), conf.getTrustStorePassword().toCharArray(), new TrustSelfSignedStrategy()).build();
// Allow protocols
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,conf.getTlsVersions(), null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
}else {
client= HttpClients.createDefault();
}
And this part is how I create the Client when I need Proxy authentication:
if(conf.isUseProxyConfig()){
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("fakeProxy.xerox.com", 80),
new UsernamePasswordCredentials("xeroxUser","fakePassword123"));
HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider).build();
}
So the bottom line is how to make the two sections work together so in case
Call with SSL + Proxy and authentication
Call with only SSL
Call with only Proxy and authentication
You can write code this way to get multiple conditions resolved :
CloseableHttpClient client = null;
if(conf.isUseSslConfig() && conf.isUseProxyConfig()) {
setSSLSetting(client);
setProxy()
}else if(conf.isUseSslConfig()) {
setSSLSetting(client);
}else {
client= HttpClients.createDefault();
}
private void setProxy(){
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope("fakeProxy.xerox.com", 80),new UsernamePasswordCredentials("xeroxUser","fakePassword123"));
}
private void setSSLSetting(CloseableHttpClient client){
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File(conf.getTrustStoreLocation()), conf.getTrustStorePassword().toCharArray(), new TrustSelfSignedStrategy()).build();
// Allow protocols
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,conf.getTlsVersions(), null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
}
or you can create methods that return client with different settings and configs like this :
final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", new PlainConnectionSocketFactory()).register("https", sslsf).build();
final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
private CloseableHttpClient createHttpClient(String headerName, String value) throws NoSuchAlgorithmException, KeyManagementException,KeyStoreException {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
Header header = new BasicHeader(headerName,value);
List<Header> headers = new ArrayList<>();
headers.add(header);
RequestConfig reqConfig = RequestConfig.custom().setConnectionRequestTimeout(long milli seconds).build();
CloseableHttpClient httpclient = HttpClients.custom().
setDefaultHeaders(headers).
setDefaultRequestConfig(reqConfig).
setConnectionManager(cm).
build();
return httpclient;
}

How to set cipher suites with javax.net.ssl.SSLContext

In a spring boot application using java8, I am setting the underlying SSLConext of an httpClient connection as follows:
import javax.net.ssl.SSLContext;
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
CloseableHttpClient httpClient = HttpClientBuilder
.create()
.setConnectionManager(myConnectionManager)
.setDefaultRequestConfig(rqConfig)
.setSSLContext(sslContext)
.build();
I need to set the cipher suites for the underlying TLS1.2 secured connection to something stronger of my choice. I don't see a way to do this with the way I am creation the sslContext in my code.
Can someone help me set up the cipher suites with my sslContext ?
================UPDATE=================
This is how I have now created my HttpClient
CloseableHttpClient httpClient = HttpClientBuilder
.create()
.setConnectionManager(myConnectionManager)
.setDefaultRequestConfig(rqConfig)
.setSSLSocketFactory(new SSLConnectionSocketFactory(
SSLContexts.createSystemDefault(),
new String[]{"TLSv1.2"},
new String[] {"some-gibberish-cipher-suite"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build();
Preferred TLS protocol versions and custom ciphers can be specified when creating a custom SSLConnectionSocketFactory instance
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(new SSLConnectionSocketFactory(
SSLContexts.createSystemDefault(),
new String[]{"TLSv1.2"},
new String[] {"TLS_RSA_WITH_AES_256_CBC_SHA256"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build();
try (CloseableHttpResponse response = client.execute(new HttpGet("https://httpbin.org/"))) {
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
}
Alternatively, one can create a custom PoolingHttpClientConnectionManager instance with the desired SSL configuration.
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", new SSLConnectionSocketFactory(
SSLContexts.createSystemDefault(),
new String[]{"TLSv1.2"},
new String[]{"TLS_RSA_WITH_AES_256_CBC_SHA256"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build());
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(cm)
.build();
try (CloseableHttpResponse response = client.execute(new HttpGet("https://httpbin.org/"))) {
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
}

Proxy configuration in OAuth2RestTemplate

I need to consume an API which is secured by OAuth2. For that I am using OAuth2RestTemplate.
But am getting below error:
java.net.ConnectException: Connection timed out: connect
This is happening due to proxy issue. I Know how to set proxy in RestTemplate :
SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("Proxy host", 8080));
clientHttpRequestFactory.setProxy(proxy);
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
The same way I tried to set for OAuth2RestTemplate :
#Bean
public OAuth2RestOperations restTemplate(OAuth2ClientContext oauth2ClientContext) {
OAuth2RestTemplate client = new OAuth2RestTemplate(resource(), oauth2ClientContext);
SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT));
clientHttpRequestFactory.setProxy(proxy);
client.setRequestFactory(clientHttpRequestFactory);
return client;
}
But it is not working and giving "Connection timed out" exception. This is happening because of this first line OAuth2RestTemplate client = new OAuth2RestTemplate(resource(), oauth2ClientContext); which tries to get Access token that means there also it needs proxy setting. if I add below lines then it works:
System.setProperty("https.proxyHost", "urproxy.com");
System.setProperty("https.proxyPort", "8080");
But I can not use System.setProperties("","") option as we do not have permission to set on tomcat server.
I researched but could not find any way to set proxy in OAuth2RestTemplate while creating this object.
Any help would be appreciated. Thanks
OAuth2RestTemplate just creates a set of AccessTokenProvider to retrieve the token from authorization server according to different kinds of grant types. For example AuthorizationCodeAccessTokenProvider is used to retrieve access token with grant type authorization_code. The token providers themselves initiate some RestTemplate to send the request but do not use OAuth2RestTemplate just created. One way might resolve the issue. That is to create you own AccessTokenProvider and set the request factory.
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
Proxy proxy= new Proxy(Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT));
requestFactory.setProxy(proxy);
AuthorizationCodeAccessTokenProvider authorizationCodeAccessTokenProvider = new AuthorizationCodeAccessTokenProvider();
authorizationCodeAccessTokenProvider.setRequestFactory(requestFactory);
ImplicitAccessTokenProvider implicitAccessTokenProvider = new ImplicitAccessTokenProvider();
implicitAccessTokenProvider.setRequestFactory(requestFactory);
AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(
Arrays.<AccessTokenProvider> asList(authorizationCodeAccessTokenProvider, implicitAccessTokenProvider));
OAuth2RestTemplate client = new OAuth2RestTemplate(github(), oauth2ClientContext);
client.setAccessTokenProvider(accessTokenProvider);
You could also add ResourceOwnerPasswordAccessTokenProvider and ClientCredentialsAccessTokenProvider to the OAuth2RestTemplate.
This RestTemplate provides a workaround:
/**
* An OAuth2RestTemplate with proxy support.
*
* #author E.K. de Lang
*/
public class ProxySupportingOAuth2RestTemplate
extends OAuth2RestTemplate
{
private static final Logger LOG = LogFactory.getLogger(ProxySupportingOAuth2RestTemplate.class);
private final SimpleClientHttpRequestFactory factory;
public ProxySupportingOAuth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context,
AccessTokenProvider accessTokenProvider)
{
super(resource, context);
factory = new SimpleClientHttpRequestFactory();
super.setRequestFactory(factory);
super.setAccessTokenProvider(accessTokenProvider);
// To fix issue: https://github.com/spring-projects/spring-security-oauth/issues/459 also set the factory of the token-provider.
if (accessTokenProvider instanceof OAuth2AccessTokenSupport) {
((OAuth2AccessTokenSupport) accessTokenProvider).setRequestFactory(factory);
}
else {
throw new UnsupportedOperationException("accessTokenProvider must extend OAuth2AccessTokenSupport");
}
}
public void setProxy(Proxy proxy)
{
if (LOG.isDebugEnabled()) {
LOG.debug("setProxy:" + proxy);
}
if (super.getRequestFactory() == factory) {
factory.setProxy(proxy);
}
else {
throw new UnsupportedOperationException("RequestFactory has changed.");
}
}
}

Add proxy information and basic auth to the resttemplate using httpClient

My development environment is behind a proxy so i need to set the proxy information to the rest template, that's all good when i use a HttpComponentsClientHttpRequestFactory and set the proxy setting in the httpClient and set it in the template.
But now i have a rest service that needs basic auth. And to set the basic auth credentials, i need to set them in the httpClient on the rest template. But i see that the getparams method in the httpClient is depricated, so i can't just update the existing client in the template, and if i create a new httpclient object, i will overwrite the proxy info that were set during the application bootstrapping.
So is there some way that i could extract the httpClient from the rest template and update it? Or is there any other way to tackle this?
Thanks.
Configure the httpClient as follows:
HttpHost target = new HttpHost("hostname", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(target.getHostName(), target.getPort()),
new UsernamePasswordCredentials("user", "passwd"));
HttpHost proxy = new HttpHost("proxy", 12345);
CloseableHttpClient httpclient = HttpClients.custom()
.setProxy(proxy)
.setDefaultCredentialsProvider(credsProvider).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpclient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
See also HttpClient Examples
The above solution did not work for me i work around the above and finally make it work with small modifications.
RestTemplate restTemplate = new RestTemplate();
HttpHost proxy =null;
RequestConfig config=null;
String credentials = this.env.getProperty("uname") + ":" + this.env.getProperty("pwd");
String encodedAuthorization = Base64.getEncoder().encodeToString(credentials.getBytes());
Header header = new BasicHeader(HttpHeaders.AUTHORIZATION, "Basic " + encodedAuthorization);
List<Header> headers = new ArrayList<>();
headers.add(header);
if(Boolean.valueOf(env.getProperty("proxyFlag"))){
proxy = new HttpHost(this.env.getProperty("proxyHost"), Integer.parseInt(env.getProperty("proxyPort")), "http");
config= RequestConfig.custom().setProxy(proxy).build();
}else{
config= RequestConfig.custom().build();
}
CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config)
.setDefaultHeaders(headers).build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
restTemplate.setRequestFactory(factory);
return restTemplate;

Resources