1

We are implementing a scheduled DB update using an API from quantum.

https://www.quantumaxis.com.br/apibasic.

This has been working with the Unirest toolkit:

try {
    response = Unirest
        .get(url)
        .header("Authorization", "Basic <maskedValue>"
        .header("cache-control", "no-cache")
        .socketTimeout(500000)
        .connectTimeout(500000)
        .proxy(proxy, Integer.valueOf(proxyPort))
        .asObject(QuantumDTO.class);
} catch (Exception e) {
    LOGGER.info(e.getMessage());
}

We had different problems to connect with the API, including firewall and proxy rules from our own domain. Before these rules were correctly adjusted, we used to get SLL Handshake Exception from the API.

Since it was fixed, it has been working well. However, what happens now is that for requests that the API response body is short, i.e. a response below 30 seconds, the response successfully arrives and integration is done correctly.

However, for requests that receive large bodies, which would take more than 30 seconds, the SLL Connection is being reset, returning javax.net.ssl.SSLException: Connection reset. What is most curious is that if I remove the proxy from request configuration, it works, even if the response takes more than 30 seconds to arrive.

Reasonably you could ask: why use the proxy then? I need to use it, because this application is being deployed in container on an OpenShift platform from which requests cannot be made for external domains without the proxy configuration.

Is there something I'm missing? Would there be any reasonable explanation for the either the client or the server to forcibly close the connection when it passes through the proxy server, and otherwise not?

EDIT: I'm in the company I work in today, and I got the logs using Java -debug=ALL argument. The error appears as follows. One answer that I would appreciate a lot to have is... according to these logs, who is reseting the connection? My application (client) or the peer (server)?

kong.unirest.UnirestException: javax.net.ssl.SSLException: Connection reset
    at kong.unirest.DefaultInterceptor.onFail(DefaultInterceptor.java:43)
    at kong.unirest.CompoundInterceptor.lambda$onFail$2(CompoundInterceptor.java:54)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
    at java.base/java.util.Collections$2.tryAdvance(Collections.java:4747)
    at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
    at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:543)
    at kong.unirest.CompoundInterceptor.onFail(CompoundInterceptor.java:56)
    at kong.unirest.apache.ApacheClient.request(ApacheClient.java:138)
    at kong.unirest.Client.request(Client.java:57)
    at kong.unirest.BaseRequest.request(BaseRequest.java:359)
    at kong.unirest.BaseRequest.asObject(BaseRequest.java:260)
    at br.com.santander.oq.service.impl.QuantumServiceImpl.getQuantumData(QuantumServiceImpl.java:102)
    at br.com.santander.oq.service.impl.QuantumServiceImpl.getQuantumFunds(QuantumServiceImpl.java:49)
    at br.com.santander.oq.scheduled.QuantumSchedule.updateQuantumFunds(QuantumSchedule.java:61)
    at br.com.santander.oq.scheduled.QuantumSchedule$$FastClassBySpringCGLIB$$993fc4e.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
    at org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAspect.traceBackgroundThread(TraceSchedulingAspect.java:76)
    at jdk.internal.reflect.GeneratedMethodAccessor157.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
    at br.com.santander.oq.scheduled.QuantumSchedule$$EnhancerBySpringCGLIB$$f085cec3.updateQuantumFunds(<generated>)
    at br.com.santander.oq.controller.QuantumController.getFunds(QuantumController.java:38)
    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 org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:665)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at brave.servlet.TracingFilter.doFilter(TracingFilter.java:68)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:209)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at brave.servlet.TracingFilter.doFilter(TracingFilter.java:87)
    at org.springframework.cloud.sleuth.instrument.web.LazyTracingFilter.doFilter(TraceWebServletAutoConfiguration.java:139)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: javax.net.ssl.SSLException: Connection reset
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:127)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:320)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:263)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:258)
    at java.base/sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1314)
    at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:839)
    at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137)
    at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:153)
    at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:280)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138)
    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:186)
    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:118)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
    at kong.unirest.apache.ApacheClient.request(ApacheClient.java:129)
    ... 98 more
    Suppressed: java.net.SocketException: Connection reset by peer: socket write error
        at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
        at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
        at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeAlert(SSLSocketOutputRecord.java:81)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:351)
        ... 120 more
