diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/HelloClient.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/HelloClient.java
index baa2e8d3771d5..2dcdb5617aca8 100644
--- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/HelloClient.java
+++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/HelloClient.java
@@ -5,6 +5,7 @@
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
@@ -23,4 +24,8 @@ public interface HelloClient {
@GET
@Path("/{name}")
public String helloWithPathParam(@PathParam("name") String name);
+
+ @GET
+ @Path("/query")
+ public String helloWithQueryParam(@QueryParam("foo") String foo);
}
diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/HelloResource.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/HelloResource.java
index 1a544e2ab878e..1e530c7ce1b25 100644
--- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/HelloResource.java
+++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/HelloResource.java
@@ -5,6 +5,7 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Request;
@@ -26,6 +27,12 @@ public String invoke(@PathParam("name") String name) {
return "Hello, " + name;
}
+ @GET
+ @Path("/query")
+ public String helloWithQueryParam(@QueryParam("foo") String foo) {
+ return "Hello, this is your query parameter: " + foo;
+ }
+
@POST
public String echo(String name, @Context Request request) {
return "hello, " + name;
diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/PassThroughResource.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/PassThroughResource.java
index 51f11c1b539ca..942914bd10198 100644
--- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/PassThroughResource.java
+++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/PassThroughResource.java
@@ -5,6 +5,7 @@
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.QueryParam;
import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.inject.RestClient;
@@ -29,6 +30,12 @@ public String invokeClientWithPathParamContainingSlash(@PathParam("name") String
return client.helloWithPathParam(name + "/" + name);
}
+ @Path("/v2/query")
+ @GET
+ public String invokeClientWithQueryParam(@QueryParam("foo") String foo) {
+ return client.helloWithQueryParam(foo);
+ }
+
@Path("/{name}")
@GET
public String invokeClientWithPathParam(@PathParam("name") String name) {
diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/StorkDevModeTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/StorkDevModeTest.java
index 5a12b520c497b..f9dd214c4e9cb 100644
--- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/StorkDevModeTest.java
+++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/StorkDevModeTest.java
@@ -79,6 +79,16 @@ void shouldSayHelloNameWithSlash() {
}
+ @Test
+ void shouldEncodeQueryCorrectly() {
+ when()
+ .get("/helper/v2/query?foo=cigüeña")
+ .then()
+ .statusCode(200)
+ .body(equalTo("Hello, this is your query parameter: cigüeña"));
+
+ }
+
@Test
void shouldSayHelloNameWithBlank() {
when()
diff --git a/independent-projects/resteasy-reactive/client/runtime/pom.xml b/independent-projects/resteasy-reactive/client/runtime/pom.xml
index 92e68aeaa6f23..098b3bb4ebb5d 100644
--- a/independent-projects/resteasy-reactive/client/runtime/pom.xml
+++ b/independent-projects/resteasy-reactive/client/runtime/pom.xml
@@ -63,6 +63,11 @@
junit-jupiter
test
+
+ org.mockito
+ mockito-core
+ test
+
org.jboss.logging
diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilter.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilter.java
index ea966459e817a..f28e673cca20b 100644
--- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilter.java
+++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilter.java
@@ -90,8 +90,8 @@ public void filter(ResteasyReactiveClientRequestContext requestContext) {
//To avoid the path double encoding we create uri with path=null and set the path after
URI newUri = new URI(scheme,
uri.getUserInfo(), host, port,
- null, uri.getQuery(), uri.getFragment());
- URI build = UriBuilder.fromUri(newUri).path(actualPath).build();
+ null, null, uri.getFragment());
+ URI build = UriBuilder.fromUri(newUri).path(actualPath).replaceQuery(uri.getRawQuery()).build();
requestContext.setUri(build);
if (measureTime && instance.gatherStatistics()) {
requestContext.setCallStatsCollector(instance);
diff --git a/independent-projects/resteasy-reactive/client/runtime/src/test/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilterTest.java b/independent-projects/resteasy-reactive/client/runtime/src/test/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilterTest.java
new file mode 100644
index 0000000000000..55127c0b249af
--- /dev/null
+++ b/independent-projects/resteasy-reactive/client/runtime/src/test/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilterTest.java
@@ -0,0 +1,56 @@
+package org.jboss.resteasy.reactive.client.impl;
+
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.net.URI;
+
+import jakarta.ws.rs.core.GenericType;
+
+import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+
+import io.smallrye.mutiny.Multi;
+import io.smallrye.mutiny.Uni;
+import io.smallrye.stork.Stork;
+import io.smallrye.stork.api.Service;
+import io.smallrye.stork.api.ServiceInstance;
+
+class StorkClientRequestFilterTest {
+ @Test
+ void testQueryEncoding() {
+ // Given
+ Stork mockStork = mock(Stork.class);
+ try (MockedStatic storkStatic = mockStatic(Stork.class)) {
+ storkStatic.when(Stork::getInstance).thenReturn(mockStork);
+ Service mockedService = mock(Service.class);
+ ServiceInstance mockedServiceInstance = mock(ServiceInstance.class);
+ when(mockStork.getService(anyString())).thenReturn(mockedService);
+ when(mockedService.selectInstanceAndRecordStart(anyBoolean()))
+ .thenReturn(Uni.createFrom().item(mockedServiceInstance));
+ StorkClientRequestFilter filter = new StorkClientRequestFilter();
+ ResteasyReactiveClientRequestContext context = mock(ResteasyReactiveClientRequestContext.class);
+
+ URI originalUri = URI
+ .create("stork://my-service/some/path?foo=bar%20baz&special=%26%3D&ae=%C3%A4&oe=%C3%B6&ue=%C3%BC");
+ when(context.getUri()).thenReturn(originalUri);
+ when(context.getResponseType()).thenReturn(new GenericType<>(Multi.class));
+
+ // When
+ filter.filter(context);
+
+ // Then
+ verify(context).setUri(argThat(uri -> {
+ // Query should be preserved and properly encoded
+ return "http://localhost:80/some/path?foo=bar%20baz&special=%26%3D&ae=%C3%A4&oe=%C3%B6&ue=%C3%BC"
+ .equals(uri.toString());
+ }));
+ }
+ }
+}