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