From 54900db4653d7f207477e6ee40135b88e9bcf832 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 28 Jul 2023 18:33:52 -0700 Subject: [PATCH] HTTPCLIENT-2288 - Retry logic in DefaultHttpClientConnectionOperator doesn't handle SocketException --- .../DefaultHttpClientConnectionOperator.java | 4 +- .../integration/TestConnectionManagement.java | 4 +- .../TestHttpClientConnectionOperator.java | 41 +++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpClientConnectionOperator.java b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpClientConnectionOperator.java index b84737a89..bd65f9e54 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpClientConnectionOperator.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpClientConnectionOperator.java @@ -30,7 +30,7 @@ import java.io.IOException; import java.net.ConnectException; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.NoRouteToHostException; +import java.net.SocketException; import java.net.Socket; import java.net.SocketTimeoutException; @@ -157,7 +157,7 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection ? new ConnectTimeoutException(ex, host, addresses) : new HttpHostConnectException(ex, host, addresses); } - } catch (final NoRouteToHostException ex) { + } catch (final SocketException ex) { if (last) { throw ex; } diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionManagement.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionManagement.java index 707ca9f2d..9a216295d 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionManagement.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionManagement.java @@ -28,6 +28,7 @@ package org.apache.http.impl.client.integration; import java.io.IOException; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; @@ -446,7 +447,8 @@ public class TestConnectionManagement extends LocalServerTestBase { this.connManager.setMaxTotal(1); - final HttpHost target = start(); + final HttpHost target = new HttpHost( + InetAddress.getByName("localhost"), "localhost", start().getPort(), this.scheme.name()); final HttpRoute route = new HttpRoute(target, null, false); final HttpContext context = new BasicHttpContext(); diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionOperator.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionOperator.java index 42aa33704..106e4c322 100644 --- a/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionOperator.java +++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionOperator.java @@ -28,6 +28,7 @@ package org.apache.http.impl.conn; import java.net.ConnectException; +import java.net.SocketException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; @@ -206,6 +207,46 @@ public class TestHttpClientConnectionOperator { Mockito.verify(conn, Mockito.times(3)).bind(socket); } + @Test + public void testConnectFailoverSocketException() throws Exception { + final HttpContext context = new BasicHttpContext(); + final HttpHost host = new HttpHost("somehost"); + final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0}); + final InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1}); + final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2}); + + Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 }); + Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); + Mockito.when(schemePortResolver.resolve(host)).thenReturn(80); + Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); + Mockito.when(plainSocketFactory.connectSocket( + Mockito.anyInt(), + Mockito.any(), + Mockito.any(), + Mockito.eq(new InetSocketAddress(ip1, 80)), + Mockito.any(), + Mockito.any())).thenThrow(new SocketException()); + Mockito.when(plainSocketFactory.connectSocket( + Mockito.anyInt(), + Mockito.any(), + Mockito.any(), + Mockito.eq(new InetSocketAddress(ip2, 80)), + Mockito.any(), + Mockito.any())).thenReturn(socket); + + final InetSocketAddress localAddress = new InetSocketAddress(local, 0); + connectionOperator.connect(conn, host, localAddress, 1000, SocketConfig.DEFAULT, context); + + Mockito.verify(plainSocketFactory).connectSocket( + 1000, + socket, + host, + new InetSocketAddress(ip2, 80), + localAddress, + context); + Mockito.verify(conn, Mockito.times(3)).bind(socket); + } + @Test public void testConnectExplicitAddress() throws Exception { final HttpContext context = new BasicHttpContext();