Caused by: java.net.SocketException: Connection reset
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
    at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:448)
    at java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:68)
    at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1104)
    at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:823)
    ... 116 more
LeoAlmDiniz
  • 75
  • 12

2 Answers2

1

According to the logs the connection is being reset by your peer:

Suppressed: java.net.SocketException: Connection reset by peer: socket write error
        at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
        at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
        at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeAlert(SSLSocketOutputRecord.java:81)
        at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:351)
        ... 120 more

And the peer in this case could be the proxy you are using: as you explained it certainly makes a difference when using the proxy or not.

I don't fully understand your setup and I am not used to work with OpenShift but please, consider read this related SO question, it could be of help: the 30 seconds you indicated is the default timeout for the ingress router and may be causing the error you are facing for long running requests.

As suggested, if necessary, try increasing this default timeout to a more suitable value:

oc annotate route <route_name> --overwrite 
  haproxy.router.openshift.io/timeout=<timeout><time_unit>
jccampanero
  • 50,989
  • 3
  • 20
  • 49
  • Thank you very much for you answer. I'll surely devote some attention to the content you've shared here. Also, today we had a meeting which included we developers of the API, our perimeter analysts (one dedicated to the firewall settings and another one dedicated with proxy issues), and network analysts from Quantum. The problem is still not solved, but we've advanced. As soon as it be solved, I will post a complete answer here describing how we proceeded, so maybe this report helps someone in the future. – LeoAlmDiniz Dec 16 '22 at 14:11
  • You are welcome @LeoAlmDiniz. On the contrary, thank you very much for the feedback you provided: I'm glad to know that you made progress in solving the problem. I hope it will be fully resolved soon. – jccampanero Dec 16 '22 at 22:38
0

We finally solved it!

I'll detail it, so maybe it helps someone in the future.

Okay, so we could solve the problem by the following approach:

  1. We reunited in a call with all the team of analysts involved: me and a colleague who are developers of the client API, 2 perimeter analysts from our company, which could clarify the firewall/proxy server policies of our company, 1 perimeter analyst of the destiny API (quantum) and 2 developers of the destiny API. I mentioned this in the answer I gave to @jccampanero

p.s.: I really thank @jcccampanero for his answer. It's always refreshing to at least know there are people willing to help. Indeed, there was no problem with our configurations in the OpenShift platform, nor could there be, because -- and maybe I forgot to mention -- the requests were being made from our API not from external stimulae, but by routines scheduled using Spring Boot functionalities. So there would be no reason for the OpenShift security policies to interfere.

  1. In the meeting, we could test all together the requests, track these requests with the proper tools each team had. With that, we could be sure the problem was indeed in our company proxy server behavior. To sum up the situation as in the day of the call: -> Firewall/Proxy access rules were correct. Every request was able to exit our application, reach the proxy server, be directed to the reverse proxy of the destiny API and then comunicate as desired; -> However, connections which the requests took more than 30 seconds to complete were being reset BY THE PROXY SERVER; -> However, our perimeter analysts did a full check in the proxy server configurations and showed us that there was NO RULE to timeout longer connections, nor any bandwith limitation.

As for why the connections were being reset without any configuration specifing the proxy server to behave that way, the analysts could not respond. They said the only thing they could do was to contact the vendor Checkpoint.

However, luckily, our company infrastructure was already planning to migrate the proxy server of our applications for a new product, ProxyWSA from Cisco. Because of the issue with the old proxy, the perimeter analysts allowed us to migrate our application sooner than others.

We then altered our application to work sending the request through the new ProxyWSA. Firewall/Proxy server rules were adjusted to allow conectivity and destiny IP to be reach. Finally, with the new proxy server from Cisco, shorter and longer requests could be completed sucessfully, and now our application is able to schedule requests without any limitations.

Long story short: Checkpoint was a bad vendor. Cisco was a good vendor.

LeoAlmDiniz
  • 75
  • 12