You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by ch...@apache.org on 2020/05/19 13:38:09 UTC
[flink] 01/02: [FLINK-17634][rest] Reject multiple registration for
the same endpoint
This is an automated email from the ASF dual-hosted git repository.
chesnay pushed a commit to branch release-1.11
in repository https://gitbox.apache.org/repos/asf/flink.git
commit fa0362f430165f6ba9e25a50ce5d98e793b6ab38
Author: Chesnay Schepler <ch...@apache.org>
AuthorDate: Tue May 12 14:56:29 2020 +0200
[FLINK-17634][rest] Reject multiple registration for the same endpoint
---
.../flink/runtime/rest/RestServerEndpoint.java | 43 +++++++++++++++++-----
.../runtime/rest/RestServerEndpointITCase.java | 21 ++++++++++-
2 files changed, 54 insertions(+), 10 deletions(-)
diff --git a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/RestServerEndpoint.java b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/RestServerEndpoint.java
index a264037..f575da2 100644
--- a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/RestServerEndpoint.java
+++ b/flink-runtime/src/main/java/org/apache/flink/runtime/rest/RestServerEndpoint.java
@@ -64,6 +64,7 @@ import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
@@ -140,7 +141,6 @@ public abstract class RestServerEndpoint implements AutoCloseableAsync {
final Router router = new Router();
final CompletableFuture<String> restAddressFuture = new CompletableFuture<>();
- final Set<ChannelInboundHandler> distinctHandlers = Collections.newSetFromMap(new IdentityHashMap<>());
handlers = initializeHandlers(restAddressFuture);
@@ -155,14 +155,8 @@ public abstract class RestServerEndpoint implements AutoCloseableAsync {
handlers,
RestHandlerUrlComparator.INSTANCE);
- handlers.forEach(handler -> {
- if (distinctHandlers.contains(handler.f1)) {
- throw new FlinkRuntimeException("Duplicate REST handler instance found."
- + " Please ensure each instance is registered only once.");
- }
- registerHandler(router, handler, log);
- distinctHandlers.add(handler.f1);
- });
+ checkAllEndpointsAndHandlersAreUnique(handlers);
+ handlers.forEach(handler -> registerHandler(router, handler, log));
ChannelInitializer<SocketChannel> initializer = new ChannelInitializer<SocketChannel>() {
@@ -491,6 +485,37 @@ public abstract class RestServerEndpoint implements AutoCloseableAsync {
}
}
+ private static void checkAllEndpointsAndHandlersAreUnique(final List<Tuple2<RestHandlerSpecification, ChannelInboundHandler>> handlers) {
+ // check for all handlers that
+ // 1) the instance is only registered once
+ // 2) only 1 handler is registered for each endpoint (defined by (version, method, url))
+ // technically the first check is redundant since a duplicate instance also returns the same headers which
+ // should fail the second check, but we get a better error message
+ final Set<String> uniqueEndpoints = new HashSet<>();
+ final Set<ChannelInboundHandler> distinctHandlers = Collections.newSetFromMap(new IdentityHashMap<>());
+ for (Tuple2<RestHandlerSpecification, ChannelInboundHandler> handler : handlers) {
+ boolean isNewHandler = distinctHandlers.add(handler.f1);
+ if (!isNewHandler) {
+ throw new FlinkRuntimeException("Duplicate REST handler instance found."
+ + " Please ensure each instance is registered only once.");
+ }
+
+ final RestHandlerSpecification headers = handler.f0;
+ for (RestAPIVersion supportedAPIVersion : headers.getSupportedAPIVersions()) {
+ final String parameterizedEndpoint = supportedAPIVersion.toString() + headers.getHttpMethod() + headers.getTargetRestEndpointURL();
+ // normalize path parameters; distinct path parameters still clash at runtime
+ final String normalizedEndpoint = parameterizedEndpoint.replaceAll(":[\\w-]+", ":param");
+ boolean isNewEndpoint = uniqueEndpoints.add(normalizedEndpoint);
+ if (!isNewEndpoint) {
+ throw new FlinkRuntimeException(
+ String.format(
+ "REST handler registration overlaps with another registration for: version=%s, method=%s, url=%s.",
+ supportedAPIVersion, headers.getHttpMethod(), headers.getTargetRestEndpointURL()));
+ }
+ }
+ }
+ }
+
/**
* Comparator for Rest URLs.
*
diff --git a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/RestServerEndpointITCase.java b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/RestServerEndpointITCase.java
index 0a95a3c..c941c45 100644
--- a/flink-runtime/src/test/java/org/apache/flink/runtime/rest/RestServerEndpointITCase.java
+++ b/flink-runtime/src/test/java/org/apache/flink/runtime/rest/RestServerEndpointITCase.java
@@ -595,12 +595,31 @@ public class RestServerEndpointITCase extends TestLogger {
}
@Test
+ public void testEndpointsMustBeUnique() throws Exception {
+ final RestServerEndpointConfiguration serverConfig = RestServerEndpointConfiguration.fromConfiguration(config);
+
+ final List<Tuple2<RestHandlerSpecification, ChannelInboundHandler>> handlers = Arrays.asList(
+ Tuple2.of(new TestHeaders(), testHandler),
+ Tuple2.of(new TestHeaders(), testUploadHandler)
+ );
+
+ assertThrows("REST handler registration",
+ FlinkRuntimeException.class,
+ () -> {
+ try (TestRestServerEndpoint restServerEndpoint = new TestRestServerEndpoint(serverConfig, handlers)) {
+ restServerEndpoint.start();
+ return null;
+ }
+ });
+ }
+
+ @Test
public void testDuplicateHandlerRegistrationIsForbidden() throws Exception {
final RestServerEndpointConfiguration serverConfig = RestServerEndpointConfiguration.fromConfiguration(config);
final List<Tuple2<RestHandlerSpecification, ChannelInboundHandler>> handlers = Arrays.asList(
Tuple2.of(new TestHeaders(), testHandler),
- Tuple2.of(new TestHeaders(), testHandler)
+ Tuple2.of(TestUploadHeaders.INSTANCE, testHandler)
);
assertThrows("Duplicate REST handler",