View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
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.io.ManagedHttpClientConnection;
43  import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
44  import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
45  import org.apache.hc.core5.http.HttpHost;
46  import org.apache.hc.core5.http.config.Lookup;
47  import org.apache.hc.core5.http.io.SocketConfig;
48  import org.apache.hc.core5.http.protocol.BasicHttpContext;
49  import org.apache.hc.core5.http.protocol.HttpContext;
50  import org.apache.hc.core5.util.TimeValue;
51  import org.junit.Before;
52  import org.junit.Test;
53  import org.mockito.Mockito;
54  
55  @SuppressWarnings({"boxing","static-access"}) // test code
56  public class TestHttpClientConnectionOperator {
57  
58      private ManagedHttpClientConnection conn;
59      private Socket socket;
60      private ConnectionSocketFactory plainSocketFactory;
61      private LayeredConnectionSocketFactory sslSocketFactory;
62      private Lookup<ConnectionSocketFactory> socketFactoryRegistry;
63      private SchemePortResolver schemePortResolver;
64      private DnsResolver dnsResolver;
65      private DefaultHttpClientConnectionOperator connectionOperator;
66  
67      @Before
68      public void setup() throws Exception {
69          conn = Mockito.mock(ManagedHttpClientConnection.class);
70          socket = Mockito.mock(Socket.class);
71          plainSocketFactory = Mockito.mock(ConnectionSocketFactory.class);
72          sslSocketFactory = Mockito.mock(LayeredConnectionSocketFactory.class);
73          socketFactoryRegistry = Mockito.mock(Lookup.class);
74          schemePortResolver = Mockito.mock(SchemePortResolver.class);
75          dnsResolver = Mockito.mock(DnsResolver.class);
76          connectionOperator = new DefaultHttpClientConnectionOperator(
77                  socketFactoryRegistry, schemePortResolver, dnsResolver);
78      }
79  
80      @Test
81      public void testConnect() throws Exception {
82          final HttpContext context = new BasicHttpContext();
83          final HttpHost host = new HttpHost("somehost");
84          final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0});
85          final InetAddress ip1 = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
86          final InetAddress ip2 = InetAddress.getByAddress(new byte[] {127, 0, 0, 2});
87  
88          Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
89          Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
90          Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
91          Mockito.when(plainSocketFactory.createSocket(Mockito.<HttpContext>any())).thenReturn(socket);
92          Mockito.when(plainSocketFactory.connectSocket(
93                  Mockito.<TimeValue>any(),
94                  Mockito.<Socket>any(),
95                  Mockito.<HttpHost>any(),
96                  Mockito.<InetSocketAddress>any(),
97                  Mockito.<InetSocketAddress>any(),
98                  Mockito.<HttpContext>any())).thenReturn(socket);
99  
100         final SocketConfig socketConfig = SocketConfig.custom()
101             .setSoKeepAlive(true)
102             .setSoReuseAddress(true)
103             .setSoTimeout(5000, TimeUnit.MILLISECONDS)
104             .setTcpNoDelay(true)
105             .setSoLinger(50, TimeUnit.MILLISECONDS)
106             .build();
107         final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
108         connectionOperator.connect(conn, host, localAddress, TimeValue.ofMilliseconds(1000), socketConfig, context);
109 
110         Mockito.verify(socket).setKeepAlive(true);
111         Mockito.verify(socket).setReuseAddress(true);
112         Mockito.verify(socket).setSoTimeout(5000);
113         Mockito.verify(socket).setSoLinger(true, 50);
114         Mockito.verify(socket).setTcpNoDelay(true);
115 
116         Mockito.verify(plainSocketFactory).connectSocket(
117                 TimeValue.ofMilliseconds(1000),
118                 socket,
119                 host,
120                 new InetSocketAddress(ip1, 80),
121                 localAddress,
122                 context);
123         Mockito.verify(conn, Mockito.times(2)).bind(socket);
124     }
125 
126     @Test(expected=ConnectTimeoutException.class)
127     public void testConnectTimeout() throws Exception {
128         final HttpContext context = new BasicHttpContext();
129         final HttpHost host = new HttpHost("somehost");
130         final InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
131         final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2});
132 
133         Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
134         Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
135         Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
136         Mockito.when(plainSocketFactory.createSocket(Mockito.<HttpContext>any())).thenReturn(socket);
137         Mockito.when(plainSocketFactory.connectSocket(
138                 Mockito.<TimeValue>any(),
139                 Mockito.<Socket>any(),
140                 Mockito.<HttpHost>any(),
141                 Mockito.<InetSocketAddress>any(),
142                 Mockito.<InetSocketAddress>any(),
143                 Mockito.<HttpContext>any())).thenThrow(new SocketTimeoutException());
144 
145         connectionOperator.connect(conn, host, null, TimeValue.ofMilliseconds(1000), SocketConfig.DEFAULT, context);
146     }
147 
148     @Test(expected=HttpHostConnectException.class)
149     public void testConnectFailure() throws Exception {
150         final HttpContext context = new BasicHttpContext();
151         final HttpHost host = new HttpHost("somehost");
152         final InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
153         final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2});
154 
155         Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
156         Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
157         Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
158         Mockito.when(plainSocketFactory.createSocket(Mockito.<HttpContext>any())).thenReturn(socket);
159         Mockito.when(plainSocketFactory.connectSocket(
160                 Mockito.<TimeValue>any(),
161                 Mockito.<Socket>any(),
162                 Mockito.<HttpHost>any(),
163                 Mockito.<InetSocketAddress>any(),
164                 Mockito.<InetSocketAddress>any(),
165                 Mockito.<HttpContext>any())).thenThrow(new ConnectException());
166 
167         connectionOperator.connect(conn, host, null, TimeValue.ofMilliseconds(1000), SocketConfig.DEFAULT, context);
168     }
169 
170     @Test
171     public void testConnectFailover() throws Exception {
172         final HttpContext context = new BasicHttpContext();
173         final HttpHost host = new HttpHost("somehost");
174         final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0});
175         final InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
176         final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2});
177 
178         Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 });
179         Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
180         Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
181         Mockito.when(plainSocketFactory.createSocket(Mockito.<HttpContext>any())).thenReturn(socket);
182         Mockito.when(plainSocketFactory.connectSocket(
183                 Mockito.<TimeValue>any(),
184                 Mockito.<Socket>any(),
185                 Mockito.<HttpHost>any(),
186                 Mockito.eq(new InetSocketAddress(ip1, 80)),
187                 Mockito.<InetSocketAddress>any(),
188                 Mockito.<HttpContext>any())).thenThrow(new ConnectException());
189         Mockito.when(plainSocketFactory.connectSocket(
190                 Mockito.<TimeValue>any(),
191                 Mockito.<Socket>any(),
192                 Mockito.<HttpHost>any(),
193                 Mockito.eq(new InetSocketAddress(ip2, 80)),
194                 Mockito.<InetSocketAddress>any(),
195                 Mockito.<HttpContext>any())).thenReturn(socket);
196 
197         final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
198         connectionOperator.connect(conn, host, localAddress, TimeValue.ofMilliseconds(1000), SocketConfig.DEFAULT, context);
199 
200         Mockito.verify(plainSocketFactory).connectSocket(
201                 TimeValue.ofMilliseconds(1000),
202                 socket,
203                 host,
204                 new InetSocketAddress(ip2, 80),
205                 localAddress,
206                 context);
207         Mockito.verify(conn, Mockito.times(3)).bind(socket);
208     }
209 
210     @Test
211     public void testConnectExplicitAddress() throws Exception {
212         final HttpContext context = new BasicHttpContext();
213         final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0});
214         final InetAddress ip = InetAddress.getByAddress(new byte[] {127, 0, 0, 23});
215         final HttpHost host = new HttpHost(ip);
216 
217         Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
218         Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
219         Mockito.when(plainSocketFactory.createSocket(Mockito.<HttpContext>any())).thenReturn(socket);
220         Mockito.when(plainSocketFactory.connectSocket(
221                 Mockito.<TimeValue>any(),
222                 Mockito.<Socket>any(),
223                 Mockito.<HttpHost>any(),
224                 Mockito.<InetSocketAddress>any(),
225                 Mockito.<InetSocketAddress>any(),
226                 Mockito.<HttpContext>any())).thenReturn(socket);
227 
228         final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
229         connectionOperator.connect(conn, host, localAddress, TimeValue.ofMilliseconds(1000), SocketConfig.DEFAULT, context);
230 
231         Mockito.verify(plainSocketFactory).connectSocket(
232                 TimeValue.ofMilliseconds(1000),
233                 socket,
234                 host,
235                 new InetSocketAddress(ip, 80),
236                 localAddress,
237                 context);
238         Mockito.verify(dnsResolver, Mockito.never()).resolve(Mockito.anyString());
239         Mockito.verify(conn, Mockito.times(2)).bind(socket);
240     }
241 
242     @Test
243     public void testUpgrade() throws Exception {
244         final HttpContext context = new BasicHttpContext();
245         final HttpHost host = new HttpHost("https", "somehost", -1);
246 
247         Mockito.when(conn.isOpen()).thenReturn(true);
248         Mockito.when(conn.getSocket()).thenReturn(socket);
249         Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslSocketFactory);
250         Mockito.when(schemePortResolver.resolve(host)).thenReturn(443);
251         Mockito.when(sslSocketFactory.createSocket(Mockito.<HttpContext>any())).thenReturn(socket);
252         Mockito.when(sslSocketFactory.createLayeredSocket(
253                 Mockito.<Socket>any(),
254                 Mockito.eq("somehost"),
255                 Mockito.eq(443),
256                 Mockito.<HttpContext>any())).thenReturn(socket);
257 
258         connectionOperator.upgrade(conn, host, context);
259 
260         Mockito.verify(conn).bind(socket);
261     }
262 
263     @Test(expected=UnsupportedSchemeException.class)
264     public void testUpgradeUpsupportedScheme() throws Exception {
265         final HttpContext context = new BasicHttpContext();
266         final HttpHost host = new HttpHost("httpsssss", "somehost", -1);
267         Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
268 
269         connectionOperator.upgrade(conn, host, context);
270     }
271 
272     @Test(expected=UnsupportedSchemeException.class)
273     public void testUpgradeNonLayeringScheme() throws Exception {
274         final HttpContext context = new BasicHttpContext();
275         final HttpHost host = new HttpHost("http", "somehost", -1);
276         Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
277 
278         connectionOperator.upgrade(conn, host, context);
279     }
280 
281 }