michael@0: /*
michael@0: * ====================================================================
michael@0: * Licensed to the Apache Software Foundation (ASF) under one
michael@0: * or more contributor license agreements. See the NOTICE file
michael@0: * distributed with this work for additional information
michael@0: * regarding copyright ownership. The ASF licenses this file
michael@0: * to you under the Apache License, Version 2.0 (the
michael@0: * "License"); you may not use this file except in compliance
michael@0: * with the License. You may obtain a copy of the License at
michael@0: *
michael@0: * http://www.apache.org/licenses/LICENSE-2.0
michael@0: *
michael@0: * Unless required by applicable law or agreed to in writing,
michael@0: * software distributed under the License is distributed on an
michael@0: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
michael@0: * KIND, either express or implied. See the License for the
michael@0: * specific language governing permissions and limitations
michael@0: * under the License.
michael@0: * ====================================================================
michael@0: *
michael@0: * This software consists of voluntary contributions made by many
michael@0: * individuals on behalf of the Apache Software Foundation. For more
michael@0: * information on the Apache Software Foundation, please see
michael@0: * .
michael@0: *
michael@0: */
michael@0:
michael@0: package ch.boye.httpclientandroidlib.conn;
michael@0:
michael@0: import java.io.IOException;
michael@0: import java.net.InetAddress;
michael@0: import java.net.InetSocketAddress;
michael@0: import java.net.Socket;
michael@0: import java.net.SocketTimeoutException;
michael@0: import java.util.ArrayList;
michael@0: import java.util.Collections;
michael@0: import java.util.List;
michael@0: import java.util.Arrays;
michael@0:
michael@0: import ch.boye.httpclientandroidlib.annotation.Immutable;
michael@0:
michael@0: import ch.boye.httpclientandroidlib.conn.scheme.SchemeSocketFactory;
michael@0: import ch.boye.httpclientandroidlib.conn.scheme.SocketFactory;
michael@0: import ch.boye.httpclientandroidlib.params.HttpConnectionParams;
michael@0: import ch.boye.httpclientandroidlib.params.HttpParams;
michael@0:
michael@0: /**
michael@0: * Socket factory that implements a simple multi-home fail-over on connect failure,
michael@0: * provided the same hostname resolves to multiple {@link InetAddress}es. Please note
michael@0: * the {@link #connectSocket(Socket, String, int, InetAddress, int, HttpParams)}
michael@0: * method cannot be reliably interrupted by closing the socket returned by the
michael@0: * {@link #createSocket()} method.
michael@0: *
michael@0: * @since 4.0
michael@0: *
michael@0: * @deprecated Do not use. For multihome support socket factories must implement
michael@0: * {@link SchemeSocketFactory} interface.
michael@0: */
michael@0: @Deprecated
michael@0: @Immutable
michael@0: public final class MultihomePlainSocketFactory implements SocketFactory {
michael@0:
michael@0: /**
michael@0: * The factory singleton.
michael@0: */
michael@0: private static final
michael@0: MultihomePlainSocketFactory DEFAULT_FACTORY = new MultihomePlainSocketFactory();
michael@0:
michael@0: /**
michael@0: * Gets the singleton instance of this class.
michael@0: * @return the one and only plain socket factory
michael@0: */
michael@0: public static MultihomePlainSocketFactory getSocketFactory() {
michael@0: return DEFAULT_FACTORY;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Restricted default constructor.
michael@0: */
michael@0: private MultihomePlainSocketFactory() {
michael@0: super();
michael@0: }
michael@0:
michael@0:
michael@0: // non-javadoc, see interface ch.boye.httpclientandroidlib.conn.SocketFactory
michael@0: public Socket createSocket() {
michael@0: return new Socket();
michael@0: }
michael@0:
michael@0: /**
michael@0: * Attempts to connects the socket to any of the {@link InetAddress}es the
michael@0: * given host name resolves to. If connection to all addresses fail, the
michael@0: * last I/O exception is propagated to the caller.
michael@0: *
michael@0: * @param sock socket to connect to any of the given addresses
michael@0: * @param host Host name to connect to
michael@0: * @param port the port to connect to
michael@0: * @param localAddress local address
michael@0: * @param localPort local port
michael@0: * @param params HTTP parameters
michael@0: *
michael@0: * @throws IOException if an error occurs during the connection
michael@0: * @throws SocketTimeoutException if timeout expires before connecting
michael@0: */
michael@0: public Socket connectSocket(Socket sock, String host, int port,
michael@0: InetAddress localAddress, int localPort,
michael@0: HttpParams params)
michael@0: throws IOException {
michael@0:
michael@0: if (host == null) {
michael@0: throw new IllegalArgumentException("Target host may not be null.");
michael@0: }
michael@0: if (params == null) {
michael@0: throw new IllegalArgumentException("Parameters may not be null.");
michael@0: }
michael@0:
michael@0: if (sock == null)
michael@0: sock = createSocket();
michael@0:
michael@0: if ((localAddress != null) || (localPort > 0)) {
michael@0:
michael@0: // we need to bind explicitly
michael@0: if (localPort < 0)
michael@0: localPort = 0; // indicates "any"
michael@0:
michael@0: InetSocketAddress isa =
michael@0: new InetSocketAddress(localAddress, localPort);
michael@0: sock.bind(isa);
michael@0: }
michael@0:
michael@0: int timeout = HttpConnectionParams.getConnectionTimeout(params);
michael@0:
michael@0: InetAddress[] inetadrs = InetAddress.getAllByName(host);
michael@0: List addresses = new ArrayList(inetadrs.length);
michael@0: addresses.addAll(Arrays.asList(inetadrs));
michael@0: Collections.shuffle(addresses);
michael@0:
michael@0: IOException lastEx = null;
michael@0: for (InetAddress remoteAddress: addresses) {
michael@0: try {
michael@0: sock.connect(new InetSocketAddress(remoteAddress, port), timeout);
michael@0: break;
michael@0: } catch (SocketTimeoutException ex) {
michael@0: throw new ConnectTimeoutException("Connect to " + remoteAddress + " timed out");
michael@0: } catch (IOException ex) {
michael@0: // create new socket
michael@0: sock = new Socket();
michael@0: // keep the last exception and retry
michael@0: lastEx = ex;
michael@0: }
michael@0: }
michael@0: if (lastEx != null) {
michael@0: throw lastEx;
michael@0: }
michael@0: return sock;
michael@0: } // connectSocket
michael@0:
michael@0:
michael@0: /**
michael@0: * Checks whether a socket connection is secure.
michael@0: * This factory creates plain socket connections
michael@0: * which are not considered secure.
michael@0: *
michael@0: * @param sock the connected socket
michael@0: *
michael@0: * @return false
michael@0: *
michael@0: * @throws IllegalArgumentException if the argument is invalid
michael@0: */
michael@0: public final boolean isSecure(Socket sock)
michael@0: throws IllegalArgumentException {
michael@0:
michael@0: if (sock == null) {
michael@0: throw new IllegalArgumentException("Socket may not be null.");
michael@0: }
michael@0: // This class check assumes that createSocket() calls the constructor
michael@0: // directly. If it was using javax.net.SocketFactory, we couldn't make
michael@0: // an assumption about the socket class here.
michael@0: if (sock.getClass() != Socket.class) {
michael@0: throw new IllegalArgumentException
michael@0: ("Socket not created by this factory.");
michael@0: }
michael@0: // This check is performed last since it calls a method implemented
michael@0: // by the argument object. getClass() is final in java.lang.Object.
michael@0: if (sock.isClosed()) {
michael@0: throw new IllegalArgumentException("Socket is closed.");
michael@0: }
michael@0:
michael@0: return false;
michael@0:
michael@0: } // isSecure
michael@0:
michael@0: }