You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2019/10/12 17:16:33 UTC

[httpcomponents-core] branch jsse-provider-tests-4.4 updated (780ecc6 -> faaf6d5)

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a change to branch jsse-provider-tests-4.4
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git.


    from 780ecc6  More integration tests
     new 29cae46  HTTPCORE-601: work-around for SSL session spin on outbound session closure with Conscrypt 2.2.1 (back-ported from master)
     new faaf6d5  Conscrypt and Bouncy Castle JSSE provider integration tests

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 httpcore-nio/pom.xml                               |  10 +
 .../apache/http/nio/reactor/ssl/SSLIOSession.java  |  14 +-
 .../integration/TestJSSEProviderIntegration.java   | 275 +++++++++++++++++++++
 .../http/nio/integration/TestTLSIntegration.java   |   2 +-
 pom.xml                                            |  12 +
 5 files changed, 311 insertions(+), 2 deletions(-)
 create mode 100644 httpcore-nio/src/test/java/org/apache/http/nio/integration/TestJSSEProviderIntegration.java


[httpcomponents-core] 01/02: HTTPCORE-601: work-around for SSL session spin on outbound session closure with Conscrypt 2.2.1 (back-ported from master)

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch jsse-provider-tests-4.4
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git

commit 29cae46595f77a290a34c870a8620d2dfa2005fb
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sat Oct 12 19:13:38 2019 +0200

    HTTPCORE-601: work-around for SSL session spin on outbound session closure with Conscrypt 2.2.1 (back-ported from master)
---
 .../java/org/apache/http/nio/reactor/ssl/SSLIOSession.java | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/httpcore-nio/src/main/java/org/apache/http/nio/reactor/ssl/SSLIOSession.java b/httpcore-nio/src/main/java/org/apache/http/nio/reactor/ssl/SSLIOSession.java
index 96447d7..71b3738 100644
--- a/httpcore-nio/src/main/java/org/apache/http/nio/reactor/ssl/SSLIOSession.java
+++ b/httpcore-nio/src/main/java/org/apache/http/nio/reactor/ssl/SSLIOSession.java
@@ -35,6 +35,7 @@ import java.nio.channels.ByteChannel;
 import java.nio.channels.CancelledKeyException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.SelectionKey;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
@@ -89,6 +90,7 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
     private final SSLBuffer inPlain;
     private final InternalByteChannel channel;
     private final SSLSetupHandler handler;
+    private final AtomicInteger outboundClosedCount;
 
     private int appEventMask;
     private SessionBufferStatus appBufferStatus;
@@ -163,6 +165,7 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
         // Allocate buffers for application (unencrypted) data
         final int appBuffersize = this.sslEngine.getSession().getApplicationBufferSize();
         this.inPlain = bufferManagementStrategy.constructBuffer(appBuffersize);
