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