From 0129d94b44e0d465822a9414a5ce884e9fb078f1 Mon Sep 17 00:00:00 2001 From: Fedor Dudinskiy Date: Mon, 30 Oct 2023 18:08:13 +0100 Subject: [PATCH] Cover bug with rest client hanging on malformed chunks https://github.com/quarkusio/quarkus/issues/36257 https://issues.redhat.com/browse/QQE-254 --- http/rest-client-reactive/pom.xml | 10 ++++++ .../restclient/reactive/MalformedClient.java | 24 +++++++++++++ .../resources/MalformedClientResource.java | 26 ++++++++++++++ .../src/main/resources/modern.properties | 1 + .../reactive/ReactiveRestClientIT.java | 35 ++++++++++++++++++- 5 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/MalformedClient.java create mode 100644 http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/resources/MalformedClientResource.java diff --git a/http/rest-client-reactive/pom.xml b/http/rest-client-reactive/pom.xml index 36453b468..dc07b8595 100644 --- a/http/rest-client-reactive/pom.xml +++ b/http/rest-client-reactive/pom.xml @@ -31,5 +31,15 @@ org.apache.commons commons-lang3 + + org.wiremock + wiremock + 3.2.0 + test + + + io.quarkus + quarkus-smallrye-fault-tolerance + diff --git a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/MalformedClient.java b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/MalformedClient.java new file mode 100644 index 000000000..6a6e286e0 --- /dev/null +++ b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/MalformedClient.java @@ -0,0 +1,24 @@ +package io.quarkus.ts.http.restclient.reactive; + +import static java.time.temporal.ChronoUnit.SECONDS; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.microprofile.faulttolerance.Timeout; +import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +@RegisterRestClient +@Path("/malformed") +@RegisterClientHeaders +@Timeout(value = 5, unit = SECONDS) +public interface MalformedClient { + + @GET + @Path("/") + @Produces(MediaType.APPLICATION_JSON) + String get(); +} diff --git a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/resources/MalformedClientResource.java b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/resources/MalformedClientResource.java new file mode 100644 index 000000000..97a788a7a --- /dev/null +++ b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/resources/MalformedClientResource.java @@ -0,0 +1,26 @@ +package io.quarkus.ts.http.restclient.reactive.resources; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import io.quarkus.ts.http.restclient.reactive.MalformedClient; + +@Path("/client/malformed") +public class MalformedClientResource { + @Inject + @RestClient + MalformedClient client; + + @GET + @Path("/") + public String getMalformed() { + try { + return client.get(); + } catch (Exception ex) { + return ex.getClass().getName(); + } + } +} diff --git a/http/rest-client-reactive/src/main/resources/modern.properties b/http/rest-client-reactive/src/main/resources/modern.properties index 4f9f899f6..f549a895c 100644 --- a/http/rest-client-reactive/src/main/resources/modern.properties +++ b/http/rest-client-reactive/src/main/resources/modern.properties @@ -4,6 +4,7 @@ quarkus.rest-client."io.quarkus.ts.http.restclient.reactive.files.FileClient".ur quarkus.rest-client."io.quarkus.ts.http.restclient.reactive.BookClient".url=http://localhost:${quarkus.http.port} quarkus.rest-client."io.quarkus.ts.http.restclient.reactive.BookClient.AuthorClient".url=http://localhost:${quarkus.http.port} quarkus.rest-client."io.quarkus.ts.http.restclient.reactive.ResourceAndSubResourcesClient".url=http://localhost:${quarkus.http.port} +quarkus.rest-client."io.quarkus.ts.http.restclient.reactive.MalformedClient".url=http://localhost:${quarkus.http.port} quarkus.rest-client.logging.scope=request-response quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG diff --git a/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ReactiveRestClientIT.java b/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ReactiveRestClientIT.java index 97e104322..d156121c1 100644 --- a/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ReactiveRestClientIT.java +++ b/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ReactiveRestClientIT.java @@ -1,5 +1,6 @@ package io.quarkus.ts.http.restclient.reactive; +import static com.github.tomakehurst.wiremock.core.Options.ChunkedEncodingPolicy.NEVER; import static io.quarkus.ts.http.restclient.reactive.resources.PlainBookResource.SEARCH_TERM_VAL; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; @@ -8,9 +9,16 @@ import java.util.UUID; import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.http.Fault; + import io.quarkus.test.bootstrap.Protocol; import io.quarkus.test.bootstrap.RestService; import io.quarkus.test.scenarios.QuarkusScenario; @@ -23,9 +31,23 @@ public class ReactiveRestClientIT { private static final String HEMINGWAY_BOOKS = "In Our Time, The Sun Also Rises, A Farewell to Arms, The Old Man and the Sea"; + private static WireMockServer mockServer; + + static { + mockServer = new WireMockServer(WireMockConfiguration.options() + .dynamicPort() + .useChunkedTransferEncoding(NEVER)); + mockServer.stubFor(WireMock.get(WireMock.urlPathMatching("/malformed/")) + .willReturn(WireMock.aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK))); + mockServer.start(); + } + + static final String MALFORMED_URL = "quarkus.rest-client.\"io.quarkus.ts.http.restclient.reactive.MalformedClient\".url"; @QuarkusApplication - static RestService app = new RestService().withProperties("modern.properties"); + static RestService app = new RestService() + .withProperties("modern.properties") + .withProperty(MALFORMED_URL, () -> mockServer.baseUrl()); @Test public void shouldGetBookFromRestClientJson() { @@ -226,4 +248,15 @@ public void programmaticRestClient() { .statusCode(200) .body(is("The Hobbit: An Unexpected Journey")); } + + @Test + public void malformedChunk() { + Response response = app.given().get("/client/malformed"); + Assertions.assertEquals("io.vertx.core.http.HttpClosedException", response.body().asString()); + } + + @AfterAll + static void afterAll() { + mockServer.stop(); + } }