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 javax.net.ssl.SSLSocket;
39
40 import org.apache.hc.client5.http.ConnectExceptionSupport;
41 import org.apache.hc.client5.http.DnsResolver;
42 import org.apache.hc.client5.http.SchemePortResolver;
43 import org.apache.hc.client5.http.SystemDefaultDnsResolver;
44 import org.apache.hc.client5.http.UnsupportedSchemeException;
45 import org.apache.hc.client5.http.impl.ConnPoolSupport;
46 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
47 import org.apache.hc.client5.http.io.DetachedSocketFactory;
48 import org.apache.hc.client5.http.io.HttpClientConnectionOperator;
49 import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
50 import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
51 import org.apache.hc.core5.annotation.Contract;
52 import org.apache.hc.core5.annotation.Internal;
53 import org.apache.hc.core5.annotation.ThreadingBehavior;
54 import org.apache.hc.core5.http.ConnectionClosedException;
55 import org.apache.hc.core5.http.HttpHost;
56 import org.apache.hc.core5.http.URIScheme;
57 import org.apache.hc.core5.http.config.Lookup;
58 import org.apache.hc.core5.http.io.SocketConfig;
59 import org.apache.hc.core5.http.protocol.HttpContext;
60 import org.apache.hc.core5.io.Closer;
61 import org.apache.hc.core5.net.NamedEndpoint;
62 import org.apache.hc.core5.util.Args;
63 import org.apache.hc.core5.util.TimeValue;
64 import org.apache.hc.core5.util.Timeout;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68
69
70
71
72
73
74
75 @Internal
76 @Contract(threading = ThreadingBehavior.STATELESS)
77 public class DefaultHttpClientConnectionOperator implements HttpClientConnectionOperator {
78
79 private static final Logger LOG = LoggerFactory.getLogger(DefaultHttpClientConnectionOperator.class);
80
81 static final DetachedSocketFactory PLAIN_SOCKET_FACTORY = socksProxy -> socksProxy == null ? new Socket() : new Socket(socksProxy);
82
83 private final DetachedSocketFactory detachedSocketFactory;
84 private final Lookup<TlsSocketStrategy> tlsSocketStrategyLookup;
85 private final SchemePortResolver schemePortResolver;
86 private final DnsResolver dnsResolver;
87
88
89
90
91 @Deprecated
92 static Lookup<TlsSocketStrategy> adapt(final Lookup<org.apache.hc.client5.http.socket.ConnectionSocketFactory> lookup) {
93
94 return name -> {
95 final org.apache.hc.client5.http.socket.ConnectionSocketFactory sf = lookup.lookup(name);
96 return sf instanceof org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory ? (socket, target, port, attachment, context) ->
97 (SSLSocket) ((org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory) sf).createLayeredSocket(socket, target, port, attachment, context) : null;
98 };
99
100 }
101
102
103 public DefaultHttpClientConnectionOperator(
104 final DetachedSocketFactory detachedSocketFactory,
105 final SchemePortResolver schemePortResolver,
106 final DnsResolver dnsResolver,
107 final Lookup<TlsSocketStrategy> tlsSocketStrategyLookup) {
108 super();
109 this.detachedSocketFactory = Args.notNull(detachedSocketFactory, "Plain socket factory");
110 this.tlsSocketStrategyLookup = Args.notNull(tlsSocketStrategyLookup, "Socket factory registry");
111 this.schemePortResolver = schemePortResolver != null ? schemePortResolver :
112 DefaultSchemePortResolver.INSTANCE;
113 this.dnsResolver = dnsResolver != null ? dnsResolver :
114 SystemDefaultDnsResolver.INSTANCE;
115 }
116
117
118
119
120 @Deprecated
121 public DefaultHttpClientConnectionOperator(
122 final Lookup<org.apache.hc.client5.http.socket.ConnectionSocketFactory> socketFactoryRegistry,
123 final SchemePortResolver schemePortResolver,
124 final DnsResolver dnsResolver) {
125 this(PLAIN_SOCKET_FACTORY, schemePortResolver, dnsResolver, adapt(socketFactoryRegistry));
126 }
127
128 public DefaultHttpClientConnectionOperator(
129 final SchemePortResolver schemePortResolver,
130 final DnsResolver dnsResolver,
131 final Lookup<TlsSocketStrategy> tlsSocketStrategyLookup) {
132 this(PLAIN_SOCKET_FACTORY, schemePortResolver, dnsResolver, tlsSocketStrategyLookup);
133 }
134
135 @Override
136 public void connect(
137 final ManagedHttpClientConnection conn,
138 final HttpHost host,
139 final InetSocketAddress localAddress,
140 final TimeValue connectTimeout,
141 final SocketConfig socketConfig,
142 final HttpContext context) throws IOException {
143 final Timeout timeout = connectTimeout != null ? Timeout.of(connectTimeout.getDuration(), connectTimeout.getTimeUnit()) : null;
144 connect(conn, host, null, localAddress, timeout, socketConfig, null, context);
145 }
146
147 @Override
148 public void connect(
149 final ManagedHttpClientConnection conn,
150 final HttpHost endpointHost,
151 final NamedEndpoint endpointName,
152 final InetSocketAddress localAddress,
153 final Timeout connectTimeout,
154 final SocketConfig socketConfig,
155 final Object attachment,
156 final HttpContext context) throws IOException {
157 Args.notNull(conn, "Connection");
158 Args.notNull(endpointHost, "Host");
159 Args.notNull(socketConfig, "Socket config");
160 Args.notNull(context, "Context");
161 final InetAddress[] remoteAddresses;
162 if (endpointHost.getAddress() != null) {
163 remoteAddresses = new InetAddress[] { endpointHost.getAddress() };
164 } else {
165 if (LOG.isDebugEnabled()) {
166 LOG.debug("{} resolving remote address", endpointHost.getHostName());
167 }
168
169 remoteAddresses = this.dnsResolver.resolve(endpointHost.getHostName());
170
171 if (LOG.isDebugEnabled()) {
172 LOG.debug("{} resolved to {}", endpointHost.getHostName(), remoteAddresses == null ? "null" : Arrays.asList(remoteAddresses));
173 }
174
175 if (remoteAddresses == null || remoteAddresses.length == 0) {
176 throw new UnknownHostException(endpointHost.getHostName());
177 }
178 }
179
180 final Timeout soTimeout = socketConfig.getSoTimeout();
181 final SocketAddress socksProxyAddress = socketConfig.getSocksProxyAddress();
182 final Proxy socksProxy = socksProxyAddress != null ? new Proxy(Proxy.Type.SOCKS, socksProxyAddress) : null;
183 final int port = this.schemePortResolver.resolve(endpointHost.getSchemeName(), endpointHost);
184 for (int i = 0; i < remoteAddresses.length; i++) {
185 final InetAddress address = remoteAddresses[i];
186 final boolean last = i == remoteAddresses.length - 1;
187 final InetSocketAddress remoteAddress = new InetSocketAddress(address, port);
188 onBeforeSocketConnect(context, endpointHost);
189 if (LOG.isDebugEnabled()) {
190 LOG.debug("{} connecting {}->{} ({})", endpointHost, localAddress, remoteAddress, connectTimeout);
191 }
192 final Socket socket = detachedSocketFactory.create(socksProxy);
193 try {
194 conn.bind(socket);
195 if (soTimeout != null) {
196 socket.setSoTimeout(soTimeout.toMillisecondsIntBound());
197 }
198 socket.setReuseAddress(socketConfig.isSoReuseAddress());
199 socket.setTcpNoDelay(socketConfig.isTcpNoDelay());
200 socket.setKeepAlive(socketConfig.isSoKeepAlive());
201 if (socketConfig.getRcvBufSize() > 0) {
202 socket.setReceiveBufferSize(socketConfig.getRcvBufSize());
203 }
204 if (socketConfig.getSndBufSize() > 0) {
205 socket.setSendBufferSize(socketConfig.getSndBufSize());
206 }
207
208 final int linger = socketConfig.getSoLinger().toMillisecondsIntBound();
209 if (linger >= 0) {
210 socket.setSoLinger(true, linger);
211 }
212
213 if (localAddress != null) {
214 socket.bind(localAddress);
215 }
216 socket.connect(remoteAddress, TimeValue.isPositive(connectTimeout) ? connectTimeout.toMillisecondsIntBound() : 0);
217 conn.bind(socket);
218 onAfterSocketConnect(context, endpointHost);
219 if (LOG.isDebugEnabled()) {
220 LOG.debug("{} {} connected {}->{}", ConnPoolSupport.getId(conn), endpointHost,
221 conn.getLocalAddress(), conn.getRemoteAddress());
222 }
223 conn.setSocketTimeout(soTimeout);
224 final TlsSocketStrategy tlsSocketStrategy = tlsSocketStrategyLookup != null ? tlsSocketStrategyLookup.lookup(endpointHost.getSchemeName()) : null;
225 if (tlsSocketStrategy != null) {
226 final NamedEndpoint tlsName = endpointName != null ? endpointName : endpointHost;
227 onBeforeTlsHandshake(context, endpointHost);
228 if (LOG.isDebugEnabled()) {
229 LOG.debug("{} {} upgrading to TLS", ConnPoolSupport.getId(conn), tlsName);
230 }
231 final SSLSocket sslSocket = tlsSocketStrategy.upgrade(socket, tlsName.getHostName(), tlsName.getPort(), attachment, context);
232 conn.bind(sslSocket, socket);
233 onAfterTlsHandshake(context, endpointHost);
234 if (LOG.isDebugEnabled()) {
235 LOG.debug("{} {} upgraded to TLS", ConnPoolSupport.getId(conn), tlsName);
236 }
237 }
238 return;
239 } catch (final RuntimeException ex) {
240 Closer.closeQuietly(socket);
241 throw ex;
242 } catch (final IOException ex) {
243 Closer.closeQuietly(socket);
244 if (last) {
245 if (LOG.isDebugEnabled()) {
246 LOG.debug("{} connection to {} failed ({}); terminating operation", endpointHost, remoteAddress, ex.getClass());
247 }
248 throw ConnectExceptionSupport.enhance(ex, endpointHost, remoteAddresses);
249 }
250 if (LOG.isDebugEnabled()) {
251 LOG.debug("{} connection to {} failed ({}); retrying connection to the next address", endpointHost, remoteAddress, ex.getClass());
252 }
253 }
254 }
255 }
256
257 @Override
258 public void upgrade(
259 final ManagedHttpClientConnection conn,
260 final HttpHost host,
261 final HttpContext context) throws IOException {
262 upgrade(conn, host, null, null, context);
263 }
264
265 @Override
266 public void upgrade(
267 final ManagedHttpClientConnection conn,
268 final HttpHost endpointHost,
269 final NamedEndpoint endpointName,
270 final Object attachment,
271 final HttpContext context) throws IOException {
272 final Socket socket = conn.getSocket();
273 if (socket == null) {
274 throw new ConnectionClosedException("Connection is closed");
275 }
276 final String newProtocol = URIScheme.HTTP.same(endpointHost.getSchemeName()) ? URIScheme.HTTPS.id : endpointHost.getSchemeName();
277 final TlsSocketStrategy tlsSocketStrategy = tlsSocketStrategyLookup != null ? tlsSocketStrategyLookup.lookup(newProtocol) : null;
278 if (tlsSocketStrategy != null) {
279 final NamedEndpoint tlsName = endpointName != null ? endpointName : endpointHost;
280 onBeforeTlsHandshake(context, endpointHost);
281 if (LOG.isDebugEnabled()) {
282 LOG.debug("{} upgrading to TLS {}:{}", ConnPoolSupport.getId(conn), tlsName.getHostName(), tlsName.getPort());
283 }
284 final SSLSocket upgradedSocket = tlsSocketStrategy.upgrade(socket, tlsName.getHostName(), tlsName.getPort(), attachment, context);
285 conn.bind(upgradedSocket);
286 onAfterTlsHandshake(context, endpointHost);
287 if (LOG.isDebugEnabled()) {
288 LOG.debug("{} upgraded to TLS {}:{}", ConnPoolSupport.getId(conn), tlsName.getHostName(), tlsName.getPort());
289 }
290 } else {
291 throw new UnsupportedSchemeException(newProtocol + " protocol is not supported");
292 }
293 }
294
295 protected void onBeforeSocketConnect(final HttpContext httpContext, final HttpHost endpointHost) {
296 }
297
298 protected void onAfterSocketConnect(final HttpContext httpContext, final HttpHost endpointHost) {
299 }
300
301 protected void onBeforeTlsHandshake(final HttpContext httpContext, final HttpHost endpointHost) {
302 }
303
304 protected void onAfterTlsHandshake(final HttpContext httpContext, final HttpHost endpointHost) {
305 }
306
307 }