From 5b1f7bde900258f7afdfaf59956adee8f335a04b Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 20 Jul 2023 09:06:31 +0300 Subject: [PATCH] Properly return HTTP 406 on invalid Accept header This is very similar to what we do with the Content-Type header Fixes: #34858 (cherry picked from commit 338fe5300acfd26692e14192ae4c054cb800eb33) --- .../server/handlers/ClassRoutingHandler.java | 19 ++++--- .../test/mediatype/InvalidAcceptTest.java | 50 +++++++++++++++++++ 2 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/mediatype/InvalidAcceptTest.java diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ClassRoutingHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ClassRoutingHandler.java index 96021b564068d..df1d6de283c35 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ClassRoutingHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ClassRoutingHandler.java @@ -127,10 +127,14 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti if (!accepts.isEmpty()) { boolean hasAtLeastOneMatch = false; for (int i = 0; i < accepts.size(); i++) { - boolean matches = acceptHeaderMatches(target, accepts.get(i)); - if (matches) { - hasAtLeastOneMatch = true; - break; + try { + boolean matches = acceptHeaderMatches(target, accepts.get(i)); + if (matches) { + hasAtLeastOneMatch = true; + break; + } + } catch (IllegalArgumentException ignored) { + // the provided header was not valid } } if (!hasAtLeastOneMatch) { @@ -150,6 +154,10 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti } } + /** + * @return {@code true} if the provided string matches one of the {@code @Produces} values of the resource method + * @throws IllegalArgumentException if the provided string cannot be parsed into a {@link MediaType} + */ private boolean acceptHeaderMatches(RequestMapper.RequestMatch target, String accepts) { if ((accepts != null) && !accepts.equals(MediaType.WILDCARD)) { int commaIndex = accepts.indexOf(','); @@ -157,9 +165,8 @@ private boolean acceptHeaderMatches(RequestMapper.RequestMatch MediaType[] producesMediaTypes = target.value.getProduces().getSortedOriginalMediaTypes(); if (!multipleAcceptsValues && (producesMediaTypes.length == 1)) { // the point of this branch is to eliminate any list creation or string indexing as none is needed - MediaType acceptsMediaType = MediaType.valueOf(accepts.trim()); MediaType providedMediaType = producesMediaTypes[0]; - return providedMediaType.isCompatible(acceptsMediaType); + return providedMediaType.isCompatible(toMediaType(accepts.trim())); } else if (multipleAcceptsValues && (producesMediaTypes.length == 1)) { // this is fairly common case, so we want it to be as fast as possible // we do that by manually splitting the accepts header and immediately checking diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/mediatype/InvalidAcceptTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/mediatype/InvalidAcceptTest.java new file mode 100644 index 0000000000000..507e8c4d6bfa9 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/mediatype/InvalidAcceptTest.java @@ -0,0 +1,50 @@ +package org.jboss.resteasy.reactive.server.vertx.test.mediatype; + +import static io.restassured.RestAssured.config; +import static io.restassured.RestAssured.given; +import static io.restassured.config.EncoderConfig.encoderConfig; + +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; + +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.restassured.http.ContentType; + +public class InvalidAcceptTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(HelloResource.class); + } + }); + + @Test + public void test() { + given().config(config().encoderConfig(encoderConfig().encodeContentTypeAs("invalid", ContentType.TEXT))).body("dummy") + .accept("invalid").get("/hello") + .then() + .statusCode(406); + } + + @Path("hello") + public static class HelloResource { + + @Produces("text/plain") + @GET + public String hello() { + return "hello"; + } + } +}