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.http.nio.integration;
29  
30  import java.io.IOException;
31  import java.net.InetSocketAddress;
32  import java.net.URL;
33  import java.security.Provider;
34  import java.security.SecureRandom;
35  import java.security.Security;
36  import java.util.Arrays;
37  import java.util.Collection;
38  import java.util.concurrent.Future;
39  import java.util.concurrent.TimeUnit;
40  
41  import javax.net.ssl.SSLContext;
42  import javax.net.ssl.SSLEngine;
43  import javax.net.ssl.SSLException;
44  import javax.net.ssl.SSLSession;
45  
46  import org.apache.http.HttpHeaders;
47  import org.apache.http.HttpHost;
48  import org.apache.http.HttpResponse;
49  import org.apache.http.config.ConnectionConfig;
50  import org.apache.http.impl.nio.pool.BasicNIOConnFactory;
51  import org.apache.http.message.BasicHttpRequest;
52  import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
53  import org.apache.http.nio.reactor.IOSession;
54  import org.apache.http.nio.reactor.ListenerEndpoint;
55  import org.apache.http.nio.reactor.ssl.SSLSetupHandler;
56  import org.apache.http.nio.testserver.HttpClientNio;
57  import org.apache.http.nio.testserver.HttpServerNio;
58  import org.apache.http.nio.testserver.ServerConnectionFactory;
59  import org.apache.http.nio.util.TestingSupport;
60  import org.apache.http.protocol.ImmutableHttpProcessor;
61  import org.apache.http.protocol.ResponseServer;
62  import org.apache.http.ssl.SSLContextBuilder;
63  import org.apache.http.util.EntityUtils;
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  
76  @RunWith(Parameterized.class)
77  public class TestJSSEProviderIntegration {
78  
79      private final static long RESULT_TIMEOUT_SEC = 30;
80      private final static int REQ_NUM = 25;
81  
82      @Parameterized.Parameters(name = "{0} {1}")
83      public static Collection<Object[]> protocols() {
84          return Arrays.asList(new Object[][]{
85                  {"Oracle", null},
86                  {"Conscrypt", "TLSv1.2"},
87                  {"Conscrypt", "TLSv1.3"}
88          });
89      }
90  
91      private final String securityProviderName;
92      private final String protocolVersion;
93  
94      private Provider securityProvider;
95      private HttpServerNio server;
96      private HttpClientNio client;
97  
98      public TestJSSEProviderIntegration(final String securityProviderName, final String protocolVersion) {
99          super();
100         this.securityProviderName = securityProviderName;
101         this.protocolVersion = protocolVersion;
102     }
103 
104     @BeforeClass
105     public static void determineJavaVersion() {
106         Assume.assumeTrue("Java version must be 8 or greater", TestingSupport.determineJRELevel() >= 8);
107     }
108 
109     @Rule
110     public TestRule resourceRules = RuleChain.outerRule(new ExternalResource() {
111 
112         @Override
113         protected void before() throws Throwable {
114             if ("Conscrypt".equalsIgnoreCase(securityProviderName)) {
115                 try {
116                     securityProvider = Conscrypt.newProviderBuilder().provideTrustManager(true).build();
117                 } catch (final UnsatisfiedLinkError e) {
118                     Assume.assumeFalse("Conscrypt provider failed to be loaded: " + e.getMessage(), true);
119                 }
120             } else {
121                 securityProvider = null;
122             }
123             if (securityProvider != null) {
124                 Security.insertProviderAt(securityProvider, 1);
125             }
126         }
127 
128         @Override
129         protected void after() {
130             if (securityProvider != null) {
131                 Security.removeProvider(securityProvider.getName());
132                 securityProvider = null;
133             }
134         }
135 
136     }).around(new ExternalResource() {
137 
138         @Override
139         protected void before() throws Throwable {
140             final URL keyStoreURL = TestJSSEProviderIntegration.class.getResource("/test-server.p12");
141             final String storePassword = "nopassword";
142             final SSLContext sslContext = SSLContextBuilder.create()
143                     .setKeyStoreType("pkcs12")
144                     .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
145                     .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
146                     .setSecureRandom(new SecureRandom())
147                     .build();
148 
149             server = new HttpServerNio();
150             server.setConnectionFactory(new ServerConnectionFactory(sslContext, new SSLSetupHandler() {
151 
152                 @Override
153                 public void initalize(final SSLEngine sslEngine) throws SSLException {
154                     if (protocolVersion != null) {
155                         sslEngine.setEnabledProtocols(new String[]{protocolVersion});
156                     }
157                 }
158 
159                 @Override
160                 public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
161                 }
162 
163             }));
164             server.setTimeout(5000);
165         }
166 
167         @Override
168         protected void after() {
169             if (server != null) {
170                 try {
171                     server.shutdown();
172                 } catch (final Exception ignore) {
173                 }
174             }
175         }
176 
177     }).around(new ExternalResource() {
178 
179         @Override
180         protected void before() throws Throwable {
181             final URL keyStoreURL = TestJSSEProviderIntegration.class.getResource("/test-client.p12");
182             final String storePassword = "nopassword";
183             final SSLContext sslContext = SSLContextBuilder.create()
184                     .setKeyStoreType("pkcs12")
185                     .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
186                     .setSecureRandom(new SecureRandom())
187                     .build();
188 
189             client = new HttpClientNio(new BasicNIOConnFactory(sslContext, new SSLSetupHandler() {
190 
191                 @Override
192                 public void initalize(final SSLEngine sslEngine) throws SSLException {
193                     if (protocolVersion != null) {
194                         sslEngine.setEnabledProtocols(new String[]{protocolVersion});
195                     }
196                 }
197 
198                 @Override
199                 public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
200                 }
201 
202             }, ConnectionConfig.DEFAULT));
203             client.setTimeout(5000);
204         }
205 
206         @Override
207         protected void after() {
208             if (client != null) {
209                 try {
210                     client.shutdown();
211                 } catch (final Exception ignore) {
212                 }
213             }
214         }
215 
216     });
217 
218     private HttpHost start() throws IOException, InterruptedException {
219         this.server.start();
220         this.client.start();
221 
222         final ListenerEndpoint endpoint = this.server.getListenerEndpoint();
223         endpoint.waitFor();
224 
225         final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
226         return new HttpHost("localhost", address.getPort(), "https");
227     }
228 
229     @Test
230     public void testHttpGets() throws Exception {
231         this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
232         final HttpHost target = start();
233 
234         this.client.setMaxPerRoute(3);
235         this.client.setMaxTotal(3);
236 
237         final String pattern = RndTestPatternGenerator.generateText();
238 
239         for (int i = 0; i < REQ_NUM; i++) {
240             final BasicHttpRequest request = new BasicHttpRequest("GET", pattern + "x1");
241             final Future<HttpResponse> future = this.client.execute(target, request);
242             final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
243             Assert.assertNotNull(response);
244             Assert.assertEquals(pattern, EntityUtils.toString(response.getEntity()));
245         }
246     }
247 
248     @Test
249     public void testHttpGetsCloseConnection() throws Exception {
250         this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
251         final HttpHost target = start();
252 
253         this.client.setMaxPerRoute(3);
254         this.client.setMaxTotal(3);
255 
256         final String pattern = RndTestPatternGenerator.generateText();
257 
258         for (int i = 0; i < REQ_NUM; i++) {
259             final BasicHttpRequest request = new BasicHttpRequest("GET", pattern + "x1");
260             request.addHeader(HttpHeaders.CONNECTION, "Close");
261             final Future<HttpResponse> future = this.client.execute(target, request);
262             final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
263             Assert.assertNotNull(response);
264             Assert.assertEquals(pattern, EntityUtils.toString(response.getEntity()));
265         }
266     }
267 
268     @Test
269     public void testHttpGetIdentityTransfer() throws Exception {
270         this.server.setHttpProcessor(new ImmutableHttpProcessor(new ResponseServer("TEST-SERVER/1.1")));
271         this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
272         final HttpHost target = start();
273 
274         this.client.setMaxPerRoute(3);
275         this.client.setMaxTotal(3);
276 
277         final String pattern = RndTestPatternGenerator.generateText();
278 
279         for (int i = 0; i < REQ_NUM; i++) {
280             final BasicHttpRequest request = new BasicHttpRequest("GET", pattern + "x1");
281             final Future<HttpResponse> future = this.client.execute(target, request);
282             final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
283             Assert.assertNotNull(response);
284             Assert.assertEquals(pattern, EntityUtils.toString(response.getEntity()));
285         }
286     }
287 
288 }