SSL Exception Tag mismatch error, with spring webflux webclient requests - spring

I just converted a service to the webflux/netty stack (formerly it was mvc/undertow)
This service makes https requests to downstream services using spring webclient(formerly okHttp client).
The service is built with JDK 17 (and runs on JVM 18)
On the production environment, we are experiencing SSL errors starting after some time (15 minutes or up to 120 minutes) which increases over time. If the service is restarted, the errors are gone, until they start again later.
The exception I get:
io.netty.handler.codec.DecoderException: javax.net.ssl.SSLException: Tag mismatch
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:480)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:279)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800)
at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe$1.run(AbstractEpollChannel.java:425)
at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:391)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:995)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: javax.net.ssl.SSLException: Tag mismatch
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:133)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:371)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:314)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:309)
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:123)
at java.base/sun.security.ssl.SSLEngineImpl.decode(SSLEngineImpl.java:736)
at java.base/sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:691)
at java.base/sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:506)
at java.base/sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:482)
at java.base/javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:679)
at io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:295)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1342)
at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1235)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1284)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:510)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:449)
... 18 common frames omitted
Caused by: javax.crypto.AEADBadTagException: Tag mismatch
at java.base/com.sun.crypto.provider.GaloisCounterMode$GCMDecrypt.doFinal(GaloisCounterMode.java:1591)
at java.base/com.sun.crypto.provider.GaloisCounterMode.engineDoFinal(GaloisCounterMode.java:454)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2501)
at java.base/sun.security.ssl.SSLCipher$T12GcmReadCipherGenerator$GcmReadCipher.decrypt(SSLCipher.java:1659)
at java.base/sun.security.ssl.SSLEngineInputRecord.decodeInputRecord(SSLEngineInputRecord.java:239)
at java.base/sun.security.ssl.SSLEngineInputRecord.decode(SSLEngineInputRecord.java:196)
at java.base/sun.security.ssl.SSLEngineInputRecord.decode(SSLEngineInputRecord.java:159)
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:111)
... 29 common frames omitted
The webclient config code:
#Bean
public WebClient webClient(MetricsWebClientCustomizer metricsCustomizer,
WebClient.Builder builder,
#Value("${s2s.client.read-timeout-ms}") int readTimeoutMs) throws SSLException {
metricsCustomizer.customize(builder);
HttpClient httpClient = createNettyHttpClient(readTimeoutMs);
WebClient webClient = builder
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
// warming up web client to allow smooth first requests
httpClient.warmup().subscribe((ignored) -> log.info("web client initialized"));
return webClient;
}
private HttpClient createNettyHttpClient(int readTimeoutMs) throws SSLException {
final ConnectionProvider connectionProvider = ConnectionProvider
.builder("webclient-connection-provider")
.maxIdleTime(Duration.ofSeconds(poolConnectionMaxIdleTimeSeconds))
.maxConnections(maxConnections)
.pendingAcquireMaxCount(pendingAcquireMaxCount)
.build();
return HttpClient.create(connectionProvider)
.doOnConnected(
c -> c.addHandlerLast(new ReadTimeoutHandler(readTimeoutMs))
.addHandlerLast(new WriteTimeoutHandler(readTimeoutMs)))
.option(CONNECT_TIMEOUT_MILLIS, readTimeoutMs)
.compress(true)
.runOn(LoopResources.create("eventloop-webclient", nettyWorkerThreadsCount, true));
}

Related

Azure SDK JAVA - How to use ProxyOptions to connect to the proxy URL and how to pass the required path to the httpClient along with the hostname

