From aa020a5c0d06627716411f6acbfea8f5c77061c9 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Wed, 6 Feb 2008 21:41:06 +0000 Subject: [PATCH] HTTPCLIENT-643: Automatic connect fail-over for multi-home remote servers git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@619163 13f79535-47bb-0310-9956-ffa450edef68 --- RELEASE_NOTES.txt | 3 + .../apache/http/conn/PlainSocketFactory.java | 7 +- .../http/conn/ssl/SSLSocketFactory.java | 7 +- .../apache/http/conn/util/SocketUtils.java | 118 ++++++++++++++++++ 4 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 module-client/src/main/java/org/apache/http/conn/util/SocketUtils.java diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index 5f7ea816a..3702f8765 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -1,6 +1,9 @@ Changes since 4.0 Alpha 2 ------------------- +* [HTTPCLIENT-643] Automatic connect fail-over for multi-home remote servers. + Contributed by Oleg Kalnichevski + * [HTTPCLIENT-735] unsetting of DEFAULT_PROXY and FORCED_ROUTE in hierarchies Contributed by Roland Weber diff --git a/module-client/src/main/java/org/apache/http/conn/PlainSocketFactory.java b/module-client/src/main/java/org/apache/http/conn/PlainSocketFactory.java index e74c2011d..24d1c7732 100644 --- a/module-client/src/main/java/org/apache/http/conn/PlainSocketFactory.java +++ b/module-client/src/main/java/org/apache/http/conn/PlainSocketFactory.java @@ -36,6 +36,7 @@ import java.net.InetSocketAddress; import java.net.Socket; +import org.apache.http.conn.util.SocketUtils; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; @@ -89,9 +90,6 @@ public Socket connectSocket(Socket sock, String host, int port, throw new IllegalArgumentException("Parameters may not be null."); } - // resolve the target hostname first - final InetSocketAddress target = new InetSocketAddress(host, port); - if (sock == null) sock = createSocket(); @@ -107,7 +105,8 @@ public Socket connectSocket(Socket sock, String host, int port, } int timeout = HttpConnectionParams.getConnectionTimeout(params); - sock.connect(target, timeout); + + SocketUtils.connect(sock, host, port, timeout); return sock; diff --git a/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java b/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java index d46996a40..21cb1d20f 100644 --- a/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java +++ b/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java @@ -32,6 +32,7 @@ package org.apache.http.conn.ssl; import org.apache.http.conn.LayeredSocketFactory; +import org.apache.http.conn.util.SocketUtils; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; @@ -272,9 +273,6 @@ public Socket connectSocket( throw new IllegalArgumentException("Parameters may not be null."); } - // resolve the target hostname first - final InetSocketAddress target = new InetSocketAddress(host, port); - SSLSocket sslock = (SSLSocket) ((sock != null) ? sock : createSocket()); @@ -292,7 +290,8 @@ public Socket connectSocket( int connTimeout = HttpConnectionParams.getConnectionTimeout(params); int soTimeout = HttpConnectionParams.getSoTimeout(params); - sslock.connect(target, connTimeout); + SocketUtils.connect(sock, host, port, connTimeout); + sslock.setSoTimeout(soTimeout); try { hostnameVerifier.verify(host, sslock); diff --git a/module-client/src/main/java/org/apache/http/conn/util/SocketUtils.java b/module-client/src/main/java/org/apache/http/conn/util/SocketUtils.java new file mode 100644 index 000000000..979f27969 --- /dev/null +++ b/module-client/src/main/java/org/apache/http/conn/util/SocketUtils.java @@ -0,0 +1,118 @@ +/* + * $HeadURL:$ + * $Revision:$ + * $Date:$ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.conn.util; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SocketUtils { + + /** + * Attempts to connects the socket to any of the {@link InetAddress}es the + * given host name resolves to. If connection to all addresses fail, the + * last I/O exception is propagated to the caller. + * + * @param sock socket to connect to any of the given addresses + * @param hostname Host name to connect to + * @param port the port to connect to + * @param timeout connection timeout + * + * @throws IOException if an error occurs during the connection + * @throws SocketTimeoutException if timeout expires before connecting + */ + public static void connect( + final Socket sock, + final String hostname, + int port, + int timeout) throws IOException { + + InetAddress[] adrs = InetAddress.getAllByName(hostname); + List list = new ArrayList(adrs.length); + for (InetAddress adr: adrs) { + list.add(adr); + } + Collections.shuffle(list); + connect(sock, list, port, timeout); + } + + /** + * Attempts to connects the socket to any of the {@link InetAddress}es given as a + * parameter, whichever succeeds first. If connection to all addresses fail, the + * last I/O exception is propagated to the caller. + * + * @param sock socket to connect to any of the given addresses + * @param addresses array of addresses + * @param port the port to connect to + * @param timeout connection timeout + * + * @throws IOException if an error occurs during the connection + * @throws SocketTimeoutException if timeout expires before connecting + */ + public static void connect( + final Socket sock, + final List addresses, + int port, + int timeout) throws IOException { + if (sock == null) { + throw new IllegalArgumentException("Socket may not be null"); + } + if (addresses == null) { + throw new IllegalArgumentException("List of addresses may not be null"); + } + if (addresses.isEmpty()) { + throw new IllegalArgumentException("List of addresses may not be empty"); + } + + IOException lastEx = null; + for (InetAddress address: addresses) { + try { + sock.connect(new InetSocketAddress(address, port), timeout); + return; + } catch (SocketTimeoutException ex) { + throw ex; + } catch (IOException ex) { + // keep the last exception and retry + lastEx = ex; + } + } + if (lastEx != null) { + throw lastEx; + } + } + +}