Consideration and coping with the fact that SSL communication errors are more likely to occur from Java 11

2020/03/13 postscript

I'm still quite happy with it, so I'll add the support status.

https://github.com/apache/httpcomponents-client/pull/178 It is supported above. https://github.com/apache/httpcomponents-client/blob/master/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryStrategy.java#L123-L135

If you use 5.0, it seems to be a solution. Only the RC version is available yet.

Introduction

At the time of Java8 + Spring Boot 1.5.X, communication was successful at 99.9999999% or more, but since Java11 + Spring Boot 2.1.X, the incidence of communication errors has increased by about 0.002% ... ?? I thought, I looked it up.

Maybe it's wrong, so I'm really happy if you can point out various things. That helps a lot.

Error that occurs

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:321)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:259)
    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:83)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
    at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:87)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:735)
    ... 81 common frames omitted
    Suppressed: java.net.SocketException: Broken pipe (Write failed)
        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:352)
        ... 106 common frames omitted
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)
    ... 102 common frames omitted

What bothered me.

--Only certain LBs (GCPs) are more prone to this error. (Others are stable and no error occurs)
So
https://stackoverflow.com/questions/49624532/java-net-socketexception-connection-reset-on-gcp
I doubted at first. But with java8, there is no error. After all Java 11 related ...? --The Connection reset system is stable and cannot be answered by stackoverflow. Basically, it's a network problem ... --Since Java 11, SSL communication has changed in various ways. --In terms of probability of occurrence, it is difficult to debug the javax.net.debug system property. Since it is operated by GKE, it is difficult in terms of disk volume. --Even if you try to do tcpdump with node, the probability of occurrence is still painful.

The answer you arrived at

When I was checking on the desk and checking for bugs in JDK, Spring Boot, and HttpClient, I suddenly noticed something.

https://bugs.openjdk.java.net/browse/JDK-8214339

is. The error returned has changed ...? Certainly SSLException. It's not an error like handshake, but SSLException. For SSLException, DefaultHttpRequestRetryHandler # INSTANCE of httpclient (/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.html#INSTANCE) does not retry communication.

I see. I wonder if it seemed that there were more errors because they weren't retried. I have arrived now. Sure, when you get this error, you get the error right after you try to start the connection.

It looks like it's backported, but it looks like it's still returning an SSLException.

http://hg.openjdk.java.net/jdk/jdk12/rev/9041178a0b69

As far as I can see, it looks like the following ... I mean, as long as I call ʻAlert # createSSLException`, I can't return anything other than SSLException.

+        if ((cause != null) && (cause instanceof IOException)) {
+            ssle = new SSLException(reason);
+        } 

Correspondence

Override the retryRequest of DefaultHttpRequestRetryHandler as shown below

HttpRequestRetryHandler.java


  public class HttpRequestRetryHandler extends DefaultHttpRequestRetryHandler {

    @Override
    public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
      IOException cause = exception;
      if (exception instanceof SSLException) {
        if (exception.getCause() != null && exception.getCause() instanceof IOException) {
          cause = (IOException) exception.getCause();
        }
      }
      return super.retryRequest(cause, executionCount, context);
    }
  }

I wonder if I should set it as follows.

CloseableHttpClient httpclient = HttpClients.custom()
        .setRetryHandler(new HttpRequestRetryHandler())
        .build();

But is there a better way? I'm asking a question on stackoverflow.

https://stackoverflow.com/questions/56306216/how-can-i-retry-using-defaulthttprequestretryhandlerhttpclient-when-socketexce

However, as I wrote above, it's hard to get an answer about Connection reset, and I don't know if I can speak English in the first place, so I'm wondering if I can get any opinions.

reference

Error for java8

Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://xxxxxxxxx": Connection reset; nested exception is java.net.SocketException: Connection reset
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:674)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:636)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:598)
	... 54 common frames omitted
Caused by: java.net.SocketException: Connection reset
	at java.net.SocketInputStream.read(SocketInputStream.java:210)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
	at sun.security.ssl.InputRecord.read(InputRecord.java:503)
	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:975)
	at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:933)
	at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
	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:282)
	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:165)
	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:111)
	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:56)
	at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:89)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:660)
	... 58 common frames omitted

Recommended Posts

Consideration and coping with the fact that SSL communication errors are more likely to occur from Java 11
10 barrages of drawing with ● or ■ that are likely to appear in training (Java)
[Ruby] 5 errors that tend to occur when scraping with Selenium and how to deal with them
Features that are likely to enter Java 10 for now
Find the address class and address type from the IP address with Java
I want to return to the previous screen with kotlin and java!
Handles various errors (exceptions) that occur during HTTP communication with RestTemplate
From fledgling Java (3 years) to Node.js (4 years). And the impression of returning to Java
[Java] How to convert from String to Path type and get the path
The road from JavaScript to Java
Find the address class and address type from the IP address with Java [No. 2 decoction]
Update JAVA to the latest version to 1.8.0_144 (when downloading from the web and updating)
Launching the production environment with docker + rails (ver5.2) and errors that occurred
I translated the grammar of R and Java [Updated from time to time]
I tried to make a program that searches for the target class from the process that is overloaded with Java