Skip to content

Commit dfa303c

Browse files
committed
Unable to use custom handlers for HTTP OPTIONS method in subresources
Fixes #45173 Signed-off-by: Martin Bartoš <[email protected]>
1 parent cfa2677 commit dfa303c

File tree

6 files changed

+118
-23
lines changed

6 files changed

+118
-23
lines changed

Diff for: extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/ResourceLocatorTest.java

+47-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.util.function.Supplier;
77

8+
import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.CorsPreflightResource;
89
import jakarta.ws.rs.client.Client;
910
import jakarta.ws.rs.client.ClientBuilder;
1011
import jakarta.ws.rs.client.Entity;
@@ -37,6 +38,13 @@
3738
import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.ResourceLocatorSubresource3Interface;
3839
import io.quarkus.resteasy.reactive.server.test.simple.PortProviderUtil;
3940
import io.quarkus.test.QuarkusUnitTest;
41+
import jakarta.ws.rs.HttpMethod;
42+
43+
import java.util.Arrays;
44+
45+
import static org.hamcrest.MatcherAssert.assertThat;
46+
import static org.hamcrest.Matchers.containsInAnyOrder;
47+
import static org.hamcrest.Matchers.notNullValue;
4048

4149
/**
4250
* @tpSubChapter Resource
@@ -68,7 +76,7 @@ public JavaArchive get() {
6876
JavaArchive war = ShrinkWrap.create(JavaArchive.class);
6977
war.addClass(ResourceLocatorQueueReceiver.class).addClass(ResourceLocatorReceiver.class)
7078
.addClass(ResourceLocatorRootInterface.class).addClass(ResourceLocatorSubInterface.class)
71-
.addClass(ResourceLocatorSubresource3Interface.class);
79+
.addClass(ResourceLocatorSubresource3Interface.class).addClass(CorsPreflightResource.class);
7280
war.addClasses(PortProviderUtil.class, ResourceLocatorAbstractAnnotationFreeResource.class,
7381
ResourceLocatorAnnotationFreeSubResource.class, ResourceLocatorBaseResource.class,
7482
ResourceLocatorCollectionResource.class, ResourceLocatorDirectory.class,
@@ -114,6 +122,44 @@ public void testSubresource() throws Exception {
114122
}
115123
}
116124

125+
/**
126+
* @tpTestDetails Return custom handler for HTTP OPTIONS method in subresource redirection. The {@link CorsPreflightResource} instance should be returned
127+
*/
128+
@Test
129+
@DisplayName("Test custom HTTP OPTIONS handler in subresource")
130+
public void testOptionsMethodInSubresource() {
131+
try (Response response = client.target(generateURL("/sub3/something/resources/test-options-method")).request().options()) {
132+
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
133+
134+
var customHeader = response.getHeaderString(CorsPreflightResource.TEST_PREFLIGHT_HEADER);
135+
assertThat(customHeader, notNullValue());
136+
assertThat(customHeader, is("test"));
137+
138+
var allowHeader = response.getHeaderString("Allow");
139+
assertThat(allowHeader, notNullValue());
140+
assertThat(Arrays.asList(allowHeader.split(", ")), containsInAnyOrder(HttpMethod.GET, HttpMethod.POST, HttpMethod.OPTIONS, HttpMethod.HEAD));
141+
}
142+
}
143+
144+
/**
145+
* @tpTestDetails Custom handler for HTTP OPTIONS method in subresource.
146+
*/
147+
@Test
148+
@DisplayName("Test custom explicit HTTP OPTIONS handler in subresource")
149+
public void testOptionsMethodExplicitInSubresource() {
150+
try (Response response = client.target(generateURL("/sub3/something/resources/test-options-method-explicit")).request().options()) {
151+
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
152+
153+
var customHeader = response.getHeaderString(ResourceLocatorSubresource2.TEST_PREFLIGHT_HEADER);
154+
assertThat(customHeader, notNullValue());
155+
assertThat(customHeader, is("test"));
156+
157+
var allowHeader = response.getHeaderString("Allow");
158+
assertThat(allowHeader, notNullValue());
159+
assertThat(Arrays.asList(allowHeader.split(", ")), containsInAnyOrder(HttpMethod.GET));
160+
}
161+
}
162+
117163
/**
118164
* @tpTestDetails Two matching methods, one a resource locator, the other a resource method.
119165
* @tpSince RESTEasy 3.0.20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.quarkus.resteasy.reactive.server.test.resource.basic.resource;
2+
3+
import jakarta.ws.rs.HttpMethod;
4+
import jakarta.ws.rs.OPTIONS;
5+
import jakarta.ws.rs.Path;
6+
import jakarta.ws.rs.core.Response;
7+
8+
public class CorsPreflightResource {
9+
public static final String TEST_PREFLIGHT_HEADER = "preflight-header-test";
10+
11+
@Path("{any:.*}")
12+
@OPTIONS
13+
public Response preflight() {
14+
return Response.ok().allow(HttpMethod.GET, HttpMethod.POST, HttpMethod.OPTIONS, HttpMethod.HEAD)
15+
.header(TEST_PREFLIGHT_HEADER, "test").build();
16+
}
17+
}

Diff for: extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorBaseResource.java

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.List;
77

88
import jakarta.ws.rs.GET;
9+
import jakarta.ws.rs.OPTIONS;
910
import jakarta.ws.rs.Path;
1011
import jakarta.ws.rs.PathParam;
1112
import jakarta.ws.rs.Produces;
@@ -61,4 +62,10 @@ public ResourceLocatorSubresource getSubresource() {
6162
return new ResourceLocatorSubresource();
6263
}
6364

65+
@OPTIONS
66+
@Path("{any:.*}")
67+
public Object preflight() {
68+
return "Here might be a custom handler for HTTP OPTIONS method";
69+
}
70+
6471
}

Diff for: extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorSubresource.java

+16
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import java.util.List;
44

5+
import io.vertx.core.http.HttpServerRequest;
56
import jakarta.ws.rs.BeanParam;
67
import jakarta.ws.rs.GET;
8+
import jakarta.ws.rs.HttpMethod;
79
import jakarta.ws.rs.Path;
810
import jakarta.ws.rs.QueryParam;
911
import jakarta.ws.rs.core.Context;
@@ -62,6 +64,20 @@ public String getValueFromBeanParam(@BeanParam Params params) {
6264
return params.param + " and " + params.value;
6365
}
6466

67+
@Path("/test-options-method-explicit")
68+
public Object testOptionsMethodExplicit() {
69+
return new ResourceLocatorSubresource2();
70+
}
71+
72+
@Path("/test-options-method")
73+
public Object testOptionsMethod(@Context HttpServerRequest request) {
74+
if (request.method().name().equals(HttpMethod.OPTIONS)) {
75+
return new CorsPreflightResource();
76+
}
77+
78+
return "Should be used only with HTTP @OPTIONS method";
79+
}
80+
6581
public static class Params {
6682
@RestPath
6783
String param;

Diff for: extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/ResourceLocatorSubresource2.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package io.quarkus.resteasy.reactive.server.test.resource.basic.resource;
22

33
import jakarta.ws.rs.GET;
4+
import jakarta.ws.rs.HttpMethod;
5+
import jakarta.ws.rs.OPTIONS;
46
import jakarta.ws.rs.Path;
57
import jakarta.ws.rs.PathParam;
68
import jakarta.ws.rs.core.Context;
9+
import jakarta.ws.rs.core.Response;
710
import jakarta.ws.rs.core.UriInfo;
811

912
import org.jboss.logging.Logger;
1013
import org.junit.jupiter.api.Assertions;
1114

1215
public class ResourceLocatorSubresource2 {
13-
16+
public static final String TEST_PREFLIGHT_HEADER = "test-preflight-header";
1417
private static final Logger LOG = Logger.getLogger(ResourceLocatorSubresource2.class);
1518

1619
@GET
@@ -35,4 +38,10 @@ public String doGet(@PathParam("param") String param, @Context UriInfo uri) {
3538
Assertions.assertEquals("2", param);
3639
return this.getClass().getName() + "-" + param;
3740
}
41+
42+
@OPTIONS
43+
@Path("{any:.*}")
44+
public Response preflight() {
45+
return Response.ok().allow(HttpMethod.GET).header(TEST_PREFLIGHT_HEADER, "test").build();
46+
}
3847
}

Diff for: independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ResourceLocatorHandler.java

+21-21
Original file line numberDiff line numberDiff line change
@@ -63,29 +63,29 @@ public void onComplete(Throwable throwable) {
6363
RequestMapper<RuntimeResource> mapper = target.get(requestContext.getMethod());
6464
boolean hadNullMethodMapper = false;
6565
if (mapper == null) {
66-
String requestMethod = requestContext.getMethod();
67-
if (requestMethod.equals(HttpMethod.HEAD)) {
68-
mapper = target.get(HttpMethod.GET);
69-
} else if (requestMethod.equals(HttpMethod.OPTIONS)) {
70-
Set<String> allowedMethods = new HashSet<>();
71-
for (String method : target.keySet()) {
72-
if (method == null) {
73-
continue;
74-
}
75-
allowedMethods.add(method);
76-
}
77-
allowedMethods.add(HttpMethod.OPTIONS);
78-
allowedMethods.add(HttpMethod.HEAD);
79-
requestContext.abortWith(Response.ok().allow(allowedMethods).build());
80-
return;
81-
}
66+
mapper = target.get(null); //another layer of resource locators maybe
67+
// we set this without checking if we matched, but we only use it after
68+
// we check for a null mapper, so by the time we use it, it must have meant that
69+
// we had a matcher for a null method
70+
hadNullMethodMapper = true;
8271

8372
if (mapper == null) {
84-
mapper = target.get(null); //another layer of resource locators maybe
85-
// we set this without checking if we matched, but we only use it after
86-
// we check for a null mapper, so by the time we use it, it must have meant that
87-
// we had a matcher for a null method
88-
hadNullMethodMapper = true;
73+
String requestMethod = requestContext.getMethod();
74+
if (requestMethod.equals(HttpMethod.HEAD)) {
75+
mapper = target.get(HttpMethod.GET);
76+
} else if (requestMethod.equals(HttpMethod.OPTIONS)) {
77+
Set<String> allowedMethods = new HashSet<>();
78+
for (String method : target.keySet()) {
79+
if (method == null) {
80+
continue;
81+
}
82+
allowedMethods.add(method);
83+
}
84+
allowedMethods.add(HttpMethod.OPTIONS);
85+
allowedMethods.add(HttpMethod.HEAD);
86+
requestContext.abortWith(Response.ok().allow(allowedMethods).build());
87+
return;
88+
}
8989
}
9090
}
9191
if (mapper == null) {

0 commit comments

Comments
 (0)