I'm developing an SSE-client using WebFlux. What I want to achieve is to setup the client in a way, that
it automatically reconnects to the SSE-server, if the SSE-server was temporarily not available
it can also connect during run time, in case the SSE-server wasn't present from the start.
However, as far as I understand JavaScript-based clients, they are basically able to fulfill (1) and (2).
My question: is there a way to configure a WebFlux client to show the desired behaviors (1) and (2) (ideally "declaratively" via a builder and fluent coding style)? - I didn't find any information on how to do this, however, I assume that those are standard requirements for an SSE-client.
Here the basic code I'm using for my tests:
final SslContext sslContext
= SslContextBuilder
.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
final HttpClient httpClient
= HttpClient
.create()
.secure(t -> t.sslContext(sslContext));
final WebClient client
= WebClient
.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
final ParameterizedTypeReference<ServerSentEvent<String>> type = new ParameterizedTypeReference<ServerSentEvent<String>>() {};
final Flux<ServerSentEvent<String>> eventStream
= client.get()
.uri(endpointURL)
.exchange()
.flatMapMany(it -> it.bodyToFlux(type))
.repeat();
eventStream
.subscribe(
this::updateInformation
, error -> LOGGER.error("Error - {}", error.getMessage())
, () -> LOGGER.info("Got event stream completion signal."));
Related
I use a httpClient of apache in java to call REST APIs which is configured as below in a static block of class, what I want is to change the connection timeout per request. is it possible? how?
static {
PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager();
pool.setMaxTotal(ChatSettings.HTTP_CLIENT_THREAD_POOL_SIZE);
httpClient = HttpClients
.custom()
.setConnectionManager(pool)
.setDefaultRequestConfig(
RequestConfig
.custom()
.setConnectTimeout(DEFAULT_HTTP_TIMEOUT)
.setSocketTimeout(DEFAULT_HTTP_TIMEOUT)
.build()
)
.build();
}
One thing you can implement for sure is to follow this example: https://github.com/apache/httpcomponents-client/blob/5.1.x/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientAbortMethod.java
It demonstrates how a client can fire a request, but eventually change it's mind and tell the server it no longer wants to receive the response.
Combine this with a 'watchdog' thread that fires after some time/when the time runs out.
I have to call different urls with different connection timeout with webclient. But i found that we can set connect timeout globally only and not per resquest .. what to do please to set this timeout on a request without creating a new weblient each time.
You need to instantiate one webclient per url. For each webclient, you can set the connection timeout :
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
WebClient client = WebClient.builder()
.baseUrl("http://yourendpoint:8080")
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Then reuse this webclient for all the calls to the configured url.
How to abort just one request in 'rest-assured' framework?
For example, in 'apache' we could do after executing:
request.abort();
You can do time out like this. or is there is a specific case when you want to abort?
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000).setConnectionRequestTimeout(5000)
.setSocketTimeout(5000).build();
HttpClientConfig httpClientFactory = HttpClientConfig.httpClientConfig()
.httpClientFactory(() -> HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build());
RestAssured.config = RestAssured.config().httpClient(httpClientFactory);
Spring docs says it is required to configure http client for WebClient manually to set timeouts: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-client-builder-reactor-timeout.
But since WebClient returns reactive Mono, it's possible (api-wise) to apply .timeout method.
Does it have the same effect?
Moreover, when one uses .timeout method, Reactor's TimeoutException is expected. Will the same error appear in the stream if configuration is done manually i.e. will doOnError(TimeoutException.class, ...) work?
My findings
Setting a timeout in a http client specific way will lead to http client specific exception i.e. WebClient doesn't wrap exceptions:
#Test
void test() {
var host = "localhost";
var endpoint = "/test";
var port = 8089;
var timeout = Duration.ofSeconds(3);
WireMockServer wireMockServer = new WireMockServer(wireMockConfig().port(8089));
wireMockServer.start();
WireMock.configureFor(host, wireMockServer.port());
WireMock.stubFor(get(urlEqualTo(endpoint))
.willReturn(aResponse().withFixedDelay((int) timeout.toMillis())));
HttpClient httpClient = HttpClient.create()
.tcpConfiguration(client ->
client.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler((int) (timeout.toSeconds() / 2)))
.addHandlerLast(new WriteTimeoutHandler((int) (timeout.toSeconds() / 2)))));
WebClient webClient = WebClient.builder()
.baseUrl(format("http://%s:%d", host, port))
.clientConnector(new ReactorClientHttpConnector(httpClient)).build();
webClient.get().uri(endpoint).retrieve().bodyToMono(Recommendation.class).block();
}
This will lead to io.netty.handler.timeout.ReadTimeoutException.
.timeout(timeout.dividedBy(2)).block() leads to regular TimeoutException (java.util.concurrent) but it's still an open question whether a web client takes care about connections afterwards (probably not).
My solution is to use http client specific configuration to ensure native and correct way to utilize connections while adding new handler that wraps http client related exception into more generic ones (or java.util.concurrent.TimeoutException) so that WebClient clients won't depend on provider exceptions.
Trying to post to a restAPI using JAVA OKHttp.
My code looks as follows:
try {
loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.build();
MediaType mediaType = MediaType.parse("app.lication/json");
RequestBody body = RequestBody.create(mediaType, FileUtils.readFileToString(file));
Request request = new Request.Builder()
.url(restAPIUrl)
.post(body)
.addHeader("content-type", "application/json")
.addHeader("cache-control", "no-cache")
.build();
Call call = client.newCall(request);
call.execute();
handleResponse(file, response.code());
} catch (Exception e) {
logger.severe("Http Request failed: " + e.getMessage());
}
On my local development box (centos) that doesn't have firewall open to the restAPI, every thing behaves as expected. call.execute() throws an exception: Could not resolve host: Name or service not available.
On the production box (rhel), where firewall is open. The method call to client.newCall(request); just hangs indefinitely. (Before calling execute()) strace just shows the thread waiting.
I've verified i can reach the restAPI from command line with:
curl -X get https://restAPIUrl/index.html
The same jar is behaving differently on the two servers. I can't figure out why differences in server configuration would affect the newCall() (I would more expect it on execute)
What could be the cause for newCall() hanging due to environment? Any help would be much appreciated.
thank you!
Update:
I found that it gets past the newCall() method if I override the defaultSocketFactory.
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.setSocketFactory(new SocketFactory () {
#Override
public Socket createSocket(String s int i) thorws IOException, UnknownHostException {
return null;
}
.
.
})
.build();
Why wouldn't the system properly allocate the socket for the client using defaultSocketFactory on the rhel env? (SELINUX disabled.)
Update 2: Resolution
In the end this issue was caused by no read permissions on 'local_policy.jar' in $JAVA_HOME/lib/security. (File didn't exist in my dev environment)
The nasty part was the permissions exception was being swallowed by the OKHttp API.
Try to patch the security provider as described in "Update your security provider to protect against SSL exploits".
Just implement ProviderInstaller.installIfNeeded (or installIfNeededAsync)