From 9d5b8e6cfd72c5e1954784bff11125cf07d8b8a6 Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Mon, 7 Aug 2017 18:39:20 -0600 Subject: [PATCH] Updates to framework changes --- .../filter/LoadBalancerClientFilter.java | 11 ++++---- .../gateway/filter/NettyRoutingFilter.java | 28 +++++++++++++++---- .../filter/RouteToRequestUrlFilter.java | 7 ++--- .../gateway/filter/WriteResponseFilter.java | 6 ++-- .../factory/SetPathWebFilterFactory.java | 15 ++++++++-- .../gateway/handler/FilteringWebHandler.java | 4 +-- .../predicate/PathRoutePredicateFactory.java | 9 ++++-- .../factory/SetPathWebFilterFactoryTests.java | 15 +++++++++- .../gateway/test/BaseWebClientTests.java | 4 +-- 9 files changed, 69 insertions(+), 30 deletions(-) diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java index aa1b7084..fd9efcd0 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java @@ -18,7 +18,6 @@ package org.springframework.cloud.gateway.filter; import java.net.URI; -import java.util.Optional; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -55,19 +54,19 @@ public class LoadBalancerClientFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - Optional url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); - if (!url.isPresent() || !url.get().getScheme().equals("lb")) { + URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); + if (url == null || !url.getScheme().equals("lb")) { return chain.filter(exchange); } - log.trace("LoadBalancerClientFilter url before: " + url.get()); + log.trace("LoadBalancerClientFilter url before: " + url); - final ServiceInstance instance = loadBalancer.choose(url.get().getHost()); + final ServiceInstance instance = loadBalancer.choose(url.getHost()); if (instance == null) { throw new NotFoundException(""); } - URI requestUrl = UriComponentsBuilder.fromUri(url.get()) + URI requestUrl = UriComponentsBuilder.fromUri(url) .scheme(instance.isSecure()? "https" : "http") //TODO: support websockets .host(instance.getHost()) .port(instance.getPort()) diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java index 1cd7714f..a7297628 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java @@ -20,7 +20,6 @@ package org.springframework.cloud.gateway.filter; import java.net.URI; import java.util.List; import java.util.Map; -import java.util.Optional; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; @@ -61,19 +60,34 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - Optional requestUrl = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); - if (!requestUrl.isPresent()) { - return Mono.error(new IllegalStateException("No URI found in attribute: " + GATEWAY_REQUEST_URL_ATTR)); - } + URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); ServerHttpRequest request = exchange.getRequest(); final HttpMethod method = HttpMethod.valueOf(request.getMethod().toString()); - final String url = requestUrl.get().toString(); + final String url = requestUrl.toString(); final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders(); request.getHeaders().forEach(httpHeaders::set); + if ("WebSocket".equalsIgnoreCase(request.getHeaders().getUpgrade())) { + return this.httpClient.ws(url) + // .flatMap(res -> { + .doOnNext(res -> { + ServerHttpResponse response = exchange.getResponse(); + // put headers and status so filters can modify the response + HttpHeaders headers = new HttpHeaders(); + res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue())); + + response.getHeaders().putAll(headers); + response.setStatusCode(HttpStatus.valueOf(res.status().code())); + + // Defer committing the response until all route filters have run + // Put client response as ServerWebExchange attribute and write response later WriteResponseFilter + exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res); + }).then(chain.filter(exchange)); + } + return this.httpClient.request(method, url, req -> { final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach) .failOnClientError(false) @@ -89,6 +103,8 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered { } }).then()) .then(chain.filter(exchange)); + } else if ("WebSocket".equalsIgnoreCase(request.getHeaders().getUpgrade())) { + return proxyRequest.sendWebsocket(); } return proxyRequest.sendHeaders() //I shouldn't need this diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java index 613b6d85..df695fc4 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java @@ -18,7 +18,6 @@ package org.springframework.cloud.gateway.filter; import java.net.URI; -import java.util.Optional; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -48,13 +47,13 @@ public class RouteToRequestUrlFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - Optional route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); - if (!route.isPresent()) { + Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); + if (route == null) { return chain.filter(exchange); } log.info("RouteToRequestUrlFilter start"); URI requestUrl = UriComponentsBuilder.fromHttpRequest(exchange.getRequest()) - .uri(route.get().getUri()) + .uri(route.getUri()) .build(true) .toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WriteResponseFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WriteResponseFilter.java index f8b373b7..6641b5c9 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WriteResponseFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WriteResponseFilter.java @@ -53,9 +53,9 @@ public class WriteResponseFilter implements GlobalFilter, Ordered { // NOTICE: nothing in "pre" filter stage as CLIENT_RESPONSE_ATTR is not added // until the WebHandler is run return chain.filter(exchange).then(Mono.defer(() -> { - Optional clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR); + HttpClientResponse clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR); // HttpClientResponse clientResponse = getAttribute(exchange, CLIENT_RESPONSE_ATTR, HttpClientResponse.class); - if (!clientResponse.isPresent()) { + if (clientResponse == null) { return Mono.empty(); } log.trace("WriteResponseFilter start"); @@ -64,7 +64,7 @@ public class WriteResponseFilter implements GlobalFilter, Ordered { NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory(); //TODO: what if it's not netty - final Flux body = clientResponse.get().receive() + final Flux body = clientResponse.receive() .retain() //TODO: needed? .map(factory::wrap); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactory.java index d73c871d..0c46b868 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactory.java @@ -22,12 +22,12 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Optional; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; import org.springframework.web.util.UriTemplate; +import org.springframework.web.util.pattern.PathPattern.PathMatchResult; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE; @@ -38,6 +38,7 @@ public class SetPathWebFilterFactory implements WebFilterFactory { public static final String TEMPLATE_KEY = "template"; + @Override public List argNames() { return Arrays.asList(TEMPLATE_KEY); @@ -50,9 +51,17 @@ public class SetPathWebFilterFactory implements WebFilterFactory { UriTemplate uriTemplate = new UriTemplate(template); return (exchange, chain) -> { - Optional> variables = exchange.getAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE); + PathMatchResult variables = exchange.getAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE); ServerHttpRequest req = exchange.getRequest(); - URI uri = uriTemplate.expand(variables.orElseGet(Collections::emptyMap)); + Map uriVariables; + + if (variables != null) { + uriVariables = variables.getUriVariables(); + } else { + uriVariables = Collections.emptyMap(); + } + + URI uri = uriTemplate.expand(uriVariables); String newPath = uri.getPath(); ServerHttpRequest request = req.mutate() diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java index 1b07dd6b..1c2a3164 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java @@ -81,8 +81,8 @@ public class FilteringWebHandler extends WebHandlerDecorator { @Override public Mono handle(ServerWebExchange exchange) { - Optional route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); - List webFilters = route.get().getWebFilters(); + Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); + List webFilters = route.getWebFilters(); List combined = new ArrayList<>(this.globalFilters); combined.addAll(webFilters); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java index be0e45e1..38f42d4c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java @@ -19,16 +19,18 @@ package org.springframework.cloud.gateway.handler.predicate; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.function.Predicate; +import org.springframework.http.server.reactive.PathContainer; import org.springframework.tuple.Tuple; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.pattern.PathPattern; +import org.springframework.web.util.pattern.PathPattern.PathMatchResult; import org.springframework.web.util.pattern.PathPatternParser; import static org.springframework.cloud.gateway.handler.support.RoutePredicateFactoryUtils.traceMatch; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE; +import static org.springframework.http.server.reactive.PathContainer.parsePath; /** * @author Spencer Gibb @@ -55,11 +57,12 @@ public class PathRoutePredicateFactory implements RoutePredicateFactory { } return exchange -> { - String path = exchange.getRequest().getURI().getPath(); + PathContainer path = parsePath(exchange.getRequest().getURI().getPath()); + boolean match = pattern.matches(path); traceMatch("Pattern", pattern.getPatternString(), path, match); if (match) { - Map uriTemplateVariables = pattern.matchAndExtract(path); + PathMatchResult uriTemplateVariables = pattern.matchAndExtract(path); exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables); return true; } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactoryTests.java index 473546e0..609647ff 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactoryTests.java @@ -17,16 +17,21 @@ package org.springframework.cloud.gateway.filter.factory; +import java.lang.reflect.Constructor; +import java.util.Collections; import java.util.HashMap; +import java.util.Map; import org.assertj.core.api.Assertions; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.MockServerWebExchange; +import org.springframework.util.ReflectionUtils; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; +import org.springframework.web.util.pattern.PathPattern; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -62,7 +67,15 @@ public class SetPathWebFilterFactoryTests { .build(); ServerWebExchange exchange = new MockServerWebExchange(request); - exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, variables); + + try { + Constructor constructor = ReflectionUtils.accessibleConstructor(PathPattern.PathMatchResult.class, Map.class, Map.class); + constructor.setAccessible(true); + PathPattern.PathMatchResult pathMatchResult = constructor.newInstance(variables, Collections.emptyMap()); + exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathMatchResult); + } catch (Exception e) { + ReflectionUtils.rethrowRuntimeException(e); + } WebFilterChain filterChain = mock(WebFilterChain.class); diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java index 1d1dda35..8d13dec3 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java @@ -174,9 +174,9 @@ public class BaseWebClientTests { public GlobalFilter modifyResponseFilter() { return (exchange, chain) -> { log.info("modifyResponseFilter start"); - String value = (String) exchange.getAttribute(GATEWAY_HANDLER_MAPPER_ATTR).orElse("N/A"); + String value = exchange.getAttributeOrDefault(GATEWAY_HANDLER_MAPPER_ATTR, "N/A"); exchange.getResponse().getHeaders().add(HANDLER_MAPPER_HEADER, value); - Route route = (Route) exchange.getAttribute(GATEWAY_ROUTE_ATTR).orElse(null); + Route route = exchange.getAttributeOrDefault(GATEWAY_ROUTE_ATTR,null); if (route != null) { exchange.getResponse().getHeaders().add(ROUTE_ID_HEADER, route.getId()); }