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.hc.client5.http.impl.io;
28
29 import java.io.IOException;
30 import java.net.InetAddress;
31 import java.net.InetSocketAddress;
32 import java.net.Proxy;
33 import java.net.Socket;
34 import java.net.SocketAddress;
35 import java.net.UnknownHostException;
36 import java.util.Arrays;
37
38 import org.apache.hc.client5.http.ConnectExceptionSupport;
39 import org.apache.hc.client5.http.DnsResolver;
40 import org.apache.hc.client5.http.SchemePortResolver;
41 import org.apache.hc.client5.http.SystemDefaultDnsResolver;
42 import org.apache.hc.client5.http.UnsupportedSchemeException;
43 import org.apache.hc.client5.http.impl.ConnPoolSupport;
44 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
45 import org.apache.hc.client5.http.io.HttpClientConnectionOperator;
46 import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
47 import org.apache.hc.client5.http.protocol.HttpClientContext;
48 import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
49 import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
50 import org.apache.hc.core5.annotation.Contract;
51 import org.apache.hc.core5.annotation.Internal;
52 import org.apache.hc.core5.annotation.ThreadingBehavior;
53 import org.apache.hc.core5.http.ConnectionClosedException;
54 import org.apache.hc.core5.http.HttpHost;
55 import org.apache.hc.core5.http.config.Lookup;
56 import org.apache.hc.core5.http.io.SocketConfig;
57 import org.apache.hc.core5.http.protocol.HttpContext;
58 import org.apache.hc.core5.util.Args;
59 import org.apache.hc.core5.util.TimeValue;
60 import org.apache.hc.core5.util.Timeout;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64
65
66
67
68
69
70
71 @Internal
72 @Contract(threading = ThreadingBehavior.STATELESS)
73 public class DefaultHttpClientConnectionOperator implements HttpClientConnectionOperator {
74
75 static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry";
76
77 private static final Logger LOG = LoggerFactory.getLogger(DefaultHttpClientConnectionOperator.class);
78
79 private final Lookup<ConnectionSocketFactory> socketFactoryRegistry;
80 private final SchemePortResolver schemePortResolver;
81 private final DnsResolver dnsResolver;
82
83 public DefaultHttpClientConnectionOperator(
84 final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
85 final SchemePortResolver schemePortResolver,
86 final DnsResolver dnsResolver) {
87 super();
88 Args.notNull(socketFactoryRegistry, "Socket factory registry");
89 this.socketFactoryRegistry = socketFactoryRegistry;
90 this.schemePortResolver = schemePortResolver != null ? schemePortResolver :
91 DefaultSchemePortResolver.INSTANCE;
92 this.dnsResolver = dnsResolver != null ? dnsResolver :
93 SystemDefaultDnsResolver.INSTANCE;
94 }
95
96 @SuppressWarnings("unchecked")
97 private Lookup<ConnectionSocketFactory> getSocketFactoryRegistry(final HttpContext context) {
98 Lookup<ConnectionSocketFactory> reg = (Lookup<ConnectionSocketFactory>) context.getAttribute(
99 SOCKET_FACTORY_REGISTRY);
100 if (reg == null) {
101 reg = this.socketFactoryRegistry;
102 }
103 return reg;
104 }
105
106 @Override
107 public void connect(
108 final ManagedHttpClientConnection conn,
109 final HttpHost host,
110 final InetSocketAddress localAddress,
111 final TimeValue connectTimeout,
112 final SocketConfig socketConfig,
113 final HttpContext context) throws IOException {
114 final Timeout timeout = connectTimeout != null ? Timeout.of(connectTimeout.getDuration(), connectTimeout.getTimeUnit()) : null;
115 connect(conn, host, localAddress, timeout, socketConfig, null, context);
116 }
117
118 @Override
119 public void connect(
120 final ManagedHttpClientConnection conn,
121 final HttpHost host,
122 final InetSocketAddress localAddress,
123 final Timeout connectTimeout,
124 final SocketConfig socketConfig,
125 final Object attachment,
126 final HttpContext context) throws IOException {
127 Args.notNull(conn, "Connection");
128 Args.notNull(host, "Host");
129 Args.notNull(socketConfig, "Socket config");
130 Args.notNull(context, "Context");
131 final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context);
132 final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
133 if (sf == null) {
134 throw new UnsupportedSchemeException(host.getSchemeName() + " protocol is not supported");
135 }
136 final InetAddress[] remoteAddresses;
137 if (host.getAddress() != null) {
138 remoteAddresses = new InetAddress[] { host.getAddress() };
139 } else {
140 if (LOG.isDebugEnabled()) {
141 LOG.debug("{} resolving remote address", host.getHostName());
142 }
143
144 remoteAddresses = this.dnsResolver.resolve(host.getHostName());
145
146 if (LOG.isDebugEnabled()) {
147 LOG.debug("{} resolved to {}", host.getHostName(), remoteAddresses == null ? "null" : Arrays.asList(remoteAddresses));
148 }
149
150 if (remoteAddresses == null || remoteAddresses.length == 0) {
151 throw new UnknownHostException(host.getHostName());
152 }
153 }
154
155 final Timeout soTimeout = socketConfig.getSoTimeout();
156 final SocketAddress socksProxyAddress = socketConfig.getSocksProxyAddress();
157 final Proxy proxy = socksProxyAddress != null ? new Proxy(Proxy.Type.SOCKS, socksProxyAddress) : null;
158 final int port = this.schemePortResolver.resolve(host);
159 for (int i = 0; i < remoteAddresses.length; i++) {
160 final InetAddress address = remoteAddresses[i];
161 final boolean last = i == remoteAddresses.length - 1;
162
163 Socket sock = sf.createSocket(proxy, context);
164 if (soTimeout != null) {
165 sock.setSoTimeout(soTimeout.toMillisecondsIntBound());
166 }
167 sock.setReuseAddress(socketConfig.isSoReuseAddress());
168 sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
169 sock.setKeepAlive(socketConfig.isSoKeepAlive());
170 if (socketConfig.getRcvBufSize() > 0) {
171 sock.setReceiveBufferSize(socketConfig.getRcvBufSize());
172 }
173 if (socketConfig.getSndBufSize() > 0) {
174 sock.setSendBufferSize(socketConfig.getSndBufSize());
175 }
176
177 final int linger = socketConfig.getSoLinger().toMillisecondsIntBound();
178 if (linger >= 0) {
179 sock.setSoLinger(true, linger);
180 }
181 conn.bind(sock);
182
183 final InetSocketAddress remoteAddress = new InetSocketAddress(address, port);
184 if (LOG.isDebugEnabled()) {
185 LOG.debug("{}:{} connecting {}->{} ({})",
186 host.getHostName(), host.getPort(), localAddress, remoteAddress, connectTimeout);
187 }
188 try {
189 sock = sf.connectSocket(sock, host, remoteAddress, localAddress, connectTimeout, attachment, context);
190 conn.bind(sock);
191 conn.setSocketTimeout(soTimeout);
192 if (LOG.isDebugEnabled()) {
193 LOG.debug("{}:{} connected {}->{} as {}",
194 host.getHostName(), host.getPort(), localAddress, remoteAddress, ConnPoolSupport.getId(conn));
195 }
196 return;
197 } catch (final IOException ex) {
198 if (last) {
199 if (LOG.isDebugEnabled()) {
200 LOG.debug("{}:{} connection to {} failed ({}); terminating operation",
201 host.getHostName(), host.getPort(), remoteAddress, ex.getClass());
202 }
203 throw ConnectExceptionSupport.enhance(ex, host, remoteAddresses);
204 } else {
205 if (LOG.isDebugEnabled()) {
206 LOG.debug("{}:{} connection to {} failed ({}); retrying connection to the next address",
207 host.getHostName(), host.getPort(), remoteAddress, ex.getClass());
208 }
209 }
210 }
211 }
212 }
213
214 @Override
215 public void upgrade(
216 final ManagedHttpClientConnection conn,
217 final HttpHost host,
218 final HttpContext context) throws IOException {
219 upgrade(conn, host, null, context);
220 }
221
222 @Override
223 public void upgrade(
224 final ManagedHttpClientConnection conn,
225 final HttpHost host,
226 final Object attachment,
227 final HttpContext context) throws IOException {
228 final HttpClientContext clientContext = HttpClientContext.adapt(context);
229 final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(clientContext);
230 final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
231 if (sf == null) {
232 throw new UnsupportedSchemeException(host.getSchemeName() +
233 " protocol is not supported");
234 }
235 if (!(sf instanceof LayeredConnectionSocketFactory)) {
236 throw new UnsupportedSchemeException(host.getSchemeName() +
237 " protocol does not support connection upgrade");
238 }
239 final LayeredConnectionSocketFactory lsf = (LayeredConnectionSocketFactory) sf;
240 Socket sock = conn.getSocket();
241 if (sock == null) {
242 throw new ConnectionClosedException("Connection is closed");
243 }
244 final int port = this.schemePortResolver.resolve(host);
245 sock = lsf.createLayeredSocket(sock, host.getHostName(), port, attachment, context);
246 conn.bind(sock);
247 }
248
249 }