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 - azure-blob-storage

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

Related

SSL Exception Tag mismatch error, with spring webflux webclient requests

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));
}

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 #Transactional with coroutines in webflux is throwing an error

When adding #Transactional annotation to a service suspend function which is being called by the handler I get the following error. If I leave it without the annotation then the code works as expected but in case of error it cannot roll back.
either is coming from arrow-kt-core
asHandlerFunction is used as a brigde to be able to document the APIs.
Any idea what happens?
Entities and repositories are placed under io.x.a. The service is inside io.x. The repository scans only io.x.a
Error:
java.lang.IllegalArgumentException: object is not an instance of declaring class
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP POST "/api/assets/130473/one-second-resolution" [ExceptionHandlingWebHandler]
Stack trace:
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at kotlin.reflect.jvm.internal.calls.InlineClassAwareCaller.call(InlineClassAwareCaller.kt:134)
at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108)
at kotlin.reflect.full.KCallables.callSuspend(KCallables.kt:55)
at org.springframework.core.CoroutinesUtils$invokeSuspendingFunction$mono$1.invokeSuspend(CoroutinesUtils.kt:64)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:377)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
at kotlinx.coroutines.reactor.MonoKt.monoInternal$lambda-2(Mono.kt:90)
at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:57)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoUsingWhen.subscribe(MonoUsingWhen.java:87)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:73)
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:127)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:249)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:249)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:249)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:284)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:187)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:232)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:203)
at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onComplete(MonoPeekTerminal.java:299)
at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:88)
at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:88)
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:2057)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:249)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:284)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:187)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:232)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:203)
at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onComplete(MonoPeekTerminal.java:299)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:209)
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:259)
at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:88)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
at reactor.core.publisher.MonoCompletionStage.lambda$subscribe$0(MonoCompletionStage.java:82)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:837)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073)
at com.github.jasync.sql.db.util.FutureUtilsKt.success(FutureUtils.kt:16)
at com.github.jasync.sql.db.mysql.MySQLConnection$succeedQueryPromise$1.accept(MySQLConnection.kt:344)
at com.github.jasync.sql.db.mysql.MySQLConnection$succeedQueryPromise$1.accept(MySQLConnection.kt:54)
at java.base/java.util.Optional.ifPresent(Optional.java:183)
at com.github.jasync.sql.db.mysql.MySQLConnection.succeedQueryPromise(MySQLConnection.kt:343)
at com.github.jasync.sql.db.mysql.MySQLConnection.onOk(MySQLConnection.kt:218)
at com.github.jasync.sql.db.mysql.codec.MySQLConnectionHandler.channelRead0(MySQLConnectionHandler.kt:119)
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)
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.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
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.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at java.base/java.lang.Thread.run(Thread.java:829)
Router:
#Bean
fun router(...): RouterFunction<ServerResponse> {
return route()
.POST(
"...",
asHandlerFunction { createNewOneSecondResolutionSession(it) })
.build()
}
private fun asHandlerFunction(init: suspend (ServerRequest) -> ServerResponse) = HandlerFunction {
mono(Dispatchers.Unconfined) {
init(it)
}
}
Handler:
private suspend fun theFun(req: ServerRequest): ServerResponse {
val a = ...
val b = ...
return service.theFun(a, b).fold(
{ error ->
internalServerErrorResponse("Client user already exists.")
},
{ ServerResponse.ok().bodyValueAndAwait(it) }
)
}
Service:
#Transactional("tm1")
suspend fun theFun(
a: A,
b: B
): Either<Error, Result> = either {
val user= userService.createNewUser(username = "test", password = "pw")
.mapLeft {
log
Error
}
.bind()
throw RuntimeException("xx")
}
Persistence Configuration:
#Configuration
#EnableR2dbcRepositories(
basePackages = ["io.x.a"],
entityOperationsRef = "operations1"
)
class PersistenceConfig1(
#Value("\${spring.datasource.d1.r2dbcUrl}") private val r2dbcUrl: String
) {
#Bean
#Qualifier("d1")
fun connectionFactory(): ConnectionFactory {
return ConnectionFactories.get(ConnectionFactoryOptions.parse(r2dbcUrl))
}
#Bean
fun r2dbcEntityOperations(#Qualifier("d1") connectionFactory: ConnectionFactory): R2dbcEntityOperations {
val databaseClient = DatabaseClient.create(connectionFactory)
return R2dbcEntityTemplate(databaseClient, DefaultReactiveDataAccessStrategy(MySqlDialect.INSTANCE));
}
#Bean("tm1")
fun transactionManager(#Qualifier("d1") connectionFactory: ConnectionFactory): ReactiveTransactionManager {
return R2dbcTransactionManager(connectionFactory)
}
}
I found a solution using TransactionalOperator.
#Bean("transactionalOperator1")
fun transactionalOperator1(#Qualifier("tm1") transactionManager: ReactiveTransactionManager): TransactionalOperator {
return TransactionalOperator.create(transactionManager)
}
#Bean("transactionalOperator2")
fun transactionalOperator2(#Qualifier("tm2") transactionManager: ReactiveTransactionManager): TransactionalOperator {
return TransactionalOperator.create(transactionManager)
}
#Qualifier("transactionalOperator1") private val transactionalOperator1: TransactionalOperator,
#Qualifier("transactionalOperator2") private val transactionalOperator2: TransactionalOperator,
suspend fun theFun(...): Either<Error, Result> =
transactionalOperator1.executeAndAwait { t1->
transactionalOperator2.executeAndAwait { t2->
...
.handleErrorWith {
t1.setRollbackOnly()
t2.setRollbackOnly()
}
}
}!!
At the end of executeAndAwait it commits the code. If the rollbackOnly flag is set then it will instruct the ReactiveTransactionManager to roll back the changes.

