1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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.NoRouteToHostException;
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.Contract;
41 import org.apache.http.annotation.ThreadingBehavior;
42 import org.apache.http.client.protocol.HttpClientContext;
43 import org.apache.http.config.Lookup;
44 import org.apache.http.config.SocketConfig;
45 import org.apache.http.conn.ConnectTimeoutException;
46 import org.apache.http.conn.DnsResolver;
47 import org.apache.http.conn.HttpClientConnectionOperator;
48 import org.apache.http.conn.HttpHostConnectException;
49 import org.apache.http.conn.ManagedHttpClientConnection;
50 import org.apache.http.conn.SchemePortResolver;
51 import org.apache.http.conn.UnsupportedSchemeException;
52 import org.apache.http.conn.socket.ConnectionSocketFactory;
53 import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
54 import org.apache.http.protocol.HttpContext;
55 import org.apache.http.util.Args;
56
57
58
59
60
61
62
63
64 @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
65 public class DefaultHttpClientConnectionOperator implements HttpClientConnectionOperator {
66
67 static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry";
68
69 private final Log log = LogFactory.getLog(getClass());
70
71 private final Lookup<ConnectionSocketFactory> socketFactoryRegistry;
72 private final SchemePortResolver schemePortResolver;
73 private final DnsResolver dnsResolver;
74
75 public DefaultHttpClientConnectionOperator(
76 final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
77 final SchemePortResolver schemePortResolver,
78 final DnsResolver dnsResolver) {
79 super();
80 Args.notNull(socketFactoryRegistry, "Socket factory registry");
81 this.socketFactoryRegistry = socketFactoryRegistry;
82 this.schemePortResolver = schemePortResolver != null ? schemePortResolver :
83 DefaultSchemePortResolver.INSTANCE;
84 this.dnsResolver = dnsResolver != null ? dnsResolver :
85 SystemDefaultDnsResolver.INSTANCE;
86 }
87
88 @SuppressWarnings("unchecked")
89 private Lookup<ConnectionSocketFactory> getSocketFactoryRegistry(final HttpContext context) {
90 Lookup<ConnectionSocketFactory> reg = (Lookup<ConnectionSocketFactory>) context.getAttribute(
91 SOCKET_FACTORY_REGISTRY);
92 if (reg == null) {
93 reg = this.socketFactoryRegistry;
94 }
95 return reg;
96 }
97
98 @Override
99 public void connect(
100 final ManagedHttpClientConnection conn,
101 final HttpHost host,
102 final InetSocketAddress localAddress,
103 final int connectTimeout,
104 final SocketConfig socketConfig,
105 final HttpContext context) throws IOException {
106 final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context);
107 final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
108 if (sf == null) {
109 throw new UnsupportedSchemeException(host.getSchemeName() +
110 " protocol is not supported");
111 }
112 final InetAddress[] addresses = host.getAddress() != null ?
113 new InetAddress[] { host.getAddress() } : this.dnsResolver.resolve(host.getHostName());
114 final int port = this.schemePortResolver.resolve(host);
115 for (int i = 0; i < addresses.length; i++) {
116 final InetAddress address = addresses[i];
117 final boolean last = i == addresses.length - 1;
118
119 Socket sock = sf.createSocket(context);
120 sock.setSoTimeout(socketConfig.getSoTimeout());
121 sock.setReuseAddress(socketConfig.isSoReuseAddress());
122 sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
123 sock.setKeepAlive(socketConfig.isSoKeepAlive());
124 if (socketConfig.getRcvBufSize() > 0) {
125 sock.setReceiveBufferSize(socketConfig.getRcvBufSize());
126 }
127 if (socketConfig.getSndBufSize() > 0) {
128 sock.setSendBufferSize(socketConfig.getSndBufSize());
129 }
130
131 final int linger = socketConfig.getSoLinger();
132 if (linger >= 0) {
133 sock.setSoLinger(true, linger);
134 }
135 conn.bind(sock);
136
137 final InetSocketAddress remoteAddress = new InetSocketAddress(address, port);
138 if (this.log.isDebugEnabled()) {
139 this.log.debug("Connecting to " + remoteAddress);
140 }
141 try {
142 sock = sf.connectSocket(
143 connectTimeout, sock, host, remoteAddress, localAddress, context);
144 conn.bind(sock);
145 if (this.log.isDebugEnabled()) {
146 this.log.debug("Connection established " + conn);
147 }
148 return;
149 } catch (final SocketTimeoutException ex) {
150 if (last) {
151 throw new ConnectTimeoutException(ex, host, addresses);
152 }
153 } catch (final ConnectException ex) {
154 if (last) {
155 final String msg = ex.getMessage();
156 throw "Connection timed out".equals(msg)
157 ? new ConnectTimeoutException(ex, host, addresses)
158 : new HttpHostConnectException(ex, host, addresses);
159 }
160 } catch (final NoRouteToHostException ex) {
161 if (last) {
162 throw ex;
163 }
164 }
165 if (this.log.isDebugEnabled()) {
166 this.log.debug("Connect to " + remoteAddress + " timed out. " +
167 "Connection will be retried using another IP address");
168 }
169 }
170 }
171
172 @Override
173 public void upgrade(
174 final ManagedHttpClientConnection conn,
175 final HttpHost host,
176 final HttpContext context) throws IOException {
177 final HttpClientContext clientContext = HttpClientContext.adapt(context);
178 final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(clientContext);
179 final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
180 if (sf == null) {
181 throw new UnsupportedSchemeException(host.getSchemeName() +
182 " protocol is not supported");
183 }
184 if (!(sf instanceof LayeredConnectionSocketFactory)) {
185 throw new UnsupportedSchemeException(host.getSchemeName() +
186 " protocol does not support connection upgrade");
187 }
188 final LayeredConnectionSocketFactoryache/http/conn/socket/LayeredConnectionSocketFactory.html#LayeredConnectionSocketFactory">LayeredConnectionSocketFactory lsf = (LayeredConnectionSocketFactory) sf;
189 Socket sock = conn.getSocket();
190 final int port = this.schemePortResolver.resolve(host);
191 sock = lsf.createLayeredSocket(sock, host.getHostName(), port, context);
192 conn.bind(sock);
193 }
194
195 }