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.core5.testing.nio;
29  
30  import java.net.InetSocketAddress;
31  import java.net.URI;
32  import java.net.URISyntaxException;
33  import java.net.URL;
34  import java.security.Provider;
35  import java.security.SecureRandom;
36  import java.security.Security;
37  import java.util.Arrays;
38  import java.util.Collection;
39  import java.util.concurrent.Future;
40  
41  import javax.net.ssl.SSLEngine;
42  
43  import org.apache.hc.core5.function.Supplier;
44  import org.apache.hc.core5.http.HttpHeaders;
45  import org.apache.hc.core5.http.HttpResponse;
46  import org.apache.hc.core5.http.Message;
47  import org.apache.hc.core5.http.Method;
48  import org.apache.hc.core5.http.config.Http1Config;
49  import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
50  import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
51  import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder;
52  import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
53  import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
54  import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
55  import org.apache.hc.core5.http.protocol.HttpProcessor;
56  import org.apache.hc.core5.http.protocol.RequestValidateHost;
57  import org.apache.hc.core5.net.NamedEndpoint;
58  import org.apache.hc.core5.reactor.IOReactorConfig;
59  import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
60  import org.apache.hc.core5.ssl.SSLContextBuilder;
61  import org.apache.hc.core5.util.ReflectionUtils;
62  import org.apache.hc.core5.util.TimeValue;
63  import org.apache.hc.core5.util.Timeout;
64  import org.conscrypt.Conscrypt;
65  import org.junit.Assert;
66  import org.junit.Assume;
67  import org.junit.BeforeClass;
68  import org.junit.Rule;
69  import org.junit.Test;
70  import org.junit.rules.ExternalResource;
71  import org.junit.rules.RuleChain;
72  import org.junit.rules.TestRule;
73  import org.junit.runner.RunWith;
74  import org.junit.runners.Parameterized;
75  import org.slf4j.Logger;
76  import org.slf4j.LoggerFactory;
77  
78  @RunWith(Parameterized.class)
79  public class JSSEProviderIntegrationTest {
80  
81      private final Logger log = LoggerFactory.getLogger(getClass());
82  
83      @Parameterized.Parameters(name = "{0} {1}")
84      public static Collection<Object[]> protocols() {
85          return Arrays.asList(new Object[][]{
86                  {"Oracle", null},
87                  {"Conscrypt", "TLSv1.2"},
88                  {"Conscrypt", "TLSv1.3"},
89          });
90      }
91  
92      private final String securityProviderName;
93      private final String protocolVersion;
94  
95      public JSSEProviderIntegrationTest(final String securityProviderName, final String protocolVersion) {
96          super();
97          this.securityProviderName = securityProviderName;
98          this.protocolVersion = protocolVersion;
99      }
100 
101     private static final Timeout TIMEOUT = Timeout.ofSeconds(30);
102     private static final int REQ_NUM = 25;
103 
104     private Provider securityProvider;
105     private Http1TestServer server;
106     private Http1TestClient client;
107 
108     @BeforeClass
109     public static void determineJavaVersion() {
110         Assume.assumeTrue("Java version must be 8 or greater", ReflectionUtils.determineJRELevel() >= 8);
111     }
112 
113     @Rule
114     public TestRule resourceRules = RuleChain.outerRule(new ExternalResource() {
115 
116         @Override
117         protected void before() throws Throwable {
118             if ("Conscrypt".equalsIgnoreCase(securityProviderName)) {
119                 try {
120                     securityProvider = Conscrypt.newProviderBuilder().provideTrustManager(true).build();
121                 } catch (final UnsatisfiedLinkError e) {
122                     Assume.assumeFalse("Conscrypt provider failed to be loaded: " + e.getMessage(), true);
123                 }
124             } else {
125                 securityProvider = null;
126             }
127             if (securityProvider != null) {
128                 Security.insertProviderAt(securityProvider, 1);
129             }
130         }
131 
132         @Override
133         protected void after() {
134             if (securityProvider != null) {
135                 Security.removeProvider(securityProvider.getName());
136                 securityProvider = null;
137             }
138         }
139 
140     }).around(new ExternalResource() {
141 
142         @Override
143         protected void before() throws Throwable {
144             log.debug("Starting up test server");
145 
146             final URL keyStoreURL = getClass().getResource("/test-server.p12");
147             final String storePassword = "nopassword";
148 
149             server = new Http1TestServer(
150                     IOReactorConfig.custom()
151                             .setSoTimeout(TIMEOUT)
152                             .build(),
153                     SSLContextBuilder.create()
154                             .setProvider(securityProvider)
155                             .setKeyStoreType("pkcs12")
156                             .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
157                             .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
158                             .setSecureRandom(new SecureRandom())
159                             .build(),
160                     new SSLSessionInitializer() {
161 
162                         @Override
163                         public void initialize(final NamedEndpoint endpoint, final SSLEngine sslEngine) {
164                             if (protocolVersion != null) {
165                                 sslEngine.setEnabledProtocols(new String[]{protocolVersion});
166                             }
167                         }
168 
169                     },
170                     null);
171         }
172 
173         @Override
174         protected void after() {
175             log.debug("Shutting down test server");
176             if (server != null) {
177                 server.shutdown(TimeValue.ofSeconds(5));
178             }
179         }
180 
181     }).around(new ExternalResource() {
182 
183         @Override
184         protected void before() throws Throwable {
185             log.debug("Starting up test client");
186 
187             final URL keyStoreURL = getClass().getResource("/test-client.p12");
188             final String storePassword = "nopassword";
189 
190             client = new Http1TestClient(
191                     IOReactorConfig.custom()
192                             .setSoTimeout(TIMEOUT)
193                             .build(),
194                     SSLContextBuilder.create()
195                             .setProvider(securityProvider)
196                             .setKeyStoreType("pkcs12")
197                             .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
198                             .setSecureRandom(new SecureRandom())
199                             .build(),
200                     new SSLSessionInitializer() {
201 
202                         @Override
203                         public void initialize(final NamedEndpoint endpoint, final SSLEngine sslEngine) {
204                             if (protocolVersion != null) {
205                                 sslEngine.setEnabledProtocols(new String[]{protocolVersion});
206                             }
207                         }
208 
209                     },
210                     null);
211         }
212 
213         @Override
214         protected void after() {
215             log.debug("Shutting down test client");
216             if (client != null) {
217                 client.shutdown(TimeValue.ofSeconds(5));
218             }
219         }
220 
221     });
222 
223     private URI createRequestURI(final InetSocketAddress serverEndpoint, final String path) {
224         try {
225             return new URI("https", null, "localhost", serverEndpoint.getPort(), path, null, null);
226         } catch (final URISyntaxException e) {
227             throw new IllegalStateException();
228         }
229     }
230 
231     @Test
232     public void testSimpleGet() throws Exception {
233         server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
234 
235             @Override
236             public AsyncServerExchangeHandler get() {
237                 return new SingleLineResponseHandler("Hi there");
238             }
239 
240         });
241         final InetSocketAddress serverEndpoint = server.start();
242 
243         client.start();
244         final Future<ClientSessionEndpoint> connectFuture = client.connect(
245                 "localhost", serverEndpoint.getPort(), TIMEOUT);
246         final ClientSessionEndpoint streamEndpoint = connectFuture.get();
247 
248         for (int i = 0; i < REQ_NUM; i++) {
249             final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
250                     new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")),
251                     new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
252             final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
253             Assert.assertNotNull(result);
254             final HttpResponse response1 = result.getHead();
255             final String entity1 = result.getBody();
256             Assert.assertNotNull(response1);
257             Assert.assertEquals(200, response1.getCode());
258             Assert.assertEquals("Hi there", entity1);
259         }
260     }
261 
262     @Test
263     public void testSimpleGetConnectionClose() throws Exception {
264         server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
265 
266             @Override
267             public AsyncServerExchangeHandler get() {
268                 return new SingleLineResponseHandler("Hi there");
269             }
270 
271         });
272         final InetSocketAddress serverEndpoint = server.start();
273 
274         client.start();
275         final URI requestURI = createRequestURI(serverEndpoint, "/hello");
276         for (int i = 0; i < REQ_NUM; i++) {
277             final Future<ClientSessionEndpoint> connectFuture = client.connect(
278                     "localhost", serverEndpoint.getPort(), TIMEOUT);
279             try (final ClientSessionEndpoint streamEndpoint = connectFuture.get()) {
280                 final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
281                         AsyncRequestBuilder.get(requestURI)
282                                 .addHeader(HttpHeaders.CONNECTION, "close")
283                                 .build(),
284                         new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
285                 final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
286                 Assert.assertNotNull(result);
287                 final HttpResponse response1 = result.getHead();
288                 final String entity1 = result.getBody();
289                 Assert.assertNotNull(response1);
290                 Assert.assertEquals(200, response1.getCode());
291                 Assert.assertEquals("Hi there", entity1);
292             }
293         }
294     }
295 
296     @Test
297     public void testSimpleGetIdentityTransfer() throws Exception {
298         server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
299 
300             @Override
301             public AsyncServerExchangeHandler get() {
302                 return new SingleLineResponseHandler("Hi there");
303             }
304 
305         });
306         final HttpProcessor httpProcessor = new DefaultHttpProcessor(new RequestValidateHost());
307         final InetSocketAddress serverEndpoint = server.start(httpProcessor, Http1Config.DEFAULT);
308 
309         client.start();
310 
311         for (int i = 0; i < REQ_NUM; i++) {
312             final Future<ClientSessionEndpoint> connectFuture = client.connect(
313                     "localhost", serverEndpoint.getPort(), TIMEOUT);
314             try (final ClientSessionEndpoint streamEndpoint = connectFuture.get()) {
315                 final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
316                         new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")),
317                         new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
318                 final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
319                 Assert.assertNotNull(result);
320                 final HttpResponse response = result.getHead();
321                 final String entity = result.getBody();
322                 Assert.assertNotNull(response);
323                 Assert.assertEquals(200, response.getCode());
324                 Assert.assertEquals("Hi there", entity);
325             }
326         }
327     }
328 
329 }