HttpMessageNotWritableException when returning a Mono for Content-Type 'multipart/form-data in Webflux

I am trying to return a Mono in an API which is used to upload a file.
This is the signature of the method.
#PatchMapping(value = {"/uploadDoc},
consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}
produces = {MediaType.APPLICATION_STREAM_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE})
Mono<MyObject> uploadDocument(#PathVariable("id") String id, #RequestPart("file") FilePart filePart);
When I return Mono<Void> or Mono<String> it works fine. But, when I return a stream of my custom object Mono<MyObject>, I'm getting the below error.
org.springframework.http.converter.HttpMessageNotWritableException: No Encoder for [com.MyObject] with preset Content-Type 'multipart/form-data;boundary=--------------------------127221087502764453476151'
at org.springframework.web.reactive.result.method.annotation.AbstractMessageWriterResultHandler.writeBody(AbstractMessageWriterResultHandler.java:172)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
The rest of the stack trace.
Stack trace:
at org.springframework.web.reactive.result.method.annotation.AbstractMessageWriterResultHandler.writeBody(AbstractMessageWriterResultHandler.java:172)
at org.springframework.web.reactive.result.method.annotation.AbstractMessageWriterResultHandler.writeBody(AbstractMessageWriterResultHandler.java:107)
at org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler.handleResult(ResponseBodyResultHandler.java:86)
at org.springframework.web.reactive.DispatcherHandler.handleResult(DispatcherHandler.java:169)
at org.springframework.web.reactive.DispatcherHandler.lambda$handle$2(DispatcherHandler.java:147)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:118)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1637)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:241)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:73)
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:203)
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:203)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1637)
at reactor.core.publisher.MonoIgnoreThen$ThenAcceptInner.onNext(MonoIgnoreThen.java:296)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1637)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:144)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1637)
Am I missing something here? Why can't I return an object while uploading a FilePart?

Springboot 2.3.1.RELEASE and Cassandra connectivity issue

I am getting authentication exception while connecting to CASSANDRA using latest springboot version.
It was working with order version . In newer version of data-cassandra a lot of changes has been introduced . Please find the below configurations I am using .
Springboot version : 2.3.1.RELEASE
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-cassandra</artifactId>
</dependency>
Cassandra Config:
#Configuration
#EnableCassandraRepositories
public class CassandraConfig extends AbstractCassandraConfiguration {
#Value("${spring.data.cassandra.contact-points}")
private String contactPoints;
#Value("${spring.data.cassandra.port}")
private int port;
#Value("${spring.data.cassandra.keyspace-name}")
private String keySpace;
#Value("${spring.data.cassandra.username}")
private String username;
#Value("${spring.data.cassandra.password}")
private String password;
#Override
protected String getContactPoints() {
return contactPoints;
}
#Override
protected int getPort() {
return port;
}
#Override
protected CqlSession getRequiredSession() {
// TODO Auto-generated method stub
List<InetSocketAddress> hostList = new ArrayList<>();
Stream.of(contactPoints.split(",")).collect(Collectors.toList()).forEach(host->
hostList.add(new InetSocketAddress(host, port))
);
return CqlSession.builder()
.addContactPoints(hostList)
.withAuthCredentials(username, password)
.withKeyspace(keySpace)
.withLocalDatacenter("local")
.build();
}
#Override
protected String getKeyspaceName() {
return keySpace;
}
}
Getting the below error at startup
requires authenti cation (org.apache.cassandra.auth.PasswordAuthenticator), but no authenticator configured at com.datastax.oss.driver.internal.core.channel.ProtocolInitHandler$InitRequest.lambda$buildAuthenticator$5(ProtocolInitHandler.java:354) at java.util.Optional.orElseThrow(Optional.java:290) at com.datastax.oss.driver.internal.core.channel.ProtocolInitHandler$InitRequest.buildAuthenticator(ProtocolInitHandler.java:350) at com.datastax.oss.driver.internal.core.channel.ProtocolInitHandler$InitRequest.onResponse(ProtocolInitHandler.java:204) at com.datastax.oss.driver.internal.core.channel.ChannelHandlerRequest.onResponse(ChannelHandlerRequest.java:94) at com.datastax.oss.driver.internal.core.channel.InFlightHandler.channelRead(InFlightHandler.java:255) 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.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296) 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.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748)
It was working perfectly with Springboot version 2.2.4.RELEASE version
You are probably hitting this issue, although the issue is marked solved in 2.3.1. Try maybe with 2.4.0, or try the following workaround:
#Bean
CqlSessionBuilderCustomizer authCustomizer(CassandraProperties properties) {
return (builder) -> builder.withAuthCredentials(
properties.getUsername(), properties.getPassword());
}
Finally I remove all the custom configuration class and kept only the below configuration in application.yml and it worked .
spring:
profiles: dev
data:
cassandra:
keyspace-name:
contact-points:
port:
username:
password:
localDatacenter:
My repo looks like below :
#Repository
public interface MyRepo extends CassandraRepository<MyEntity,String>

Resources