-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Populate the default content type even when using Response
Even when we use Response, it's interesting to push the default content type to the context as it might get used by ContainerResponseFilters. If the Response actually overrides it, it's all taken care of. Fixes #34839
- Loading branch information
Showing
6 changed files
with
306 additions
and
0 deletions.
There are no files selected for viewing
60 changes: 60 additions & 0 deletions
60
...sy/reactive/server/test/providers/ContainerResponseFilterContentTypeNoResolutionTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package io.quarkus.resteasy.reactive.server.test.providers; | ||
|
||
import java.io.IOException; | ||
|
||
import jakarta.annotation.Priority; | ||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.Path; | ||
import jakarta.ws.rs.Produces; | ||
import jakarta.ws.rs.container.ContainerRequestContext; | ||
import jakarta.ws.rs.container.ContainerResponseContext; | ||
import jakarta.ws.rs.container.ContainerResponseFilter; | ||
import jakarta.ws.rs.core.Response; | ||
import jakarta.ws.rs.ext.Provider; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.restassured.RestAssured; | ||
|
||
public class ContainerResponseFilterContentTypeNoResolutionTest { | ||
@RegisterExtension | ||
static final QuarkusUnitTest TEST = new QuarkusUnitTest().withApplicationRoot( | ||
jar -> jar.addClasses(MediaTypeContainerResponseFilter.class, TestResource.class)); | ||
|
||
@Test | ||
public void producesMediaTypePresentInWriterInterceptor() { | ||
RestAssured | ||
.given().accept("text/*") | ||
.when().get("/test").then().statusCode(406); | ||
} | ||
|
||
@Path("/test") | ||
public static class TestResource { | ||
|
||
@GET | ||
@Produces("text/*") | ||
public Response hello() { | ||
Greeting greeting = new Greeting("Hello"); | ||
return Response.ok(greeting).build(); | ||
} | ||
} | ||
|
||
public record Greeting(String message) { | ||
} | ||
|
||
@Priority(5000) | ||
@Provider | ||
public static class MediaTypeContainerResponseFilter implements ContainerResponseFilter { | ||
|
||
@Override | ||
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) | ||
throws IOException { | ||
if (responseContext.getMediaType() != null) { | ||
throw new IllegalStateException( | ||
"MediaType shouldn't have been resolved but got: " + responseContext.getMediaType()); | ||
} | ||
} | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
...steasy/reactive/server/test/providers/ContainerResponseFilterContentTypeOverrideTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package io.quarkus.resteasy.reactive.server.test.providers; | ||
|
||
import java.io.IOException; | ||
|
||
import jakarta.annotation.Priority; | ||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.Path; | ||
import jakarta.ws.rs.Produces; | ||
import jakarta.ws.rs.container.ContainerRequestContext; | ||
import jakarta.ws.rs.container.ContainerResponseContext; | ||
import jakarta.ws.rs.container.ContainerResponseFilter; | ||
import jakarta.ws.rs.core.MediaType; | ||
import jakarta.ws.rs.core.Response; | ||
import jakarta.ws.rs.ext.Provider; | ||
|
||
import org.hamcrest.Matchers; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.restassured.RestAssured; | ||
|
||
public class ContainerResponseFilterContentTypeOverrideTest { | ||
@RegisterExtension | ||
static final QuarkusUnitTest TEST = new QuarkusUnitTest().withApplicationRoot( | ||
jar -> jar.addClasses(MediaTypeContainerResponseFilter.class, TestResource.class)); | ||
|
||
@Test | ||
public void producesMediaTypePresentInWriterInterceptor() { | ||
RestAssured.when().get("/test").then().body(Matchers.containsString("Hello")); | ||
} | ||
|
||
@Path("/test") | ||
public static class TestResource { | ||
|
||
@GET | ||
@Produces(MediaType.TEXT_XML) | ||
public Response hello() { | ||
Greeting greeting = new Greeting("Hello"); | ||
return Response.ok(greeting).type(MediaType.TEXT_PLAIN).build(); | ||
} | ||
} | ||
|
||
public record Greeting(String message) { | ||
} | ||
|
||
@Priority(5000) | ||
@Provider | ||
public static class MediaTypeContainerResponseFilter implements ContainerResponseFilter { | ||
|
||
@Override | ||
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) | ||
throws IOException { | ||
if (!responseContext.getMediaType().isCompatible(MediaType.TEXT_PLAIN_TYPE)) { | ||
throw new IllegalStateException( | ||
"MediaType was not overridden by Response, got: " + responseContext.getMediaType() | ||
+ " instead of expected: " + MediaType.TEXT_PLAIN); | ||
} | ||
} | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
...arkus/resteasy/reactive/server/test/providers/ContainerResponseFilterContentTypeTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package io.quarkus.resteasy.reactive.server.test.providers; | ||
|
||
import java.io.IOException; | ||
|
||
import jakarta.annotation.Priority; | ||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.Path; | ||
import jakarta.ws.rs.Produces; | ||
import jakarta.ws.rs.container.ContainerRequestContext; | ||
import jakarta.ws.rs.container.ContainerResponseContext; | ||
import jakarta.ws.rs.container.ContainerResponseFilter; | ||
import jakarta.ws.rs.core.MediaType; | ||
import jakarta.ws.rs.core.Response; | ||
import jakarta.ws.rs.ext.Provider; | ||
|
||
import org.hamcrest.Matchers; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.restassured.RestAssured; | ||
|
||
public class ContainerResponseFilterContentTypeTest { | ||
@RegisterExtension | ||
static final QuarkusUnitTest TEST = new QuarkusUnitTest().withApplicationRoot( | ||
jar -> jar.addClasses(MediaTypeContainerResponseFilter.class, TestResource.class)); | ||
|
||
@Test | ||
public void producesMediaTypePresentInWriterInterceptor() { | ||
RestAssured.when().get("/test").then().body(Matchers.containsString("Hello")); | ||
} | ||
|
||
@Path("/test") | ||
public static class TestResource { | ||
|
||
@GET | ||
@Produces(MediaType.TEXT_XML) | ||
public Response hello() { | ||
Greeting greeting = new Greeting("Hello"); | ||
return Response.ok(greeting).build(); | ||
} | ||
} | ||
|
||
public record Greeting(String message) { | ||
} | ||
|
||
@Priority(5000) | ||
@Provider | ||
public static class MediaTypeContainerResponseFilter implements ContainerResponseFilter { | ||
|
||
@Override | ||
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) | ||
throws IOException { | ||
if (!responseContext.getMediaType().isCompatible(MediaType.TEXT_XML_TYPE)) { | ||
throw new IllegalStateException("MediaType was not provided, got: " + responseContext.getMediaType() | ||
+ " instead of expected: " + MediaType.TEXT_XML); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
.../resteasy/reactive/server/handlers/FixedProducesSetDefaultContentTypeResponseHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package org.jboss.resteasy.reactive.server.handlers; | ||
|
||
import java.util.List; | ||
import java.util.Locale; | ||
|
||
import jakarta.ws.rs.core.HttpHeaders; | ||
import jakarta.ws.rs.core.MediaType; | ||
|
||
import org.jboss.resteasy.reactive.server.core.EncodedMediaType; | ||
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; | ||
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; | ||
|
||
/** | ||
* Handler that defines the default content type when a Response is returned. | ||
* While it might not be the final content type, we still need to make sure | ||
* the default content type is provided to {@code ContainerResponseFilter}. | ||
* <p> | ||
* This particular one is for endpoints that only produce one content type. | ||
*/ | ||
@SuppressWarnings("ForLoopReplaceableByForEach") | ||
public class FixedProducesSetDefaultContentTypeResponseHandler implements ServerRestHandler { | ||
|
||
private final EncodedMediaType mediaType; | ||
private final String mediaTypeString; | ||
private final String mediaTypeSubstring; | ||
|
||
public FixedProducesSetDefaultContentTypeResponseHandler(MediaType mediaType) { | ||
this.mediaType = new EncodedMediaType(mediaType); | ||
this.mediaTypeString = mediaType.getType() + "/" + mediaType.getSubtype(); | ||
this.mediaTypeSubstring = mediaType.getType() + "/*"; | ||
} | ||
|
||
@Override | ||
public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { | ||
List<String> acceptValues; | ||
if (requestContext.isProducesChecked() || | ||
(acceptValues = (List<String>) requestContext.getHeader(HttpHeaders.ACCEPT, false)).isEmpty()) { | ||
requestContext.setResponseContentType(mediaType); | ||
} else { | ||
for (int i = 0; i < acceptValues.size(); i++) { | ||
String accept = acceptValues.get(i); | ||
//TODO: this needs to be optimized | ||
if (accept.contains(mediaTypeString) || accept.contains("*/*") || accept.contains(mediaTypeSubstring)) { | ||
requestContext.setResponseContentType(mediaType); | ||
break; | ||
} else { | ||
// some clients might be sending the header with incorrect casing... | ||
String lowercaseAccept = accept.toLowerCase(Locale.ROOT); | ||
if (lowercaseAccept.contains(mediaTypeString) || lowercaseAccept.contains(mediaTypeSubstring)) { | ||
requestContext.setResponseContentType(mediaType); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
...steasy/reactive/server/handlers/VariableProducesSetDefaultContentTypeResponseHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package org.jboss.resteasy.reactive.server.handlers; | ||
|
||
import java.util.List; | ||
|
||
import jakarta.ws.rs.NotAcceptableException; | ||
import jakarta.ws.rs.core.HttpHeaders; | ||
import jakarta.ws.rs.core.MediaType; | ||
|
||
import org.jboss.resteasy.reactive.common.util.MediaTypeHelper; | ||
import org.jboss.resteasy.reactive.common.util.ServerMediaType; | ||
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; | ||
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; | ||
|
||
/** | ||
* Handler that defines the default content type when a Response is returned. | ||
* While it might not be the final content type, we still need to make sure | ||
* the default content type is provided to {@code ContainerResponseFilter}. | ||
* <p> | ||
* This particular one negotiates the content type when there are multiple ones defined. | ||
*/ | ||
public class VariableProducesSetDefaultContentTypeResponseHandler implements ServerRestHandler { | ||
|
||
private final ServerMediaType mediaTypeList; | ||
|
||
public VariableProducesSetDefaultContentTypeResponseHandler(ServerMediaType mediaTypeList) { | ||
this.mediaTypeList = mediaTypeList; | ||
} | ||
|
||
@Override | ||
public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { | ||
MediaType res = null; | ||
List<String> accepts = requestContext.getHttpHeaders().getRequestHeader(HttpHeaders.ACCEPT); | ||
for (String accept : accepts) { | ||
res = mediaTypeList.negotiateProduces(accept).getKey(); | ||
if (res != null) { | ||
break; | ||
} | ||
} | ||
if (res == null) { // fallback to ensure that MessageBodyWriter is passed the proper media type | ||
res = mediaTypeList.negotiateProduces(requestContext.serverRequest().getRequestHeader(HttpHeaders.ACCEPT)) | ||
.getKey(); | ||
} | ||
|
||
if (res == null) { | ||
return; | ||
} | ||
|
||
if (MediaTypeHelper.isUnsupportedWildcardSubtype(res)) { // spec says the acceptable wildcard subtypes are */* or application/* | ||
throw new NotAcceptableException(); | ||
} | ||
|
||
requestContext.setResponseContentType(res); | ||
} | ||
} |