Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I retry using DefaultHttpRequestRetryHandler(HTTPClient) when SocketException occurs during SSL communication on java 11?

How should I implement it to be able to retry SSL communication using httpclient on java11?

Is it better to override DefaultHttpRequestRetryHandler as shown below?
Is there a more better way?

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

In Java 11, SocketException is not retried , because SocketException is wrapped with SSLException.

I read the following content.

  • https://bugs.openjdk.java.net/browse/JDK-8214339
  • http://hg.openjdk.java.net/jdk-updates/jdk11u/rev/18b4acfaaa97

    if ((cause != null) && (cause instanceof IOException)) {
        ssle = new SSLException(reason);
    } 
    
  • https://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html
    DefaultHttpRequestRetryHandler does not retry when an SSLException occurs.

When using java11, the following error occurs randomly with a probability of less than 0.01%.

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
like image 645
Hiroki Matsumoto Avatar asked Mar 04 '23 22:03

Hiroki Matsumoto


1 Answers

I made HttpRequestRetryHandler class.

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

And it set as HttpClientBuilder.

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

Conclusion

  • The SSL communication quality is the same as when using java8.
  • The underlying problem in the server or network still exists, but the impact of java11 has been resolved.
like image 103
Hiroki Matsumoto Avatar answered May 01 '23 08:05

Hiroki Matsumoto