I am trying utilise the ProxyOptions to re-route the blob upload to a proxy URL. In this case localhost
Demo Project:
Gradle Dependency:
implementation 'com.azure:azure-storage-blob'
implementation 'com.azure:azure-core'
implementation 'com.azure:azure-core-http-netty'
implementation 'com.azure:azure-identity'
implementation 'com.azure:azure-security-keyvault-secrets'
implementation ('com.azure.resourcemanager:azure-resourcemanager:2.18.0') {
exclude group: 'com.azure', module: 'azure-core'
}
configurations.all{
resolutionStrategy{
force 'com.azure:azure-storage-common:12.18.0', 'com.azure:azure-storage-common:12.14.3'
}
}
#Bean
public BlobClient blobClient() throws URISyntaxException, UnknownHostException {
HttpClient client = new NettyAsyncHttpClientBuilder()
.proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("localhost", 8080)))
.build();
String accountName = "xxxx";
String accountkey = "yyyyy";
String endPoint = String.format(java.util.Locale.ROOT, "https://%s.blob.core.windows.net", accountName);
BlobClient blobClient = new BlobClientBuilder()
.endpoint(endPoint)
.credential(new StorageSharedKeyCredential(accountName, accountkey))
.containerName("test-bucket-1")
.blobName("test.txt")
.httpClient(client)
.buildClient();
return blobClient;
}
and the controller which consumes the above blobClient:
#PutMapping(value ={"/azure/test"})
public boolean uploadAzureBlob(
#RequestParam("containerName") String containerName,
#RequestParam("blobName") Optional\<String\> blobName,
HttpServletRequest request, HttpServletResponse response) throws IOException, URISyntaxException {
InputStream inputStream = request.getInputStream();
BinaryData binaryData = BinaryData.fromStream(inputStream);
BlobParallelUploadOptions blobParallelUploadOptions = new BlobParallelUploadOptions(binaryData);
Response\<BlockBlobItem\> blobItemResponse =
blobClient.uploadWithResponse(
blobParallelUploadOptions,
Duration.ofSeconds(120L),
Context.NONE);
return true;
}
When the blobClient is trying to upload the blob object it should re-route to the following controller which is running in my local machine. I want to basically intercept the API call and redirect it to the proxy application running in my local. At least to the #RequestMapping(value ={"\*"})
Proxy Project:
Gradle Dependency:
implementation platform('com.azure:azure-sdk-bom:1.1.1')
implementation 'com.azure:azure-storage-blob'
implementation 'com.azure:azure-core'
implementation 'com.azure:azure-core-http-netty'
implementation 'com.azure:azure-identity'
implementation 'com.azure:azure-security-keyvault-secrets'
implementation ('com.azure.resourcemanager:azure-resourcemanager:2.18.0') {
exclude group: 'com.azure', module: 'azure-core'
}
configurations.all{
resolutionStrategy{
force 'com.azure:azure-storage-common:12.18.0', 'com.azure:azure-storage-common:12.14.3'
}
}
The controller code of the proxy:
#RequestMapping("/v2/azure-proxy")
public class AzureBlobProxyController {
#PutMapping(value ={"/{containerName}/{blobName}"})
public boolean uploadSecureStream(#PathVariable("containerName") String containerName,
#PathVariable("blobName") String blobName,
HttpServletRequest request, HttpServletResponse response) throws Exception
{
return true;
}
#RequestMapping(value ={"/*"})
public boolean getBlobContainerAsyncClient(
HttpServletRequest request,
HttpServletResponse httpServletResponse) throws Exception
{
return true;
}
}
But i am getting the following exception:
2022-11-28 00:22:12.535 WARN 86304 --- \[r-http-kqueue-1\] r.netty.http.client.HttpClientConnect : \[c80a00e1, L:/127.0.0.1:54822 - R:localhost/127.0.0.1:8080\] The connection observed an error
javax.net.ssl.SSLException: failure when writing TLS control frames
at io.netty.handler.ssl.SslHandler.setHandshakeFailureTransportFailure(SslHandler.java:1896) \~\[netty-handler-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.ssl.SslHandler.access$600(SslHandler.java:168) \~\[netty-handler-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.ssl.SslHandler$2.operationComplete(SslHandler.java:933) \~\[netty-handler-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.ssl.SslHandler$2.operationComplete(SslHandler.java:928) \~\[netty-handler-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:578) \~\[netty-common-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:552) \~\[netty-common-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:491) \~\[netty-common-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:616) \~\[netty-common-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.util.concurrent.DefaultPromise.setFailure0(DefaultPromise.java:609) \~\[netty-common-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.util.concurrent.DefaultPromise.tryFailure(DefaultPromise.java:117) \~\[netty-common-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.PendingWriteQueue.safeFail(PendingWriteQueue.java:288) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.PendingWriteQueue.removeAndFailAll(PendingWriteQueue.java:186) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.proxy.ProxyHandler.failPendingWrites(ProxyHandler.java:434) \~\[netty-handler-proxy-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.proxy.ProxyHandler.failPendingWritesAndClose(ProxyHandler.java:351) \~\[netty-handler-proxy-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.proxy.ProxyHandler.setConnectFailure(ProxyHandler.java:346) \~\[netty-handler-proxy-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.proxy.ProxyHandler.channelRead(ProxyHandler.java:266) \~\[netty-handler-proxy-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:327) \~\[netty-codec-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:299) \~\[netty-codec-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.proxy.HttpProxyHandler$HttpClientCodecWrapper.channelRead(HttpProxyHandler.java:272) \~\[netty-handler-proxy-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) \~\[netty-transport-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.kqueue.AbstractKQueueStreamChannel$KQueueStreamUnsafe.readReady(AbstractKQueueStreamChannel.java:544) \~\[netty-transport-classes-kqueue-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.kqueue.AbstractKQueueChannel$AbstractKQueueUnsafe.readReady(AbstractKQueueChannel.java:383) \~\[netty-transport-classes-kqueue-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.kqueue.KQueueEventLoop.processReady(KQueueEventLoop.java:211) \~\[netty-transport-classes-kqueue-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:289) \~\[netty-transport-classes-kqueue-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:995) \~\[netty-common-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) \~\[netty-common-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) \~\[netty-common-4.1.77.Final.jar:4.1.77.Final\]
at java.base/java.lang.Thread.run(Thread.java:832) \~\[na:na\]
Caused by: io.netty.handler.proxy.HttpProxyHandler$HttpProxyConnectException: http, none, localhost/127.0.0.1:8080 =\> xxxx.blob.core.windows.net/\<unresolved\>:443, status: 400
at io.netty.handler.proxy.HttpProxyHandler.handleResponse(HttpProxyHandler.java:200) \~\[netty-handler-proxy-4.1.77.Final.jar:4.1.77.Final\]
at io.netty.handler.proxy.ProxyHandler.channelRead(ProxyHandler.java:257) \~\[netty-handler-proxy-4.1.77.Final.jar:4.1.77.Final\]
... 23 common frames omitted
Caused by: io.netty.handler.proxy.HttpProxyHandler$HttpProxyConnectException: http, none, localhost/127.0.0.1:8080 =\> xxxx.blob.core.windows.net/\<unresolved\>:443, status: 400
How to fix the above issue and how can i make the proxy reach the #RequestMapping("/v2/azure-proxy"). In the socketAddress i am able to give hostname how can i give the path variables?
I have tried using the com.azure.core.http.HttpClient to re-route:
HttpClient client = new NettyAsyncHttpClientBuilder()
.proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("localhost", 8080)))
.build();
but i need to be able to use the proxy path:
http://localhost:8080/v2/azure-proxy

Not able to bypass javax.net.ssl.SSLHandshakeException: General SSLEngine problem

I do understand it is not a best practice to bypass SSL certification
but for my local testing I need that. So I have two secured(https)
localhost and I want to access the second localhost from the first
one using spring gateway
For generating SSL certificate I have used this link
https://howtodoinjava.com/spring-boot/spring-boot-ssl-https-example/
To bypass SSL check I have added this piece of the code in the main class for both gateway and consumer
public static void main(String[] args) {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
System.out.println("Error" + e);
}
SpringApplication.run(Gateway.class, args);
}
Yet I am still getting this handshake exception. What I am missing?
io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:477) ~[netty-codec-4.1.66.Final.jar:4.1.66.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276) ~[netty-codec-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) [netty-transport-4.1.66.Final.jar:4.1.66.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986) [netty-common-4.1.66.Final.jar:4.1.66.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-common-4.1.66.Final.jar:4.1.66.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-common-4.1.66.Final.jar:4.1.66.Final]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_301]
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alert.createSSLException(Alert.java:131) ~[na:1.8.0_301]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP GET "/consumer" [ExceptionHandlingWebHandler]
Apparently there is a hack in spring boot gateway to achieve this
spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager=true
You could check who is calling hostname verifier (via catching Exception with a breakpoint).. your stack might be not exactly the same as in tutorial someone wrote 1 year ago.
In my case I had to add
#Bean
public TLSProtocolConfigurer tlsProtocolConfigurer() {
TLSProtocolConfigurer tlsProtocolConfigurer = new TLSProtocolConfigurer();
tlsProtocolConfigurer.setSslHostnameVerification("allowAll");
return tlsProtocolConfigurer;
}
Have you checked that thread?
Unable to find valid certification path to requested target - error even after cert imported

Spring-Boot WebClient block() method returns error java.lang.IllegalStateException

I'm trying to fetch value(string) using Spring WebFlux WebClient, (Using SpringBoot version 2.4.5,)
#GetMapping("/test")
public Mono<String> getData(){
WebClient webClient = WebClient.create("http://localhost:9999");
Mono<String> stringMono = webClient.get()
.uri("/some/thing")
.retrieve()
.bodyToMono(String.class);
stringMono.subscribe( System.out::println);
System.out.println("Value : " + stringMono.block()); // this doesn't work, expecting to return ResponseBody as "Hello World" ,
return stringMono;
}
But getting below error
2021-05-11 20:02:15.521 ERROR 55613 --- [ctor-http-nio-2] a.w.r.e.AbstractErrorWebExceptionHandler : [19114471-1] 500 Server Error for HTTP GET "/test"
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.4.3.jar:3.4.3]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ HTTP GET "/test" [ExceptionHandlingWebHandler]
Stack trace:
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.4.3.jar:3.4.3]
at reactor.core.publisher.Mono.block(Mono.java:1703) ~[reactor-core-3.4.3.jar:3.4.3]
at com.example.demo.DemoApplication.getData(DemoApplication.java:28) ~[main/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$invoke$0(InvocableHandlerMethod.java:146) ~[spring-webflux-5.3.4.jar:5.3.4]
at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:151) ~[reactor-core-3.4.3.jar:3.4.3]
Reference for block method - https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-client-synchronous
When using Webflux, the whole idea is that you don't block - you'll cause massive performance problems if you do (see here for a related answer that explains why) so the framework explicitly disallows it, throwing an exception if you try.
You also shouldn't subscribe manually - while not a "mortal sin" in the reactive world as blocking is, that's certainly another red flag. Subscription is handled by the framework. You just need to return the Mono, and Webflux will subscribe when required to handle your request. In your case your manual subscription means the entire chain will actually be executed twice for each request - once to print the result out to the terminal, and once to return the result to the /test endpoint.
Instead, if you want a "side-effect" like this (printing out the value when you have it) then you need to alter the reactive chain to do so, using the doOnNext() operator. This means you can then do:
return webClient.get()
.uri("/some/thing")
.retrieve()
.bodyToMono(String.class)
.doOnNext(s -> System.out.println("Value: " + s));
This will ensure the value is printed out to the terminal, but without blocking, and without manually subscribing.

How to enabled http1.1 and http2.0 both together in a spring boot REST application in tomcat 9.0.14

I have enabled http 2.0 with SSL in a spring boot 2.1.2 REST application with embedded tomcat using configuration "server.http2.enabled=true" and other SSL configurations in application.properties file. It is working fine. When a http2 client post https request, my server is processing and responding properly (java 9 http client is being used). But when the http client version chanced to 1.1, below exception is seen -
WARNING: Using incubator modules: jdk.incubator.httpclient
java.util.concurrent.ExecutionException: java.io.IOException: Engine is closed
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395)
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2022)
at KnHttp2Client.sendGet(KnHttp2Client.java:81)
at KnHttp2Client.main(KnHttp2Client.java:113)
Caused by: java.io.IOException: Engine is closed
I have tried to post https request using apache-http-client4.5.7 also still I am getting below Exception
Jan 30, 2019 4:45:12 PM org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (org.apache.http.NoHttpResponseException) caught when processing request to {s}->https://localhost:8443: The target server failed to respond
Jan 30, 2019 4:45:12 PM org.apache.http.impl.execchain.RetryExec execute
INFO: Retrying request to {s}->https://localhost:8443
org.apache.http.NoHttpResponseException: localhost:8443 failed to respond
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:141)
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:157)
at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at apacheclient.KnConnPoolClient$1.run(KnConnPoolClient.java:48)
at java.base/java.lang.Thread.run(Thread.java:844)
After setting setRetryHandler(new DefaultHttpRequestRetryHandler(0, true)) got below exception <br>
org.apache.http.NoHttpResponseException: localhost:8443 failed to respond
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:141)
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:157)
at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at apacheclient.KnConnPoolClient$1.run(KnConnPoolClient.java:48)
at java.base/java.lang.Thread.run(Thread.java:844)
Working client for with http 2.0
{HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://localhost:8443/....."))
.GET()
.build();
CompletableFuture<HttpResponse<String>> resp = httpClient.sendAsync(request, HttpResponse.BodyHandler.asString()); }
When the http client version is changed to 1.1 as below it is not working
{HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.build();}
Below client code with Apache client is also not working
{
final CloseableHttpClient httpclient =
HttpClients.custom().setConnectionManager(pool).build();
final String url = "https://localhost:8443/....";
for (int i = 0; i < 1; i++) {
new Thread(new Runnable() {
public void run() {
try {
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response =httpclient.execute(httpGet);
HttpEntity entity = response.getEntity();
System.out.println(EntityUtils.toString(entity));
EntityUtils.consume(entity);
response.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
I expect that the server which supports http 2.0 should support http 1.1 also.
This is a known issue in Spring Boot, see issue #15764.
Currently, only http/2 is supported when Tomcat is configured, but Jetty and Undertow are supporting both. Until this issue is addressed, you can switch to Jetty or Undertow as a workaround.

Workaround for the slowness of the WebClient first request

I am using WebClient in a Spring Boot MVC 2.1 project and found that the first request made by the client takes up to 6 seconds. Subsequent requests are way faster (~30ms).
There's a closed issue in Spring's JIRA that advices using Jetty as the WebClient Http connector. I have tried that approach, improving the figures, with a ~800ms first request. This time is an improvement but it's still far from RestTemplate which usally takes <200ms.
Netty approach (5s first request):
Conf:
#Bean
public WebClient webClient() {
return WebClient.create();
}
Usage:
private final WebClient webClient;
#GetMapping(value="/wc", produces = APPLICATION_JSON_UTF8_VALUE)
public Mono<String> findWc() throws URISyntaxException {
URI uri = new URI("http://xxx");
final Mono<String> response = webClient.get().uri(uri).retrieve().bodyToMono(String.class);
return response;
}
Jetty approach (800ms first request):
Conf:
#Bean
public JettyResourceFactory resourceFactory() {
return new JettyResourceFactory();
}
#Bean
public WebClient webClient() {
ClientHttpConnector connector = new JettyClientHttpConnector(resourceFactory(), null);
return WebClient.builder().clientConnector(connector).build();
}
Usage: same as before.
There's another "problem" with the Jetty approach. On server shutdown it always produces the following exception:
27-Dec-2018 11:24:20.463 INFO [jetty-http#74305db9-65] org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading Illegal access: this web application instance has been stopped already. Could not load [org.eclipse.jetty.io.ManagedSelector$StopSelector]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [org.eclipse.jetty.io.ManagedSelector$StopSelector]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1348)
at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1336)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1195)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1157)
at java.lang.Class.getDeclaringClass0(Native Method)
at java.lang.Class.getDeclaringClass(Class.java:1235)
at java.lang.Class.getEnclosingClass(Class.java:1277)
at java.lang.Class.getSimpleBinaryName(Class.java:1443)
at java.lang.Class.getSimpleName(Class.java:1309)
at org.eclipse.jetty.io.ManagedSelector$SelectorProducer.toString(ManagedSelector.java:534)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.getString(EatWhatYouKill.java:458)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.toStringLocked(EatWhatYouKill.java:447)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.toString(EatWhatYouKill.java:440)
at org.slf4j.helpers.MessageFormatter.safeObjectAppend(MessageFormatter.java:299)
at org.slf4j.helpers.MessageFormatter.deeplyAppendParameter(MessageFormatter.java:271)
at org.slf4j.helpers.MessageFormatter.arrayFormat(MessageFormatter.java:233)
at org.slf4j.helpers.MessageFormatter.arrayFormat(MessageFormatter.java:173)
at org.eclipse.jetty.util.log.JettyAwareLogger.log(JettyAwareLogger.java:680)
at org.eclipse.jetty.util.log.JettyAwareLogger.debug(JettyAwareLogger.java:224)
at org.eclipse.jetty.util.log.Slf4jLog.debug(Slf4jLog.java:97)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:288)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
at java.lang.Thread.run(Thread.java:748)
SLF4J: Failed toString() invocation on an object of type [org.eclipse.jetty.util.thread.strategy.EatWhatYouKill]
Reported exception:
java.lang.NoClassDefFoundError: org/eclipse/jetty/io/ManagedSelector$StopSelector
at java.lang.Class.getDeclaringClass0(Native Method)
at java.lang.Class.getDeclaringClass(Class.java:1235)
at java.lang.Class.getEnclosingClass(Class.java:1277)
at java.lang.Class.getSimpleBinaryName(Class.java:1443)
at java.lang.Class.getSimpleName(Class.java:1309)
at org.eclipse.jetty.io.ManagedSelector$SelectorProducer.toString(ManagedSelector.java:534)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.getString(EatWhatYouKill.java:458)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.toStringLocked(EatWhatYouKill.java:447)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.toString(EatWhatYouKill.java:440)
at org.slf4j.helpers.MessageFormatter.safeObjectAppend(MessageFormatter.java:299)
at org.slf4j.helpers.MessageFormatter.deeplyAppendParameter(MessageFormatter.java:271)
at org.slf4j.helpers.MessageFormatter.arrayFormat(MessageFormatter.java:233)
at org.slf4j.helpers.MessageFormatter.arrayFormat(MessageFormatter.java:173)
at org.eclipse.jetty.util.log.JettyAwareLogger.log(JettyAwareLogger.java:680)
at org.eclipse.jetty.util.log.JettyAwareLogger.debug(JettyAwareLogger.java:224)
at org.eclipse.jetty.util.log.Slf4jLog.debug(Slf4jLog.java:97)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:288)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassNotFoundException: Illegal access: this web application instance has been stopped already. Could not load [org.eclipse.jetty.io.ManagedSelector$StopSelector]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1338)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1195)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1157)
... 25 more
Caused by: java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [org.eclipse.jetty.io.ManagedSelector$StopSelector]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1348)
at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1336)
... 27 more
27-Dec-2018 11:24:20.467 INFO [jetty-http#74305db9-65] org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading Illegal access: this web application instance has been stopped already. Could not load [ch.qos.logback.classic.spi.ThrowableProxy]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [ch.qos.logback.classic.spi.ThrowableProxy]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1348)
at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1336)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1195)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1157)
at ch.qos.logback.classic.spi.LoggingEvent.<init>(LoggingEvent.java:119)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:419)
at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:383)
at ch.qos.logback.classic.Logger.log(Logger.java:765)
at org.eclipse.jetty.util.log.JettyAwareLogger.log(JettyAwareLogger.java:668)
at org.eclipse.jetty.util.log.JettyAwareLogger.warn(JettyAwareLogger.java:474)
at org.eclipse.jetty.util.log.Slf4jLog.warn(Slf4jLog.java:73)
at org.eclipse.jetty.util.log.Slf4jLog.warn(Slf4jLog.java:67)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.execute(EatWhatYouKill.java:375)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:305)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "jetty-http#74305db9-65" java.lang.NoClassDefFoundError: ch/qos/logback/classic/spi/ThrowableProxy
at ch.qos.logback.classic.spi.LoggingEvent.<init>(LoggingEvent.java:119)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:419)
at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:383)
at ch.qos.logback.classic.Logger.log(Logger.java:765)
at org.eclipse.jetty.util.log.JettyAwareLogger.log(JettyAwareLogger.java:668)
at org.eclipse.jetty.util.log.JettyAwareLogger.warn(JettyAwareLogger.java:474)
at org.eclipse.jetty.util.log.Slf4jLog.warn(Slf4jLog.java:73)
at org.eclipse.jetty.util.log.Slf4jLog.warn(Slf4jLog.java:67)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:740)
at java.lang.Thread.run(Thread.java:748)
How can I avoid this exception?
Is there any other way we can use to improve the WebClient first request slowness?
I went through the same problem and managed to solve it by changing the connector used by the WebClient.
Below the configuration file with the appropriate imports
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.JettyClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
The class
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.setFollowRedirects(false);
httpClient.setConnectTimeout(TIMEOUT);
httpClient.start();
final ClientHttpConnector connector = new JettyClientHttpConnector(httpClient);
final WebClient webClient = WebClient.builder()
.clientConnector(connector)
.baseUrl(BASE_URL)
.build();
It is important to know how to add the right libs to your project, so below is how I managed to import using gradle:
implementation 'org.eclipse.jetty:jetty-client'
implementation 'org.eclipse.jetty:jetty-reactive-httpclient'
We upgraded Spring Boot to 2.4.2 with reactor-netty 1.0.3 but still encounter this issue.
Here is our upgraded configuration:
#Bean
public WebClient createWebClient(WebClient.Builder webClientBuilder) {
log.info("Initializing WebClient Bean");
final int timeoutInMillis = Long.valueOf(TimeUnit.SECONDS.toMillis(timeout)).intValue();
final HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeoutInMillis)
.responseTimeout(Duration.ofMillis(timeoutInMillis))
.doOnConnected(conn ->
conn.addHandlerLast(new ReadTimeoutHandler(timeoutInMillis, TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler(timeoutInMillis, TimeUnit.MILLISECONDS)));
final ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
final WebClient webClient = webClientBuilder
.clientConnector(connector)
.defaultHeader("x-clientname", clientname)
.build();
httpClient.warmup().block();
log.info("WebClient initialized");
return webClient;
}
Our call with WebClient:
ResoponseObject doCall() {
return this.webClient
.get()
.uri("http://***.de/api/rest/***")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(ResponseObject.class)
.block();
}
With debugging enabled via application.yaml:
logging.level.reactor.netty: debug
We now see this during application startup:
2021-02-10 17:02:31,922 INFO d.t.e.b.c.c.WebClientAutoConfiguration - Initializing WebClient Bean
2021-02-10 17:02:31,959 DEBUG r.n.r.DefaultLoopIOUring - Default io_uring support : false
2021-02-10 17:02:31,967 DEBUG r.n.r.DefaultLoopEpoll - Default Epoll support : true
2021-02-10 17:02:31,997 INFO d.t.e.b.c.c.WebClientAutoConfiguration - WebClient initialized
This should be an indication that the warmup works as expected?
But on first request this happens:
2021-02-10 17:05:16,045 DEBUG o.s.w.r.f.c.ExchangeFunctions - [73d400c8] HTTP GET http://***.de/api/rest/***
2021-02-10 17:05:16,050 DEBUG r.n.r.PooledConnectionProvider - Creating a new [http] client pool [PoolFactory{evictionInterval=PT0S, leasingStrategy=fifo, maxConnections=500, maxIdleTime=-1, maxLifeTime=-1, metricsEnabled=false, pendingAcquireMaxCount=1000, pendingAcquireTimeout=45000}] for [***.de/<unresolved>:80]
2021-02-10 17:05:29,619 DEBUG r.n.r.DefaultPooledConnectionProvider - [id: 0x71b840f4] Created a new pooled channel, now 1 active connections and 0 inactive connections
2021-02-10 17:05:29,635 DEBUG r.n.t.TransportConfig - [id: 0x71b840f4] Initialized pipeline DefaultChannelPipeline{(reactor.left.httpCodec = io.netty.handler.codec.http.HttpClientCodec), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)}
...
In our case, creating the client pool is the problem. On a decent machine, it takes about 13 seconds.
Can you give us any comment on that? This is very this is very frustrating for us.
Thanks a lot!

Resources