From 6ff67bd0c6753c9dd2e74a586319cbcb7a4d4a5d Mon Sep 17 00:00:00 2001 From: brasseld Date: Mon, 24 Sep 2018 16:41:39 +0200 Subject: [PATCH] fix(endpoint): Do not resolve an endpoint if reference is unknown or invalid Closes gravitee-io/issues#1515 --- .../gateway/core/endpoint/ref/Reference.java | 2 + .../ref/impl/DefaultReferenceRegister.java | 140 +++++++++++++++++- .../resolver/impl/TargetEndpointResolver.java | 7 +- .../gateway/core/invoker/EndpointInvoker.java | 1 + .../impl/TargetEndpointResolverTest.java | 1 - .../http/connector/VertxHttpClient.java | 2 - .../gateway/standalone/dynamic-routing.json | 2 +- 7 files changed, 148 insertions(+), 7 deletions(-) diff --git a/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/ref/Reference.java b/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/ref/Reference.java index 616e86663..36d7e03c1 100644 --- a/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/ref/Reference.java +++ b/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/ref/Reference.java @@ -23,6 +23,8 @@ */ public interface Reference { + String UNKNOWN_REFERENCE = "ref:unknown"; + String key(); String name(); diff --git a/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/ref/impl/DefaultReferenceRegister.java b/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/ref/impl/DefaultReferenceRegister.java index ea7bb6ce6..708243bd5 100644 --- a/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/ref/impl/DefaultReferenceRegister.java +++ b/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/ref/impl/DefaultReferenceRegister.java @@ -23,6 +23,9 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; @@ -72,6 +75,141 @@ public void provide(TemplateContext context) { entry -> entry.getValue().name(), entry -> entry.getKey() + ':')); - context.setVariable(TEMPLATE_VARIABLE_KEY, refs); + context.setVariable(TEMPLATE_VARIABLE_KEY, new EndpointReferenceMap(refs)); + } + + private static class EndpointReferenceMap implements Map { + + private final Map wrapped; + + EndpointReferenceMap(Map wrapped) { + this.wrapped = wrapped; + } + + @Override + public int size() { + return wrapped.size(); + } + + @Override + public boolean isEmpty() { + return wrapped.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return wrapped.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return wrapped.containsValue(value); + } + + @Override + public String get(Object key) { + String reference = wrapped.get(key); + return (reference != null) ? reference : Reference.UNKNOWN_REFERENCE; + } + + @Override + public String put(String key, String value) { + return wrapped.put(key, value); + } + + @Override + public String remove(Object key) { + return wrapped.remove(key); + } + + @Override + public void putAll(Map m) { + wrapped.putAll(m); + } + + @Override + public void clear() { + wrapped.clear(); + } + + @Override + public Set keySet() { + return wrapped.keySet(); + } + + @Override + public Collection values() { + return wrapped.values(); + } + + @Override + public Set> entrySet() { + return wrapped.entrySet(); + } + + @Override + public boolean equals(Object o) { + return wrapped.equals(o); + } + + @Override + public int hashCode() { + return wrapped.hashCode(); + } + + @Override + public String getOrDefault(Object key, String defaultValue) { + return wrapped.getOrDefault(key, defaultValue); + } + + @Override + public void forEach(BiConsumer action) { + wrapped.forEach(action); + } + + @Override + public void replaceAll(BiFunction function) { + wrapped.replaceAll(function); + } + + @Override + public String putIfAbsent(String key, String value) { + return wrapped.putIfAbsent(key, value); + } + + @Override + public boolean remove(Object key, Object value) { + return wrapped.remove(key, value); + } + + @Override + public boolean replace(String key, String oldValue, String newValue) { + return wrapped.replace(key, oldValue, newValue); + } + + @Override + public String replace(String key, String value) { + return wrapped.replace(key, value); + } + + @Override + public String computeIfAbsent(String key, Function mappingFunction) { + return wrapped.computeIfAbsent(key, mappingFunction); + } + + @Override + public String computeIfPresent(String key, BiFunction remappingFunction) { + return wrapped.computeIfPresent(key, remappingFunction); + } + + @Override + public String compute(String key, BiFunction remappingFunction) { + return wrapped.compute(key, remappingFunction); + } + + @Override + public String merge(String key, String value, BiFunction remappingFunction) { + return wrapped.merge(key, value, remappingFunction); + } } } diff --git a/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/resolver/impl/TargetEndpointResolver.java b/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/resolver/impl/TargetEndpointResolver.java index 862df413e..904dd35b2 100644 --- a/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/resolver/impl/TargetEndpointResolver.java +++ b/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/endpoint/resolver/impl/TargetEndpointResolver.java @@ -119,8 +119,11 @@ private ResolvedEndpoint selectUserDefinedEndpoint(Request serverRequest, String Endpoint endpoint = reference.endpoint(); return createEndpoint(endpoint, encodedTarget.replaceFirst(sRef + ':', endpoint.target())); + } else if (encodedTarget.startsWith(Reference.UNKNOWN_REFERENCE)) { + return null; } else { - // Try to match an endpoint according to the target URL + // When the user selected endpoint which is not defined (according to the given target), the gateway + // is always returning the first endpoints reference and took into account its configuration. Collection endpoints = referenceRegister.referencesByType(EndpointReference.class); Reference reference = endpoints .stream() @@ -128,7 +131,7 @@ private ResolvedEndpoint selectUserDefinedEndpoint(Request serverRequest, String .findFirst() .orElse(endpoints.iterator().next()); - return createEndpoint(reference.endpoint(), encodedTarget); + return (reference != null) ? createEndpoint(reference.endpoint(), encodedTarget) : null; } } } diff --git a/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/invoker/EndpointInvoker.java b/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/invoker/EndpointInvoker.java index 3a8110f3c..36afd2c28 100644 --- a/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/invoker/EndpointInvoker.java +++ b/gravitee-gateway-core/src/main/java/io/gravitee/gateway/core/invoker/EndpointInvoker.java @@ -58,6 +58,7 @@ public class EndpointInvoker implements Invoker { public Request invoke(ExecutionContext executionContext, Request serverRequest, ReadStream stream, Handler connectionHandler) { EndpointResolver.ResolvedEndpoint endpoint = endpointResolver.resolve(serverRequest, executionContext); + // Endpoint can be null if none endpoint can be selected or if the selected endpoint is unavailable if (endpoint == null) { DirectProxyConnection statusOnlyConnection = new DirectProxyConnection(HttpStatusCode.SERVICE_UNAVAILABLE_503); connectionHandler.handle(statusOnlyConnection); diff --git a/gravitee-gateway-core/src/test/java/io/gravitee/gateway/core/endpoint/resolver/impl/TargetEndpointResolverTest.java b/gravitee-gateway-core/src/test/java/io/gravitee/gateway/core/endpoint/resolver/impl/TargetEndpointResolverTest.java index cbb9f8675..64aebb435 100644 --- a/gravitee-gateway-core/src/test/java/io/gravitee/gateway/core/endpoint/resolver/impl/TargetEndpointResolverTest.java +++ b/gravitee-gateway-core/src/test/java/io/gravitee/gateway/core/endpoint/resolver/impl/TargetEndpointResolverTest.java @@ -78,7 +78,6 @@ public void shouldResolveUserDefinedEndpoint_selectFirstEndpoint() { .thenAnswer(invocation -> Collections.singleton(new EndpointReference(endpoint))); EndpointResolver.ResolvedEndpoint resolvedEndpoint = resolver.resolve(serverRequest, executionContext); - Assert.assertEquals(targetUri, resolvedEndpoint.getUri()); } diff --git a/gravitee-gateway-http/src/main/java/io/gravitee/gateway/http/connector/VertxHttpClient.java b/gravitee-gateway-http/src/main/java/io/gravitee/gateway/http/connector/VertxHttpClient.java index a255f26cb..5d206e8c4 100644 --- a/gravitee-gateway-http/src/main/java/io/gravitee/gateway/http/connector/VertxHttpClient.java +++ b/gravitee-gateway-http/src/main/java/io/gravitee/gateway/http/connector/VertxHttpClient.java @@ -210,8 +210,6 @@ private HttpMethod convert(io.gravitee.common.http.HttpMethod httpMethod) { @Override protected void doStart() throws Exception { - // TODO: Prepare HttpClientOptions according to the endpoint to improve performance when creating a new - // instance of the Vertx client httpClientOptions = new HttpClientOptions(); httpClientOptions.setPipelining(endpoint.getHttpClientOptions().isPipelining()); diff --git a/gravitee-gateway-standalone/gravitee-gateway-standalone-container/src/test/resources/io/gravitee/gateway/standalone/dynamic-routing.json b/gravitee-gateway-standalone/gravitee-gateway-standalone-container/src/test/resources/io/gravitee/gateway/standalone/dynamic-routing.json index a727d4f0a..f9f2b9830 100644 --- a/gravitee-gateway-standalone/gravitee-gateway-standalone-container/src/test/resources/io/gravitee/gateway/standalone/dynamic-routing.json +++ b/gravitee-gateway-standalone/gravitee-gateway-standalone-container/src/test/resources/io/gravitee/gateway/standalone/dynamic-routing.json @@ -7,7 +7,7 @@ "endpoints": [ { "name": "default", - "target": "http://bad_url_but_overriden:8080/team" + "target": "http://localhost:8080/team" } ], "strip_context_path": false,