Home | History | Annotate | Download | only in conn
      1 /*
      2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java $
      3  * $Revision: 652193 $
      4  * $Date: 2008-04-29 17:10:36 -0700 (Tue, 29 Apr 2008) $
      5  *
      6  * ====================================================================
      7  * Licensed to the Apache Software Foundation (ASF) under one
      8  * or more contributor license agreements.  See the NOTICE file
      9  * distributed with this work for additional information
     10  * regarding copyright ownership.  The ASF licenses this file
     11  * to you under the Apache License, Version 2.0 (the
     12  * "License"); you may not use this file except in compliance
     13  * with the License.  You may obtain a copy of the License at
     14  *
     15  *   http://www.apache.org/licenses/LICENSE-2.0
     16  *
     17  * Unless required by applicable law or agreed to in writing,
     18  * software distributed under the License is distributed on an
     19  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     20  * KIND, either express or implied.  See the License for the
     21  * specific language governing permissions and limitations
     22  * under the License.
     23  * ====================================================================
     24  *
     25  * This software consists of voluntary contributions made by many
     26  * individuals on behalf of the Apache Software Foundation.  For more
     27  * information on the Apache Software Foundation, please see
     28  * <http://www.apache.org/>.
     29  *
     30  */
     31 
     32 package org.apache.http.impl.conn;
     33 
     34 import java.io.IOException;
     35 import java.net.ConnectException;
     36 import java.net.Socket;
     37 import java.net.InetAddress;
     38 
     39 import java.net.SocketException;
     40 import org.apache.http.HttpHost;
     41 import org.apache.http.params.HttpParams;
     42 import org.apache.http.params.HttpConnectionParams;
     43 import org.apache.http.protocol.HttpContext;
     44 
     45 import org.apache.http.conn.HttpHostConnectException;
     46 import org.apache.http.conn.OperatedClientConnection;
     47 import org.apache.http.conn.ClientConnectionOperator;
     48 import org.apache.http.conn.ConnectTimeoutException;
     49 import org.apache.http.conn.scheme.LayeredSocketFactory;
     50 import org.apache.http.conn.scheme.PlainSocketFactory;
     51 import org.apache.http.conn.scheme.Scheme;
     52 import org.apache.http.conn.scheme.SchemeRegistry;
     53 import org.apache.http.conn.scheme.SocketFactory;
     54 
     55 
     56 /**
     57  * Default implementation of a
     58  * {@link ClientConnectionOperator ClientConnectionOperator}.
     59  * It uses a {@link SchemeRegistry SchemeRegistry} to look up
     60  * {@link SocketFactory SocketFactory} objects.
     61  *
     62  * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
     63  *
     64  *
     65  * <!-- empty lines to avoid svn diff problems -->
     66  * @version   $Revision: 652193 $ $Date: 2008-04-29 17:10:36 -0700 (Tue, 29 Apr 2008) $
     67  *
     68  * @since 4.0
     69  */
     70 public class DefaultClientConnectionOperator
     71     implements ClientConnectionOperator {
     72 
     73     private static final PlainSocketFactory staticPlainSocketFactory = new PlainSocketFactory();
     74 
     75     /** The scheme registry for looking up socket factories. */
     76     protected SchemeRegistry schemeRegistry;
     77 
     78 
     79     /**
     80      * Creates a new client connection operator for the given scheme registry.
     81      *
     82      * @param schemes   the scheme registry
     83      */
     84     public DefaultClientConnectionOperator(SchemeRegistry schemes) {
     85         if (schemes == null) {
     86             throw new IllegalArgumentException
     87                 ("Scheme registry must not be null.");
     88         }
     89         schemeRegistry = schemes;
     90     }
     91 
     92 
     93     // non-javadoc, see interface ClientConnectionOperator
     94     public OperatedClientConnection createConnection() {
     95         return new DefaultClientConnection();
     96     }
     97 
     98 
     99     // non-javadoc, see interface ClientConnectionOperator
    100     public void openConnection(OperatedClientConnection conn,
    101                                HttpHost target,
    102                                InetAddress local,
    103                                HttpContext context,
    104                                HttpParams params)
    105         throws IOException {
    106 
    107         if (conn == null) {
    108             throw new IllegalArgumentException
    109                 ("Connection must not be null.");
    110         }
    111         if (target == null) {
    112             throw new IllegalArgumentException
    113                 ("Target host must not be null.");
    114         }
    115         // local address may be null
    116         //@@@ is context allowed to be null?
    117         if (params == null) {
    118             throw new IllegalArgumentException
    119                 ("Parameters must not be null.");
    120         }
    121         if (conn.isOpen()) {
    122             throw new IllegalArgumentException
    123                 ("Connection must not be open.");
    124         }
    125 
    126         final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
    127         final SocketFactory sf = schm.getSocketFactory();
    128         final SocketFactory plain_sf;
    129         final LayeredSocketFactory layered_sf;
    130         if (sf instanceof LayeredSocketFactory) {
    131             plain_sf = staticPlainSocketFactory;
    132             layered_sf = (LayeredSocketFactory)sf;
    133         } else {
    134             plain_sf = sf;
    135             layered_sf = null;
    136         }
    137         InetAddress[] addresses = InetAddress.getAllByName(target.getHostName());
    138 
    139         for (int i = 0; i < addresses.length; ++i) {
    140             Socket sock = plain_sf.createSocket();
    141             conn.opening(sock, target);
    142 
    143             try {
    144                 Socket connsock = plain_sf.connectSocket(sock,
    145                     addresses[i].getHostAddress(),
    146                     schm.resolvePort(target.getPort()),
    147                     local, 0, params);
    148                 if (sock != connsock) {
    149                     sock = connsock;
    150                     conn.opening(sock, target);
    151                 }
    152                 /*
    153                  * prepareSocket is called on the just connected
    154                  * socket before the creation of the layered socket to
    155                  * ensure that desired socket options such as
    156                  * TCP_NODELAY, SO_RCVTIMEO, SO_LINGER will be set
    157                  * before any I/O is performed on the socket. This
    158                  * happens in the common case as
    159                  * SSLSocketFactory.createSocket performs hostname
    160                  * verification which requires that SSL handshaking be
    161                  * performed.
    162                  */
    163                 prepareSocket(sock, context, params);
    164                 if (layered_sf != null) {
    165                     Socket layeredsock = layered_sf.createSocket(sock,
    166                         target.getHostName(),
    167                         schm.resolvePort(target.getPort()),
    168                         true);
    169                     if (layeredsock != sock) {
    170                         conn.opening(layeredsock, target);
    171                     }
    172                     conn.openCompleted(sf.isSecure(layeredsock), params);
    173                 } else {
    174                     conn.openCompleted(sf.isSecure(sock), params);
    175                 }
    176                 break;
    177             // BEGIN android-changed
    178             //       catch SocketException to cover any kind of connect failure
    179             } catch (SocketException ex) {
    180                 if (i == addresses.length - 1) {
    181                     ConnectException cause = ex instanceof ConnectException
    182                             ? (ConnectException) ex : new ConnectException(ex.getMessage(), ex);
    183                     throw new HttpHostConnectException(target, cause);
    184                 }
    185             // END android-changed
    186             } catch (ConnectTimeoutException ex) {
    187                 if (i == addresses.length - 1) {
    188                     throw ex;
    189                 }
    190             }
    191         }
    192     } // openConnection
    193 
    194 
    195     // non-javadoc, see interface ClientConnectionOperator
    196     public void updateSecureConnection(OperatedClientConnection conn,
    197                                        HttpHost target,
    198                                        HttpContext context,
    199                                        HttpParams params)
    200         throws IOException {
    201 
    202 
    203         if (conn == null) {
    204             throw new IllegalArgumentException
    205                 ("Connection must not be null.");
    206         }
    207         if (target == null) {
    208             throw new IllegalArgumentException
    209                 ("Target host must not be null.");
    210         }
    211         //@@@ is context allowed to be null?
    212         if (params == null) {
    213             throw new IllegalArgumentException
    214                 ("Parameters must not be null.");
    215         }
    216         if (!conn.isOpen()) {
    217             throw new IllegalArgumentException
    218                 ("Connection must be open.");
    219         }
    220 
    221         final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
    222         if (!(schm.getSocketFactory() instanceof LayeredSocketFactory)) {
    223             throw new IllegalArgumentException
    224                 ("Target scheme (" + schm.getName() +
    225                  ") must have layered socket factory.");
    226         }
    227 
    228         final LayeredSocketFactory lsf = (LayeredSocketFactory) schm.getSocketFactory();
    229         final Socket sock;
    230         try {
    231             sock = lsf.createSocket
    232                 (conn.getSocket(), target.getHostName(), schm.resolvePort(target.getPort()), true);
    233         } catch (ConnectException ex) {
    234             throw new HttpHostConnectException(target, ex);
    235         }
    236         prepareSocket(sock, context, params);
    237         conn.update(sock, target, lsf.isSecure(sock), params);
    238         //@@@ error handling: close the layered socket in case of exception?
    239 
    240     } // updateSecureConnection
    241 
    242 
    243     /**
    244      * Performs standard initializations on a newly created socket.
    245      *
    246      * @param sock      the socket to prepare
    247      * @param context   the context for the connection
    248      * @param params    the parameters from which to prepare the socket
    249      *
    250      * @throws IOException      in case of an IO problem
    251      */
    252     protected void prepareSocket(Socket sock, HttpContext context,
    253                                  HttpParams params)
    254         throws IOException {
    255 
    256         // context currently not used, but derived classes may need it
    257         //@@@ is context allowed to be null?
    258 
    259         sock.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params));
    260         sock.setSoTimeout(HttpConnectionParams.getSoTimeout(params));
    261 
    262         int linger = HttpConnectionParams.getLinger(params);
    263         if (linger >= 0) {
    264             sock.setSoLinger(linger > 0, linger);
    265         }
    266 
    267     } // prepareSocket
    268 
    269 
    270 } // class DefaultClientConnectionOperator
    271