+        this.outboundClosedCount = new AtomicInteger(0);
     }
 
     /**
@@ -294,7 +297,15 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
 
         SSLEngineResult result = null;
         while (handshaking) {
-            switch (this.sslEngine.getHandshakeStatus()) {
+            HandshakeStatus handshakeStatus = this.sslEngine.getHandshakeStatus();
+
+            // Work-around for what appears to be a bug in Conscrypt SSLEngine that does not
+            // transition into the handshaking state upon #closeOutbound() call but still
+            // has some handshake data stuck in its internal buffer.
+            if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING && outboundClosedCount.get() > 0) {
+                handshakeStatus = HandshakeStatus.NEED_WRAP;
+            }
+            switch (handshakeStatus) {
             case NEED_WRAP:
                // Generate outgoing handshake data
 
@@ -370,6 +381,7 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
         }
         if (this.status == CLOSING && !this.outEncrypted.hasData()) {
             this.sslEngine.closeOutbound();
+            this.outboundClosedCount.incrementAndGet();
         }
         if (this.status == CLOSING && this.sslEngine.isOutboundDone()
                 && (this.endOfStream || this.sslEngine.isInboundDone())


[httpcomponents-core] 02/02: Conscrypt and Bouncy Castle JSSE provider integration tests

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch jsse-provider-tests-4.4
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git

commit faaf6d510f7c4d2056c7e808780e382ccff2c36d
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sat Oct 12 17:00:48 2019 +0200

    Conscrypt and Bouncy Castle JSSE provider integration tests
---
 httpcore-nio/pom.xml                               |  10 +
 .../integration/TestJSSEProviderIntegration.java   | 275 +++++++++++++++++++++
 .../http/nio/integration/TestTLSIntegration.java   |   2 +-
 pom.xml                                            |  12 +
 4 files changed, 298 insertions(+), 1 deletion(-)

diff --git a/httpcore-nio/pom.xml b/httpcore-nio/pom.xml
index 84e0c36..efcebef 100644
--- a/httpcore-nio/pom.xml
+++ b/httpcore-nio/pom.xml
@@ -47,6 +47,16 @@
       <scope>compile</scope>
     </dependency>
     <dependency>
+      <groupId>org.conscrypt</groupId>
+      <artifactId>conscrypt-openjdk-uber</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.bouncycastle</groupId>
+      <artifactId>bctls-jdk15on</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
diff --git a/httpcore-nio/src/test/java/org/apache/http/nio/integration/TestJSSEProviderIntegration.java b/httpcore-nio/src/test/java/org/apache/http/nio/integration/TestJSSEProviderIntegration.java
new file mode 100644
index 0000000..a5789be
--- /dev/null
+++ b/httpcore-nio/src/test/java/org/apache/http/nio/integration/TestJSSEProviderIntegration.java
@@ -0,0 +1,275 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.nio.integration;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.URL;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.config.ConnectionConfig;
+import org.apache.http.impl.nio.pool.BasicNIOConnFactory;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.nio.reactor.ListenerEndpoint;
+import org.apache.http.nio.testserver.HttpClientNio;
+import org.apache.http.nio.testserver.HttpServerNio;
+import org.apache.http.nio.testserver.ServerConnectionFactory;
+import org.apache.http.nio.util.TestingSupport;
+import org.apache.http.protocol.ImmutableHttpProcessor;
+import org.apache.http.protocol.ResponseServer;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.util.EntityUtils;
+import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
+import org.conscrypt.Conscrypt;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExternalResource;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TestJSSEProviderIntegration {
+
+    private final static long RESULT_TIMEOUT_SEC = 30;
+    private final static int REQ_NUM = 25;
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> protocols() {
+        return Arrays.asList(new Object[][]{
+                {"Oracle"},
+                {"Conscrypt"},
+//                {"BouncyCastle"}
+        });
+    }
+
+    @BeforeClass
+    public static void determineJavaVersion() {
+        Assume.assumeTrue("Java version must be 8 or greater", TestingSupport.determineJRELevel() >= 8);
+    }
+
+    private final String securityProviderName;
+
+    private Provider securityProvider;
+    private HttpServerNio server;
+    private HttpClientNio client;
+
+    public TestJSSEProviderIntegration(final String securityProviderName) {
+        super();
+        this.securityProviderName = securityProviderName;
+    }
+
+    @Rule
+    public TestRule resourceRules = RuleChain.outerRule(new ExternalResource() {
+
+        @Override
+        protected void before() throws Throwable {
+
+            if ("Conscrypt".equalsIgnoreCase(securityProviderName)) {
+                securityProvider = Conscrypt.newProviderBuilder().provideTrustManager(true).build();
+            } else if ("BouncyCastle".equalsIgnoreCase(securityProviderName)) {
+                securityProvider = new BouncyCastleJsseProvider();
+            } else {
+                securityProvider = null;
+            }
+            if (securityProvider != null) {
+                Security.insertProviderAt(securityProvider, 1);
+            }
+        }
+
+        @Override
+        protected void after() {
+            if (securityProvider != null) {
+                Security.removeProvider(securityProvider.getName());
+                securityProvider = null;
+            }
+        }
+
+    }).around(new ExternalResource() {
+
+        @Override
+        protected void before() throws Throwable {
+            final URL keyStoreURL = TestJSSEProviderIntegration.class.getResource("/test-server.p12");
+            final String storePassword = "nopassword";
+            final SSLContext sslContext = SSLContextBuilder.create()
+                    .setKeyStoreType("pkcs12")
+                    .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
+                    .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
+                    .setSecureRandom(new SecureRandom())
+                    .build();
+
+            server = new HttpServerNio();
+            server.setConnectionFactory(new ServerConnectionFactory(sslContext, null));
+            server.setTimeout(5000);
+        }
+
+        @Override
+        protected void after() {
+            if (server != null) {
+                try {
+                    server.shutdown();
+                } catch (final Exception ignore) {
+                }
+            }
+        }
+
+    }).around(new ExternalResource() {
+
+        @Override
+        protected void before() throws Throwable {
+            final URL keyStoreURL = TestJSSEProviderIntegration.class.getResource("/test-client.p12");
+            final String storePassword = "nopassword";
+            final SSLContext sslContext = SSLContextBuilder.create()
+                    .setKeyStoreType("pkcs12")
+                    .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
+                    .setSecureRandom(new SecureRandom())
+                    .build();
+
+            client = new HttpClientNio(new BasicNIOConnFactory(sslContext, null, ConnectionConfig.DEFAULT));
+            client.setTimeout(5000);
+        }
+
+        @Override
+        protected void after() {
+            if (client != null) {
+                try {
+                    client.shutdown();
+                } catch (final Exception ignore) {
+                }
+            }
+        }
+
+    });
+
+    private HttpHost start() throws IOException, InterruptedException {
+        this.server.start();
+        this.client.start();
+
+        final ListenerEndpoint endpoint = this.server.getListenerEndpoint();
+        endpoint.waitFor();
+
+        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
+        return new HttpHost("localhost", address.getPort(), "https");
+    }
+
+    @Test
+    public void testHttpGets() throws Exception {
+        this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
+        final HttpHost target = start();
+
+        this.client.setMaxPerRoute(3);
+        this.client.setMaxTotal(3);
+
+        final String pattern = RndTestPatternGenerator.generateText();
+
+        final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
+        for (int i = 0; i < REQ_NUM; i++) {
+            final BasicHttpRequest request = new BasicHttpRequest("GET", pattern + "x1");
+            final Future<HttpResponse> future = this.client.execute(target, request);
+            queue.add(future);
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<HttpResponse> future = queue.remove();
+            final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
+            Assert.assertNotNull(response);
+            Assert.assertEquals(pattern, EntityUtils.toString(response.getEntity()));
+        }
+    }
+
+    @Test
+    public void testHttpGetsCloseConnection() throws Exception {
+        this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
+        final HttpHost target = start();
+
+        this.client.setMaxPerRoute(3);
+        this.client.setMaxTotal(3);
+
+        final String pattern = RndTestPatternGenerator.generateText();
+
+        final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
+        for (int i = 0; i < REQ_NUM; i++) {
+            final BasicHttpRequest request = new BasicHttpRequest("GET", pattern + "x1");
+            request.addHeader(HttpHeaders.CONNECTION, "Close");
+            final Future<HttpResponse> future = this.client.execute(target, request);
+            queue.add(future);
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<HttpResponse> future = queue.remove();
+            final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
+            Assert.assertNotNull(response);
+            Assert.assertEquals(pattern, EntityUtils.toString(response.getEntity()));
+        }
+    }
+
+    @Test
+    public void testHttpGetIdentityTransfer() throws Exception {
+        this.server.setHttpProcessor(new ImmutableHttpProcessor(new ResponseServer("TEST-SERVER/1.1")));
+        this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
+        final HttpHost target = start();
+
+        this.client.setMaxPerRoute(3);
+        this.client.setMaxTotal(3);
+
+        final String pattern = RndTestPatternGenerator.generateText();
+
+        final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
+        for (int i = 0; i < REQ_NUM; i++) {
+            final BasicHttpRequest request = new BasicHttpRequest("GET", pattern + "x1");
+            final Future<HttpResponse> future = this.client.execute(target, request);
+            queue.add(future);
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<HttpResponse> future = queue.remove();
+            final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
+            Assert.assertNotNull(response);
+            Assert.assertEquals(pattern, EntityUtils.toString(response.getEntity()));
+        }
+    }
+
+}
diff --git a/httpcore-nio/src/test/java/org/apache/http/nio/integration/TestTLSIntegration.java b/httpcore-nio/src/test/java/org/apache/http/nio/integration/TestTLSIntegration.java
index 975d2fa..21f70e6 100644
--- a/httpcore-nio/src/test/java/org/apache/http/nio/integration/TestTLSIntegration.java
+++ b/httpcore-nio/src/test/java/org/apache/http/nio/integration/TestTLSIntegration.java
@@ -203,7 +203,7 @@ public class TestTLSIntegration {
         server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
         server.start();
 
-        this.client = new HttpClientNio(new BasicNIOConnFactory(SSLContexts.createDefault(), null, ConnectionConfig.DEFAULT));
+        client = new HttpClientNio(new BasicNIOConnFactory(SSLContexts.createDefault(), null, ConnectionConfig.DEFAULT));
         client.setTimeout(5000);
         client.start();
 
diff --git a/pom.xml b/pom.xml
index d8b2f7f..4d70cf3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -76,6 +76,8 @@
     <maven.compiler.source>1.6</maven.compiler.source>
     <maven.compiler.target>1.6</maven.compiler.target>
     <maven.compiler.showDeprecation>false</maven.compiler.showDeprecation>
+    <conscrypt.version>2.2.1</conscrypt.version>
+    <bouncycastle.version>1.63</bouncycastle.version>
     <junit.version>4.12</junit.version>
     <mockito.version>1.10.19</mockito.version>
     <commons-logging.version>1.2</commons-logging.version>
@@ -86,6 +88,16 @@
   <dependencyManagement>
     <dependencies>
       <dependency>
+        <groupId>org.conscrypt</groupId>
+        <artifactId>conscrypt-openjdk-uber</artifactId>
+        <version>${conscrypt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.bouncycastle</groupId>
+        <artifactId>bctls-jdk15on</artifactId>
+        <version>${bouncycastle.version}</version>
+      </dependency>
+      <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>${junit.version}</version>