Skip to content

Commit

Permalink
Validate media types for sub resources in Resteasy Reactive
Browse files Browse the repository at this point in the history
At the moment, there are two places where resteasy reactive is verifying the media types: (1) at the handler `ClassRoutingHandler` only for the main resources (it's not invoked for sub-resources; and (2) at the handler `RequestDeserializeHandler` for the rest. 
The issue is that the verification at `RequestDeserializeHandler` takes the one from the user which might not be compatible with the one set by the user using the annotation `@Consumes`. 
Note that I tried to make this handler to be invoked also for sub resources but it caused more troubles and it would not fix the root cause of the issue that is the logic in the handler `RequestDeserializeHandler` is wrong. 
Fix quarkusio#28460
  • Loading branch information
Sgitario authored and igorregis committed Oct 17, 2022
1 parent a33b0ba commit 93ca22a
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 10 deletions.
Expand Up @@ -8,6 +8,7 @@

import java.util.function.Supplier;

import org.apache.http.HttpStatus;
import org.hamcrest.Matchers;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
Expand All @@ -19,6 +20,7 @@

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.http.Headers;

public class SimpleQuarkusRestTestCase {
Expand Down Expand Up @@ -107,6 +109,12 @@ public void testSubResource() {
.then().body(Matchers.equalTo("otherSub"));
RestAssured.get("/simple/sub")
.then().body(Matchers.equalTo("sub"));

RestAssured.with()
.contentType(ContentType.JSON)
.body("{\"test\": true}")
.patch("/simple/sub/patch/text")
.then().statusCode(HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE);
}

@Test
Expand Down
@@ -1,7 +1,10 @@
package io.quarkus.resteasy.reactive.server.test.simple;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PATCH;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;

public class SubResource {

Expand All @@ -15,4 +18,11 @@ public String sub() {
public String otherPath() {
return "otherSub";
}

@Path("patch/text")
@PATCH
@Consumes(MediaType.TEXT_PLAIN)
public String patchWithTextPlain(String patch) {
return "test-value: " + patch;
}
}
Expand Up @@ -312,8 +312,8 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz,
// we only need to parse the signature and create generic type when the declared type differs from the type
genericType = TypeSignatureParser.parse(bodyParameter.signature);
}
handlers.add(new RequestDeserializeHandler(typeClass, genericType,
consumesMediaTypes.isEmpty() ? null : consumesMediaTypes.get(0), serialisers, bodyParameterIndex));
handlers.add(new RequestDeserializeHandler(typeClass, genericType, consumesMediaTypes, serialisers,
bodyParameterIndex));
}

// given that we may inject form params in the endpoint we need to make sure we read the body before
Expand Down
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;

import javax.ws.rs.BadRequestException;
Expand All @@ -17,6 +18,7 @@
import javax.ws.rs.ext.ReaderInterceptor;

import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.common.util.MediaTypeHelper;
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
import org.jboss.resteasy.reactive.server.core.ServerSerialisers;
import org.jboss.resteasy.reactive.server.jaxrs.ReaderInterceptorContextImpl;
Expand All @@ -29,30 +31,40 @@ public class RequestDeserializeHandler implements ServerRestHandler {

private final Class<?> type;
private final Type genericType;
private final MediaType mediaType;
private final List<MediaType> acceptableMediaTypes;
private final ServerSerialisers serialisers;
private final int parameterIndex;

public RequestDeserializeHandler(Class<?> type, Type genericType, MediaType mediaType, ServerSerialisers serialisers,
public RequestDeserializeHandler(Class<?> type, Type genericType, List<MediaType> acceptableMediaTypes,
ServerSerialisers serialisers,
int parameterIndex) {
this.type = type;
this.genericType = genericType;
this.mediaType = mediaType;
this.acceptableMediaTypes = acceptableMediaTypes;
this.serialisers = serialisers;
this.parameterIndex = parameterIndex;
}

@Override
public void handle(ResteasyReactiveRequestContext requestContext) throws Exception {
MediaType effectiveRequestType = mediaType;
String requestTypeString = requestContext.serverRequest().getRequestHeader(HttpHeaders.CONTENT_TYPE);
if (requestTypeString != null) {
MediaType effectiveRequestType = null;
Object requestType = requestContext.getHeader(HttpHeaders.CONTENT_TYPE, true);
if (requestType != null) {
try {
effectiveRequestType = MediaType.valueOf(requestTypeString);
effectiveRequestType = MediaType.valueOf((String) requestType);
} catch (Exception e) {
throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST).build());
}
} else if (effectiveRequestType == null) {

// We need to verify media type for sub-resources, this mimics what is done in {@code ClassRoutingHandler}
if (MediaTypeHelper.getFirstMatch(
acceptableMediaTypes,
Collections.singletonList(effectiveRequestType)) == null) {
throw new NotSupportedException("The content-type header value did not match the value in @Consumes");
}
} else if (!acceptableMediaTypes.isEmpty()) {
effectiveRequestType = acceptableMediaTypes.get(0);
} else {
effectiveRequestType = MediaType.APPLICATION_OCTET_STREAM_TYPE;
}
List<MessageBodyReader<?>> readers = serialisers.findReaders(null, type, effectiveRequestType, RuntimeType.SERVER);
Expand Down

0 comments on commit 93ca22a

Please sign in to comment.