Deadlock reproducer

Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
diff --git a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/StressTest.java b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/StressTest.java
new file mode 100644
index 0000000..40d5bc9
--- /dev/null
+++ b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/StressTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jdk.connector.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.SslConfigurator;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.junit.Test;
+
+public class StressTest extends JerseyTest {
+
+    private static final int SSL_PORT = 29999;
+    private static final int ITERATIONS = 10000;
+    // Must be less than the server thread pool. Is there any way to get that value?.
+    private static final int N_REQUESTS = 10;
+    private static final SSLContext SSL_CONTEXT;
+    private static final ExecutorService executor = Executors.newFixedThreadPool(N_REQUESTS);
+    private static CountDownLatch requests;
+    private static CountDownLatch latch;
+
+    static {
+        System.setProperty("javax.net.ssl.keyStore", SslFilterTest.class.getResource("/keystore_server").getPath());
+        System.setProperty("javax.net.ssl.keyStorePassword", "asdfgh");
+        System.setProperty("javax.net.ssl.trustStore", SslFilterTest.class.getResource("/truststore_server").getPath());
+        System.setProperty("javax.net.ssl.trustStorePassword", "asdfgh");
+        try {
+            SSL_CONTEXT = SslConfigurator.newInstance()
+                    .trustStoreFile(SslFilterTest.class.getResource("/truststore_server").getPath())
+                    .trustStorePassword("asdfgh")
+                    .keyStoreFile(SslFilterTest.class.getResource("/keystore_server").getPath())
+                    .keyStorePassword("asdfgh").createSSLContext();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new IllegalStateException(e);
+        }
+    }
+
+    @Path("/test")
+    public static class TestResource {
+        @GET
+        @Path("/1")
+        public String test1() throws InterruptedException {
+            requests.countDown();
+            if (latch.await(100, TimeUnit.SECONDS)) {
+                return "test1";
+            } else {
+                throw new IllegalStateException("Timeout");
+            }
+        }
+        @GET
+        @Path("/2")
+        public String test2() {
+            return "test2";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.connectorProvider(new JdkConnectorProvider());
+    }
+
+    @Override
+    protected Optional<SSLContext> getSslContext() {
+        return Optional.of(SSL_CONTEXT);
+    }
+
+    @Override
+    protected Optional<SSLParameters> getSslParameters() {
+        return Optional.of(SSL_CONTEXT.createSSLEngine("localhost", SSL_PORT).getSSLParameters());
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri("https://localhost/").port(SSL_PORT).build();
+    }
+
+    @Test
+    public void hangAllRequestsStatus200() throws InterruptedException, ExecutionException {
+        assertEquals("https", getBaseUri().getScheme());
+        for (int i = 0; i < ITERATIONS; i++) {
+            requests = new CountDownLatch(N_REQUESTS);
+            latch = new CountDownLatch(1);
+            List<Future<Response>> responses = new ArrayList<>();
+            for (int j = 0; j < N_REQUESTS; j++) {
+                Future<Response> future = executor.submit(() -> target("/test/1").request().get());
+                responses.add(future);
+            }
+            assertTrue(requests.await(100, TimeUnit.SECONDS));
+            latch.countDown();
+            for (Future<Response> response : responses) {
+                assertEquals(200, response.get().getStatus());
+            }
+        }
+    }
+
+    @Test
+    public void randomnessStatus200() throws InterruptedException, ExecutionException {
+        assertEquals("https", getBaseUri().getScheme());
+        for (int i = 0; i < ITERATIONS; i++) {
+            List<Future<Response>> responses = new ArrayList<>();
+            for (int j = 0; j < N_REQUESTS; j++) {
+                Future<Response> future = executor.submit(() -> target("/test/2").request().get());
+                responses.add(future);
+            }
+            for (Future<Response> response : responses) {
+                assertEquals(200, response.get().getStatus());
+            }
+        }
+    }
+
+}