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
28 package org.apache.hc.client5.http.impl.io;
29
30 import java.net.ConnectException;
31 import java.net.InetAddress;
32 import java.net.InetSocketAddress;
33 import java.net.Socket;
34 import java.net.SocketTimeoutException;
35 import java.util.concurrent.TimeUnit;
36
37 import org.apache.hc.client5.http.ConnectTimeoutException;
38 import org.apache.hc.client5.http.DnsResolver;
39 import org.apache.hc.client5.http.HttpHostConnectException;
40 import org.apache.hc.client5.http.SchemePortResolver;
41 import org.apache.hc.client5.http.UnsupportedSchemeException;
42 import org.apache.hc.client5.http.config.TlsConfig;
43 import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
44 import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
45 import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
46 import org.apache.hc.core5.http.HttpHost;
47 import org.apache.hc.core5.http.config.Lookup;
48 import org.apache.hc.core5.http.io.SocketConfig;
49 import org.apache.hc.core5.http.protocol.BasicHttpContext;
50 import org.apache.hc.core5.http.protocol.HttpContext;
51 import org.apache.hc.core5.util.TimeValue;
52 import org.apache.hc.core5.util.Timeout;
53 import org.junit.jupiter.api.Assertions;
54 import org.junit.jupiter.api.BeforeEach;
55 import org.junit.jupiter.api.Test;
56 import org.mockito.Mockito;
57
58 @SuppressWarnings({"boxing","static-access"})
59 public class TestHttpClientConnectionOperator {
60
61 private ManagedHttpClientConnection conn;
62 private Socket socket;
63 private ConnectionSocketFactory plainSocketFactory;
64 private LayeredConnectionSocketFactory sslSocketFactory;
65 private Lookup<ConnectionSocketFactory> socketFactoryRegistry;
66 private SchemePortResolver schemePortResolver;
67 private DnsResolver dnsResolver;
68 private DefaultHttpClientConnectionOperator connectionOperator;
69
70 @BeforeEach
71 public void setup() throws Exception {
72 conn = Mockito.mock(ManagedHttpClientConnection.class);
73 socket = Mockito.mock(Socket.class);
74 plainSocketFactory = Mockito.mock(ConnectionSocketFactory.class);
75 sslSocketFactory = Mockito.mock(LayeredConnectionSocketFactory.class);
76 socketFactoryRegistry = Mockito.mock(Lookup.class);
77 schemePortResolver = Mockito.mock(SchemePortResolver.class);
78 dnsResolver = Mockito.mock(DnsResolver.class);
79 connectionOperator = new DefaultHttpClientConnectionOperator(
80 socketFactoryRegistry, schemePortResolver, dnsResolver);
81 }
82
83 @Test
84 public void testConnect() throws Exception {
85 final HttpContext context = new BasicHttpContext();
86 final HttpHost host = new HttpHost("somehost");
87 final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0});
88 final InetAddress ip1 = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
89 final InetAddress ip2 = InetAddress.getByAddress(new byte[] {127, 0, 0, 2});
90
91 Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
92 Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
93 Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
94 Mockito.when(plainSocketFactory.createSocket(Mockito.any(), Mockito.any())).thenReturn(socket);
95 Mockito.when(plainSocketFactory.connectSocket(
96 Mockito.any(),
97 Mockito.any(),
98 Mockito.any(),
99 Mockito.any(),
100 Mockito.any(),
101 Mockito.any(),
102 Mockito.any())).thenReturn(socket);
103
104 final SocketConfig socketConfig = SocketConfig.custom()
105 .setSoKeepAlive(true)
106 .setSoReuseAddress(true)
107 .setSoTimeout(5000, TimeUnit.MILLISECONDS)
108 .setTcpNoDelay(true)
109 .setSoLinger(50, TimeUnit.MILLISECONDS)
110 .build();
111 final TlsConfig tlsConfig = TlsConfig.custom()
112 .build();
113 final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
114 connectionOperator.connect(conn, host, localAddress,
115 Timeout.ofMilliseconds(123), socketConfig, tlsConfig, context);
116
117 Mockito.verify(socket).setKeepAlive(true);
118 Mockito.verify(socket).setReuseAddress(true);
119 Mockito.verify(socket).setSoTimeout(5000);
120 Mockito.verify(socket).setSoLinger(true, 50);
121 Mockito.verify(socket).setTcpNoDelay(true);
122
123 Mockito.verify(plainSocketFactory).connectSocket(
124 socket,
125 host,
126 new InetSocketAddress(ip1, 80),
127 localAddress,
128 Timeout.ofMilliseconds(123),
129 tlsConfig,
130 context);
131 Mockito.verify(conn, Mockito.times(2)).bind(socket);
132 }
133
134 @Test
135 public void testConnectTimeout() throws Exception {
136 final HttpContext context = new BasicHttpContext();
137 final HttpHost host = new HttpHost("somehost");
138 final InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
139 final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2});
140
141 Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
142 Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
143 Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
144 Mockito.when(plainSocketFactory.createSocket(Mockito.any(), Mockito.any())).thenReturn(socket);
145 Mockito.when(plainSocketFactory.connectSocket(
146 Mockito.any(),
147 Mockito.any(),
148 Mockito.any(),
149 Mockito.any(),
150 Mockito.any(),
151 Mockito.any(),
152 Mockito.any())).thenThrow(new SocketTimeoutException());
153
154 Assertions.assertThrows(ConnectTimeoutException.class, () ->
155 connectionOperator.connect(
156 conn, host, null, TimeValue.ofMilliseconds(1000), SocketConfig.DEFAULT, context));
157 }
158
159 @Test
160 public void testConnectFailure() throws Exception {
161 final HttpContext context = new BasicHttpContext();
162 final HttpHost host = new HttpHost("somehost");
163 final InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
164 final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2});
165
166 Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
167 Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
168 Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
169 Mockito.when(plainSocketFactory.createSocket(Mockito.any(), Mockito.any())).thenReturn(socket);
170 Mockito.when(plainSocketFactory.connectSocket(
171 Mockito.any(),
172 Mockito.any(),
173 Mockito.any(),
174 Mockito.any(),
175 Mockito.any(),
176 Mockito.any(),
177 Mockito.any())).thenThrow(new ConnectException());
178
179 Assertions.assertThrows(HttpHostConnectException.class, () ->
180 connectionOperator.connect(
181 conn, host, null, TimeValue.ofMilliseconds(1000), SocketConfig.DEFAULT, context));
182 }
183
184 @Test
185 public void testConnectFailover() throws Exception {
186 final HttpContext context = new BasicHttpContext();
187 final HttpHost host = new HttpHost("somehost");
188 final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0});
189 final InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
190 final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2});
191
192 Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
193 Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
194 Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
195 Mockito.when(plainSocketFactory.createSocket(Mockito.any(), Mockito.any())).thenReturn(socket);
196 Mockito.when(plainSocketFactory.connectSocket(
197 Mockito.any(),
198 Mockito.any(),
199 Mockito.eq(new InetSocketAddress(ip1, 80)),
200 Mockito.any(),
201 Mockito.any(),
202 Mockito.any(),
203 Mockito.any())).thenThrow(new ConnectException());
204 Mockito.when(plainSocketFactory.connectSocket(
205 Mockito.any(),
206 Mockito.any(),
207 Mockito.eq(new InetSocketAddress(ip2, 80)),
208 Mockito.any(),
209 Mockito.any(),
210 Mockito.any(),
211 Mockito.any())).thenReturn(socket);
212
213 final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
214 final TlsConfig tlsConfig = TlsConfig.custom()
215 .build();
216 connectionOperator.connect(conn, host, localAddress,
217 Timeout.ofMilliseconds(123), SocketConfig.DEFAULT, tlsConfig, context);
218
219 Mockito.verify(plainSocketFactory).connectSocket(
220 socket,
221 host,
222 new InetSocketAddress(ip2, 80),
223 localAddress,
224 Timeout.ofMilliseconds(123),
225 tlsConfig,
226 context);
227 Mockito.verify(conn, Mockito.times(3)).bind(socket);
228 }
229
230 @Test
231 public void testConnectExplicitAddress() throws Exception {
232 final HttpContext context = new BasicHttpContext();
233 final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0});
234 final InetAddress ip = InetAddress.getByAddress(new byte[] {127, 0, 0, 23});
235 final HttpHost host = new HttpHost(ip);
236
237 Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
238 Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
239 Mockito.when(plainSocketFactory.createSocket(Mockito.any(), Mockito.any())).thenReturn(socket);
240 Mockito.when(plainSocketFactory.connectSocket(
241 Mockito.any(),
242 Mockito.any(),
243 Mockito.any(),
244 Mockito.any(),
245 Mockito.any(),
246 Mockito.any(),
247 Mockito.any())).thenReturn(socket);
248
249 final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
250 final TlsConfig tlsConfig = TlsConfig.custom()
251 .build();
252 connectionOperator.connect(conn, host, localAddress,
253 Timeout.ofMilliseconds(123), SocketConfig.DEFAULT, tlsConfig, context);
254
255 Mockito.verify(plainSocketFactory).connectSocket(
256 socket,
257 host,
258 new InetSocketAddress(ip, 80),
259 localAddress,
260 Timeout.ofMilliseconds(123),
261 tlsConfig,
262 context);
263 Mockito.verify(dnsResolver, Mockito.never()).resolve(Mockito.anyString());
264 Mockito.verify(conn, Mockito.times(2)).bind(socket);
265 }
266
267 @Test
268 public void testUpgrade() throws Exception {
269 final HttpContext context = new BasicHttpContext();
270 final HttpHost host = new HttpHost("https", "somehost", -1);
271
272 Mockito.when(conn.isOpen()).thenReturn(true);
273 Mockito.when(conn.getSocket()).thenReturn(socket);
274 Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslSocketFactory);
275 Mockito.when(schemePortResolver.resolve(host)).thenReturn(443);
276 Mockito.when(sslSocketFactory.createSocket(Mockito.any())).thenReturn(socket);
277 Mockito.when(sslSocketFactory.createLayeredSocket(
278 Mockito.any(),
279 Mockito.eq("somehost"),
280 Mockito.eq(443),
281 Mockito.eq(Timeout.ofMilliseconds(345)),
282 Mockito.any())).thenReturn(socket);
283
284 connectionOperator.upgrade(conn, host, Timeout.ofMilliseconds(345), context);
285
286 Mockito.verify(conn).bind(socket);
287 }
288
289 @Test
290 public void testUpgradeUpsupportedScheme() throws Exception {
291 final HttpContext context = new BasicHttpContext();
292 final HttpHost host = new HttpHost("httpsssss", "somehost", -1);
293 Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
294
295 Assertions.assertThrows(UnsupportedSchemeException.class, () ->
296 connectionOperator.upgrade(conn, host, context));
297 }
298
299 @Test
300 public void testUpgradeNonLayeringScheme() throws Exception {
301 final HttpContext context = new BasicHttpContext();
302 final HttpHost host = new HttpHost("http", "somehost", -1);
303 Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
304
305 Assertions.assertThrows(UnsupportedSchemeException.class, () ->
306 connectionOperator.upgrade(conn, host, context));
307 }
308
309 }