From a3c43ddeec3090b9d89eb0076e0129f4a12a305d Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 25 Jun 2024 11:22:35 +0300 Subject: [PATCH] Ensure that MessageBodyWriter is passed the proper media type Fixes: #41354 --- .../serialization/DynamicEntityWriter.java | 10 +- .../handlers/VariableProducesHandler.java | 2 +- .../matching/StringMessageBodyWriterTest.java | 93 +++++++++++++++++++ 3 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/matching/StringMessageBodyWriterTest.java diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/serialization/DynamicEntityWriter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/serialization/DynamicEntityWriter.java index 48b88ce0c0719..36bb1c27f2457 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/serialization/DynamicEntityWriter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/serialization/DynamicEntityWriter.java @@ -11,6 +11,7 @@ import jakarta.ws.rs.ext.MessageBodyWriter; import org.jboss.resteasy.reactive.common.util.MediaTypeHelper; +import org.jboss.resteasy.reactive.common.util.ServerMediaType; import org.jboss.resteasy.reactive.server.core.EncodedMediaType; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.core.ServerSerialisers; @@ -41,14 +42,19 @@ public void write(ResteasyReactiveRequestContext context, Object entity) throws ServerHttpRequest vertxRequest = context.serverRequest(); // first check and see if the resource method defined a media type and try to use it if ((context.getTarget() != null) && (context.getTarget().getProduces() != null)) { + ServerMediaType producesServerMediaType = context.getTarget().getProduces(); MediaType negotiatedMediaType = null; List accepts = context.getHttpHeaders().getRequestHeader(HttpHeaders.ACCEPT); for (String accept : accepts) { - negotiatedMediaType = context.getTarget().getProduces().negotiateProduces(accept).getKey(); + negotiatedMediaType = producesServerMediaType.negotiateProduces(accept).getKey(); if (negotiatedMediaType != null) { break; } } + if (negotiatedMediaType == null) { // fallback to ensure that MessageBodyWriter is passed the proper media type + negotiatedMediaType = producesServerMediaType + .negotiateProduces(vertxRequest.getRequestHeader(HttpHeaders.ACCEPT)).getKey(); + } List> writersList = serialisers.findWriters(null, entity.getClass(), negotiatedMediaType, RuntimeType.SERVER); @@ -56,7 +62,7 @@ public void write(ResteasyReactiveRequestContext context, Object entity) throws writers = writersList.toArray(EMPTY_ARRAY); // use the actual type the method declares as this is what the spec expects despite the fact that we might // have used the suffix of the subtype to determine a MessageBodyWriter - MediaType[] sortedOriginalMediaTypes = context.getTarget().getProduces().getSortedOriginalMediaTypes(); + MediaType[] sortedOriginalMediaTypes = producesServerMediaType.getSortedOriginalMediaTypes(); for (MediaType methodMediaType : sortedOriginalMediaTypes) { if (methodMediaType.isCompatible(negotiatedMediaType)) { selectedMediaType = methodMediaType; diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/VariableProducesHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/VariableProducesHandler.java index 77faad733c928..714b6b7844b12 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/VariableProducesHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/VariableProducesHandler.java @@ -52,7 +52,7 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti break; } } - if (res == null) { // fallback for some tests + if (res == null) { // fallback to ensure that MessageBodyWriter is passed the proper media type res = mediaTypeList.negotiateProduces(requestContext.serverRequest().getRequestHeader(HttpHeaders.ACCEPT)) .getKey(); } diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/matching/StringMessageBodyWriterTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/matching/StringMessageBodyWriterTest.java new file mode 100644 index 0000000000000..8ff31573b302c --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/matching/StringMessageBodyWriterTest.java @@ -0,0 +1,93 @@ +package org.jboss.resteasy.reactive.server.vertx.test.matching; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.MessageBodyWriter; +import jakarta.ws.rs.ext.Provider; + +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.Header; + +public class StringMessageBodyWriterTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class); + } + }); + + @Test + void testHelloEndpoint() { + given() + .when().get("/hello") + .then() + .statusCode(200) + .body(is("Greeting response: Hello from Quarkus REST")); + } + + @Test + void testWithNoAcceptHeader() { + // Prevent RestAssured from setting any Accept header + final var header = new Header("Accept", null); + + given() + .when() + .header(header) + .get("/hello") + .then() + .statusCode(200) + .body(is("Greeting response: Hello from Quarkus REST")); + } + + @Path("/hello") + public static class GreetingResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public Response hello() { + return Response.ok("Hello from Quarkus REST").build(); + } + } + + @Provider + public static class GreetingMessageBodyWriter implements MessageBodyWriter { + + @Override + public boolean isWriteable(final Class aClass, final Type type, final Annotation[] annotations, + final MediaType mediaType) { + return String.class.isAssignableFrom(aClass) && MediaType.TEXT_PLAIN_TYPE.isCompatible(mediaType); + } + + @Override + public void writeTo(final String s, final Class aClass, final Type type, final Annotation[] annotations, + final MediaType mediaType, + final MultivaluedMap multivaluedMap, final OutputStream outputStream) + throws IOException, WebApplicationException { + + final var content = "Greeting response: " + s; + outputStream.write(content.getBytes()); + } + } +}