View Javadoc

1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.impl.conn;
28  
29  import java.io.IOException;
30  import java.net.ConnectException;
31  import java.net.NoRouteToHostException;
32  import java.net.InetAddress;
33  import java.net.InetSocketAddress;
34  import java.net.Socket;
35  import java.net.SocketTimeoutException;
36  
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  import org.apache.http.HttpHost;
40  import org.apache.http.annotation.Immutable;
41  import org.apache.http.client.protocol.HttpClientContext;
42  import org.apache.http.config.Lookup;
43  import org.apache.http.config.SocketConfig;
44  import org.apache.http.conn.ConnectTimeoutException;
45  import org.apache.http.conn.DnsResolver;
46  import org.apache.http.conn.HttpHostConnectException;
47  import org.apache.http.conn.ManagedHttpClientConnection;
48  import org.apache.http.conn.SchemePortResolver;
49  import org.apache.http.conn.UnsupportedSchemeException;
50  import org.apache.http.conn.socket.ConnectionSocketFactory;
51  import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
52  import org.apache.http.protocol.HttpContext;
53  import org.apache.http.util.Args;
54  
55  @Immutable
56  class HttpClientConnectionOperator {
57  
58      static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry";
59  
60      private final Log log = LogFactory.getLog(getClass());
61  
62      private final Lookup<ConnectionSocketFactory> socketFactoryRegistry;
63      private final SchemePortResolver schemePortResolver;
64      private final DnsResolver dnsResolver;
65  
66      HttpClientConnectionOperator(
67              final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
68              final SchemePortResolver schemePortResolver,
69              final DnsResolver dnsResolver) {
70          super();
71          Args.notNull(socketFactoryRegistry, "Socket factory registry");
72          this.socketFactoryRegistry = socketFactoryRegistry;
73          this.schemePortResolver = schemePortResolver != null ? schemePortResolver :
74              DefaultSchemePortResolver.INSTANCE;
75          this.dnsResolver = dnsResolver != null ? dnsResolver :
76              SystemDefaultDnsResolver.INSTANCE;
77      }
78  
79      @SuppressWarnings("unchecked")
80      private Lookup<ConnectionSocketFactory> getSocketFactoryRegistry(final HttpContext context) {
81          Lookup<ConnectionSocketFactory> reg = (Lookup<ConnectionSocketFactory>) context.getAttribute(
82                  SOCKET_FACTORY_REGISTRY);
83          if (reg == null) {
84              reg = this.socketFactoryRegistry;
85          }
86          return reg;
87      }
88  
89      public void connect(
90              final ManagedHttpClientConnection conn,
91              final HttpHost host,
92              final InetSocketAddress localAddress,
93              final int connectTimeout,
94              final SocketConfig socketConfig,
95              final HttpContext context) throws IOException {
96          final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context);
97          final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
98          if (sf == null) {
99              throw new UnsupportedSchemeException(host.getSchemeName() +
100                     " protocol is not supported");
101         }
102         final InetAddress[] addresses = host.getAddress() != null ?
103                 new InetAddress[] { host.getAddress() } : this.dnsResolver.resolve(host.getHostName());
104         final int port = this.schemePortResolver.resolve(host);
105         for (int i = 0; i < addresses.length; i++) {
106             final InetAddress address = addresses[i];
107             final boolean last = i == addresses.length - 1;
108 
109             Socket sock = sf.createSocket(context);
110             sock.setSoTimeout(socketConfig.getSoTimeout());
111             sock.setReuseAddress(socketConfig.isSoReuseAddress());
112             sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
113             sock.setKeepAlive(socketConfig.isSoKeepAlive());
114             final int linger = socketConfig.getSoLinger();
115             if (linger >= 0) {
116                 sock.setSoLinger(linger > 0, linger);
117             }
118             conn.bind(sock);
119 
120             final InetSocketAddress remoteAddress = new InetSocketAddress(address, port);
121             if (this.log.isDebugEnabled()) {
122                 this.log.debug("Connecting to " + remoteAddress);
123             }
124             try {
125                 sock = sf.connectSocket(
126                         connectTimeout, sock, host, remoteAddress, localAddress, context);
127                 conn.bind(sock);
128                 if (this.log.isDebugEnabled()) {
129                     this.log.debug("Connection established " + conn);
130                 }
131                 return;
132             } catch (final SocketTimeoutException ex) {
133                 if (last) {
134                     throw new ConnectTimeoutException(ex, host, addresses);
135                 }
136             } catch (final ConnectException ex) {
137                 if (last) {
138                     final String msg = ex.getMessage();
139                     if ("Connection timed out".equals(msg)) {
140                         throw new ConnectTimeoutException(ex, host, addresses);
141                     } else {
142                         throw new HttpHostConnectException(ex, host, addresses);
143                     }
144                 }
145             } catch (final NoRouteToHostException ex) {
146                 if (last) {
147                     throw ex;
148                 }
149             }
150             if (this.log.isDebugEnabled()) {
151                 this.log.debug("Connect to " + remoteAddress + " timed out. " +
152                         "Connection will be retried using another IP address");
153             }
154         }
155     }
156 
157     public void upgrade(
158             final ManagedHttpClientConnection conn,
159             final HttpHost host,
160             final HttpContext context) throws IOException {
161         final HttpClientContext clientContext = HttpClientContext.adapt(context);
162         final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(clientContext);
163         final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
164         if (sf == null) {
165             throw new UnsupportedSchemeException(host.getSchemeName() +
166                     " protocol is not supported");
167         }
168         if (!(sf instanceof LayeredConnectionSocketFactory)) {
169             throw new UnsupportedSchemeException(host.getSchemeName() +
170                     " protocol does not support connection upgrade");
171         }
172         final LayeredConnectionSocketFactory lsf = (LayeredConnectionSocketFactory) sf;
173         Socket sock = conn.getSocket();
174         final int port = this.schemePortResolver.resolve(host);
175         sock = lsf.createLayeredSocket(sock, host.getHostName(), port, context);
176         conn.bind(sock);
177     }
178 
179 }