From f3e217e2895c6510ec353a44bc9da585ab03dfd1 Mon Sep 17 00:00:00 2001 From: Nicolas Henneaux Date: Thu, 11 Jan 2024 20:51:01 +0100 Subject: [PATCH] Align with master Upgrade dependencies aligned with Jersey 2.x --- .github/workflows/codeql-analysis.yml | 15 +++-- .github/workflows/main.yml | 22 ++++--- pom.xml | 36 +++++----- .../httpclient/HttpClientConnector.java | 40 +++++++++--- .../httpclient/HttpClientConnectorIT.java | 65 +++++++++++++------ .../httpclient/HttpClientConnectorTest.java | 1 - .../connector/httpclient/JettyServerTest.java | 52 +++++++++++---- 7 files changed, 150 insertions(+), 81 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e4041d5..f9d8906 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,7 +18,7 @@ on: # The branches below must be a subset of the branches above branches: [ master ] schedule: - - cron: '21 13 * * 1' + - cron: '20 20 * * 2' jobs: analyze: @@ -35,11 +35,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -50,11 +50,12 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Setup Java JDK - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 11 + distribution: 'temurin' + java-version: 21 - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -68,4 +69,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 34d5287..09989d0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,9 @@ name: Java CI -on: [ push ] +on: + push: + schedule: + - cron: '0 5 */1 * *' jobs: build: @@ -10,35 +13,36 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] - java: [ 11, 17] + java: [ 11, 17, 21] experimental: [ false ] include: - - java: 19-ea + - java: 22-ea os: ubuntu-latest experimental: true - - java: 19-ea + - java: 22-ea os: macos-latest experimental: true - - java: 19-ea + - java: 22-ea os: windows-latest experimental: true name: Build with Java ${{ matrix.java }} on ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Setup Java - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: + distribution: 'temurin' java-version: ${{ matrix.java }} - name: Cache SonarCloud packages - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Maven packages - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/pom.xml b/pom.xml index 57cbb7f..516dbe7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.nhenneaux.jersey.connector.httpclient jersey-httpclient-connector - 0.3.5 + 0.3.6 Jersey Connector using java.net.http.HttpClient A Jersey connector using java.net.http.HttpClient and supporting HTTP/1.1 and HTTP/2.0 with the same client. The protocol is selected based on the server capabilities. @@ -22,11 +22,11 @@ nhenneaux https://sonarcloud.io - 2.37 + 2.41 - 9.4.49.v20220914 + 9.4.53.v20231009 3.1.9.Final - 2.19.0 + 2.22.1 @@ -104,7 +104,7 @@ org.slf4j slf4j-api - 2.0.2 + 2.0.11 test @@ -129,7 +129,7 @@ org.junit.jupiter junit-jupiter - 5.9.0 + 5.10.1 test @@ -141,7 +141,7 @@ org.mockito mockito-junit-jupiter - 4.8.0 + 5.8.0 test @@ -149,7 +149,7 @@ org.glassfish.jaxb jaxb-runtime - 2.3.6 + 2.3.8 provided @@ -159,7 +159,7 @@ org.jacoco jacoco-maven-plugin - 0.8.8 + 0.8.10 prepare-agent @@ -226,7 +226,7 @@ sign - 24CB0C2DAB84FC5C84BC0631A41ACFF32C0A49B9 + 8C334D53759C56CAFF1B5E9BBE0ED98B46B1B2EA @@ -272,9 +272,8 @@ - org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.12.1 maven-source-plugin @@ -282,29 +281,26 @@ maven-javadoc-plugin - 3.4.1 + 3.5.0 maven-deploy-plugin - 3.0.0 + 3.1.1 - org.apache.maven.plugins maven-jar-plugin 3.3.0 - org.apache.maven.plugins maven-failsafe-plugin - 2.22.2 + 3.2.5 false - org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.2.5 false @@ -343,7 +339,7 @@ Nicolas Henneaux - nicolas.henneaux@gmail.com + nicolas@henneaux.io Lead maintainer diff --git a/src/main/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnector.java b/src/main/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnector.java index 5091f7c..a48bfc3 100644 --- a/src/main/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnector.java +++ b/src/main/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnector.java @@ -57,10 +57,14 @@ public class HttpClientConnector implements Connector { private static final Runnable NO_OP = () -> { }; - private final HttpClient httpClient; + private final Supplier httpClientSupplier; public HttpClientConnector(HttpClient httpClient) { - this.httpClient = httpClient; + this(() -> httpClient); + } + + public HttpClientConnector(Supplier httpClientSupplier) { + this.httpClientSupplier = httpClientSupplier; } public HttpClientConnector(Client jaxRsClient, Configuration configuration) { @@ -73,10 +77,11 @@ public HttpClientConnector(Client jaxRsClient, Configuration configuration) { .map(URI::create) .ifPresent(proxyUri -> builder.proxy(ProxySelector.of(InetSocketAddress.createUnresolved(proxyUri.getHost(), proxyUri.getPort())))); - this.httpClient = getDurationTimeout(configuration, CONNECT_TIMEOUT) + final var client = getDurationTimeout(configuration, CONNECT_TIMEOUT) .map(builder::connectTimeout) .orElse(builder) .build(); + this.httpClientSupplier = () -> client; } static R handleInterruption(Interruptable interruptable) { @@ -112,9 +117,9 @@ public ClientResponse apply(ClientRequest clientRequest) { HttpResponse send(HttpRequest request) { return handleInterruption(() -> { try { - return httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); + return getHttpClient().send(request, HttpResponse.BodyHandlers.ofInputStream()); } catch (IOException e) { - throw new ProcessingException("The async sending process failed with error, " + e.getMessage(), e); + throw new ProcessingException("The HTTP sending process failed with error, " + e.getMessage(), e); } }); } @@ -122,9 +127,21 @@ HttpResponse send(HttpRequest request) { private ClientResponse toJerseyResponse(ClientRequest clientRequest, HttpResponse inputStreamHttpResponse) { final Response.StatusType responseStatus = Statuses.from(inputStreamHttpResponse.statusCode()); final ClientResponse jerseyResponse = new ClientResponse(responseStatus, clientRequest); + final var headers = inputStreamHttpResponse.headers(); + + final var contentLengthHeader = headers.firstValueAsLong("content-length"); + if ((contentLengthHeader.isEmpty() || contentLengthHeader.getAsLong() > 0) && inputStreamHttpResponse.statusCode() != Response.Status.NO_CONTENT.getStatusCode()) { final InputStream entityStream = inputStreamHttpResponse.body(); jerseyResponse.setEntityStream(entityStream); - inputStreamHttpResponse.headers().map().forEach((name, values) -> values.forEach(value -> jerseyResponse.header(name, value))); + } else { + //noinspection EmptyTryBlock + try (var ignored = inputStreamHttpResponse.body()) { + // nothing to do + } catch (IOException e) { + // ignored exception since stream is not used + } + } + headers.map().forEach((name, values) -> values.forEach(value -> jerseyResponse.header(name, value))); return jerseyResponse; } @@ -135,7 +152,7 @@ public Future apply(ClientRequest clientRequest, AsyncConnectorCallback async } private CompletableFuture> getSendAsync(HttpRequest request) { - final var httpResponseCompletableFuture = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()); + final var httpResponseCompletableFuture = getHttpClient().sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()); return futureTimeout(request, httpResponseCompletableFuture); } @@ -199,6 +216,9 @@ Future toJerseyResponseWithCallback(ClientRequest clientRequest, asyncConnectorCallback.response(response); } else { asyncConnectorCallback.failure(cause); + if (response != null) { + response.close(); + } } }); return clientResponseCompletableFuture; @@ -230,11 +250,11 @@ R streamRequestBody(ClientRequest clientRequest, HttpRequest.Builder request } private CompletableFuture supplyAsync(HttpRequest httpRequest, Supplier entityWriter) { - return futureTimeout(httpRequest, httpClient.executor().map(executor -> CompletableFuture.supplyAsync(entityWriter, executor)).orElseGet(() -> CompletableFuture.supplyAsync(entityWriter))); + return futureTimeout(httpRequest, getHttpClient().executor().map(executor -> CompletableFuture.supplyAsync(entityWriter, executor)).orElseGet(() -> CompletableFuture.supplyAsync(entityWriter))); } private static Void writeEntity(ClientRequest clientRequest, Runnable onError) { - try { + try (var ignoredOnlyForClose = clientRequest.getEntityStream()) { clientRequest.writeEntity(); return null; } catch (IOException e) { @@ -244,7 +264,7 @@ private static Void writeEntity(ClientRequest clientRequest, Runnable onError) { } public HttpClient getHttpClient() { - return httpClient; + return httpClientSupplier.get(); } @Override diff --git a/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnectorIT.java b/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnectorIT.java index e1cb179..b9957f6 100644 --- a/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnectorIT.java +++ b/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnectorIT.java @@ -12,12 +12,7 @@ import org.junit.jupiter.api.Timeout; import javax.ws.rs.ProcessingException; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.client.InvocationCallback; -import javax.ws.rs.client.WebTarget; +import javax.ws.rs.client.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.IOException; @@ -36,13 +31,8 @@ import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.both; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.lessThan; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertSame; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.*; @Tag("unstableGithub") class HttpClientConnectorIT { @@ -176,7 +166,9 @@ void shouldWorkWithJaxRsClientWithPost() { assertEquals(200, response.getStatus()); assertNotNull(response.readEntity(String.class)); } - } @Test + } + + @Test void shouldWorkWithJaxRsClientWithPostChunk() { final Client client = ClientBuilder.newClient(new ClientConfig().property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED").connectorProvider((jaxRsClient, config) -> new HttpClientConnector(HttpClient.newBuilder().sslContext(jaxRsClient.getSslContext()).build()))); final WebTarget target = client.target(HTTPS_DEVOXX_BE); @@ -196,7 +188,9 @@ void shouldWorkWithJaxRsClientWithMethodPost() { assertEquals(200, response.getStatus()); assertNotNull(response.readEntity(String.class)); response.close(); - } @Test + } + + @Test void shouldWorkWithJaxRsClientWithMethodPostChunk() { final Client client = ClientBuilder.newClient(new ClientConfig().property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED").connectorProvider((jaxRsClient, config) -> new HttpClientConnector(HttpClient.newBuilder().sslContext(jaxRsClient.getSslContext()).build()))); final WebTarget target = client.target(HTTPS_DEVOXX_BE); @@ -217,6 +211,7 @@ void shouldWorkWithJaxRsClientWithJsonPost() { assertNotNull(response.readEntity(String.class)); } } + @Test void shouldWorkWithJaxRsClientWithJsonPostChunk() { final Client client = ClientBuilder.newClient(new ClientConfig().property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED").connectorProvider((jaxRsClient, config) -> new HttpClientConnector(HttpClient.newBuilder().sslContext(jaxRsClient.getSslContext()).build()))); @@ -238,12 +233,14 @@ void shouldWorkWithJaxRsClientWithJsonPostAndShortTimeout() { final Exception expectedException = Assertions.assertThrows(Exception.class, () -> { //noinspection EmptyTryBlock - try (var ignored = request.post(Entity.entity(JSON, MediaType.APPLICATION_JSON_TYPE))){ + try (var ignored = request.post(Entity.entity(JSON, MediaType.APPLICATION_JSON_TYPE))) { // nothing to do } }); assertEquals(HttpConnectTimeoutException.class, expectedException.getCause().getClass()); - } @Test + } + + @Test @Timeout(2L) void shouldWorkWithJaxRsClientWithJsonPostAndShortTimeoutChunk() { final Client client = ClientBuilder.newClient(new ClientConfig().property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED").connectorProvider((jaxRsClient, config) -> new HttpClientConnector(HttpClient.newBuilder().sslContext(jaxRsClient.getSslContext()).build()))); @@ -253,13 +250,32 @@ void shouldWorkWithJaxRsClientWithJsonPostAndShortTimeoutChunk() { final Exception expectedException = Assertions.assertThrows(Exception.class, () -> { //noinspection EmptyTryBlock - try (var ignored = request.post(Entity.entity(JSON, MediaType.APPLICATION_JSON_TYPE))){ + try (var ignored = request.post(Entity.entity(JSON, MediaType.APPLICATION_JSON_TYPE))) { // nothing to do } }); assertEquals(HttpConnectTimeoutException.class, expectedException.getCause().getClass()); } + @Test + void shouldWorkWithEmptyResponse() { + try (final Response response = ClientBuilder.newClient(new ClientConfig().connectorProvider((jaxRsClient, config) -> new HttpClientConnector(HttpClient.newBuilder().sslContext(jaxRsClient.getSslContext()).build()))).target("https://httpstat.us") + .path("204").request().get()) { + assertEquals(204, response.getStatus()); + assertNull(response.readEntity(Object.class)); + } + } + + @Test + void shouldWorkWithEmptyResponseAndCode205() { + try (final Response response = ClientBuilder.newClient(new ClientConfig().connectorProvider((jaxRsClient, config) -> new HttpClientConnector(HttpClient.newBuilder().sslContext(jaxRsClient.getSslContext()).build()))).target("https://httpstat.us") + .path("205").request().get()) { + assertEquals("0", response.getHeaderString("content-length")); + assertEquals(205, response.getStatus()); + assertNull(response.readEntity(Object.class)); + } + } + @Test @Timeout(3L) void shouldWorkWithJaxRsClientWithLongTimeoutFailure() { @@ -341,7 +357,9 @@ void shouldWorkWithJaxRsClientWithJsonPostAsync() throws ExecutionException, Int final Response response = responseFuture.get(2, TimeUnit.SECONDS); assertEquals(200, response.getStatus()); assertNotNull(response.readEntity(String.class)); - } @Test + } + + @Test void shouldWorkWithJaxRsClientWithJsonPostAsyncChunk() throws ExecutionException, InterruptedException, TimeoutException { final Client client = ClientBuilder.newClient(new ClientConfig().property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED").connectorProvider((jaxRsClient, config) -> new HttpClientConnector(HttpClient.newBuilder().sslContext(jaxRsClient.getSslContext()).build()))); final WebTarget target = client.target(HTTPS_DEVOXX_BE); @@ -359,7 +377,9 @@ void shouldWorkWithJaxRsClientWithJsonPostAsyncWithCallback() throws ExecutionEx final Response response = responseFuture.get(2, TimeUnit.SECONDS); assertEquals(200, response.getStatus()); assertNotNull(response.readEntity(String.class)); - } @Test + } + + @Test void shouldWorkWithJaxRsClientWithJsonPostAsyncWithCallbackChunk() throws ExecutionException, InterruptedException, TimeoutException { final Client client = ClientBuilder.newClient(new ClientConfig().property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED").connectorProvider((jaxRsClient, config) -> new HttpClientConnector(HttpClient.newBuilder().sslContext(jaxRsClient.getSslContext()).build()))); final WebTarget target = client.target(HTTPS_DEVOXX_BE); @@ -395,6 +415,7 @@ public void failed(Throwable throwable) { assertEquals(200, objectAtomicReference.get().getStatus()); assertNotNull(response.readEntity(String.class)); } + @Test void shouldWorkWithJaxRsClientWithJsonPostAsyncWithCallbackCheckChunk() throws ExecutionException, InterruptedException, TimeoutException { final Client client = ClientBuilder.newClient(new ClientConfig().property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED").connectorProvider((jaxRsClient, config) -> new HttpClientConnector(HttpClient.newBuilder().sslContext(jaxRsClient.getSslContext()).build()))); @@ -445,7 +466,9 @@ void shouldWorkWithJaxRsClientWithStreamPost() throws IOException { assertEquals(200, response.getStatus()); assertNotNull(response.readEntity(String.class)); } - } @Test + } + + @Test void shouldWorkWithJaxRsClientWithStreamPostChunk() throws IOException { final ClientConfig configuration = new ClientConfig().property(ClientProperties.REQUEST_ENTITY_PROCESSING, "CHUNKED").connectorProvider((jaxRsClient, config) -> new HttpClientConnector(HttpClient.newBuilder().sslContext(jaxRsClient.getSslContext()).build())); configuration.register(MultiPartFeature.class); diff --git a/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnectorTest.java b/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnectorTest.java index d8b9139..a0a6754 100644 --- a/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnectorTest.java +++ b/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/HttpClientConnectorTest.java @@ -6,7 +6,6 @@ import org.glassfish.jersey.client.ClientResponse; import org.glassfish.jersey.client.spi.AsyncConnectorCallback; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; import org.mockito.ArgumentCaptor; import javax.net.ssl.SSLContext; diff --git a/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/JettyServerTest.java b/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/JettyServerTest.java index 1d571e4..99c24ae 100644 --- a/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/JettyServerTest.java +++ b/src/test/java/com/github/nhenneaux/jersey/connector/httpclient/JettyServerTest.java @@ -3,10 +3,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; +import org.hamcrest.Matchers; import org.jboss.weld.environment.se.Weld; import org.jboss.weld.environment.se.WeldContainer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.*; import javax.ws.rs.HttpMethod; import javax.ws.rs.ProcessingException; @@ -21,6 +21,8 @@ import java.net.ConnectException; import java.net.http.HttpClient; import java.security.KeyStore; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -29,6 +31,7 @@ import java.util.stream.IntStream; import static com.github.nhenneaux.jersey.connector.httpclient.JettyServer.TlsSecurityConfiguration.getKeyStore; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -37,6 +40,20 @@ class JettyServerTest { static final int PORT = 2223; private static final String PING = "/ping"; + @BeforeEach + void setUp(TestInfo testInfo) { + var testClass = testInfo.getTestClass().orElseThrow(); + var testMethod = testInfo.getTestMethod().orElseThrow(); + System.out.println(testClass.getSimpleName() + "::" + testMethod.getName() + " test has started."); + } + + @AfterEach + void tearDown(TestInfo testInfo) { + var testClass = testInfo.getTestClass().orElseThrow(); + var testMethod = testInfo.getTestMethod().orElseThrow(); + System.out.println(testClass.getSimpleName() + "::" + testMethod.getName() + " test has finished."); + } + private static WebTarget getClient(int port, KeyStore trustStore, ClientConfig clientConfig) { return ClientBuilder.newBuilder() .trustStore(trustStore) @@ -337,7 +354,7 @@ void testConcurrent() throws Exception { } @Test - @Timeout(60) + @Timeout(120) void testConcurrentHttp1() throws Exception { testConcurrent(new ClientConfig()); } @@ -387,14 +404,14 @@ private void testConcurrent(ClientConfig clientConfig, String method, String pat .trustStore(truststore) .withConfig(clientConfig) .build(); - client.target("https://localhost:" + port).path(path).request().method(method).close(); + final var webTarget = client.target("https://localhost:" + port).path(path); + webTarget.request().method(method).close(); AtomicInteger counter = new AtomicInteger(); final Runnable runnable = () -> { long start = System.nanoTime(); for (int i = 0; i < iterations; i++) { - try (Response response = client - .target("https://localhost:" + port).path(path).request().method(method)) { + try (Response response = webTarget.request().method(method)) { response.readEntity(InputStream.class).readAllBytes(); response.getStatus(); counter.incrementAndGet(); @@ -403,12 +420,23 @@ private void testConcurrent(ClientConfig clientConfig, String method, String pat System.out.println(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start) * 1.0 / reportEveryRequests); start = System.nanoTime(); } - } catch (IOException e) { + } catch (ProcessingException | IOException e) { + if (e.getMessage().contains("GOAWAY") + || e.getMessage().contains("Broken pipe") // The HTTP sending process failed with error, Broken pipe + || e.getMessage().contains("EOF reached while reading") + || e.getMessage().contains(" cancelled")) {// The HTTP sending process failed with error, Stream 673 cancelled + i--; + } else { throw new IllegalStateException(e); } } + } }; - Thread.setDefaultUncaughtExceptionHandler((t1, e) -> e.printStackTrace()); + List thrown = new ArrayList<>(); + Thread.setDefaultUncaughtExceptionHandler((t1, e) -> { + thrown.add(e); + e.printStackTrace(); + }); final Set threads = IntStream .range(0, nThreads) .mapToObj(i -> runnable) @@ -421,7 +449,7 @@ private void testConcurrent(ClientConfig clientConfig, String method, String pat for (Thread thread : threads) { thread.join(); } - + assertThat(thrown, Matchers.empty()); assertEquals((long) nThreads * iterations, counter.get()); } @@ -434,15 +462,13 @@ void shouldWorkInLoop() throws Exception { JettyServer.TlsSecurityConfiguration tlsSecurityConfiguration = tlsConfig(); for (int i = 0; i < 100; i++) { try ( - @SuppressWarnings("unused") WeldContainer container = new Weld().initialize(); - AutoCloseable ignored = jerseyServer(port, tlsSecurityConfiguration, DummyRestService.class) + var ignored = jerseyServer(port, tlsSecurityConfiguration, DummyRestService.class); + final var head = getClient(port).path(PING).request().head() ) { - try (final var head = getClient(port).path(PING).request().head()) { assertEquals(204, head.getStatus()); } } } - } } \ No newline at end of file