This is an automated email from the ASF dual-hosted git repository.
mpochatkin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 5f256c844b7 IGNITE-23421 Not found URLs respond with 409 instead of
404 (#7577)
5f256c844b7 is described below
commit 5f256c844b72c6cdfde30ed3e056ab08a143e47b
Author: Vadim Pakhnushev <[email protected]>
AuthorDate: Fri Feb 13 14:32:47 2026 +0300
IGNITE-23421 Not found URLs respond with 409 instead of 404 (#7577)
---
...lusterStateHttpServerFilterInitializedTest.java | 21 +++-------
...terStateHttpServerFilterNotInitializedTest.java | 45 +++++++++++++---------
.../ClusterStateHttpServerFilter.java | 17 +++++++-
.../ignite/internal/rest/RestComponentTest.java | 17 ++++++++
4 files changed, 66 insertions(+), 34 deletions(-)
diff --git
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterInitializedTest.java
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterInitializedTest.java
index 90dd83a39ca..c8c27461ebe 100644
---
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterInitializedTest.java
+++
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterInitializedTest.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.rest;
+import static
org.apache.ignite.internal.rest.ItClusterStateHttpServerFilterNotInitializedTest.disabledEndpoints;
+import static
org.apache.ignite.internal.rest.ItClusterStateHttpServerFilterNotInitializedTest.enabledEndpoints;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import io.micronaut.http.HttpRequest;
@@ -28,7 +30,6 @@ import java.util.stream.Stream;
import org.apache.ignite.internal.ClusterConfiguration;
import org.apache.ignite.internal.ClusterPerClassIntegrationTest;
import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
/** Tests that after cluster is initialized, all endpoints are available. */
@@ -40,23 +41,13 @@ public class ItClusterStateHttpServerFilterInitializedTest
extends ClusterPerCla
@Client(NODE_URL + "/management/v1")
HttpClient client;
- private static Stream<Arguments> endpoints() {
- return Stream.of(
- Arguments.of("deployment/cluster/units"),
- Arguments.of("node/state"),
- Arguments.of("configuration/cluster"),
- Arguments.of("configuration/node"),
- Arguments.of("configuration/node/ignite.rest"),
- Arguments.of("cluster/topology/logical"),
- Arguments.of("cluster/topology/physical")
- );
+ private static Stream<HttpRequest<String>> endpoints() {
+ return Stream.concat(disabledEndpoints(), enabledEndpoints());
}
@ParameterizedTest
@MethodSource("endpoints")
- void clusterEndpointsEnabled(String path) {
- assertDoesNotThrow(
- () -> client.toBlocking().retrieve(HttpRequest.GET(path))
- );
+ void clusterEndpointsEnabled(HttpRequest<String> request) {
+ assertDoesNotThrow(() -> client.toBlocking().exchange(request));
}
}
diff --git
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
index b3603017951..1a82432b5aa 100644
---
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
+++
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
@@ -20,11 +20,12 @@ package org.apache.ignite.internal.rest;
import static io.micronaut.http.HttpRequest.GET;
import static io.micronaut.http.HttpRequest.PATCH;
import static io.micronaut.http.HttpStatus.CONFLICT;
+import static io.micronaut.http.HttpStatus.NOT_FOUND;
+import static io.micronaut.http.MediaType.TEXT_PLAIN;
import static
org.apache.ignite.internal.rest.matcher.MicronautHttpResponseMatcher.assertThrowsProblem;
import static org.apache.ignite.internal.rest.matcher.ProblemMatcher.isProblem;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import com.fasterxml.jackson.databind.ObjectMapper;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.annotation.Client;
@@ -34,9 +35,9 @@ import java.util.stream.Stream;
import org.apache.ignite.internal.ClusterConfiguration;
import org.apache.ignite.internal.ClusterPerClassIntegrationTest;
import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
/** Tests that before cluster is initialized, only a subset of endpoints are
available. */
@@ -44,28 +45,27 @@ import org.junit.jupiter.params.provider.MethodSource;
public class ItClusterStateHttpServerFilterNotInitializedTest extends
ClusterPerClassIntegrationTest {
private static final String NODE_URL = "http://localhost:" +
ClusterConfiguration.DEFAULT_BASE_HTTP_PORT;
- private final ObjectMapper mapper = new ObjectMapper();
-
@Inject
@Client(NODE_URL + "/management/v1")
HttpClient client;
- private static Stream<Arguments> disabledEndpoints() {
+ static Stream<HttpRequest<String>> disabledEndpoints() {
return Stream.of(
- Arguments.of(GET("deployment/units")),
- Arguments.of(GET("cluster/state")),
- Arguments.of(GET("configuration/cluster")),
- Arguments.of(PATCH("configuration/cluster",
"any.key=any-value")),
- Arguments.of(GET("cluster/topology/logical"))
+ GET("deployment/cluster/units"),
+ GET("cluster/state"),
+ GET("configuration/cluster"),
+ PATCH("configuration/cluster",
"ignite.system.idleSafeTimeSyncIntervalMillis=2000").contentType(TEXT_PLAIN),
+ GET("cluster/topology/logical")
);
}
- private static Stream<Arguments> enabledEndpoints() {
+ static Stream<HttpRequest<String>> enabledEndpoints() {
return Stream.of(
- Arguments.of("node/state"),
- Arguments.of("configuration/node"),
- Arguments.of("configuration/node/ignite.rest"),
- Arguments.of("cluster/topology/physical")
+ GET("node/state"),
+ GET("configuration/node"),
+ GET("configuration/node/ignite.rest"),
+ PATCH("configuration/node",
"ignite.deployment.location=deployment").contentType(TEXT_PLAIN),
+ GET("cluster/topology/physical")
);
}
@@ -99,10 +99,19 @@ public class
ItClusterStateHttpServerFilterNotInitializedTest extends ClusterPer
@ParameterizedTest
@MethodSource("enabledEndpoints")
- void nodeConfigAndClusterInitAreEnabled(String path) {
+ void nodeConfigAndClusterInitAreEnabled(HttpRequest<String> request) {
// But node config and cluster init endpoints are enabled
- assertDoesNotThrow(
- () -> client.toBlocking().retrieve(GET(path))
+ assertDoesNotThrow(() -> client.toBlocking().exchange(request));
+ }
+
+ @Test
+ void nonExistentUrlReturns404WhenNotInitialized() {
+ assertThrowsProblem(
+ () -> client.toBlocking().retrieve("nonExistentEndpoint"),
+ isProblem()
+ .withStatus(NOT_FOUND)
+ .withTitle("Not Found")
+ .withDetail("Requested resource not found:
/management/v1/nonExistentEndpoint")
);
}
}
diff --git
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/ClusterStateHttpServerFilter.java
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/ClusterStateHttpServerFilter.java
index a014e160dc4..8909fde3a79 100644
---
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/ClusterStateHttpServerFilter.java
+++
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/ClusterStateHttpServerFilter.java
@@ -23,6 +23,9 @@ import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.annotation.Filter;
import io.micronaut.http.filter.HttpServerFilter;
import io.micronaut.http.filter.ServerFilterChain;
+import io.micronaut.web.router.Router;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.internal.rest.ResourceHolder;
import org.apache.ignite.internal.rest.RestManager;
import org.apache.ignite.internal.rest.api.Problem;
@@ -37,14 +40,26 @@ import reactor.core.publisher.Mono;
@Filter(Filter.MATCH_ALL_PATTERN)
@Requires(property = "ignite.endpoints.filter-non-initialized", value =
"true", defaultValue = "true")
public class ClusterStateHttpServerFilter implements HttpServerFilter,
ResourceHolder {
+ private static final IgniteLogger LOG =
Loggers.forClass(ClusterStateHttpServerFilter.class);
+
private RestManager restManager;
- public ClusterStateHttpServerFilter(RestManager restManager) {
+ private final Router router;
+
+ public ClusterStateHttpServerFilter(RestManager restManager, Router
router) {
this.restManager = restManager;
+ this.router = router;
}
@Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request,
ServerFilterChain chain) {
+ // If no route matches this path, skip state filtering and let the
chain return 404. findAllClosest is the method used in the
+ // RoutingInBoundHandler to find the route.
+ if (router.findAllClosest(request).isEmpty()) {
+ LOG.debug("No route found for request {}, skip availability
check", request);
+ return chain.proceed(request);
+ }
+
return
Mono.just(restManager.pathAvailability(request.getPath())).<MutableHttpResponse<?>>flatMap(availability
-> {
if (!availability.isAvailable()) {
return Mono.just(HttpProblemResponse.from(
diff --git
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
b/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
index 76b1e7a05c1..25719833021 100644
---
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
+++
b/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.rest;
import static io.micronaut.http.HttpStatus.CONFLICT;
+import static io.micronaut.http.HttpStatus.NOT_FOUND;
import static io.micronaut.http.HttpStatus.OK;
import static
org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
import static org.apache.ignite.internal.rest.RestState.INITIALIZATION;
@@ -62,6 +63,8 @@ import
org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.Mockito;
/**
@@ -128,6 +131,20 @@ public class RestComponentTest extends
BaseIgniteAbstractTest {
assertThat(restComponent.stopAsync(new ComponentContext()),
willCompleteSuccessfully());
}
+ @ParameterizedTest
+ @EnumSource(RestState.class)
+ public void nonExistentEndpoint(RestState state) {
+ restManager.setState(state);
+
+ assertThrowsProblem(
+ () -> client.toBlocking().retrieve("nonExistentEndpoint"),
+ isProblem()
+ .withStatus(NOT_FOUND)
+ .withTitle("Not Found")
+ .withDetail("Requested resource not found:
/management/v1/nonExistentEndpoint")
+ );
+ }
+
@Test
public void nodeConfigTest() {
assertThat(getConfig(), hasStatus(OK));