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.InetAddress;
32  import java.net.InetSocketAddress;
33  import java.net.Socket;
34  import java.net.SocketTimeoutException;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.apache.http.HttpHost;
39  import org.apache.http.annotation.Immutable;
40  import org.apache.http.client.protocol.HttpClientContext;
41  import org.apache.http.config.Lookup;
42  import org.apache.http.config.SocketConfig;
43  import org.apache.http.conn.ConnectTimeoutException;
44  import org.apache.http.conn.DnsResolver;
45  import org.apache.http.conn.HttpHostConnectException;
46  import org.apache.http.conn.ManagedHttpClientConnection;
47  import org.apache.http.conn.SchemePortResolver;
48  import org.apache.http.conn.UnsupportedSchemeException;
49  import org.apache.http.conn.socket.ConnectionSocketFactory;
50  import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
51  import org.apache.http.protocol.HttpContext;
52  import org.apache.http.util.Args;
53  
54  @Immutable
55  class HttpClientConnectionOperator {
56  
57      static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry";
58  
59      private final Log log = LogFactory.getLog(getClass());
60  
61      private final Lookup<ConnectionSocketFactory> socketFactoryRegistry;
62      private final SchemePortResolver schemePortResolver;
63      private final DnsResolver dnsResolver;
64  
65      HttpClientConnectionOperator(
66              final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
67              final SchemePortResolver schemePortResolver,
68              final DnsResolver dnsResolver) {
69          super();
70          Args.notNull(socketFactoryRegistry, "Socket factory registry");
71          this.socketFactoryRegistry = socketFactoryRegistry;
72          this.schemePortResolver = schemePortResolver != null ? schemePortResolver :
73              DefaultSchemePortResolver.INSTANCE;
74          this.dnsResolver = dnsResolver != null ? dnsResolver :
75              SystemDefaultDnsResolver.INSTANCE;
76      }
77  
78      @SuppressWarnings("unchecked")
79      private Lookup<ConnectionSocketFactory> getSocketFactoryRegistry(final HttpContext context) {
80          Lookup<ConnectionSocketFactory> reg = (Lookup<ConnectionSocketFactory>) context.getAttribute(
81                  SOCKET_FACTORY_REGISTRY);
82          if (reg == null) {
83              reg = this.socketFactoryRegistry;
84          }
85          return reg;
86      }
87  
88      public void connect(
89              final ManagedHttpClientConnection conn,
90              final HttpHost host,
91              final InetSocketAddress localAddress,
92              final int connectTimeout,
93              final SocketConfig socketConfig,
94              final HttpContext context) throws IOException {
95          final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context);
96          final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
97          if (sf == null) {
98              throw new UnsupportedSchemeException(host.getSchemeName() +
99                      " protocol is not supported");
100         }
101         final InetAddress[] addresses = this.dnsResolver.resolve(host.getHostName());
102         final int port = this.schemePortResolver.resolve(host);
103         for (int i = 0; i < addresses.length; i++) {
104             final InetAddress address = addresses[i];
105             final boolean last = i == addresses.length - 1;
106 
107             Socket sock = sf.createSocket(context);
108             sock.setReuseAddress(socketConfig.isSoReuseAddress());
109             conn.bind(sock);
110 
111             final InetSocketAddress remoteAddress = new InetSocketAddress(address, port);
112             if (this.log.isDebugEnabled()) {
113                 this.log.debug("Connecting to " + remoteAddress);
114             }
115             try {
116                 sock.setSoTimeout(socketConfig.getSoTimeout());
117                 sock = sf.connectSocket(
118                         connectTimeout, sock, host, remoteAddress, localAddress, context);
119                 sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
120                 sock.setKeepAlive(socketConfig.isSoKeepAlive());
121                 final int linger = socketConfig.getSoLinger();
122                 if (linger >= 0) {
123                     sock.setSoLinger(linger > 0, linger);
124                 }
125                 conn.bind(sock);
126                 if (this.log.isDebugEnabled()) {
127                     this.log.debug("Connection established " + conn);
128                 }
129                 return;
130             } catch (final SocketTimeoutException ex) {
131                 if (last) {
132                     throw new ConnectTimeoutException(ex, host, addresses);
133                 }
134             } catch (final ConnectException ex) {
135                 if (last) {
136                     final String msg = ex.getMessage();
137                     if ("Connection timed out".equals(msg)) {
138                         throw new ConnectTimeoutException(ex, host, addresses);
139                     } else {
140                         throw new HttpHostConnectException(ex, host, addresses);
141                     }
142                 }
143             }
144             if (this.log.isDebugEnabled()) {
145                 this.log.debug("Connect to " + remoteAddress + " timed out. " +
146                         "Connection will be retried using another IP address");
147             }
148         }
149     }
150 
151     public void upgrade(
152             final ManagedHttpClientConnection conn,
153             final HttpHost host,
154             final HttpContext context) throws IOException {
155         final HttpClientContext clientContext = HttpClientContext.adapt(context);
156         final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(clientContext);
157         final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
158         if (sf == null) {
159             throw new UnsupportedSchemeException(host.getSchemeName() +
160                     " protocol is not supported");
161         }
162         if (!(sf instanceof LayeredConnectionSocketFactory)) {
163             throw new UnsupportedSchemeException(host.getSchemeName() +
164                     " protocol does not support connection upgrade");
165         }
166         final LayeredConnectionSocketFactory lsf = (LayeredConnectionSocketFactory) sf;
167         Socket sock = conn.getSocket();
168         final int port = this.schemePortResolver.resolve(host);
169         sock = lsf.createLayeredSocket(sock, host.getHostName(), port, context);
170         conn.bind(sock);
171     }
172 
173 }