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