Repository: quarkusio/quarkus (2025-07-23T10:44:31Z)

Issue:
UriInfo.getRequestUri loses query parameters if ContainerRequestContext.setRequestUri is called
### Describe the bug

If a pre-matching ContainerRequestFilter updates the request URI via ContainerRequestContext.setRequestUri then UriInfo.getRequestUri does not contain the query parameters anymore.

### Expected behavior

After an update UriInfo.getRequestUri should contain the query parameters based on the URI set by ContainerRequestContext.setRequestUri.

### Actual behavior

The query parameters are extracted from the specified URI and stored internally but the URI which is returned by UriInfo.getRequestUri does not contain any query parameters (neither the old nor the new).

### How to Reproduce?

Create a resource:

```
@Path("/hello")
public class GreetingResource {
    
    @Context
    UriInfo uriInfo;
    
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        System.out.println("URI inside resource: "+uriInfo.getRequestUri());
        System.out.println("Query parameters inside resource: "+uriInfo.getQueryParameters());
        
        return "Hello from Quarkus REST";
    }
}
```
Use a container request filter:

```
@Provider
@PreMatching
public class MyFilter implements ContainerRequestFilter {
    
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        // Unmodified: "http://localhost:8080/hello.pdf?a=b&x=y"
        System.out.println("URI before filter update: "+requestContext.getUriInfo().getRequestUri());
        System.out.println("Query parameters before filter update: "+requestContext.getUriInfo().getQueryParameters());
        
        // update: cut out ".pdf" and remove "x=y"
        requestContext.setRequestUri(URI.create("http://localhost:8080/hello?a=b"));
        
        // Actual result:   "http://localhost:8080/hello"
        // Expected result: "http://localhost:8080/hello?a=b"
        System.out.println("URI after filter update: "+requestContext.getUriInfo().getRequestUri());
        System.out.println("Query parameters after filter update: "+requestContext.getUriInfo().getQueryParameters());
    }
}
```

Start with `quarkus dev`.
Use `curl -X GET "http://localhost:8080/hello.pdf?a=b&x=y"` to print the values.


### Output of `uname -a` or `ver`

Microsoft Windows [Version 10.0.26100.3775]

### Output of `java -version`

java 24.0.1 2025-04-15 Java(TM) SE Runtime Environment (build 24.0.1+9-30) Java HotSpot(TM) 64-Bit Server VM (build 24.0.1+9-30, mixed mode, sharing)

### Quarkus version or git rev

3.24.4

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)

### Additional information

The issue seems to be caused by:

`ResteasyReactiveRequestContext.getAbsoluteURI()`

1. It uses `serverRequest().getRequestAbsoluteUri()` if path is null (includes query parameters)
2. It uses `new URI(getScheme(), getAuthority(), path, null, null).toASCIIString()` if path is not null (ignores query parameters)


Discussion:
/cc @FroMage (rest)



Patch:
diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java
index 741dbea07b294..340906c2c6641 100644
--- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java
+++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java
@@ -131,6 +131,8 @@ public abstract class ResteasyReactiveRequestContext
     // this is only set if we override the requestUri
     private String scheme;
     // this is only set if we override the requestUri
+    private String query;
+    // this is only set if we override the requestUri
     private String authority;
     private String remaining;
     private EncodedMediaType responseContentType;
@@ -510,7 +512,7 @@ public String getAbsoluteURI() {
         // Note: we could store our cache as normalised, but I'm not sure if the vertx one is normalised
         if (absoluteUri == null) {
             try {
-                absoluteUri = new URI(getScheme(), getAuthority(), path, null, null).toASCIIString();
+                absoluteUri = new URI(getScheme(), getAuthority(), path, query, null).toASCIIString();
             } catch (URISyntaxException e) {
                 throw new RuntimeException(e);
             }
@@ -536,6 +538,7 @@ public ResteasyReactiveRequestContext setRequestUri(URI requestURI) {
         this.path = requestURI.getPath();
         this.authority = requestURI.getRawAuthority();
         this.scheme = requestURI.getScheme();
+        this.query = requestURI.getQuery();
         setQueryParamsFrom(requestURI.toString());
         // invalidate those
         this.uriInfo = null;


Test patch:
diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/resource/basic/RequestFilterQueryParamsTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/resource/basic/RequestFilterQueryParamsTest.java
index 1c286fcd5422f..a295578b9b56e 100644
--- a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/resource/basic/RequestFilterQueryParamsTest.java
+++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/resource/basic/RequestFilterQueryParamsTest.java
@@ -13,8 +13,10 @@
 import jakarta.ws.rs.container.ContainerRequestContext;
 import jakarta.ws.rs.container.ContainerRequestFilter;
 import jakarta.ws.rs.container.PreMatching;
+import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.UriBuilder;
+import jakarta.ws.rs.core.UriInfo;
 import jakarta.ws.rs.ext.Provider;
 
 import org.hamcrest.Matchers;
@@ -37,16 +39,31 @@ public void test() {
                 .then()
                 .statusCode(200)
                 .body(Matchers.equalTo("Hello filter"));
+
+        get("/dummy/2")
+                .then()
+                .statusCode(200)
+                .body(Matchers.equalTo("Hello name=filter"));
     }
 
     @Path("hello")
     public static class BlockingHelloResource {
 
+        @Context
+        UriInfo uriInfo;
+
         @GET
         @Produces(MediaType.TEXT_PLAIN)
         public String hello(@QueryParam("name") @DefaultValue("world") String name) {
             return "Hello " + name;
         }
+
+        @Path("2")
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        public String hello2() {
+            return "Hello " + uriInfo.getRequestUri().getQuery();
+        }
     }
 
     @Provider
@@ -56,8 +73,9 @@ public static class ReplacePathAndQueryParamsFilter implements ContainerRequestF
         @Override
         public void filter(ContainerRequestContext requestContext)
                 throws IOException {
-            UriBuilder builder = requestContext.getUriInfo().getRequestUriBuilder();
-            builder.replacePath("/hello");
+            UriInfo originalUriInfo = requestContext.getUriInfo();
+            UriBuilder builder = originalUriInfo.getRequestUriBuilder();
+            builder.replacePath(originalUriInfo.getPath().contains("2") ? "/hello/2" : "/hello");
             builder.replaceQueryParam("name", "filter");
             URI uri = builder.build();
             requestContext.setRequestUri(uri);