How can I use a custom configured Apache HttpClient with Jersey Client 2?
HttpClient with Caching (from apache docs)
CacheConfig cacheConfig = CacheConfig.custom()
.setMaxCacheEntries(1000)
.setMaxObjectSize(8192)
.build();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(30000)
.setSocketTimeout(30000)
.build();
CloseableHttpClient cachingClient = CachingHttpClients.custom()
.setCacheConfig(cacheConfig)
.setDefaultRequestConfig(requestConfig)
.build();
Using Apache Http in general works by setting
protected ClientConfig getClientConfig() {
ClientConfig config = new ClientConfig();
config.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
config.property(ApacheClientProperties.CONNECTION_MANAGER, connectionManager);
final ApacheConnectorProvider connector = new ApacheConnectorProvider();
config.connectorProvider(connector);
return config;
}
But how to use custom http client config like above?
You can use ApacheHttpClientBuilderConfigurator. So you can do:
config.register(new ApacheHttpClientBuilderConfigurator() {
public HttpClientBuilder configure(HttpClientBuilder httpClientBuilder) {
return httpClientBuilder.setDefaultRequestConfig(requestConfig);
}
});
See the example.
The Apache caching client is not properly supported at the moment. You may return CachingHttpClientBuilder from the method, but the HttpClientBuilder does not have getters to see what is already set, so you need to set everything from scratch.
Related
I have a requirement to use springboot rest template which calls 3rd party and connects over 2-way ssl but it should go through the proxy, but I am getting "
Encountered connectivity issue while reaching APIsun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
" exception. This is very generic exception. Keystore and certificate are accessible. Without proxy I can able to call same 3rd party API with same set of certificates in different environement. So no issue with certs and location.
Looks like proxy is not able to forward/find certificates to server. Anyone knows how to solve this? following is the code for creating rest template.
HttpClientBuilder httpClientBuilder = null;
if(proxyEnabled){
httpClientBuilder = getHttpClientBuilderWithProxy();
} else{
httpClientBuilder = getHttpClientBuilderWithoutProxy();
}
CloseableHttpClient client = null;
if(isSslEnabled){
logger.info("SSL enabled for closable http client");
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory( new SSLContextBuilder()
.loadKeyMaterial( ResourceUtils.getFile(keyStore) , keyStorePassword.toCharArray(), keyStorePassword.toCharArray())
.loadTrustMaterial(ResourceUtils.getFile(trustStore), trustStorePassword.toCharArray()) .build());
client = httpClientBuilder
.setSSLSocketFactory(csf)
.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy())
.build();
} else{
logger.info("SSL disabled for closable http client");
client = httpClientBuilder
.build();
}
clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);
private HttpClientBuilder getHttpClientBuilderWithoutProxy(){
return HttpClientBuilder.create()
.disableAutomaticRetries();
}
private HttpClientBuilder getHttpClientBuilderWithProxy(){
HttpHost proxy = new HttpHost(httpProxyHost, httpProxyPort);
return HttpClientBuilder.create()
.setProxy(proxy)
.disableAutomaticRetries();
}
I am expecting it to call 3rd party API with proxy and ssl.
I have one requirement i.e. to bypass SSL verification while connecting to webservice using rest template.
Currently I am implementing proxy to rest template via below code.
SimpleClientHttpRequestFactory clientHttpRequestFactory = new
SimpleClientHttpRequestFactory();
Proxy proxy = new Proxy(Proxy.Type.HTTP,new InetSocketAddress(proxyHost,
Integer.parseInt(proxyPort)));
clientHttpRequestFactory.setProxy(proxy);
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
ResponseEntity<String> responseEntity= restTemplate.exchange(url,HttpMethod.POST,
entity,String.class);
response = responseEntity.getBody();
How can I modify my Rest template so while connecting to URL it doesn't look for SSL certificate Validation.
I do checked couple of solutions online for this but all are implemented with
HttpComponentsClientHttpRequestFactory
which don't have provision to pass proxy host and server (Java.net.proxy object).
Can anyone help me here, I am stuck from long on this.
Solution I found online is below
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
restTemplate = new RestTemplate(requestFactory);
Thanks,
Anshu
I developed a service in RESTEasy using ProxyFactory and ClientExecutor like this:
PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager();
DefaultHttpClient httpClient = new DefaultHttpClient(connectionManager);
HttpParams params = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(params, 5000);
HttpConnectionParams.setSoTimeout(params, 5000);
ClientExecutor clientExecutor = new ApacheHttpClient4Executor(httpClient);
MyClass client = ProxyFactory.create(MyClass.class, "http://www.example.com", clientExecutor);
It always worked perfectly. After RESTEasy deprecated both ClientExecutor and ProxyFactory, they provided a new ResteasyClient for external connections, but I don't know if this new ResteasyClient is threadsafe. This is the new sample code from the documentation:
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://example.com/base/uri");
SimpleClient simple = target.proxy(SimpleClient.class);
UPDATE: I used the code with the ResteasyClient and I got many of these errors:
javax.ws.rs.ProcessingException: Unable to invoke request
Caused by
java.lang.IllegalStateException: Invalid use of BasicClientConnManager: connection still allocated. Make sure to release the connection before allocating another one.
We use this:
final ResteasyClient client = new ResteasyClientBuilder()
.connectionPoolSize(10)
.maxPooledPerRoute(5)
.build();
And after debugging I found out that (at least in our situation) the RESTEasy client uses the ThreadSafeClientConnManager by default so I think there is no need to specify a different one although according to the JavaDoc it is deprecated in favour of PoolingHttpClientConnectionManager (note the extra Http). But this has been fixed in RESTEasy client 3.0.5.Final: https://issues.jboss.org/browse/RESTEASY-948
It's a jungle of HTTP connection managers out there..
This worked for me. Just needed to find the hook into setting up the Apache HTTP engine. Mostly based on RestEasy 3.0.5.Final API
public static Object setupServiceProxy(#NotNull Class responseClass) {
ResteasyProviderFactory factory = ResteasyProviderFactory.getInstance();
ResteasyClientBuilder builder = new ResteasyClientBuilder().providerFactory(factory);
ResteasyClient client = builder.httpEngine(setupHttpDefaults()).build();
ResteasyWebTarget target = client.target(url);
return target.proxy(responseClass);
}
public static ClientHttpEngine setupHttpDefaults() {
PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager();
DefaultHttpClient httpClient = new DefaultHttpClient(connectionManager);
HttpParams params = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(params, 30000);
HttpConnectionParams.setSoTimeout(params, 30000);
BasicHttpContext localContext = new BasicHttpContext();
return new ApacheHttpClient4Engine(httpClient, localContext);
}
I have inherited the code
import org.apache.http.client.HttpClient;
...
HttpClient httpclient = createHttpClientOrProxy();
...
private HttpClient createHttpClientOrProxy() {
HttpClient httpclient = new DefaultHttpClient();
/*
* Set an HTTP proxy if it is specified in system properties.
*
* http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html
* http://hc.apache.org/httpcomponents-client-ga/httpclient/examples/org/apache/http/examples/client/ClientExecuteProxy.java
*/
if( isSet(System.getProperty("http.proxyHost")) ) {
int port = 80;
if( isSet(System.getProperty("http.proxyPort")) ) {
port = Integer.parseInt(System.getProperty("http.proxyPort"));
}
HttpHost proxy = new HttpHost(System.getProperty("http.proxyHost"), port, "http");
// #Deprecated methods here... getParams() and ConnRoutePNames
httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
}
return httpclient;
}
httpClient.getParams() is #Deprecated and reads "
HttpParams getParams()
Deprecated.
(4.3) use RequestConfig.
There are no class docs for RequestConfig and I do not know what method should be used to replace getParams() and ConnRoutePNames.DEFAULT_PROXY.
This is more of a follow-up to the answer given by #Stephane Lallemagne
There is a much conciser way of making HttpClient pick up system proxy settings
CloseableHttpClient client = HttpClients.custom()
.setRoutePlanner(
new SystemDefaultRoutePlanner(ProxySelector.getDefault()))
.build();
or this if you want an instance of HttpClient fully configured with system defaults
CloseableHttpClient client = HttpClients.createSystem();
You are using apache HttpClient 4.3 library with apache HttpClient 4.2 code.
Please notice that getParams() and ConnRoutePNames are not the only deprecated methods in your case. The DefaultHttpClient class itself rely on 4.2 implementation and is also deprecated in 4.3.
In regard to the 4.3 documentation here (http://hc.apache.org/httpcomponents-client-4.3.x/tutorial/html/connmgmt.html#d5e473), you can rewrite it this way:
private HttpClient createHttpClientOrProxy() {
HttpClientBuilder hcBuilder = HttpClients.custom();
// Set HTTP proxy, if specified in system properties
if( isSet(System.getProperty("http.proxyHost")) ) {
int port = 80;
if( isSet(System.getProperty("http.proxyPort")) ) {
port = Integer.parseInt(System.getProperty("http.proxyPort"));
}
HttpHost proxy = new HttpHost(System.getProperty("http.proxyHost"), port, "http");
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
hcBuilder.setRoutePlanner(routePlanner);
}
CloseableHttpClient httpClient = hcBuilder.build();
return httpClient;
}
I am using jersey client with ApacheConnection Provider.
Builder builder = RequestConfig.custom().setConnectTimeout(timeout);
List<Proxy> proxies = ProxyManager.getInstance().select(baseUrl.toURI());
if (useProxy) {
...
builder.setProxy(new HttpHost(proxyUri.getHost(), proxyUri.getPort()));
}
RequestConfig requestConfig = builder.build();
final ClientConfig clientConfig = new ClientConfig();
clientConfig.property(ApacheClientProperties.REQUEST_CONFIG, requestConfig);
clientConfig.connectorProvider(new ApacheConnectorProvider());
client = ClientBuilder.newBuilder().withConfig(clientConfig).sslContext(getSSLContext()).build();
client.property(ClientProperties.CONNECT_TIMEOUT, 5000);
But how to add username and password for Proxy authentication?
Seems like apache connection provider does not use the standard java proxy selector mechanisms.
I finally found the solution by myself. Unfortunately this is documented nowhere:
HttpHost proxyhost = new HttpHost(host,pw);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(proxyhost), new UsernamePasswordCredentials(user, pw));
clientConfig.property(ApacheClientProperties.CREDENTIALS_PROVIDER, credsProvider);
builder.setProxy(proxyhost);
I think you should add few more lines of code
builder.setProxy(proxyhost).setDefaultCredentialsProvider(credsProvider)
.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
otherwise it wont really authenticate the proxy host I feel. In your case it might be bypassing the proxy. ?