Is it possible to add/remove Authenticators and/or Interceptors to an existing Okhttp instance? If yes, how?
No, it is not possible.
However, you can create a builder from an existing client, and make changes to that. This will share the dispatcher, connectionPool etc.
OkHttpClient.Builder clientBuilder = client1.newBuilder();
clientBuilder.networkInterceptors().add(0, serviceInterceptor);
OkHttpClient client2 = clientBuilder.build();
There is an example for adjusting the timeout of a client in the javadoc https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html
Related
Context
I have an instance of org.apache.hc.client5.http.impl.classic.CloseableHttpClient
and a singleton instance of org.apache.hc.client5.http.io.HttpClientConnectionManager to communicate with some remote API.
I have created a singleton pool of HTTP connections to create an instance of the HTTP client.
What is the right way to use HttpClientConnectionManager?
Should I really use try-with-resource (or old-style try-finally) to work with CloseableHttpClient?
If I close the HTTP client, a connection will be closed also from this pool. And then I can't use this pool to communicate with a remote API.
Of course, I've read the documentation https://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html
Maybe something I missed. Could anyone explain me?
You should also be using CloseableHttpClient as a singleton (on a per distinct service basis).
If you want to continue creating short lived instances of CloseableHttpClient with the same HttpClientConnectionManager please make sure to mark it as shared when creating CloseableHttpClient instances with HttpClientBuilder.
Once I've created an Apache HttpClient, how do I get the configuration values from it (what used to be HttpParams)?
For example, if I do something like this to create a client:
RequestConfig requestConfig = RequestConfig.custom()
.setCircularRedirectsAllowed(true)
.setAuthenticationEnabled(true)
.setExpectContinueEnabled(true)
.setConnectionRequestTimeout(10000);
.build();
HttpClient client = HttpClientBuilder
.create()
.setConnectionManager(new PoolingHttpClientConnectionManager(createSchemeRegistry()))
.setDefaultRequestConfig(requestConfig)
.build();
How would I get the value of the connection request timeout from the org.apache.http.client.HttpClient instance? other configuration items?
For AbstractHttpClient there was a method getParams (or similar) but that's all gone away in favour of the HttpClient interface. There's an InternalHttpClient instance that's generated along the way but I think that's not exposed.
The reason I'm interested in this is firstly for testing - I want to verify a HTTP Client was configured correctly, the other case is to present that information in a UI when debugging HTTP traffic - think of something like Charles proxy.
I have a scenario where there is an aggregate endpoint to call multiple downstream systems which are RESTful and gives back the consolidated response from all these systems.
I am currently using a rest template that is configured as a singleton bean and injects it to the corresponding services to make the rest call. The RestTemplate is using the default CloseableHttpClient as the HttpClient, which will close the connections once the request is successful.
Would this be a good approach or would it be better if the rest template is configured per service that is calling its RESTful service?
RestTemplate is thread safe. You could use a pooling connection manager:
#Bean
public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
PoolingHttpClientConnectionManager result = new PoolingHttpClientConnectionManager();
result.setMaxTotal(20); // FIXME Consider making this value configurable
return result;
}
#Bean
public RequestConfig requestConfig() {
RequestConfig result = RequestConfig.custom()
// FIXME Consider making these values configurable
.setConnectionRequestTimeout(2000)
.setConnectTimeout(2000)
.setSocketTimeout(2000)
.build();
return result;
}
#Bean
public CloseableHttpClient httpClient(PoolingHttpClientConnectionManager poolingHttpClientConnectionManager, RequestConfig requestConfig) {
CloseableHttpClient result = HttpClientBuilder
.create()
.setConnectionManager(poolingHttpClientConnectionManager)
.setDefaultRequestConfig(requestConfig)
.build();
return result;
}
#Bean
public RestTemplate restTemplate(HttpClient httpClient) {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return new RestTemplate(requestFactory);
}
And also important, you might need to change RestTemplate's default settings based on observation / load tests, RestTemplate doesn't necessary use the whole pool to prevent a host from hijacking it.
You can read more at my blog Troubleshooting Spring's RestTemplate Requests Timeout
From Spring Docs
RestTemplate
The RestTemplate is the central Spring class for client-side HTTP
access. Conceptually, it is very similar to the JdbcTemplate,
JmsTemplate, and the various other templates found in the Spring
Framework and other portfolio projects. This means, for instance, that
the RestTemplate is thread-safe once constructed, and that you can use
callbacks to customize its operations.
Hence you can create your RestTemplate its safe to share with multiple threads invoking a REST call simultaneously.
You should also consider cost of creating and destroying an instance. If each thread or each rest call creates a dedicated RestTemplate it will hamper your apps performance.
Ref: https://spring.io/blog/2009/03/27/rest-in-spring-3-resttemplate
It would be better if you're injecting the services in the rest template if they have something in common. You can inject services with some common behavior in one rest template. This way you'll be able to implement some resuable code in say a parent class. Just because they all are a service, injection them in a single rest template might not be proper from design point of view.
I am using apache httpclient 4.x with PoolingHttpClientConnectionManager. Typically you create this as follows:
PoolingHttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolingConnManager.setMaxTotal(10);
poolingConnManager.setDefaultMaxPerRoute(10);
We are working on scenario wherein we need to set this configuration parameter when this instance is in use.
For example, if there is a need of more number of HTTP request to be handled. In such case, is it possible to change these values without affecting current operations.
When I use a SimpleRequestFactory with my AsyncRestTemplate I can easily configure an HTTP proxy server.
I can either do (sample code in Kotlin):
#Bean
open fun asyncRestTemplate(): AsyncRestTemplate {
val proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress("127.0.0.1", 8008))
val requestFactory = SimpleClientHttpRequestFactory().apply {
this.setConnectTimeout(TimeUnit.SECONDS.toMillis(10).toInt())
this.setReadTimeout(TimeUnit.SECONDS.toMillis(10).toInt())
this.setProxy(proxy)
this.setTaskExecutor(taskExecutor())
}
return AsyncRestTemplate(requestFactory)
}
Or I can simply set the corresponding system properties: -Dhttp.proxyHost=127.0.0.1 -Dhttp.proxyPort=8008.
However, in the moment that I switch from the SimpleClientHttpRequestFactory to a Netty4ClientHttpRequestFactory there is no evident way to configure the proxy directly and it seems this client does not respect the system properties either.
val requestFactory = Netty4ClientHttpRequestFactory().apply {
this.setConnectTimeout(TimeUnit.SECONDS.toMillis(10).toInt())
this.setReadTimeout(TimeUnit.SECONDS.toMillis(10).toInt())
//this.setProxy(proxy) //???
}
Once I change for the netty client, I have no clue on how to make it go through the proxy.
My interest in using the netty client was that I not only wanted to make async requests, but also I wanted this to be non-blocking. I hope I'm not making a wrong assumption here.
Does anyone know how can I use a proxy server when using the Netty4ClientHttpRequestFactory or perhaps know of an alternative non-blocking client supported by Spring that I could use?
The Netty4ClientHttpRequestFactory (source) and related classes such as Netty4ClientHttpRequest (source) use SimpleChannelInboundHandler for the channel and do not use the proxy handler. Everything is private and unable to be overridden within the source, so there is no way to change it to support Proxies. You would have to almost rewrite the whole thing.
You have other async client options that will work very well and allow you more configuration options. The included Netty one is fairly basic anyway. OkHttpClientHttpRequestFactory and HttpComponentsAsyncClientHttpRequestFactory both let you pass in your own configured client.
To your interest, AsyncRestTemplate's different implementation:
SimpleClientHttpRequestFactory -> simple thread pool, blocking api, proxy supported
OkHttpClient (OkHttp3) -> blocking api, proxy supported
CloseableHttpAsyncClient -> non-blocking nio api, proxy supported
Netty4ClientHttpRequestFactory -> non-blocking nio api, proxy not supported
you can visit https://github.com/wuxudong/VariousAsyncHttpClientPerformance for more details