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