Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redirect to static content not working in production mode when using RESTEasy, quarkus.http.root-path and no trailing slash #35076

Open
FrenkelS opened this issue Jul 28, 2023 · 2 comments
Labels
area/vertx kind/bug Something isn't working

Comments

@FrenkelS
Copy link

FrenkelS commented Jul 28, 2023

Describe the bug

Quarkus behaves differently in development mode compared to production mode when using RESTEasy, a custom quarkus.http.root-path and static content.

My application.properties contains

quarkus.http.root-path=/testing
quarkus.swagger-ui.always-include=true

and I have one REST service:

package my.problem;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("")
public class ExampleResource {

    @GET
    @Path("/hello")
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}

I also have an index.html in src/main/resources/META-INF/resources that has a link to Swagger UI.
When Quarkus runs in development mode (mvn quarkus:dev) localhost:8080/testing redirects to localhost:8080/testing/ and index.html is shown in my browser. But when Quarkus runs in production mode (java -jar target\quarkus-app\quarkus-run.jar), localhost:8080/testing returns HTTP status code 404.

The following urls behave the same in both modes:
localhost:8080/testing/
localhost:8080/testing/q/swagger-ui
localhost:8080/testing/q/swagger-ui/
localhost:8080/testing/hello
localhost:8080/testing/hello/

Expected behavior

In production mode, curl localhost:8080/testing -i should show HTTP status code 301 and a redirect to /testing/.

Actual behavior

In production mode, curl localhost:8080/testing -i shows HTTP status code 404.

How to Reproduce?

my-redirect-problem.zip

Output of uname -a or ver

Microsoft Windows [Version 10.0.22621.1992]

Output of java -version

openjdk version "17" 2021-09-14 OpenJDK Runtime Environment (build 17+35-2724) OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)

GraalVM version (if different from Java)

No response

Quarkus version or git rev

3.2.2.Final

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

Apache Maven 3.9.3 (21122926829f1ead511c958d89bd2f672198ae9f) Maven home: C:\Progs\apache-maven-3.9.3 Java version: 17, vendor: Oracle Corporation, runtime: C:\Progs\Java\jdk-17 Default locale: nl_NL, platform encoding: Cp1252 OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

Additional information

When quarkus-undertow is added as a dependency, production mode behaves the same as development mode.
But curl localhost:8080/testing -i shows HTTP 302 instead of 301, and the redirect is to http://localhost:8080/testing/ instead of /testing/.

This problem is similar to #3091, #6760 and #19492

@FrenkelS FrenkelS added the kind/bug Something isn't working label Jul 28, 2023
@quarkus-bot quarkus-bot bot added env/windows Impacts Windows machines triage/needs-triage labels Jul 28, 2023
@geoand geoand added area/vertx and removed triage/needs-triage env/windows Impacts Windows machines labels Jul 28, 2023
@geoand
Copy link
Contributor

geoand commented Jul 28, 2023

I took a quick look at this and even if we apply the following patch to Quarkus:

diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java
index 7738d9bfe34..df476450956 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java
@@ -88,10 +88,17 @@ public void handle(RoutingContext ctx) {
             handlers.add(new Handler<>() {
                 @Override
                 public void handle(RoutingContext ctx) {
-                    String rel = ctx.mountPoint() == null ? ctx.normalizedPath()
-                            : ctx.normalizedPath().substring(
-                                    // let's be extra careful here in case Vert.x normalizes the mount points at some point
-                                    ctx.mountPoint().endsWith("/") ? ctx.mountPoint().length() - 1 : ctx.mountPoint().length());
+                    String rel;
+                    if (ctx.mountPoint() == null) {
+                        rel = ctx.normalizedPath();
+                    } else {
+                        rel = ctx.normalizedPath().substring(
+                                // let's be extra careful here in case Vert.x normalizes the mount points at some point

then Vert.x's StaticResourceHandler still does not return the file for curl localhost:8080/testing because of how the non-root path is handled.

What I think would also be needed to fix things would be to in Vert.x Web is:

diff --git a/vertx-web/src/main/java/io/vertx/ext/web/handler/impl/StaticHandlerImpl.java b/vertx-web/src/main/java/io/vertx/ext/web/handler/impl/StaticHandlerImpl.java
--- a/vertx-web/src/main/java/io/vertx/ext/web/handler/impl/StaticHandlerImpl.java	(revision 58678777c7607289a0b6b8cb87a514155017217f)
+++ b/vertx-web/src/main/java/io/vertx/ext/web/handler/impl/StaticHandlerImpl.java	(revision 12fd7ed21e85bc09ff5a5dfe92f1e62fb1e9843a)
@@ -195,9 +195,21 @@
           path,
           // only root is known for sure to be a directory. all other directories must be
           // identified as such.
-          !directoryListing && "/".equals(path));
+          !directoryListing && isPathRoot(path, context));
     }
   }
+
+  private static boolean isPathRoot(String path, RoutingContext context) {
+    if ("/".equals(path)) {
+      return true;
+    }
+
+    String mountPoint = context.mountPoint();
+    if (mountPoint == null) {
+      return false;
+    }
+    return mountPoint.equals(path.endsWith("/") ? path : path + "/");
+  }

WDYT @cescoffier @vietj?

@FrenkelS
Copy link
Author

FrenkelS commented Sep 7, 2023

As a workaround I've added dependency quarkus-reactive-routes and do the redirect myself in a @RouteFilter:

package my.problem;

import java.net.HttpURLConnection;
import java.util.HashSet;
import java.util.Set;

import io.quarkus.vertx.web.RouteFilter;
import io.vertx.ext.web.RoutingContext;
import org.eclipse.microprofile.config.inject.ConfigProperty;

class RedirectBugFixFilter {

    private final Set<String> rootPaths = new HashSet<>();

    RedirectBugFixFilter(@ConfigProperty(name = "quarkus.http.root-path",         defaultValue = "/") String quarkusHttpRootPath,
                         @ConfigProperty(name = "quarkus.resteasy.path",          defaultValue = "/") String quarkusResteasyPath,
                         @ConfigProperty(name = "quarkus.resteasy-reactive.path", defaultValue = "/") String quarkusResteasyReactivePath) {
        addRootPath(quarkusHttpRootPath);
        addRootPath(quarkusResteasyPath);
        addRootPath(quarkusResteasyReactivePath);
    }

    private void addRootPath(String rootPath) {
        if (!"/".equals(rootPath)) {
            rootPaths.add(rootPath.endsWith("/") ? rootPath.substring(0, rootPath.length() - 1) : rootPath);
        }
    }

    @RouteFilter( 100 )
    void redirect(RoutingContext routingContext) {
        String uri = routingContext.request().uri();
        if (rootPaths.contains(uri)) {
            routingContext.response()
                          .setStatusCode(HttpURLConnection.HTTP_MOVED_PERM)
                          .putHeader("location", uri + '/')
                          .end();
        } else {
            routingContext.next();
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/vertx kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants