Remove 2.0.x modules
This commit is contained in:
80
pom.xml
80
pom.xml
@@ -14,7 +14,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.3.2.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<scm>
|
||||
@@ -48,9 +48,6 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-commons.version>2.0.0.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>2.0.0.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-tuple.version>1.0.0.RELEASE</spring-tuple.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -62,87 +59,12 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons-dependencies</artifactId>
|
||||
<version>${spring-cloud-commons.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
||||
<version>${spring-cloud-netflix.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-ribbon</artifactId>
|
||||
<version>${spring-cloud-netflix.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-transport</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxnetty</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-eureka</artifactId>
|
||||
<version>${spring-cloud-netflix.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<!-- TODO: this should be optional -->
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-hystrix</artifactId>
|
||||
<version>${spring-cloud-netflix.version}</version>
|
||||
</dependency>
|
||||
<!-- TODO: should be part of spring-cloud-starter-netflix? -->
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava-reactive-streams</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tuple</artifactId>
|
||||
<version>${spring-tuple.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<modules>
|
||||
<module>spring-cloud-gateway-dependencies</module>
|
||||
<module>spring-cloud-gateway-mvc</module>
|
||||
<module>spring-cloud-gateway-core</module>
|
||||
<module>spring-cloud-starter-gateway</module>
|
||||
<module>spring-cloud-gateway-sample</module>
|
||||
<module>docs</module>
|
||||
</modules>
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-gateway</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-gateway-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Gateway Core</name>
|
||||
<description>Spring Cloud Gateway Core</description>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-ribbon</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-hystrix</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- TODO: should be part of spring-cloud-starter-netflix -->
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava-reactive-streams</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tuple</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-eureka</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.synchronoss.cloud</groupId>
|
||||
<artifactId>nio-multipart-parser</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>javax.mail-api</artifactId>
|
||||
<version>1.6.0-rc1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>javax.mail</artifactId>
|
||||
<version>1.6.0-rc1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>java8plus</id>
|
||||
<activation>
|
||||
<jdk>[1.8,2.0)</jdk>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.cloud.gateway.config.GatewayConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Annotation to activate Spring Cloud Gateway configuration {@link org.springframework.cloud.gateway.config.GatewayAutoConfiguration}
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
*
|
||||
*/
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(GatewayConfiguration.class)
|
||||
public @interface EnableGateway {
|
||||
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.actuate;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
import org.springframework.cloud.gateway.route.RefreshRoutesEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
//TODO: move to new Spring Boot 2.0 actuator when ready
|
||||
//@ConfigurationProperties(prefix = "endpoints.gateway")
|
||||
@RestController
|
||||
@RequestMapping("/admin/gateway")
|
||||
public class GatewayEndpoint implements ApplicationEventPublisherAware {/*extends AbstractEndpoint<Map<String, Object>> {*/
|
||||
|
||||
private static final Log log = LogFactory.getLog(GatewayEndpoint.class);
|
||||
|
||||
private RouteDefinitionLocator routeDefinitionLocator;
|
||||
private List<GlobalFilter> globalFilters;
|
||||
private List<WebFilterFactory> webFilterFactories;
|
||||
private RouteDefinitionWriter routeDefinitionWriter;
|
||||
private RouteLocator routeLocator;
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
public GatewayEndpoint(RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,
|
||||
List<WebFilterFactory> webFilterFactories, RouteDefinitionWriter routeDefinitionWriter,
|
||||
RouteLocator routeLocator) {
|
||||
//super("gateway");
|
||||
this.routeDefinitionLocator = routeDefinitionLocator;
|
||||
this.globalFilters = globalFilters;
|
||||
this.webFilterFactories = webFilterFactories;
|
||||
this.routeDefinitionWriter = routeDefinitionWriter;
|
||||
this.routeLocator = routeLocator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public Map<String, Object> invoke() {
|
||||
}*/
|
||||
|
||||
// TODO: Add uncommited or new but not active routes endpoint
|
||||
|
||||
//TODO: this should really be a listener that responds to a RefreshEvent
|
||||
@PostMapping("/refresh")
|
||||
public Mono<Void> refresh() {
|
||||
this.publisher.publishEvent(new RefreshRoutesEvent(this));
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@GetMapping("/globalfilters")
|
||||
public Map<String, Object> globalfilters() {
|
||||
return getNamesToOrders(this.globalFilters);
|
||||
}
|
||||
|
||||
@GetMapping("/routefilters")
|
||||
public Map<String, Object> routefilers() {
|
||||
return getNamesToOrders(this.webFilterFactories);
|
||||
}
|
||||
|
||||
private <T> Map<String, Object> getNamesToOrders(List<T> list) {
|
||||
HashMap<String, Object> filters = new HashMap<>();
|
||||
|
||||
for (Object o : list) {
|
||||
Integer order = null;
|
||||
if (o instanceof Ordered) {
|
||||
order = ((Ordered)o).getOrder();
|
||||
}
|
||||
//filters.put(o.getClass().getName(), order);
|
||||
filters.put(o.toString(), order);
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
@GetMapping("/routes")
|
||||
public Mono<List<RouteDefinition>> routes() {
|
||||
return this.routeDefinitionLocator.getRouteDefinitions().collectList();
|
||||
}
|
||||
|
||||
/*
|
||||
http POST :8080/admin/gateway/routes/apiaddreqhead uri=http://httpbin.org:80 predicates:='["Host=**.apiaddrequestheader.org", "Path=/headers"]' filters:='["AddRequestHeader=X-Request-ApiFoo, ApiBar"]'
|
||||
*/
|
||||
@PostMapping("/routes/{id}")
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<ResponseEntity<Void>> save(@PathVariable String id, @RequestBody Mono<RouteDefinition> route) {
|
||||
return this.routeDefinitionWriter.save(route.map(r -> {
|
||||
r.setId(id);
|
||||
log.debug("Saving route: " + route);
|
||||
return r;
|
||||
})).then(Mono.defer(() ->
|
||||
Mono.just(ResponseEntity.created(URI.create("/routes/"+id)).build())
|
||||
));
|
||||
}
|
||||
|
||||
@DeleteMapping("/routes/{id}")
|
||||
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
|
||||
return this.routeDefinitionWriter.delete(Mono.just(id))
|
||||
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
|
||||
.onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()));
|
||||
}
|
||||
|
||||
@GetMapping("/routes/{id}")
|
||||
public Mono<ResponseEntity<RouteDefinition>> route(@PathVariable String id) {
|
||||
return this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.filter(route -> route.getId().equals(id))
|
||||
.singleOrEmpty()
|
||||
.map(route -> ResponseEntity.ok(route))
|
||||
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
|
||||
}
|
||||
|
||||
@GetMapping("/routes/{id}/combinedfilters")
|
||||
public Map<String, Object> combinedfilters(@PathVariable String id) {
|
||||
List<Route> routes = this.routeLocator.getRoutes().collectList().block();
|
||||
return getNamesToOrders(routes);
|
||||
}
|
||||
}
|
||||
@@ -1,368 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.gateway.actuate.GatewayEndpoint;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.NettyRoutingFilter;
|
||||
import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter;
|
||||
import org.springframework.cloud.gateway.filter.WriteResponseFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.AddRequestParameterWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.AddResponseHeaderWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.HystrixWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.PrefixPathWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RedirectToWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveNonProxyHeadersWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveRequestHeaderWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveResponseHeaderWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SecureHeadersProperties;
|
||||
import org.springframework.cloud.gateway.filter.factory.SecureHeadersWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetPathWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetResponseHeaderWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetStatusWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
|
||||
import org.springframework.cloud.gateway.handler.FilteringWebHandler;
|
||||
import org.springframework.cloud.gateway.handler.NettyProxyWebHandler;
|
||||
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
|
||||
import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.CookieRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.route.CachingRouteLocator;
|
||||
import org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.route.CompositeRouteLocator;
|
||||
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
import com.netflix.hystrix.HystrixObservableCommand;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.data.redis.core.script.RedisScript;
|
||||
import org.springframework.scripting.support.ResourceScriptSource;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.ipc.netty.http.client.HttpClient;
|
||||
import reactor.ipc.netty.resources.PoolResources;
|
||||
import rx.RxReactiveStreams;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnBean(GatewayConfiguration.Marker.class)
|
||||
@EnableConfigurationProperties
|
||||
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
|
||||
@AutoConfigureAfter(GatewayLoadBalancerClientAutoConfiguration.class)
|
||||
public class GatewayAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(HttpClient.class)
|
||||
protected static class NettyConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HttpClient httpClient() {
|
||||
return HttpClient.create(opts -> {
|
||||
opts.poolResources(PoolResources.elastic("proxy"));
|
||||
// opts.disablePool(); //TODO: why do I need this again?
|
||||
});
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NettyProxyWebHandler proxyWebHandler(HttpClient httpClient) {
|
||||
return new NettyProxyWebHandler(httpClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NettyRoutingFilter routingFilter(HttpClient httpClient) {
|
||||
return new NettyRoutingFilter(httpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
|
||||
return new PropertiesRouteDefinitionLocator(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(RouteDefinitionRepository.class)
|
||||
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
|
||||
return new InMemoryRouteDefinitionRepository();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
|
||||
return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
|
||||
List<WebFilterFactory> webFilterFactories,
|
||||
List<RoutePredicateFactory> predicates,
|
||||
RouteDefinitionLocator routeDefinitionLocator) {
|
||||
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, webFilterFactories, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public RouteLocator routeLocator(List<RouteLocator> routeLocators) {
|
||||
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
|
||||
return new FilteringWebHandler(globalFilters);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler,
|
||||
RouteLocator routeLocator) {
|
||||
return new RoutePredicateHandlerMapping(webHandler, routeLocator);
|
||||
}
|
||||
|
||||
// ConfigurationProperty beans
|
||||
|
||||
@Bean
|
||||
public GatewayProperties gatewayProperties() {
|
||||
return new GatewayProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecureHeadersProperties secureHeadersProperties() {
|
||||
return new SecureHeadersProperties();
|
||||
}
|
||||
|
||||
// GlobalFilter beans
|
||||
|
||||
@Bean
|
||||
public RouteToRequestUrlFilter routeToRequestUrlFilter() {
|
||||
return new RouteToRequestUrlFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WriteResponseFilter writeResponseFilter() {
|
||||
return new WriteResponseFilter();
|
||||
}
|
||||
|
||||
// Predicate Factory beans
|
||||
|
||||
@Bean
|
||||
public AfterRoutePredicateFactory afterRoutePredicateFactory() {
|
||||
return new AfterRoutePredicateFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeforeRoutePredicateFactory beforeRoutePredicateFactory() {
|
||||
return new BeforeRoutePredicateFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BetweenRoutePredicateFactory betweenRoutePredicateFactory() {
|
||||
return new BetweenRoutePredicateFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CookieRoutePredicateFactory cookieRoutePredicateFactory() {
|
||||
return new CookieRoutePredicateFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HeaderRoutePredicateFactory headerRoutePredicateFactory() {
|
||||
return new HeaderRoutePredicateFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HostRoutePredicateFactory hostRoutePredicateFactory() {
|
||||
return new HostRoutePredicateFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MethodRoutePredicateFactory methodRoutePredicateFactory() {
|
||||
return new MethodRoutePredicateFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PathRoutePredicateFactory pathRoutePredicateFactory() {
|
||||
return new PathRoutePredicateFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public QueryRoutePredicateFactory queryRoutePredicateFactory() {
|
||||
return new QueryRoutePredicateFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RemoteAddrRoutePredicateFactory remoteAddrRoutePredicateFactory() {
|
||||
return new RemoteAddrRoutePredicateFactory();
|
||||
}
|
||||
|
||||
// WebFilter Factory beans
|
||||
|
||||
@Bean
|
||||
public AddRequestHeaderWebFilterFactory addRequestHeaderWebFilterFactory() {
|
||||
return new AddRequestHeaderWebFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AddRequestParameterWebFilterFactory addRequestParameterWebFilterFactory() {
|
||||
return new AddRequestParameterWebFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AddResponseHeaderWebFilterFactory addResponseHeaderWebFilterFactory() {
|
||||
return new AddResponseHeaderWebFilterFactory();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({HystrixObservableCommand.class, RxReactiveStreams.class})
|
||||
protected static class HystrixConfiguration {
|
||||
@Bean
|
||||
public HystrixWebFilterFactory hystrixWebFilterFactory() {
|
||||
return new HystrixWebFilterFactory();
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PrefixPathWebFilterFactory prefixPathWebFilterFactory() {
|
||||
return new PrefixPathWebFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedirectToWebFilterFactory redirectToWebFilterFactory() {
|
||||
return new RedirectToWebFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RemoveNonProxyHeadersWebFilterFactory removeNonProxyHeadersWebFilterFactory() {
|
||||
return new RemoveNonProxyHeadersWebFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RemoveRequestHeaderWebFilterFactory removeRequestHeaderWebFilterFactory() {
|
||||
return new RemoveRequestHeaderWebFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RemoveResponseHeaderWebFilterFactory removeResponseHeaderWebFilterFactory() {
|
||||
return new RemoveResponseHeaderWebFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean({RateLimiter.class, KeyResolver.class})
|
||||
public RequestRateLimiterWebFilterFactory requestRateLimiterWebFilterFactory(RateLimiter rateLimiter) {
|
||||
return new RequestRateLimiterWebFilterFactory(rateLimiter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RewritePathWebFilterFactory rewritePathWebFilterFactory() {
|
||||
return new RewritePathWebFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SetPathWebFilterFactory setPathWebFilterFactory() {
|
||||
return new SetPathWebFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecureHeadersWebFilterFactory secureHeadersWebFilterFactory(SecureHeadersProperties properties) {
|
||||
return new SecureHeadersWebFilterFactory(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SetResponseHeaderWebFilterFactory setResponseHeaderWebFilterFactory() {
|
||||
return new SetResponseHeaderWebFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SetStatusWebFilterFactory setStatusWebFilterFactory() {
|
||||
return new SetStatusWebFilterFactory();
|
||||
}
|
||||
|
||||
|
||||
@ConditionalOnClass(RedisTemplate.class)
|
||||
protected static class GatewayRedisConfiguration {
|
||||
@Bean
|
||||
public RedisScript<List> redistRequestRateLimiterScript() {
|
||||
DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
|
||||
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/scripts/request_rate_limiter.lua")));
|
||||
redisScript.setResultType(List.class);
|
||||
return redisScript;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisRateLimiter redisRateLimiter(StringRedisTemplate redisTemplate,
|
||||
@Qualifier("redistRequestRateLimiterScript") RedisScript<List> redisScript) {
|
||||
return new RedisRateLimiter(redisTemplate, redisScript);
|
||||
}
|
||||
}
|
||||
|
||||
/*@Bean
|
||||
public RouterFunction<ServerResponse> test() {
|
||||
RouterFunction<ServerResponse> route = RouterFunctions.route(
|
||||
RequestPredicates.path("/testfun"),
|
||||
request -> ServerResponse.ok().body(BodyInserters.fromObject("hello")));
|
||||
return route;
|
||||
}*/
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
protected static class GatewayActuatorConfiguration {
|
||||
|
||||
@Bean
|
||||
public GatewayEndpoint gatewayEndpoint(RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,
|
||||
List<WebFilterFactory> webFilterFactories, RouteDefinitionWriter routeDefinitionWriter,
|
||||
RouteLocator routeLocator) {
|
||||
return new GatewayEndpoint(routeDefinitionLocator, globalFilters, webFilterFactories, routeDefinitionWriter, routeLocator);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
public class GatewayConfiguration {
|
||||
|
||||
@Bean
|
||||
public Marker gatewayMarker() {
|
||||
return new Marker();
|
||||
}
|
||||
|
||||
class Marker { }
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({LoadBalancerClient.class, RibbonAutoConfiguration.class})
|
||||
@ConditionalOnBean(GatewayConfiguration.Marker.class)
|
||||
@AutoConfigureAfter(RibbonAutoConfiguration.class)
|
||||
public class GatewayLoadBalancerClientAutoConfiguration {
|
||||
|
||||
// GlobalFilter beans
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(LoadBalancerClient.class)
|
||||
public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client) {
|
||||
return new LoadBalancerClientFilter(client);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.gateway.filter.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveNonProxyHeadersWebFilterFactory;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.NameUtils.normalizeFilterName;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.gateway")
|
||||
@Validated
|
||||
public class GatewayProperties {
|
||||
|
||||
/**
|
||||
* List of Routes
|
||||
*/
|
||||
@NotNull
|
||||
@Valid
|
||||
private List<RouteDefinition> routes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* List of filter definitions that are applied to every route.
|
||||
*/
|
||||
private List<FilterDefinition> defaultFilters = loadDefaults();
|
||||
|
||||
private ArrayList<FilterDefinition> loadDefaults() {
|
||||
ArrayList<FilterDefinition> defaults = new ArrayList<>();
|
||||
FilterDefinition definition = new FilterDefinition();
|
||||
definition.setName(normalizeFilterName(RemoveNonProxyHeadersWebFilterFactory.class));
|
||||
defaults.add(definition);
|
||||
return defaults;
|
||||
}
|
||||
|
||||
public List<RouteDefinition> getRoutes() {
|
||||
return routes;
|
||||
}
|
||||
|
||||
public void setRoutes(List<RouteDefinition> routes) {
|
||||
this.routes = routes;
|
||||
}
|
||||
|
||||
public List<FilterDefinition> getDefaultFilters() {
|
||||
return defaultFilters;
|
||||
}
|
||||
|
||||
public void setDefaultFilters(List<FilterDefinition> defaultFilters) {
|
||||
this.defaultFilters = defaultFilters;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
|
||||
|
||||
private final GatewayProperties properties;
|
||||
|
||||
public PropertiesRouteDefinitionLocator(GatewayProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return Flux.fromIterable(this.properties.getRoutes());
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.discovery;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.filter.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
|
||||
import static org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory.REGEXP_KEY;
|
||||
import static org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory.REPLACEMENT_KEY;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory.PATTERN_KEY;
|
||||
import static org.springframework.cloud.gateway.support.NameUtils.normalizeFilterName;
|
||||
import static org.springframework.cloud.gateway.support.NameUtils.normalizePredicateName;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* TODO: developer configuration, in zuul, this was opt out, should be opt in
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {
|
||||
|
||||
private final DiscoveryClient discoveryClient;
|
||||
private final String routeIdPrefix;
|
||||
|
||||
public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {
|
||||
this.discoveryClient = discoveryClient;
|
||||
this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + "_";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return Flux.fromIterable(discoveryClient.getServices())
|
||||
.map(serviceId -> {
|
||||
RouteDefinition routeDefinition = new RouteDefinition();
|
||||
routeDefinition.setId(this.routeIdPrefix + serviceId);
|
||||
routeDefinition.setUri(URI.create("lb://" + serviceId));
|
||||
|
||||
// add a predicate that matches the url at /serviceId*
|
||||
PredicateDefinition predicate = new PredicateDefinition();
|
||||
predicate.setName(normalizePredicateName(PathRoutePredicateFactory.class));
|
||||
predicate.addArg(PATTERN_KEY, "/" + serviceId + "*");
|
||||
routeDefinition.getPredicates().add(predicate);
|
||||
|
||||
//TODO: support for other default predicates
|
||||
|
||||
// add a filter that removes /serviceId by default
|
||||
FilterDefinition filter = new FilterDefinition();
|
||||
filter.setName(normalizeFilterName(RewritePathWebFilterFactory.class));
|
||||
String regex = "/" + serviceId + "/(?<remaining>.*)";
|
||||
String replacement = "/${remaining}";
|
||||
filter.addArg(REGEXP_KEY, regex);
|
||||
filter.addArg(REPLACEMENT_KEY, replacement);
|
||||
routeDefinition.getFilters().add(filter);
|
||||
|
||||
//TODO: support for default filters
|
||||
|
||||
return routeDefinition;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static org.springframework.util.StringUtils.tokenizeToStringArray;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Validated
|
||||
public class FilterDefinition {
|
||||
@NotNull
|
||||
private String name;
|
||||
private Map<String, String> args = new LinkedHashMap<>();
|
||||
|
||||
public FilterDefinition() {
|
||||
}
|
||||
|
||||
public FilterDefinition(String text) {
|
||||
int eqIdx = text.indexOf("=");
|
||||
if (eqIdx <= 0) {
|
||||
setName(text);
|
||||
return;
|
||||
}
|
||||
setName(text.substring(0, eqIdx));
|
||||
|
||||
String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ",");
|
||||
|
||||
for (int i=0; i < args.length; i++) {
|
||||
this.args.put(NameUtils.generateName(i), args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Map<String, String> getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public void setArgs(Map<String, String> args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public void addArg(String key, String value) {
|
||||
this.args.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
FilterDefinition that = (FilterDefinition) o;
|
||||
return Objects.equals(name, that.name) &&
|
||||
Objects.equals(args, that.args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("FilterDefinition{");
|
||||
sb.append("name='").append(name).append('\'');
|
||||
sb.append(", args=").append(args);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Contract for interception-style, chained processing of Web requests that may
|
||||
* be used to implement cross-cutting, application-agnostic requirements such
|
||||
* as security, timeouts, and others.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface GlobalFilter {
|
||||
|
||||
/**
|
||||
* Process the Web request and (optionally) delegate to the next
|
||||
* {@code WebFilter} through the given {@link WebFilterChain}.
|
||||
* @param exchange the current server exchange
|
||||
* @param chain provides a way to delegate to the next filter
|
||||
* @return {@code Mono<Void>} to indicate when request processing is complete
|
||||
*/
|
||||
Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain);
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
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;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class LoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LoadBalancerClientFilter.class);
|
||||
public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10100;
|
||||
|
||||
private final LoadBalancerClient loadBalancer;
|
||||
|
||||
public LoadBalancerClientFilter(LoadBalancerClient loadBalancer) {
|
||||
this.loadBalancer = loadBalancer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return LOAD_BALANCER_CLIENT_FILTER_ORDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
Optional<URI> url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
if (!url.isPresent() || !url.get().getScheme().equals("lb")) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
log.trace("LoadBalancerClientFilter url before: " + url.get());
|
||||
|
||||
final ServiceInstance instance = loadBalancer.choose(url.get().getHost());
|
||||
|
||||
if (instance == null) {
|
||||
throw new NotFoundException("");
|
||||
}
|
||||
|
||||
URI requestUrl = UriComponentsBuilder.fromUri(url.get())
|
||||
.scheme(instance.isSecure()? "https" : "http") //TODO: support websockets
|
||||
.host(instance.getHost())
|
||||
.port(instance.getPort())
|
||||
.build(true)
|
||||
.toUri();
|
||||
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
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;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.ipc.netty.NettyPipeline;
|
||||
import reactor.ipc.netty.http.client.HttpClient;
|
||||
import reactor.ipc.netty.http.client.HttpClientRequest;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
public NettyRoutingFilter(HttpClient httpClient) {
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 2000000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
Optional<URI> requestUrl = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
if (!requestUrl.isPresent()) {
|
||||
return Mono.error(new IllegalStateException("No URI found in attribute: " + GATEWAY_REQUEST_URL_ATTR));
|
||||
}
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
|
||||
final HttpMethod method = HttpMethod.valueOf(request.getMethod().toString());
|
||||
final String url = requestUrl.get().toString();
|
||||
|
||||
final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
|
||||
request.getHeaders().forEach(httpHeaders::set);
|
||||
|
||||
return this.httpClient.request(method, url, req -> {
|
||||
final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach)
|
||||
.failOnClientError(false)
|
||||
.headers(httpHeaders);
|
||||
|
||||
if (MediaType.APPLICATION_FORM_URLENCODED.includes(request.getHeaders().getContentType())) {
|
||||
return exchange.getFormData()
|
||||
.flatMap(map -> proxyRequest.sendForm(form -> {
|
||||
for (Map.Entry<String, List<String>> entry: map.entrySet()) {
|
||||
for (String value : entry.getValue()) {
|
||||
form.attr(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
}).then())
|
||||
.then(chain.filter(exchange));
|
||||
}
|
||||
|
||||
return proxyRequest.sendHeaders()
|
||||
.send(request.getBody()
|
||||
.map(DataBuffer::asByteBuffer)
|
||||
.map(Unpooled::wrappedBuffer));
|
||||
}).flatMap(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);
|
||||
return Mono.empty();
|
||||
}).then(chain.filter(exchange));
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class OrderedWebFilter implements WebFilter, Ordered {
|
||||
|
||||
private final WebFilter delegate;
|
||||
private final int order;
|
||||
|
||||
public OrderedWebFilter(WebFilter delegate, int order) {
|
||||
this.delegate = delegate;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
return this.delegate.filter(exchange, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("OrderedWebFilter{");
|
||||
sb.append("delegate=").append(delegate);
|
||||
sb.append(", order=").append(order);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
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;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {
|
||||
|
||||
private static final Log log = LogFactory.getLog(RouteToRequestUrlFilter.class);
|
||||
public static final int ROUTE_TO_URL_FILTER_ORDER = 10000;
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return ROUTE_TO_URL_FILTER_ORDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
Optional<Route> route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||
if (!route.isPresent()) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
log.info("RouteToRequestUrlFilter start");
|
||||
URI requestUrl = UriComponentsBuilder.fromHttpRequest(exchange.getRequest())
|
||||
.uri(route.get().getUri())
|
||||
.build(true)
|
||||
.toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.buffer.NettyDataBuffer;
|
||||
import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.ipc.netty.http.client.HttpClientResponse;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class WriteResponseFilter implements GlobalFilter, Ordered {
|
||||
|
||||
private static final Log log = LogFactory.getLog(WriteResponseFilter.class);
|
||||
|
||||
public static final int WRITE_RESPONSE_FILTER_ORDER = -1;
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return WRITE_RESPONSE_FILTER_ORDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
// 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<HttpClientResponse> clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR);
|
||||
// HttpClientResponse clientResponse = getAttribute(exchange, CLIENT_RESPONSE_ATTR, HttpClientResponse.class);
|
||||
if (!clientResponse.isPresent()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
log.trace("WriteResponseFilter start");
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
|
||||
NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory();
|
||||
//TODO: what if it's not netty
|
||||
|
||||
final Flux<NettyDataBuffer> body = clientResponse.get().receive()
|
||||
.retain() //TODO: needed?
|
||||
.map(factory::wrap);
|
||||
|
||||
return response.writeWith(body);
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AddRequestHeaderWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(NAME_KEY, VALUE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
String name = args.getString(NAME_KEY);
|
||||
String value = args.getString(VALUE_KEY);
|
||||
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.header(name, value)
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.server.reactive.RequestPath;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AddRequestParameterWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(NAME_KEY, VALUE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
String parameter = args.getString(NAME_KEY);
|
||||
String value = args.getString(VALUE_KEY);
|
||||
|
||||
return (exchange, chain) -> {
|
||||
|
||||
URI uri = exchange.getRequest().getURI();
|
||||
StringBuilder query = new StringBuilder();
|
||||
String originalQuery = uri.getQuery();
|
||||
|
||||
if (StringUtils.hasText(originalQuery)) {
|
||||
query.append(originalQuery);
|
||||
if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
|
||||
query.append('&');
|
||||
}
|
||||
}
|
||||
|
||||
//TODO urlencode?
|
||||
query.append(parameter);
|
||||
query.append('=');
|
||||
query.append(value);
|
||||
|
||||
ServerHttpRequest request = new QueryParamServerHttpRequestBuilder(exchange.getRequest())
|
||||
.query(query.toString())
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
};
|
||||
}
|
||||
|
||||
class QueryParamServerHttpRequestBuilder implements ServerHttpRequest.Builder {
|
||||
|
||||
private final ServerHttpRequest delegate;
|
||||
private String query;
|
||||
|
||||
public QueryParamServerHttpRequestBuilder(ServerHttpRequest delegate) {
|
||||
Assert.notNull(delegate, "ServerHttpRequest delegate is required");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ServerHttpRequest.Builder method(HttpMethod httpMethod) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerHttpRequest.Builder path(String path) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public ServerHttpRequest.Builder query(String query) {
|
||||
this.query = query;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerHttpRequest.Builder contextPath(String contextPath) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerHttpRequest.Builder header(String key, String value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerHttpRequest build() {
|
||||
URI uri = null;
|
||||
if (this.query != null) {
|
||||
uri = this.delegate.getURI();
|
||||
try {
|
||||
uri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
|
||||
uri.getPath(), this.query, uri.getFragment());
|
||||
} catch (URISyntaxException ex) {
|
||||
throw new IllegalStateException("Invalid URI query: \"" + this.query + "\"");
|
||||
}
|
||||
}
|
||||
return new MutativeDecorator(this.delegate, uri);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An immutable wrapper of a request returning property overrides -- given
|
||||
* to the constructor -- or original values otherwise.
|
||||
*/
|
||||
private class MutativeDecorator extends ServerHttpRequestDecorator {
|
||||
|
||||
private final URI uri;
|
||||
|
||||
public MutativeDecorator(ServerHttpRequest delegate, URI uri) {
|
||||
super(delegate);
|
||||
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpMethod getMethod() {
|
||||
return super.getMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return (this.uri != null ? this.uri : super.getURI());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestPath getPath() {
|
||||
return super.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return super.getHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AddResponseHeaderWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(NAME_KEY, VALUE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
final String header = args.getString(NAME_KEY);
|
||||
final String value = args.getString(VALUE_KEY);
|
||||
|
||||
return (exchange, chain) -> {
|
||||
exchange.getResponse().getHeaders().add(header, value);
|
||||
|
||||
return chain.filter(exchange);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
import com.netflix.hystrix.HystrixCommandKey;
|
||||
import com.netflix.hystrix.HystrixObservableCommand;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Observable;
|
||||
import rx.RxReactiveStreams;
|
||||
import rx.Subscription;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class HystrixWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(NAME_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
//TODO: if no name is supplied, generate one from command id (useful for default filter)
|
||||
final String commandName = args.getString(NAME_KEY);
|
||||
final HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey(getClass().getSimpleName());
|
||||
final HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(commandName);
|
||||
|
||||
final HystrixObservableCommand.Setter setter = HystrixObservableCommand.Setter
|
||||
.withGroupKey(groupKey)
|
||||
.andCommandKey(commandKey);
|
||||
|
||||
return (exchange, chain) -> {
|
||||
RouteHystrixCommand command = new RouteHystrixCommand(setter, exchange, chain);
|
||||
|
||||
return Mono.create(s -> {
|
||||
Subscription sub = command.toObservable().subscribe(s::success, s::error, s::success);
|
||||
s.onCancel(sub::unsubscribe);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
//TODO: replace with HystrixMonoCommand that we write
|
||||
private class RouteHystrixCommand extends HystrixObservableCommand<Void> {
|
||||
private final ServerWebExchange exchange;
|
||||
private final WebFilterChain chain;
|
||||
|
||||
RouteHystrixCommand(Setter setter, ServerWebExchange exchange, WebFilterChain chain) {
|
||||
super(setter);
|
||||
this.exchange = exchange;
|
||||
this.chain = chain;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Observable<Void> construct() {
|
||||
return RxReactiveStreams.toObservable(this.chain.filter(this.exchange));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class PrefixPathWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
public static final String PREFIX_KEY = "prefix";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(PREFIX_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public WebFilter apply(Tuple args) {
|
||||
final String prefix = args.getString(PREFIX_KEY);
|
||||
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
String newPath = prefix + req.getURI().getPath();
|
||||
|
||||
ServerHttpRequest request = req.mutate()
|
||||
.path(newPath)
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.parse;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RedirectToWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
public static final String STATUS_KEY = "status";
|
||||
public static final String URL_KEY = "url";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(STATUS_KEY, URL_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
String statusString = args.getRawString(STATUS_KEY);
|
||||
String urlString = args.getString(URL_KEY);
|
||||
|
||||
final HttpStatus httpStatus = parse(statusString);
|
||||
final URL url;
|
||||
try {
|
||||
url = URI.create(urlString).toURL();
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException("Invalid url " + urlString, e);
|
||||
}
|
||||
|
||||
return (exchange, chain) ->
|
||||
chain.filter(exchange).then(Mono.defer(() -> {
|
||||
if (!exchange.getResponse().isCommitted()) {
|
||||
setResponseStatus(exchange, httpStatus);
|
||||
|
||||
final ServerHttpResponse response = exchange.getResponse();
|
||||
response.getHeaders().set(HttpHeaders.LOCATION, url.toString());
|
||||
return response.setComplete();
|
||||
}
|
||||
return Mono.empty();
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Hop-by-hop header fields, which are meaningful only for a single transport-level connection,
|
||||
* and are not stored by caches or forwarded by proxies. The following HTTP/1.1 header fields
|
||||
* are hop-by-hop header fields:
|
||||
* <ul>
|
||||
* <li>Connection
|
||||
* <li>Keep-Alive
|
||||
* <li>Proxy-Authenticate
|
||||
* <li>Proxy-Authorization
|
||||
* <li>TE
|
||||
* <li>Trailer
|
||||
* <li>Transfer-Encoding
|
||||
* <li>Upgrade
|
||||
* </ul>
|
||||
*
|
||||
* See https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.gateway.filter.remove-non-proxy-headers")
|
||||
public class RemoveNonProxyHeadersWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
private static final String FAKE_HEADER = "_______force_______";
|
||||
public static final String[] DEFAULT_HEADERS_TO_REMOVE = new String[] {"Connection", "Keep-Alive",
|
||||
"Proxy-Authenticate", "Proxy-Authorization", "TE", "Trailer", "Transfer-Encoding", "Upgrade"};
|
||||
|
||||
private List<String> headers = Arrays.asList(DEFAULT_HEADERS_TO_REMOVE);
|
||||
|
||||
public List<String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public void setHeaders(List<String> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
//TODO: support filter args
|
||||
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.header(FAKE_HEADER, "mutable") //TODO: is there a better way?
|
||||
.build();
|
||||
|
||||
request.getHeaders().remove(FAKE_HEADER);
|
||||
|
||||
for (String header : this.headers) {
|
||||
request.getHeaders().remove(header);
|
||||
}
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RemoveRequestHeaderWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
private static final String FAKE_HEADER = "_______force_______";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(NAME_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
final String header = args.getString(NAME_KEY);
|
||||
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.header(FAKE_HEADER, "mutable") //TODO: is there a better way?
|
||||
.build();
|
||||
|
||||
request.getHeaders().remove(FAKE_HEADER);
|
||||
request.getHeaders().remove(header);
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RemoveResponseHeaderWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(NAME_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
final String header = args.getString(NAME_KEY);
|
||||
|
||||
return (exchange, chain) -> {
|
||||
exchange.getResponse().getHeaders().remove(header);
|
||||
|
||||
return chain.filter(exchange);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter.Response;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User Request Rate Limiter filter.
|
||||
* See https://stripe.com/blog/rate-limiters and
|
||||
*/
|
||||
public class RequestRateLimiterWebFilterFactory implements WebFilterFactory, ApplicationContextAware {
|
||||
|
||||
public static final String REPLENISH_RATE_KEY = "replenishRate";
|
||||
public static final String BURST_CAPACITY_KEY = "burstCapacity";
|
||||
public static final String KEY_RESOLVER_NAME_KEY = "keyResolverName";
|
||||
|
||||
private final RateLimiter rateLimiter;
|
||||
private ApplicationContext context;
|
||||
|
||||
public RequestRateLimiterWebFilterFactory(RateLimiter rateLimiter) {
|
||||
this.rateLimiter = rateLimiter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(REPLENISH_RATE_KEY, BURST_CAPACITY_KEY, KEY_RESOLVER_NAME_KEY);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
// How many requests per second do you want a user to be allowed to do?
|
||||
int replenishRate = args.getInt(REPLENISH_RATE_KEY);
|
||||
|
||||
// How much bursting do you want to allow?
|
||||
int capacity = args.getInt(BURST_CAPACITY_KEY);
|
||||
|
||||
String beanName = args.getString(KEY_RESOLVER_NAME_KEY);
|
||||
KeyResolver keyResolver = this.context.getBean(beanName, KeyResolver.class);
|
||||
|
||||
return (exchange, chain) ->
|
||||
keyResolver.resolve(exchange).flatMap(key -> {
|
||||
Response response = rateLimiter.isAllowed(key, replenishRate, capacity);
|
||||
|
||||
//TODO: set some headers for rate, tokens left
|
||||
|
||||
if (response.isAllowed()) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
|
||||
return exchange.getResponse().setComplete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RewritePathWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
public static final String REGEXP_KEY = "regexp";
|
||||
public static final String REPLACEMENT_KEY = "replacement";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(REGEXP_KEY, REPLACEMENT_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
final String regex = args.getString(REGEXP_KEY);
|
||||
String replacement = args.getString(REPLACEMENT_KEY).replace("$\\", "$");
|
||||
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
String path = req.getURI().getPath();
|
||||
String newPath = path.replaceAll(regex, replacement);
|
||||
|
||||
ServerHttpRequest request = req.mutate()
|
||||
.path(newPath)
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.gateway.filter.secure-headers")
|
||||
public class SecureHeadersProperties {
|
||||
public static final String X_XSS_PROTECTION_HEADER_DEFAULT = "1; mode=block";
|
||||
public static final String STRICT_TRANSPORT_SECURITY_HEADER_DEFAULT = "max-age=631138519"; //; includeSubDomains preload")
|
||||
public static final String X_FRAME_OPTIONS_HEADER_DEFAULT = "DENY"; //SAMEORIGIN = ALLOW-FROM
|
||||
public static final String X_CONTENT_TYPE_OPTIONS_HEADER_DEFAULT = "nosniff";
|
||||
public static final String REFERRER_POLICY_HEADER_DEFAULT = "no-referrer"; //no-referrer-when-downgrade = origin = origin-when-cross-origin = same-origin = strict-origin = strict-origin-when-cross-origin = unsafe-url
|
||||
public static final String CONTENT_SECURITY_POLICY_HEADER_DEFAULT = "default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'";
|
||||
public static final String X_DOWNLOAD_OPTIONS_HEADER_DEFAULT = "noopen";
|
||||
public static final String X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER_DEFAULT = "none";
|
||||
|
||||
private String xssProtectionHeader = X_XSS_PROTECTION_HEADER_DEFAULT;
|
||||
private String strictTransportSecurity = STRICT_TRANSPORT_SECURITY_HEADER_DEFAULT;
|
||||
private String frameOptions = X_FRAME_OPTIONS_HEADER_DEFAULT;
|
||||
private String contentTypeOptions = X_CONTENT_TYPE_OPTIONS_HEADER_DEFAULT;
|
||||
private String referrerPolicy = REFERRER_POLICY_HEADER_DEFAULT;
|
||||
private String contentSecurityPolicy = CONTENT_SECURITY_POLICY_HEADER_DEFAULT;
|
||||
private String downloadOptions = X_DOWNLOAD_OPTIONS_HEADER_DEFAULT;
|
||||
private String permittedCrossDomainPolicies = X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER_DEFAULT;
|
||||
|
||||
public String getXssProtectionHeader() {
|
||||
return xssProtectionHeader;
|
||||
}
|
||||
|
||||
public void setXssProtectionHeader(String xssProtectionHeader) {
|
||||
this.xssProtectionHeader = xssProtectionHeader;
|
||||
}
|
||||
|
||||
public String getStrictTransportSecurity() {
|
||||
return strictTransportSecurity;
|
||||
}
|
||||
|
||||
public void setStrictTransportSecurity(String strictTransportSecurity) {
|
||||
this.strictTransportSecurity = strictTransportSecurity;
|
||||
}
|
||||
|
||||
public String getFrameOptions() {
|
||||
return frameOptions;
|
||||
}
|
||||
|
||||
public void setFrameOptions(String frameOptions) {
|
||||
this.frameOptions = frameOptions;
|
||||
}
|
||||
|
||||
public String getContentTypeOptions() {
|
||||
return contentTypeOptions;
|
||||
}
|
||||
|
||||
public void setContentTypeOptions(String contentTypeOptions) {
|
||||
this.contentTypeOptions = contentTypeOptions;
|
||||
}
|
||||
|
||||
public String getReferrerPolicy() {
|
||||
return referrerPolicy;
|
||||
}
|
||||
|
||||
public void setReferrerPolicy(String referrerPolicy) {
|
||||
this.referrerPolicy = referrerPolicy;
|
||||
}
|
||||
|
||||
public String getContentSecurityPolicy() {
|
||||
return contentSecurityPolicy;
|
||||
}
|
||||
|
||||
public void setContentSecurityPolicy(String contentSecurityPolicy) {
|
||||
this.contentSecurityPolicy = contentSecurityPolicy;
|
||||
}
|
||||
|
||||
public String getDownloadOptions() {
|
||||
return downloadOptions;
|
||||
}
|
||||
|
||||
public void setDownloadOptions(String downloadOptions) {
|
||||
this.downloadOptions = downloadOptions;
|
||||
}
|
||||
|
||||
public String getPermittedCrossDomainPolicies() {
|
||||
return permittedCrossDomainPolicies;
|
||||
}
|
||||
|
||||
public void setPermittedCrossDomainPolicies(String permittedCrossDomainPolicies) {
|
||||
this.permittedCrossDomainPolicies = permittedCrossDomainPolicies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("SecureHeadersProperties{");
|
||||
sb.append("xssProtectionHeader='").append(xssProtectionHeader).append('\'');
|
||||
sb.append(", strictTransportSecurity='").append(strictTransportSecurity).append('\'');
|
||||
sb.append(", frameOptions='").append(frameOptions).append('\'');
|
||||
sb.append(", contentTypeOptions='").append(contentTypeOptions).append('\'');
|
||||
sb.append(", referrerPolicy='").append(referrerPolicy).append('\'');
|
||||
sb.append(", contentSecurityPolicy='").append(contentSecurityPolicy).append('\'');
|
||||
sb.append(", downloadOptions='").append(downloadOptions).append('\'');
|
||||
sb.append(", permittedCrossDomainPolicies='").append(permittedCrossDomainPolicies).append('\'');
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
/**
|
||||
* https://blog.appcanary.com/2017/http-security-headers.html
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SecureHeadersWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
public static final String X_XSS_PROTECTION_HEADER = "X-Xss-Protection";
|
||||
public static final String STRICT_TRANSPORT_SECURITY_HEADER = "Strict-Transport-Security";
|
||||
public static final String X_FRAME_OPTIONS_HEADER = "X-Frame-Options";
|
||||
public static final String X_CONTENT_TYPE_OPTIONS_HEADER = "X-Content-Type-Options";
|
||||
public static final String REFERRER_POLICY_HEADER = "Referrer-Policy";
|
||||
public static final String CONTENT_SECURITY_POLICY_HEADER = "Content-Security-Policy";
|
||||
public static final String X_DOWNLOAD_OPTIONS_HEADER = "X-Download-Options";
|
||||
public static final String X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER = "X-Permitted-Cross-Domain-Policies";
|
||||
|
||||
private final SecureHeadersProperties properties;
|
||||
|
||||
public SecureHeadersWebFilterFactory(SecureHeadersProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
//TODO: allow args to override properties
|
||||
|
||||
return (exchange, chain) -> {
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
|
||||
headers.add(X_XSS_PROTECTION_HEADER, properties.getXssProtectionHeader());
|
||||
headers.add(STRICT_TRANSPORT_SECURITY_HEADER, properties.getStrictTransportSecurity());
|
||||
headers.add(X_FRAME_OPTIONS_HEADER, properties.getFrameOptions());
|
||||
headers.add(X_CONTENT_TYPE_OPTIONS_HEADER, properties.getContentTypeOptions());
|
||||
headers.add(REFERRER_POLICY_HEADER, properties.getReferrerPolicy());
|
||||
headers.add(CONTENT_SECURITY_POLICY_HEADER, properties.getContentSecurityPolicy());
|
||||
headers.add(X_DOWNLOAD_OPTIONS_HEADER, properties.getDownloadOptions());
|
||||
headers.add(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER, properties.getPermittedCrossDomainPolicies());
|
||||
|
||||
return chain.filter(exchange);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import java.net.URI;
|
||||
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 static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SetPathWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
public static final String TEMPLATE_KEY = "template";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(TEMPLATE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public WebFilter apply(Tuple args) {
|
||||
String template = args.getString(TEMPLATE_KEY);
|
||||
UriTemplate uriTemplate = new UriTemplate(template);
|
||||
|
||||
return (exchange, chain) -> {
|
||||
Optional<Map<String, String>> variables = exchange.getAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
URI uri = uriTemplate.expand(variables.orElseGet(Collections::emptyMap));
|
||||
String newPath = uri.getPath();
|
||||
|
||||
ServerHttpRequest request = req.mutate()
|
||||
.path(newPath)
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SetResponseHeaderWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(NAME_KEY, VALUE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
final String header = args.getString(NAME_KEY);
|
||||
final String value = args.getString(VALUE_KEY);
|
||||
|
||||
return (exchange, chain) -> {
|
||||
exchange.getResponse().getHeaders().set(header, value);
|
||||
|
||||
return chain.filter(exchange);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SetStatusWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
public static final String STATUS_KEY = "status";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(STATUS_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebFilter apply(Tuple args) {
|
||||
String status = args.getRawString(STATUS_KEY);
|
||||
final HttpStatus httpStatus = ServerWebExchangeUtils.parse(status);
|
||||
|
||||
return (exchange, chain) -> {
|
||||
|
||||
// option 1 (runs in filter order)
|
||||
/*exchange.getResponse().beforeCommit(() -> {
|
||||
exchange.getResponse().setStatusCode(finalStatus);
|
||||
return Mono.empty();
|
||||
});
|
||||
return chain.filter(exchange);*/
|
||||
|
||||
// option 2 (runs in reverse filter order)
|
||||
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
|
||||
// check not really needed, since it is guarded in setStatusCode,
|
||||
// but it's a good example
|
||||
if (!exchange.getResponse().isCommitted()) {
|
||||
setResponseStatus(exchange, httpStatus);
|
||||
}
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import static org.springframework.cloud.gateway.filter.factory.WebFilterFactory.NAME_KEY;
|
||||
import static org.springframework.cloud.gateway.filter.factory.WebFilterFactory.VALUE_KEY;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class WebFilterFactories {
|
||||
//TODO: add support for AddRequestHeaderWebFilterFactory
|
||||
|
||||
//TODO: add support for AddRequestParameterWebFilterFactory
|
||||
|
||||
public static WebFilter addResponseHeader(String headerName, String headerValue) {
|
||||
Tuple args = tuple().of(NAME_KEY, headerName, VALUE_KEY, headerValue);
|
||||
return new AddResponseHeaderWebFilterFactory().apply(args);
|
||||
}
|
||||
|
||||
//TODO: add support for HystrixWebFilterFactory
|
||||
|
||||
//TODO: add support for PrefixPathWebFilterFactory
|
||||
|
||||
//TODO: add support for RedirectToWebFilterFactory
|
||||
|
||||
//TODO: add support for RemoveNonProxyHeadersWebFilterFactory
|
||||
|
||||
//TODO: add support for RemoveRequestHeaderWebFilterFactory
|
||||
|
||||
//TODO: add support for RemoveResponseHeaderWebFilterFactory
|
||||
|
||||
//TODO: add support for RewritePathWebFilterFactory
|
||||
|
||||
//TODO: add support for SecureHeadersProperties
|
||||
|
||||
//TODO: add support for SecureHeadersWebFilterFactory
|
||||
|
||||
//TODO: add support for SetPathWebFilterFactory
|
||||
|
||||
//TODO: add support for SetResponseHeaderWebFilterFactory
|
||||
|
||||
//TODO: add support for SetStatusWebFilterFactory
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.springframework.cloud.gateway.support.ArgumentHints;
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface WebFilterFactory extends ArgumentHints {
|
||||
|
||||
String NAME_KEY = "name";
|
||||
String VALUE_KEY = "value";
|
||||
|
||||
WebFilter apply(Tuple args);
|
||||
|
||||
default String name() {
|
||||
return NameUtils.normalizeFilterName(getClass());
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.springframework.cloud.gateway.filter.ratelimit;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
//TODO: KeyResolver for exchange.getPrincipal().flatMap(principal -> {})
|
||||
public interface KeyResolver {
|
||||
Mono<String> resolve(ServerWebExchange exchange);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.springframework.cloud.gateway.filter.ratelimit;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface RateLimiter {
|
||||
Response isAllowed(String id, int replenishRate, int burstCapacity);
|
||||
|
||||
class Response {
|
||||
private final boolean allowed;
|
||||
private final long tokensRemaining;
|
||||
|
||||
public Response(boolean allowed, long tokensRemaining) {
|
||||
this.allowed = allowed;
|
||||
this.tokensRemaining = tokensRemaining;
|
||||
}
|
||||
|
||||
public boolean isAllowed() {
|
||||
return allowed;
|
||||
}
|
||||
|
||||
public long getTokensRemaining() {
|
||||
return tokensRemaining;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("Response{");
|
||||
sb.append("allowed=").append(allowed);
|
||||
sb.append(", tokensRemaining=").append(tokensRemaining);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package org.springframework.cloud.gateway.filter.ratelimit;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.script.RedisScript;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* See https://stripe.com/blog/rate-limiters and
|
||||
* https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L11-L34
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RedisRateLimiter implements RateLimiter {
|
||||
private Log log = LogFactory.getLog(getClass());
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
private final RedisScript<List> script;
|
||||
|
||||
public RedisRateLimiter(StringRedisTemplate redisTemplate, RedisScript<List> script) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
/**
|
||||
* This uses a basic token bucket algorithm and relies on the fact that Redis scripts execute atomically.
|
||||
* No other operations can run between fetching the count and writing the new count.
|
||||
* @param replenishRate
|
||||
* @param burstCapacity
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
//TODO: signature? params (tuple?).
|
||||
public Response isAllowed(String id, int replenishRate, int burstCapacity) {
|
||||
|
||||
try {
|
||||
// Make a unique key per user.
|
||||
String prefix = "request_rate_limiter." + id;
|
||||
|
||||
// You need two Redis keys for Token Bucket.
|
||||
List<String> keys = Arrays.asList(prefix + ".tokens", prefix + ".timestamp");
|
||||
|
||||
// The arguments to the LUA script. time() returns unixtime in seconds.
|
||||
Object[] args = new String[]{ replenishRate+"", burstCapacity +"", Instant.now().getEpochSecond()+"", "1"};
|
||||
// allowed, tokens_left = redis.eval(SCRIPT, keys, args)
|
||||
List results = this.redisTemplate.execute(this.script, keys, args);
|
||||
|
||||
boolean allowed = new Long(1L).equals(results.get(0));
|
||||
Long tokensLeft = (Long) results.get(1);
|
||||
|
||||
Response response = new Response(allowed, tokensLeft);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("response: "+response);
|
||||
}
|
||||
return response;
|
||||
|
||||
} catch (Exception e) {
|
||||
/* We don't want a hard dependency on Redis to allow traffic.
|
||||
Make sure to set an alert so you know if this is happening too much.
|
||||
Stripe's observed failure rate is 0.01%. */
|
||||
log.error("Error determining if user allowed from redis", e);
|
||||
}
|
||||
return new Response(true, -1);
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.OrderedWebFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
import org.springframework.web.server.handler.WebHandlerDecorator;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* WebHandler that delegates to a chain of {@link GlobalFilter} instances and
|
||||
* {@link WebFilterFactory} instances then to the target {@link WebHandler}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Spencer Gibb
|
||||
* @since 0.1
|
||||
*/
|
||||
public class FilteringWebHandler extends WebHandlerDecorator {
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final List<WebFilter> globalFilters;
|
||||
|
||||
public FilteringWebHandler(List<GlobalFilter> globalFilters) {
|
||||
this(new EmptyWebHandler(), globalFilters);
|
||||
}
|
||||
|
||||
public FilteringWebHandler(WebHandler targetHandler, List<GlobalFilter> globalFilters) {
|
||||
super(targetHandler);
|
||||
this.globalFilters = loadFilters(globalFilters);
|
||||
}
|
||||
|
||||
private static List<WebFilter> loadFilters(List<GlobalFilter> filters) {
|
||||
return filters.stream()
|
||||
.map(filter -> {
|
||||
WebFilterAdapter webFilter = new WebFilterAdapter(filter);
|
||||
if (filter instanceof Ordered) {
|
||||
int order = ((Ordered) filter).getOrder();
|
||||
return new OrderedWebFilter(webFilter, order);
|
||||
}
|
||||
return webFilter;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/* TODO: relocate @EventListener(RefreshRoutesEvent.class)
|
||||
void handleRefresh() {
|
||||
this.combinedFiltersForRoute.clear();
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange) {
|
||||
Optional<Route> route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||
List<WebFilter> webFilters = route.get().getWebFilters();
|
||||
|
||||
List<WebFilter> combined = new ArrayList<>(this.globalFilters);
|
||||
combined.addAll(webFilters);
|
||||
//TODO: needed or cached?
|
||||
AnnotationAwareOrderComparator.sort(combined);
|
||||
|
||||
logger.debug("Sorted webFilterFactories: "+ combined);
|
||||
|
||||
return new DefaultWebFilterChain(combined, getDelegate()).filter(exchange);
|
||||
}
|
||||
|
||||
private static class DefaultWebFilterChain implements WebFilterChain {
|
||||
|
||||
private int index;
|
||||
private final List<WebFilter> filters;
|
||||
private final WebHandler delegate;
|
||||
|
||||
public DefaultWebFilterChain(List<WebFilter> filters, WebHandler delegate) {
|
||||
this.filters = filters;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange) {
|
||||
if (this.index < filters.size()) {
|
||||
WebFilter filter = filters.get(this.index++);
|
||||
return filter.filter(exchange, this);
|
||||
}
|
||||
else {
|
||||
return this.delegate.handle(exchange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class WebFilterAdapter implements WebFilter {
|
||||
|
||||
private final GlobalFilter delegate;
|
||||
|
||||
public WebFilterAdapter(GlobalFilter delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
return this.delegate.filter(exchange, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("WebFilterAdapter{");
|
||||
sb.append("delegate=").append(delegate);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class EmptyWebHandler implements WebHandler {
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.ipc.netty.NettyPipeline;
|
||||
import reactor.ipc.netty.http.client.HttpClient;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class NettyProxyWebHandler implements WebHandler {
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
public NettyProxyWebHandler(HttpClient httpClient) {
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange) {
|
||||
Optional<URI> requestUrl = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
|
||||
final HttpMethod method = HttpMethod.valueOf(request.getMethod().toString());
|
||||
final String url = requestUrl.get().toString();
|
||||
|
||||
final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
|
||||
request.getHeaders().forEach(httpHeaders::set);
|
||||
|
||||
return this.httpClient.request(method, url, req ->
|
||||
req.options(NettyPipeline.SendOptions::flushOnEach)
|
||||
.headers(httpHeaders)
|
||||
.sendHeaders() // I shouldn't have to do this
|
||||
.send(request.getBody()
|
||||
.map(DataBuffer::asByteBuffer)
|
||||
.map(Unpooled::wrappedBuffer)))
|
||||
.doOnNext(res -> {
|
||||
// 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);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
// put headers and status so filters can modify the response
|
||||
final 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()));
|
||||
}).then();
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
private final WebHandler webHandler;
|
||||
private final RouteLocator routeLocator;
|
||||
|
||||
public RoutePredicateHandlerMapping(WebHandler webHandler, RouteLocator routeLocator) {
|
||||
this.webHandler = webHandler;
|
||||
this.routeLocator = routeLocator;
|
||||
|
||||
setOrder(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
|
||||
exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getClass().getSimpleName());
|
||||
|
||||
return lookupRoute(exchange)
|
||||
.log() //name this
|
||||
.flatMap((Function<Route, Mono<?>>) r -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);
|
||||
}
|
||||
|
||||
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
|
||||
return Mono.just(webHandler);
|
||||
}).switchIfEmpty(Mono.empty().then(Mono.defer(() -> {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");
|
||||
}
|
||||
return Mono.empty();
|
||||
})));
|
||||
}
|
||||
|
||||
//TODO: get desc from factory?
|
||||
private String getExchangeDesc(ServerWebExchange exchange) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
out.append("Exchange: ");
|
||||
out.append(exchange.getRequest().getMethod());
|
||||
out.append(" ");
|
||||
out.append(exchange.getRequest().getURI());
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
|
||||
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
|
||||
return this.routeLocator.getRoutes()
|
||||
.filter(route -> route.getPredicate().test(exchange))
|
||||
// .defaultIfEmpty() put a static Route not found
|
||||
// or .switchIfEmpty()
|
||||
// .switchIfEmpty(Mono.<Route>empty().log("noroute"))
|
||||
.next()
|
||||
//TODO: error handling
|
||||
.map(route -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RouteDefinition matched: " + route.getId());
|
||||
}
|
||||
validateRoute(route, exchange);
|
||||
return route;
|
||||
});
|
||||
|
||||
/* TODO: trace logging
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("RouteDefinition did not match: " + routeDefinition.getId());
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given handler against the current request.
|
||||
* <p>The default implementation is empty. Can be overridden in subclasses,
|
||||
* for example to enforce specific preconditions expressed in URL mappings.
|
||||
* @param route the Route object to validate
|
||||
* @param exchange current exchange
|
||||
* @throws Exception if validation failed
|
||||
*/
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
protected void validateRoute(Route route, ServerWebExchange exchange) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory.parseZonedDateTime;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AfterRoutePredicateFactory implements RoutePredicateFactory {
|
||||
|
||||
public static final String DATETIME_KEY = "datetime";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Collections.singletonList(DATETIME_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Tuple args) {
|
||||
final ZonedDateTime dateTime = parseZonedDateTime(args.getString(DATETIME_KEY));
|
||||
|
||||
return exchange -> {
|
||||
final ZonedDateTime now = ZonedDateTime.now();
|
||||
return now.isAfter(dateTime);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory.parseZonedDateTime;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class BeforeRoutePredicateFactory implements RoutePredicateFactory {
|
||||
|
||||
public static final String DATETIME_KEY = "datetime";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Collections.singletonList(DATETIME_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Tuple args) {
|
||||
final ZonedDateTime dateTime = parseZonedDateTime(args.getString(DATETIME_KEY));
|
||||
|
||||
return exchange -> {
|
||||
final ZonedDateTime now = ZonedDateTime.now();
|
||||
return now.isBefore(dateTime);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class BetweenRoutePredicateFactory implements RoutePredicateFactory {
|
||||
|
||||
public static final String DATETIME1_KEY = "datetime1";
|
||||
public static final String DATETIME2_KEY = "datetime2";
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Tuple args) {
|
||||
//TODO: is ZonedDateTime the right thing to use?
|
||||
final ZonedDateTime dateTime1 = parseZonedDateTime(args.getString(DATETIME1_KEY));
|
||||
final ZonedDateTime dateTime2 = parseZonedDateTime(args.getString(DATETIME2_KEY));
|
||||
Assert.isTrue(dateTime1.isBefore(dateTime2), args.getString(DATETIME1_KEY) +
|
||||
" must be before " + args.getString(DATETIME2_KEY));
|
||||
|
||||
return exchange -> {
|
||||
final ZonedDateTime now = ZonedDateTime.now();
|
||||
return now.isAfter(dateTime1) && now.isBefore(dateTime2);
|
||||
};
|
||||
}
|
||||
|
||||
public static ZonedDateTime parseZonedDateTime(String dateString) {
|
||||
ZonedDateTime dateTime;
|
||||
try {
|
||||
long epoch = Long.parseLong(dateString);
|
||||
|
||||
dateTime = Instant.ofEpochMilli(epoch).atOffset(ZoneOffset.ofTotalSeconds(0))
|
||||
.toZonedDateTime();
|
||||
} catch (NumberFormatException e) {
|
||||
// try ZonedDateTime instead
|
||||
dateTime = ZonedDateTime.parse(dateString);
|
||||
}
|
||||
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CookieRoutePredicateFactory implements RoutePredicateFactory {
|
||||
|
||||
public static final String NAME_KEY = "name";
|
||||
public static final String REGEXP_KEY = "regexp";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(NAME_KEY, REGEXP_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Tuple args) {
|
||||
String name = args.getString(NAME_KEY);
|
||||
String regexp = args.getString(REGEXP_KEY);
|
||||
|
||||
return exchange -> {
|
||||
List<HttpCookie> cookies = exchange.getRequest().getCookies().get(name);
|
||||
for (HttpCookie cookie : cookies) {
|
||||
if (cookie.getValue().matches(regexp)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class HeaderRoutePredicateFactory implements RoutePredicateFactory {
|
||||
|
||||
public static final String HEADER_KEY = "header";
|
||||
public static final String REGEXP_KEY = "regexp";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(HEADER_KEY, REGEXP_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Tuple args) {
|
||||
String header = args.getString(HEADER_KEY);
|
||||
String regexp = args.getString(REGEXP_KEY);
|
||||
|
||||
return exchange -> {
|
||||
List<String> values = exchange.getRequest().getHeaders().get(header);
|
||||
for (String value : values) {
|
||||
if (value.matches(regexp)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class HostRoutePredicateFactory implements RoutePredicateFactory {
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher(".");
|
||||
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
this.pathMatcher = pathMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Collections.singletonList(PATTERN_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Tuple args) {
|
||||
String pattern = args.getString(PATTERN_KEY);
|
||||
|
||||
return exchange -> {
|
||||
String host = exchange.getRequest().getHeaders().getFirst("Host");
|
||||
return this.pathMatcher.match(pattern, host);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class MethodRoutePredicateFactory implements RoutePredicateFactory {
|
||||
|
||||
public static final String METHOD_KEY = "method";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(METHOD_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Tuple args) {
|
||||
String method = args.getString(METHOD_KEY);
|
||||
return exchange -> {
|
||||
HttpMethod requestMethod = exchange.getRequest().getMethod();
|
||||
return requestMethod.matches(method);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
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.tuple.Tuple;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class PathRoutePredicateFactory implements RoutePredicateFactory {
|
||||
|
||||
private PathPatternParser pathPatternParser = new PathPatternParser();
|
||||
|
||||
public void setPathPatternParser(PathPatternParser pathPatternParser) {
|
||||
this.pathPatternParser = pathPatternParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Collections.singletonList(PATTERN_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Tuple args) {
|
||||
String unparsedPattern = args.getString(PATTERN_KEY);
|
||||
PathPattern pattern;
|
||||
synchronized (this.pathPatternParser) {
|
||||
pattern = this.pathPatternParser.parse(unparsedPattern);
|
||||
}
|
||||
|
||||
return exchange -> {
|
||||
String path = exchange.getRequest().getURI().getPath();
|
||||
boolean match = pattern.matches(path);
|
||||
traceMatch("Pattern", pattern.getPatternString(), path, match);
|
||||
if (match) {
|
||||
Map<String, String> uriTemplateVariables = pattern.matchAndExtract(path);
|
||||
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.validation.ValidationException;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static org.springframework.util.StringUtils.tokenizeToStringArray;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Validated
|
||||
public class PredicateDefinition {
|
||||
@NotNull
|
||||
private String name;
|
||||
private Map<String, String> args = new LinkedHashMap<>();
|
||||
|
||||
public PredicateDefinition() {
|
||||
}
|
||||
|
||||
public PredicateDefinition(String text) {
|
||||
int eqIdx = text.indexOf("=");
|
||||
if (eqIdx <= 0) {
|
||||
throw new ValidationException("Unable to parse PredicateDefinition text '" + text + "'" +
|
||||
", must be of the form name=value");
|
||||
}
|
||||
setName(text.substring(0, eqIdx));
|
||||
|
||||
String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ",");
|
||||
|
||||
for (int i=0; i < args.length; i++) {
|
||||
this.args.put(NameUtils.generateName(i), args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Map<String, String> getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public void setArgs(Map<String, String> args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public void addArg(String key, String value) {
|
||||
this.args.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PredicateDefinition that = (PredicateDefinition) o;
|
||||
return Objects.equals(name, that.name) &&
|
||||
Objects.equals(args, that.args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("PredicateDefinition{");
|
||||
sb.append("name='").append(name).append('\'');
|
||||
sb.append(", args=").append(args);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class QueryRoutePredicateFactory implements RoutePredicateFactory {
|
||||
|
||||
public static final String PARAM_KEY = "param";
|
||||
public static final String REGEXP_KEY = "regexp";
|
||||
|
||||
@Override
|
||||
public List<String> argNames() {
|
||||
return Arrays.asList(PARAM_KEY, REGEXP_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Tuple args) {
|
||||
validateMin(1, args);
|
||||
String param = args.getString(PARAM_KEY);
|
||||
|
||||
return exchange -> {
|
||||
if (!args.hasFieldName(REGEXP_KEY)) {
|
||||
// check existence of header
|
||||
return exchange.getRequest().getQueryParams().containsKey(param);
|
||||
}
|
||||
|
||||
String regexp = args.getString(REGEXP_KEY);
|
||||
|
||||
List<String> values = exchange.getRequest().getQueryParams().get(param);
|
||||
for (String value : values) {
|
||||
if (value.matches(regexp)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.gateway.support.SubnetUtils;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RemoteAddrRoutePredicateFactory implements RoutePredicateFactory {
|
||||
|
||||
private static final Log log = LogFactory.getLog(RemoteAddrRoutePredicateFactory.class);
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Tuple args) {
|
||||
validate(1, args);
|
||||
|
||||
List<SubnetUtils> sources = new ArrayList<>();
|
||||
if (args != null) {
|
||||
for (Object arg : args.getValues()) {
|
||||
addSource(sources, (String) arg);
|
||||
}
|
||||
}
|
||||
|
||||
return exchange -> {
|
||||
InetSocketAddress remoteAddress = exchange.getRequest().getRemoteAddress();
|
||||
if (remoteAddress != null) {
|
||||
String hostAddress = remoteAddress.getAddress().getHostAddress();
|
||||
String host = exchange.getRequest().getURI().getHost();
|
||||
|
||||
if (!hostAddress.equals(host)) {
|
||||
log.warn("Remote addresses didn't match " + hostAddress + " != " + host);
|
||||
}
|
||||
|
||||
for (SubnetUtils source : sources) {
|
||||
if (source.getInfo().isInRange(hostAddress)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
private void addSource(List<SubnetUtils> sources, String source) {
|
||||
boolean inclusiveHostCount = false;
|
||||
if (!source.contains("/")) { // no netmask, add default
|
||||
source = source + "/32";
|
||||
}
|
||||
if (source.endsWith("/32")) {
|
||||
//http://stackoverflow.com/questions/2942299/converting-cidr-address-to-subnet-mask-and-network-address#answer-6858429
|
||||
inclusiveHostCount = true;
|
||||
}
|
||||
//TODO: howto support ipv6 as well?
|
||||
SubnetUtils subnetUtils = new SubnetUtils(source);
|
||||
subnetUtils.setInclusiveHostCount(inclusiveHostCount);
|
||||
sources.add(subnetUtils);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.cloud.gateway.support.ArgumentHints;
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RoutePredicateFactory extends ArgumentHints {
|
||||
String PATTERN_KEY = "pattern";
|
||||
|
||||
Predicate<ServerWebExchange> apply(Tuple args);
|
||||
|
||||
default String name() {
|
||||
return NameUtils.normalizePredicateName(getClass());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory.METHOD_KEY;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory.PATTERN_KEY;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RoutePredicates {
|
||||
|
||||
//TODO: add support for AfterRoutePredicateFactory
|
||||
|
||||
//TODO: add support for BeforeRoutePredicateFactory
|
||||
|
||||
//TODO: add support for BetweenRoutePredicateFactory
|
||||
|
||||
//TODO: add support for CookieRoutePredicateFactory
|
||||
|
||||
//TODO: add support for RoutePredicates
|
||||
|
||||
//TODO: add support for HeaderRoutePredicateFactory
|
||||
|
||||
public static Predicate<ServerWebExchange> host(String pattern) {
|
||||
return new HostRoutePredicateFactory().apply(tuple().of(PATTERN_KEY, pattern));
|
||||
}
|
||||
|
||||
public static Predicate<ServerWebExchange> method(String method) {
|
||||
return new MethodRoutePredicateFactory().apply(tuple().of(METHOD_KEY, method));
|
||||
}
|
||||
|
||||
public static Predicate<ServerWebExchange> path(String pattern) {
|
||||
return new PathRoutePredicateFactory().apply(tuple().of(PATTERN_KEY, pattern));
|
||||
}
|
||||
|
||||
//TODO: add support for PredicateDefinition
|
||||
|
||||
//TODO: add support for QueryRoutePredicateFactory
|
||||
|
||||
//TODO: add support for RemoteAddrRoutePredicateFactory
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RoutePredicateFactoryUtils {
|
||||
private static final Log logger = LogFactory.getLog(RoutePredicateFactory.class);
|
||||
|
||||
public static void traceMatch(String prefix, Object desired, Object actual, boolean match) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
String message = String.format("%s \"%s\" %s against value \"%s\"",
|
||||
prefix, desired, match ? "matches" : "does not match", actual);
|
||||
logger.trace(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.context.event.EventListener;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CachingRouteDefinitionLocator implements RouteDefinitionLocator {
|
||||
|
||||
private final RouteDefinitionLocator delegate;
|
||||
private final AtomicReference<List<RouteDefinition>> cachedRoutes = new AtomicReference<>();
|
||||
|
||||
public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) {
|
||||
this.delegate = delegate;
|
||||
this.cachedRoutes.compareAndSet(null, collectRoutes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return Flux.fromIterable(this.cachedRoutes.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new routes
|
||||
* @return old routes
|
||||
*/
|
||||
public Flux<RouteDefinition> refresh() {
|
||||
return Flux.fromIterable(this.cachedRoutes.getAndUpdate(
|
||||
routes -> CachingRouteDefinitionLocator.this.collectRoutes()));
|
||||
}
|
||||
|
||||
private List<RouteDefinition> collectRoutes() {
|
||||
return this.delegate.getRouteDefinitions().collectList().block();
|
||||
}
|
||||
|
||||
@EventListener(RefreshRoutesEvent.class)
|
||||
/* for testing */ void handleRefresh() {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CachingRouteLocator implements RouteLocator {
|
||||
|
||||
private final RouteLocator delegate;
|
||||
private final AtomicReference<List<Route>> cachedRoutes = new AtomicReference<>();
|
||||
|
||||
public CachingRouteLocator(RouteLocator delegate) {
|
||||
this.delegate = delegate;
|
||||
this.cachedRoutes.compareAndSet(null, collectRoutes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Route> getRoutes() {
|
||||
return Flux.fromIterable(this.cachedRoutes.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new routes
|
||||
* @return old routes
|
||||
*/
|
||||
public Flux<Route> refresh() {
|
||||
return Flux.fromIterable(this.cachedRoutes.getAndUpdate(
|
||||
routes -> CachingRouteLocator.this.collectRoutes()));
|
||||
}
|
||||
|
||||
private List<Route> collectRoutes() {
|
||||
List<Route> routes = this.delegate.getRoutes().collectList().block();
|
||||
AnnotationAwareOrderComparator.sort(routes);
|
||||
return routes;
|
||||
}
|
||||
|
||||
@EventListener(RefreshRoutesEvent.class)
|
||||
/* for testing */ void handleRefresh() {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
|
||||
|
||||
private final Flux<RouteDefinitionLocator> delegates;
|
||||
|
||||
public CompositeRouteDefinitionLocator(Flux<RouteDefinitionLocator> delegates) {
|
||||
this.delegates = delegates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CompositeRouteLocator implements RouteLocator {
|
||||
|
||||
private final Flux<RouteLocator> delegates;
|
||||
|
||||
public CompositeRouteLocator(Flux<RouteLocator> delegates) {
|
||||
this.delegates = delegates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Route> getRoutes() {
|
||||
return this.delegates.flatMap(RouteLocator::getRoutes);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
|
||||
import static java.util.Collections.synchronizedMap;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
|
||||
|
||||
private final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());
|
||||
|
||||
@Override
|
||||
public Mono<Void> save(Mono<RouteDefinition> route) {
|
||||
return route.flatMap( r -> {
|
||||
routes.put(r.getId(), r);
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> delete(Mono<String> routeId) {
|
||||
return routeId.flatMap(id -> {
|
||||
if (routes.containsKey(id)) {
|
||||
routes.remove(id);
|
||||
return Mono.empty();
|
||||
}
|
||||
return Mono.error(new NotFoundException("RouteDefinition not found: "+routeId));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return Flux.fromIterable(routes.values());
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RefreshRoutesEvent extends ApplicationEvent {
|
||||
|
||||
/**
|
||||
* Create a new ApplicationEvent.
|
||||
*
|
||||
* @param source the object on which the event initially occurred (never {@code null})
|
||||
*/
|
||||
public RefreshRoutesEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class Route implements Ordered {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final URI uri;
|
||||
|
||||
private final int order;
|
||||
|
||||
private final Predicate<ServerWebExchange> predicate;
|
||||
|
||||
private final List<WebFilter> webFilters;
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static Builder builder(RouteDefinition routeDefinition) {
|
||||
return new Builder()
|
||||
.id(routeDefinition.getId())
|
||||
.uri(routeDefinition.getUri())
|
||||
.order(routeDefinition.getOrder());
|
||||
}
|
||||
|
||||
public Route(String id, URI uri, int order, Predicate<ServerWebExchange> predicate, List<WebFilter> webFilters) {
|
||||
this.id = id;
|
||||
this.uri = uri;
|
||||
this.order = order;
|
||||
this.predicate = predicate;
|
||||
this.webFilters = webFilters;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private String id;
|
||||
|
||||
private URI uri;
|
||||
|
||||
private int order = 0;
|
||||
|
||||
private Predicate<ServerWebExchange> predicate;
|
||||
|
||||
private List<WebFilter> webFilters = new ArrayList<>();
|
||||
|
||||
private Builder() {}
|
||||
|
||||
public Builder id(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder uri(String uri) {
|
||||
this.uri = URI.create(uri);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder order(int order) {
|
||||
this.order = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder uri(URI uri) {
|
||||
this.uri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder predicate(Predicate<ServerWebExchange> predicate) {
|
||||
this.predicate = predicate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder webFilters(List<WebFilter> webFilters) {
|
||||
this.webFilters = webFilters;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder add(WebFilter webFilter) {
|
||||
this.webFilters.add(webFilter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addAll(Collection<WebFilter> webFilters) {
|
||||
this.webFilters.addAll(webFilters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Route build() {
|
||||
Assert.notNull(this.id, "id can not be null");
|
||||
Assert.notNull(this.uri, "uri can not be null");
|
||||
//TODO: Assert.notNull(this.predicate, "predicate can not be null");
|
||||
|
||||
return new Route(this.id, this.uri, this.order, this.predicate, this.webFilters);
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public URI getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public Predicate<ServerWebExchange> getPredicate() {
|
||||
return this.predicate;
|
||||
}
|
||||
|
||||
public List<WebFilter> getWebFilters() {
|
||||
return Collections.unmodifiableList(this.webFilters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Route route = (Route) o;
|
||||
return Objects.equals(id, route.id) &&
|
||||
Objects.equals(uri, route.uri) &&
|
||||
Objects.equals(order, route.order) &&
|
||||
Objects.equals(predicate, route.predicate) &&
|
||||
Objects.equals(webFilters, route.webFilters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, uri, predicate, webFilters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("Route{");
|
||||
sb.append("id='").append(id).append('\'');
|
||||
sb.append(", uri=").append(uri);
|
||||
sb.append(", order=").append(order);
|
||||
sb.append(", predicate=").append(predicate);
|
||||
sb.append(", webFilters=").append(webFilters);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import org.springframework.cloud.gateway.filter.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.ValidationException;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.springframework.util.StringUtils.tokenizeToStringArray;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Validated
|
||||
public class RouteDefinition {
|
||||
@NotEmpty
|
||||
private String id = UUID.randomUUID().toString();
|
||||
|
||||
@NotEmpty
|
||||
@Valid
|
||||
private List<PredicateDefinition> predicates = new ArrayList<>();
|
||||
|
||||
@Valid
|
||||
private List<FilterDefinition> filters = new ArrayList<>();
|
||||
|
||||
@NotNull
|
||||
private URI uri;
|
||||
|
||||
private int order = 0;
|
||||
|
||||
public RouteDefinition() {}
|
||||
|
||||
public RouteDefinition(String text) {
|
||||
int eqIdx = text.indexOf("=");
|
||||
if (eqIdx <= 0) {
|
||||
throw new ValidationException("Unable to parse RouteDefinition text '" + text + "'" +
|
||||
", must be of the form name=value");
|
||||
}
|
||||
|
||||
setId(text.substring(0, eqIdx));
|
||||
|
||||
String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ",");
|
||||
|
||||
setUri(URI.create(args[0]));
|
||||
|
||||
for (int i=1; i < args.length; i++) {
|
||||
this.predicates.add(new PredicateDefinition(args[i]));
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<PredicateDefinition> getPredicates() {
|
||||
return predicates;
|
||||
}
|
||||
|
||||
public void setPredicates(List<PredicateDefinition> predicates) {
|
||||
this.predicates = predicates;
|
||||
}
|
||||
|
||||
public List<FilterDefinition> getFilters() {
|
||||
return filters;
|
||||
}
|
||||
|
||||
public void setFilters(List<FilterDefinition> filters) {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
public URI getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void setUri(URI uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RouteDefinition routeDefinition = (RouteDefinition) o;
|
||||
return Objects.equals(id, routeDefinition.id) &&
|
||||
Objects.equals(predicates, routeDefinition.predicates) &&
|
||||
Objects.equals(order, routeDefinition.order) &&
|
||||
Objects.equals(uri, routeDefinition.uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, predicates, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RouteDefinition{" +
|
||||
"id='" + id + '\'' +
|
||||
", predicates=" + predicates +
|
||||
", filters=" + filters +
|
||||
", uri=" + uri +
|
||||
", order=" + order +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface RouteDefinitionLocator {
|
||||
|
||||
Flux<RouteDefinition> getRouteDefinitions();
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter {
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.filter.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.filter.OrderedWebFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.support.ArgumentHints;
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.tuple.TupleBuilder;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* {@link RouteLocator} that loads routes from a {@link RouteDefinitionLocator}
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RouteDefinitionRouteLocator implements RouteLocator {
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RouteDefinitionLocator routeDefinitionLocator;
|
||||
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
|
||||
private final Map<String, WebFilterFactory> webFilterFactories = new HashMap<>();
|
||||
private final GatewayProperties gatewayProperties;
|
||||
|
||||
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<RoutePredicateFactory> predicates,
|
||||
List<WebFilterFactory> webFilterFactories,
|
||||
GatewayProperties gatewayProperties) {
|
||||
this.routeDefinitionLocator = routeDefinitionLocator;
|
||||
initFactories(predicates);
|
||||
webFilterFactories.forEach(factory -> this.webFilterFactories.put(factory.name(), factory));
|
||||
this.gatewayProperties = gatewayProperties;
|
||||
}
|
||||
|
||||
private void initFactories(List<RoutePredicateFactory> predicates) {
|
||||
predicates.forEach(factory -> {
|
||||
String key = factory.name();
|
||||
if (this.predicates.containsKey(key)) {
|
||||
this.logger.warn("A RoutePredicateFactory named "+ key
|
||||
+ " already exists, class: " + this.predicates.get(key)
|
||||
+ ". It will be overwritten.");
|
||||
}
|
||||
this.predicates.put(key, factory);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loaded RoutePredicateFactory [" + key + "]");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Route> getRoutes() {
|
||||
return this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.map(this::convertToRoute)
|
||||
//TODO: error handling
|
||||
.map(route -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RouteDefinition matched: " + route.getId());
|
||||
}
|
||||
return route;
|
||||
});
|
||||
|
||||
|
||||
/* TODO: trace logging
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("RouteDefinition did not match: " + routeDefinition.getId());
|
||||
}*/
|
||||
}
|
||||
|
||||
private Route convertToRoute(RouteDefinition routeDefinition) {
|
||||
Predicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
|
||||
List<WebFilter> webFilters = getFilters(routeDefinition);
|
||||
|
||||
return Route.builder(routeDefinition)
|
||||
.predicate(predicate)
|
||||
.webFilters(webFilters)
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<WebFilter> loadWebFilters(String id, List<FilterDefinition> filterDefinitions) {
|
||||
List<WebFilter> filters = filterDefinitions.stream()
|
||||
.map(definition -> {
|
||||
WebFilterFactory filter = this.webFilterFactories.get(definition.getName());
|
||||
if (filter == null) {
|
||||
throw new IllegalArgumentException("Unable to find WebFilterFactory with name " + definition.getName());
|
||||
}
|
||||
Map<String, String> args = definition.getArgs();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
|
||||
}
|
||||
|
||||
Tuple tuple = getTuple(filter, args);
|
||||
|
||||
return filter.apply(tuple);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ArrayList<WebFilter> ordered = new ArrayList<>(filters.size());
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
ordered.add(new OrderedWebFilter(filters.get(i), i+1));
|
||||
}
|
||||
|
||||
return ordered;
|
||||
}
|
||||
|
||||
private Tuple getTuple(ArgumentHints hasArguments, Map<String, String> args) {
|
||||
TupleBuilder builder = TupleBuilder.tuple();
|
||||
|
||||
List<String> argNames = hasArguments.argNames();
|
||||
if (!argNames.isEmpty()) {
|
||||
// ensure size is the same for key replacement later
|
||||
if (hasArguments.validateArgs() && args.size() != argNames.size()) {
|
||||
throw new IllegalArgumentException("Wrong number of arguments. Expected " + argNames
|
||||
+ " " + argNames + ". Found " + args.size() + " " + args + "'");
|
||||
}
|
||||
}
|
||||
|
||||
int entryIdx = 0;
|
||||
for (Map.Entry<String, String> entry : args.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
|
||||
// RoutePredicateFactory has name hints and this has a fake key name
|
||||
// replace with the matching key hint
|
||||
if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && !argNames.isEmpty()
|
||||
&& entryIdx < args.size()) {
|
||||
key = argNames.get(entryIdx);
|
||||
}
|
||||
|
||||
builder.put(key, entry.getValue());
|
||||
entryIdx++;
|
||||
}
|
||||
|
||||
Tuple tuple = builder.build();
|
||||
|
||||
if (hasArguments.validateArgs()) {
|
||||
for (String name : argNames) {
|
||||
if (!tuple.hasFieldName(name)) {
|
||||
throw new IllegalArgumentException("Missing argument '" + name + "'. Given " + tuple);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tuple;
|
||||
}
|
||||
|
||||
private List<WebFilter> getFilters(RouteDefinition routeDefinition) {
|
||||
List<WebFilter> filters = new ArrayList<>();
|
||||
|
||||
//TODO: support option to apply defaults after route specific filters?
|
||||
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
|
||||
filters.addAll(loadWebFilters("defaultFilters",
|
||||
this.gatewayProperties.getDefaultFilters()));
|
||||
}
|
||||
|
||||
if (!routeDefinition.getFilters().isEmpty()) {
|
||||
filters.addAll(loadWebFilters(routeDefinition.getId(), routeDefinition.getFilters()));
|
||||
}
|
||||
|
||||
AnnotationAwareOrderComparator.sort(filters);
|
||||
return filters;
|
||||
}
|
||||
|
||||
private Predicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
|
||||
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
|
||||
Predicate<ServerWebExchange> predicate = lookup(routeDefinition, predicates.get(0));
|
||||
|
||||
for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
|
||||
Predicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
|
||||
predicate = predicate.and(found);
|
||||
}
|
||||
|
||||
return predicate;
|
||||
}
|
||||
|
||||
private Predicate<ServerWebExchange> lookup(RouteDefinition routeDefinition, PredicateDefinition predicate) {
|
||||
RoutePredicateFactory found = this.predicates.get(predicate.getName());
|
||||
if (found == null) {
|
||||
throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
|
||||
}
|
||||
Map<String, String> args = predicate.getArgs();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RouteDefinition " + routeDefinition.getId() + " applying "
|
||||
+ args + " to " + predicate.getName());
|
||||
}
|
||||
|
||||
Tuple tuple = getTuple(found, args);
|
||||
|
||||
return found.apply(tuple);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface RouteDefinitionWriter {
|
||||
|
||||
Mono<Void> save(Mono<RouteDefinition> route);
|
||||
|
||||
Mono<Void> delete(Mono<String> routeId);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
//TODO: rename to Routes?
|
||||
public interface RouteLocator {
|
||||
|
||||
Flux<Route> getRoutes();
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.factory.WebFilterFactories;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class Routes {
|
||||
|
||||
public static LocatorBuilder locator() {
|
||||
return new LocatorBuilder();
|
||||
}
|
||||
|
||||
public static class LocatorBuilder {
|
||||
|
||||
private List<Route> routes = new ArrayList<>();
|
||||
|
||||
public RouteSpec route(String id) {
|
||||
return new RouteSpec(this).id(id);
|
||||
}
|
||||
|
||||
private void add(Route route) {
|
||||
this.routes.add(route);
|
||||
}
|
||||
|
||||
public RouteLocator build() {
|
||||
return () -> Flux.fromIterable(this.routes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class RouteSpec {
|
||||
private final Route.Builder builder = Route.builder();
|
||||
private final LocatorBuilder locatorBuilder;
|
||||
|
||||
private RouteSpec(LocatorBuilder locatorBuilder) {
|
||||
this.locatorBuilder = locatorBuilder;
|
||||
}
|
||||
|
||||
public RouteSpec id(String id) {
|
||||
this.builder.id(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RouteSpec order(int order) {
|
||||
this.builder.order(order);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PredicateSpec uri(String uri) {
|
||||
this.builder.uri(uri);
|
||||
return predicateBuilder();
|
||||
}
|
||||
|
||||
public PredicateSpec uri(URI uri) {
|
||||
this.builder.uri(uri);
|
||||
return predicateBuilder();
|
||||
}
|
||||
|
||||
private PredicateSpec predicateBuilder() {
|
||||
return new PredicateSpec(this.builder, this.locatorBuilder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PredicateSpec {
|
||||
|
||||
private final Route.Builder routeBuilder;
|
||||
private LocatorBuilder locatorBuilder;
|
||||
|
||||
private PredicateSpec(Route.Builder routeBuilder, LocatorBuilder locatorBuilder) {
|
||||
this.routeBuilder = routeBuilder;
|
||||
this.locatorBuilder = locatorBuilder;
|
||||
}
|
||||
|
||||
/* TODO: has and, or & negate of Predicate with terminal andFilters()?
|
||||
public RoutePredicateBuilder predicate() {
|
||||
}
|
||||
// this goes in new class
|
||||
public RoutePredicateBuilder host(String pattern) {
|
||||
Predicate<ServerWebExchange> predicate = RoutePredicates.host(pattern);
|
||||
}*/
|
||||
|
||||
public WebFilterSpec predicate(Predicate<ServerWebExchange> predicate) {
|
||||
this.routeBuilder.predicate(predicate);
|
||||
return webFilterBuilder();
|
||||
}
|
||||
|
||||
private WebFilterSpec webFilterBuilder() {
|
||||
return new WebFilterSpec(this.routeBuilder, this.locatorBuilder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WebFilterSpec {
|
||||
private Route.Builder builder;
|
||||
private LocatorBuilder locatorBuilder;
|
||||
|
||||
public WebFilterSpec(Route.Builder routeBuilder, LocatorBuilder locatorBuilder) {
|
||||
this.builder = routeBuilder;
|
||||
this.locatorBuilder = locatorBuilder;
|
||||
}
|
||||
|
||||
public WebFilterSpec webFilters(List<WebFilter> webFilters) {
|
||||
this.builder.webFilters(webFilters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WebFilterSpec add(WebFilter webFilter) {
|
||||
this.builder.add(webFilter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WebFilterSpec addAll(Collection<WebFilter> webFilters) {
|
||||
this.builder.addAll(webFilters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WebFilterSpec addResponseHeader(String headerName, String headerValue) {
|
||||
return add(WebFilterFactories.addResponseHeader(headerName, headerValue));
|
||||
}
|
||||
|
||||
// TODO: build()?
|
||||
public LocatorBuilder and() {
|
||||
Route route = this.builder.build();
|
||||
this.locatorBuilder.add(route);
|
||||
return this.locatorBuilder;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.support;
|
||||
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface ArgumentHints {
|
||||
|
||||
/**
|
||||
* Returns hints about the number of args and the order for shortcut parsing.
|
||||
* @return
|
||||
*/
|
||||
default List<String> argNames() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate supplied argument size against {@see #argNames} size.
|
||||
* Useful for variable arg predicates.
|
||||
* @return
|
||||
*/
|
||||
default boolean validateArgs() {
|
||||
return true;
|
||||
}
|
||||
|
||||
default void validate(int requiredSize, Tuple args) {
|
||||
Assert.isTrue(args != null && args.size() == requiredSize,
|
||||
"args must have "+ requiredSize +" entry(s)");
|
||||
}
|
||||
|
||||
default void validateMin(int minSize, Tuple args) {
|
||||
Assert.isTrue(args != null && args.size() >= minSize,
|
||||
"args must have at least "+ minSize +" entry(s)");
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.support;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class NameUtils {
|
||||
public static final String GENERATED_NAME_PREFIX = "_genkey_";
|
||||
|
||||
public static String generateName(int i) {
|
||||
return GENERATED_NAME_PREFIX + i;
|
||||
}
|
||||
|
||||
public static String normalizePredicateName(Class<? extends RoutePredicateFactory> clazz) {
|
||||
return clazz.getSimpleName().replace(RoutePredicateFactory.class.getSimpleName(), "");
|
||||
}
|
||||
|
||||
public static String normalizeFilterName(Class<? extends WebFilterFactory> clazz) {
|
||||
return clazz.getSimpleName().replace(WebFilterFactory.class.getSimpleName(), "");
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.support;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class NotFoundException extends RuntimeException {
|
||||
public NotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public NotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class ServerWebExchangeUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ServerWebExchangeUtils.class);
|
||||
|
||||
public static final String URI_TEMPLATE_VARIABLES_ATTRIBUTE = qualify("uriTemplateVariables");
|
||||
|
||||
public static final String CLIENT_RESPONSE_ATTR = qualify("webHandlerClientResponse");
|
||||
public static final String GATEWAY_ROUTE_ATTR = qualify("gatewayRoute");
|
||||
public static final String GATEWAY_REQUEST_URL_ATTR = qualify("gatewayRequestUrl");
|
||||
public static final String GATEWAY_HANDLER_MAPPER_ATTR = qualify("gatewayHandlerMapper");
|
||||
|
||||
private static String qualify(String attr) {
|
||||
return ServerWebExchangeUtils.class.getName() + "." + attr;
|
||||
}
|
||||
|
||||
public static boolean setResponseStatus(ServerWebExchange exchange, HttpStatus httpStatus) {
|
||||
boolean response = exchange.getResponse().setStatusCode(httpStatus);
|
||||
if (!response && logger.isWarnEnabled()) {
|
||||
logger.warn("Unable to set status code to "+ httpStatus + ". Response already committed.");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public static HttpStatus parse(String statusString) {
|
||||
HttpStatus httpStatus;
|
||||
|
||||
try {
|
||||
int status = Integer.parseInt(statusString);
|
||||
httpStatus = HttpStatus.valueOf(status);
|
||||
} catch (NumberFormatException e) {
|
||||
// try the enum string
|
||||
httpStatus = HttpStatus.valueOf(statusString.toUpperCase());
|
||||
}
|
||||
return httpStatus;
|
||||
}
|
||||
}
|
||||
@@ -1,364 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.support;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A class that performs some subnet calculations given a network address and a subnet mask.
|
||||
* See original from commons-net org.apache.commons.net.util.SubnetUtils
|
||||
* @see "http://www.faqs.org/rfcs/rfc1519.html"
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class SubnetUtils {
|
||||
|
||||
private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})";
|
||||
private static final String SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,3})";
|
||||
private static final Pattern addressPattern = Pattern.compile(IP_ADDRESS);
|
||||
private static final Pattern cidrPattern = Pattern.compile(SLASH_FORMAT);
|
||||
private static final int NBITS = 32;
|
||||
|
||||
private int netmask = 0;
|
||||
private int address = 0;
|
||||
private int network = 0;
|
||||
private int broadcast = 0;
|
||||
|
||||
/** Whether the broadcast/network address are included in host count */
|
||||
private boolean inclusiveHostCount = false;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor that takes a CIDR-notation string, e.g. "192.168.0.1/16"
|
||||
* @param cidrNotation A CIDR-notation string, e.g. "192.168.0.1/16"
|
||||
* @throws IllegalArgumentException if the parameter is invalid,
|
||||
* i.e. does not match n.n.n.n/m where n=1-3 decimal digits, m = 1-3 decimal digits in range 1-32
|
||||
*/
|
||||
public SubnetUtils(String cidrNotation) {
|
||||
calculate(cidrNotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that takes a dotted decimal address and a dotted decimal mask.
|
||||
* @param address An IP address, e.g. "192.168.0.1"
|
||||
* @param mask A dotted decimal netmask e.g. "255.255.0.0"
|
||||
* @throws IllegalArgumentException if the address or mask is invalid,
|
||||
* i.e. does not match n.n.n.n where n=1-3 decimal digits and the mask is not all zeros
|
||||
*/
|
||||
public SubnetUtils(String address, String mask) {
|
||||
calculate(toCidrNotation(address, mask));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the return value of {@link SubnetInfo#getAddressCount()}
|
||||
* includes the network and broadcast addresses.
|
||||
* @since 2.2
|
||||
* @return true if the hostcount includes the network and broadcast addresses
|
||||
*/
|
||||
public boolean isInclusiveHostCount() {
|
||||
return inclusiveHostCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to <code>true</code> if you want the return value of {@link SubnetInfo#getAddressCount()}
|
||||
* to include the network and broadcast addresses.
|
||||
* @param inclusiveHostCount true if network and broadcast addresses are to be included
|
||||
* @since 2.2
|
||||
*/
|
||||
public void setInclusiveHostCount(boolean inclusiveHostCount) {
|
||||
this.inclusiveHostCount = inclusiveHostCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convenience container for subnet summary information.
|
||||
*
|
||||
*/
|
||||
public final class SubnetInfo {
|
||||
/* Mask to convert unsigned int to a long (i.e. keep 32 bits) */
|
||||
private static final long UNSIGNED_INT_MASK = 0x0FFFFFFFFL;
|
||||
|
||||
private SubnetInfo() {}
|
||||
|
||||
private int netmask() { return netmask; }
|
||||
private int network() { return network; }
|
||||
private int address() { return address; }
|
||||
private int broadcast() { return broadcast; }
|
||||
|
||||
// long versions of the values (as unsigned int) which are more suitable for range checking
|
||||
private long networkLong() { return network & UNSIGNED_INT_MASK; }
|
||||
private long broadcastLong(){ return broadcast & UNSIGNED_INT_MASK; }
|
||||
|
||||
private int low() {
|
||||
return (isInclusiveHostCount() ? network() :
|
||||
broadcastLong() - networkLong() > 1 ? network() + 1 : 0);
|
||||
}
|
||||
|
||||
private int high() {
|
||||
return (isInclusiveHostCount() ? broadcast() :
|
||||
broadcastLong() - networkLong() > 1 ? broadcast() -1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the parameter <code>address</code> is in the
|
||||
* range of usable endpoint addresses for this subnet. This excludes the
|
||||
* network and broadcast adresses.
|
||||
* @param address A dot-delimited IPv4 address, e.g. "192.168.0.1"
|
||||
* @return True if in range, false otherwise
|
||||
*/
|
||||
public boolean isInRange(String address) {
|
||||
return isInRange(toInteger(address));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param address the address to check
|
||||
* @return true if it is in range
|
||||
* @since 3.4 (made public)
|
||||
*/
|
||||
public boolean isInRange(int address) {
|
||||
long addLong = address & UNSIGNED_INT_MASK;
|
||||
long lowLong = low() & UNSIGNED_INT_MASK;
|
||||
long highLong = high() & UNSIGNED_INT_MASK;
|
||||
return addLong >= lowLong && addLong <= highLong;
|
||||
}
|
||||
|
||||
public String getBroadcastAddress() {
|
||||
return format(toArray(broadcast()));
|
||||
}
|
||||
|
||||
public String getNetworkAddress() {
|
||||
return format(toArray(network()));
|
||||
}
|
||||
|
||||
public String getNetmask() {
|
||||
return format(toArray(netmask()));
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return format(toArray(address()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the low address as a dotted IP address.
|
||||
* Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false.
|
||||
*
|
||||
* @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address
|
||||
*/
|
||||
public String getLowAddress() {
|
||||
return format(toArray(low()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the high address as a dotted IP address.
|
||||
* Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false.
|
||||
*
|
||||
* @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address
|
||||
*/
|
||||
public String getHighAddress() {
|
||||
return format(toArray(high()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of available addresses.
|
||||
* Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false.
|
||||
* @return the count of addresses, may be zero.
|
||||
* @throws RuntimeException if the correct count is greater than {@code Integer.MAX_VALUE}
|
||||
* @deprecated (3.4) use {@link #getAddressCountLong()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public int getAddressCount() {
|
||||
long countLong = getAddressCountLong();
|
||||
if (countLong > Integer.MAX_VALUE) {
|
||||
throw new RuntimeException("Count is larger than an integer: " + countLong);
|
||||
}
|
||||
// N.B. cannot be negative
|
||||
return (int)countLong;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of available addresses.
|
||||
* Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false.
|
||||
* @return the count of addresses, may be zero.
|
||||
* @since 3.4
|
||||
*/
|
||||
public long getAddressCountLong() {
|
||||
long b = broadcastLong();
|
||||
long n = networkLong();
|
||||
long count = b - n + (isInclusiveHostCount() ? 1 : -1);
|
||||
return count < 0 ? 0 : count;
|
||||
}
|
||||
|
||||
public int asInteger(String address) {
|
||||
return toInteger(address);
|
||||
}
|
||||
|
||||
public String getCidrSignature() {
|
||||
return toCidrNotation(
|
||||
format(toArray(address())),
|
||||
format(toArray(netmask()))
|
||||
);
|
||||
}
|
||||
|
||||
public String[] getAllAddresses() {
|
||||
int ct = getAddressCount();
|
||||
String[] addresses = new String[ct];
|
||||
if (ct == 0) {
|
||||
return addresses;
|
||||
}
|
||||
for (int add = low(), j=0; add <= high(); ++add, ++j) {
|
||||
addresses[j] = format(toArray(add));
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @since 2.2
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
buf.append("CIDR Signature:\t[").append(getCidrSignature()).append("]")
|
||||
.append(" Netmask: [").append(getNetmask()).append("]\n")
|
||||
.append("Network:\t[").append(getNetworkAddress()).append("]\n")
|
||||
.append("Broadcast:\t[").append(getBroadcastAddress()).append("]\n")
|
||||
.append("First Address:\t[").append(getLowAddress()).append("]\n")
|
||||
.append("Last Address:\t[").append(getHighAddress()).append("]\n")
|
||||
.append("# Addresses:\t[").append(getAddressCount()).append("]\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link SubnetInfo} instance that contains subnet-specific statistics
|
||||
* @return new instance
|
||||
*/
|
||||
public final SubnetInfo getInfo() { return new SubnetInfo(); }
|
||||
|
||||
/*
|
||||
* Initialize the internal fields from the supplied CIDR mask
|
||||
*/
|
||||
private void calculate(String mask) {
|
||||
Matcher matcher = cidrPattern.matcher(mask);
|
||||
|
||||
if (matcher.matches()) {
|
||||
address = matchAddress(matcher);
|
||||
|
||||
/* Create a binary netmask from the number of bits specification /x */
|
||||
int cidrPart = rangeCheck(Integer.parseInt(matcher.group(5)), 0, NBITS);
|
||||
for (int j = 0; j < cidrPart; ++j) {
|
||||
netmask |= (1 << 31 - j);
|
||||
}
|
||||
|
||||
/* Calculate base network address */
|
||||
network = (address & netmask);
|
||||
|
||||
/* Calculate broadcast address */
|
||||
broadcast = network | ~(netmask);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not parse [" + mask + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a dotted decimal format address to a packed integer format
|
||||
*/
|
||||
private int toInteger(String address) {
|
||||
Matcher matcher = addressPattern.matcher(address);
|
||||
if (matcher.matches()) {
|
||||
return matchAddress(matcher);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not parse [" + address + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience method to extract the components of a dotted decimal address and
|
||||
* pack into an integer using a regex match
|
||||
*/
|
||||
private int matchAddress(Matcher matcher) {
|
||||
int addr = 0;
|
||||
for (int i = 1; i <= 4; ++i) {
|
||||
int n = (rangeCheck(Integer.parseInt(matcher.group(i)), 0, 255));
|
||||
addr |= ((n & 0xff) << 8*(4-i));
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a packed integer address into a 4-element array
|
||||
*/
|
||||
private int[] toArray(int val) {
|
||||
int ret[] = new int[4];
|
||||
for (int j = 3; j >= 0; --j) {
|
||||
ret[j] |= ((val >>> 8*(3-j)) & (0xff));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a 4-element array into dotted decimal format
|
||||
*/
|
||||
private String format(int[] octets) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (int i =0; i < octets.length; ++i){
|
||||
str.append(octets[i]);
|
||||
if (i != octets.length - 1) {
|
||||
str.append(".");
|
||||
}
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience function to check integer boundaries.
|
||||
* Checks if a value x is in the range [begin,end].
|
||||
* Returns x if it is in range, throws an exception otherwise.
|
||||
*/
|
||||
private int rangeCheck(int value, int begin, int end) {
|
||||
if (value >= begin && value <= end) { // (begin,end]
|
||||
return value;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Value [" + value + "] not in range ["+begin+","+end+"]");
|
||||
}
|
||||
|
||||
/*
|
||||
* Count the number of 1-bits in a 32-bit integer using a divide-and-conquer strategy
|
||||
* see Hacker's Delight section 5.1
|
||||
*/
|
||||
int pop(int x) {
|
||||
x = x - ((x >>> 1) & 0x55555555);
|
||||
x = (x & 0x33333333) + ((x >>> 2) & 0x33333333);
|
||||
x = (x + (x >>> 4)) & 0x0F0F0F0F;
|
||||
x = x + (x >>> 8);
|
||||
x = x + (x >>> 16);
|
||||
return x & 0x0000003F;
|
||||
}
|
||||
|
||||
/* Convert two dotted decimal addresses to a single xxx.xxx.xxx.xxx/yy format
|
||||
* by counting the 1-bit population in the mask address. (It may be better to count
|
||||
* NBITS-#trailing zeroes for this case)
|
||||
*/
|
||||
private String toCidrNotation(String addr, String mask) {
|
||||
return addr + "/" + pop(toInteger(mask));
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
local tokens_key = KEYS[1]
|
||||
local timestamp_key = KEYS[2]
|
||||
--redis.log(redis.LOG_WARNING, "tokens_key " .. tokens_key)
|
||||
|
||||
local rate = tonumber(ARGV[1])
|
||||
local capacity = tonumber(ARGV[2])
|
||||
local now = tonumber(ARGV[3])
|
||||
local requested = tonumber(ARGV[4])
|
||||
|
||||
local fill_time = capacity/rate
|
||||
local ttl = math.floor(fill_time*2)
|
||||
|
||||
local last_tokens = tonumber(redis.call("get", tokens_key))
|
||||
if last_tokens == nil then
|
||||
last_tokens = capacity
|
||||
end
|
||||
|
||||
local last_refreshed = tonumber(redis.call("get", timestamp_key))
|
||||
if last_refreshed == nil then
|
||||
last_refreshed = 0
|
||||
end
|
||||
|
||||
local delta = math.max(0, now-last_refreshed)
|
||||
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
|
||||
local allowed = filled_tokens >= requested
|
||||
local new_tokens = filled_tokens
|
||||
if allowed then
|
||||
new_tokens = filled_tokens - requested
|
||||
end
|
||||
|
||||
redis.call("setex", tokens_key, ttl, new_tokens)
|
||||
redis.call("setex", timestamp_key, ttl, now)
|
||||
|
||||
return { allowed, new_tokens }
|
||||
@@ -1,4 +0,0 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
|
||||
org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.getMap;
|
||||
import static org.springframework.web.reactive.function.BodyExtractors.toMono;
|
||||
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Biju Kunjummen
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
@ActiveProfiles(profiles = "request-header-web-filter")
|
||||
public class AddRequestHeaderWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void addRequestHeaderFilterWorks() {
|
||||
Mono<Map> result = webClient.get()
|
||||
.uri("/headers")
|
||||
.header("Host", "www.addrequestheader.org")
|
||||
.exchange()
|
||||
.flatMap(response -> response.body(toMono(Map.class)));
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
Map<String, Object> headers = getMap(response, "headers");
|
||||
assertThat(headers).containsEntry("X-Request-Foo", "Bar");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(Duration.ofMinutes(10));
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.getMap;
|
||||
import static org.springframework.web.reactive.function.BodyExtractors.toMono;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
@ActiveProfiles(profiles = "request-parameter-web-filter")
|
||||
public class AddRequestParameterWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void addRequestParameterFilterWorksBlankQuery() {
|
||||
testRequestParameterFilter("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addRequestParameterFilterWorksNonBlankQuery() {
|
||||
testRequestParameterFilter("?baz=bam");
|
||||
}
|
||||
|
||||
private void testRequestParameterFilter(String query) {
|
||||
Mono<Map> result = webClient.get()
|
||||
.uri("/get" + query)
|
||||
.header("Host", "www.addrequestparameter.org")
|
||||
.exchange()
|
||||
.flatMap(response -> response.body(toMono(Map.class)));
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
Map<String, Object> args = getMap(response, "args");
|
||||
assertThat(args).containsEntry("foo", "bar");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.assertStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class HystrixWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void hystrixFilterWorks() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/get")
|
||||
.header("Host", "www.hystrixsuccess.org")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
assertStatus(response, HttpStatus.OK);
|
||||
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
|
||||
assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER))
|
||||
.isEqualTo("hystrix_success_test");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hystrixFilterTimesout() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/delay/3")
|
||||
.header("Host", "www.hystrixfailure.org")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError() //TODO: can we get more specific as to the error?
|
||||
.verify();
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.assertStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class RedirectToWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void redirectToFilterWorks() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/")
|
||||
.header("Host", "www.redirectto.org")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
assertStatus(response, HttpStatus.FOUND);
|
||||
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
|
||||
assertThat(httpHeaders.getFirst(HttpHeaders.LOCATION))
|
||||
.isEqualTo("http://example.org");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.filter.factory.RemoveNonProxyHeadersWebFilterFactory.DEFAULT_HEADERS_TO_REMOVE;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.getMap;
|
||||
import static org.springframework.web.reactive.function.BodyExtractors.toMono;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
//TODO: why does this break other tests if not in a profile?
|
||||
@ActiveProfiles("removenonproxyheaders")
|
||||
@DirtiesContext
|
||||
public class RemoveNonProxyHeadersWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void removeNonProxyHeadersFilterWorks() {
|
||||
Mono<Map> result = webClient.get()
|
||||
.uri("/headers")
|
||||
.header("Host", "www.removenonproxyheaders.org")
|
||||
.header("Proxy-Authorization", "myauth")
|
||||
.exchange()
|
||||
.flatMap(response -> response.body(toMono(Map.class)));
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
Map<String, Object> headers = getMap(response, "headers");
|
||||
//FIXME for (String header : DEFAULT_HEADERS_TO_REMOVE) {
|
||||
assertThat(headers).doesNotContainKey("Proxy-Authorization");
|
||||
// }
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.getMap;
|
||||
import static org.springframework.web.reactive.function.BodyExtractors.toMono;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class RemoveRequestHeaderWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void removeRequestHeaderFilterWorks() {
|
||||
Mono<Map> result = webClient.get()
|
||||
.uri("/headers")
|
||||
.header("Host", "www.removerequestheader.org")
|
||||
.header("X-Request-Foo", "Bar")
|
||||
.exchange()
|
||||
.flatMap(response -> response.body(toMono(Map.class)));
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
Map<String, Object> headers = getMap(response, "headers");
|
||||
assertThat(headers).doesNotContainKey("X-Request-Foo");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class RemoveResponseHeaderWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void removeResponseHeaderFilterWorks() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/headers")
|
||||
.header("Host", "www.removereresponseheader.org")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
|
||||
assertThat(httpHeaders).doesNotContainKey("X-Request-Foo");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter.Response;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.MockServerWebExchange;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.filter.factory.RequestRateLimiterWebFilterFactory.BURST_CAPACITY_KEY;
|
||||
import static org.springframework.cloud.gateway.filter.factory.RequestRateLimiterWebFilterFactory.KEY_RESOLVER_NAME_KEY;
|
||||
import static org.springframework.cloud.gateway.filter.factory.RequestRateLimiterWebFilterFactory.REPLENISH_RATE_KEY;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* see https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L36-L62
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class RequestRateLimiterWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Autowired
|
||||
private RequestRateLimiterWebFilterFactory filterFactory;
|
||||
|
||||
@MockBean
|
||||
private RateLimiter rateLimiter;
|
||||
|
||||
@MockBean
|
||||
private WebFilterChain filterChain;
|
||||
|
||||
@Test
|
||||
public void allowedWorks() throws Exception {
|
||||
assertFilterFactory("resolver1", "allowedkey", true, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notAllowedWorks() throws Exception {
|
||||
assertFilterFactory("resolver2", "notallowedkey", false, HttpStatus.TOO_MANY_REQUESTS);
|
||||
}
|
||||
|
||||
private void assertFilterFactory(String keyResolverName, String key, boolean allowed, HttpStatus expectedStatus) {
|
||||
int replenishRate = 10;
|
||||
int burstCapacity = 2 * replenishRate;
|
||||
Tuple args = tuple().of(REPLENISH_RATE_KEY, replenishRate,
|
||||
BURST_CAPACITY_KEY, burstCapacity,
|
||||
KEY_RESOLVER_NAME_KEY, keyResolverName);
|
||||
|
||||
when(rateLimiter.isAllowed(key, replenishRate, burstCapacity))
|
||||
.thenReturn(new Response(allowed, 1));
|
||||
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/").build();
|
||||
MockServerWebExchange exchange = new MockServerWebExchange(request);
|
||||
exchange.getResponse().setStatusCode(HttpStatus.OK);
|
||||
|
||||
when(this.filterChain.filter(exchange)).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> response = filterFactory.apply(args).filter(exchange, this.filterChain);
|
||||
response.subscribe(aVoid -> {
|
||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(expectedStatus);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(BaseWebClientTests.DefaultTestConfig.class)
|
||||
public static class TestConfig {
|
||||
@Bean
|
||||
KeyResolver resolver1() {
|
||||
return exchange -> Mono.just("allowedkey");
|
||||
}
|
||||
|
||||
@Bean
|
||||
KeyResolver resolver2() {
|
||||
return exchange -> Mono.just("notallowedkey");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.assertStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class RewritePathWebFilterFactoryIntegrationTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void rewritePathFilterWorks() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/foo/get")
|
||||
.header("Host", "www.baz.org")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
assertStatus(response, HttpStatus.OK);
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
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.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory.REGEXP_KEY;
|
||||
import static org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory.REPLACEMENT_KEY;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RewritePathWebFilterFactoryTests {
|
||||
|
||||
@Test
|
||||
public void rewritePathFilterWorks() {
|
||||
testRewriteFilter("/foo", "/baz", "/foo/bar", "/baz/bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rewritePathFilterWithNamedGroupWorks() {
|
||||
testRewriteFilter("/foo/(?<id>\\d.*)", "/bar/baz/$\\{id}", "/foo/123", "/bar/baz/123");
|
||||
}
|
||||
|
||||
private void testRewriteFilter(String regex, String replacement, String actualPath, String expectedPath) {
|
||||
WebFilter filter = new RewritePathWebFilterFactory().apply(tuple().of(REGEXP_KEY, regex, REPLACEMENT_KEY, replacement));
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("http://localhost"+ actualPath)
|
||||
.build();
|
||||
|
||||
ServerWebExchange exchange = new MockServerWebExchange(request);
|
||||
|
||||
WebFilterChain filterChain = mock(WebFilterChain.class);
|
||||
|
||||
ArgumentCaptor<ServerWebExchange> captor = ArgumentCaptor.forClass(ServerWebExchange.class);
|
||||
when(filterChain.filter(captor.capture())).thenReturn(Mono.empty());
|
||||
|
||||
filter.filter(exchange, filterChain);
|
||||
|
||||
ServerWebExchange webExchange = captor.getValue();
|
||||
|
||||
Assertions.assertThat(webExchange.getRequest().getURI().getPath()).isEqualTo(expectedPath);
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.filter.factory.SecureHeadersWebFilterFactory.CONTENT_SECURITY_POLICY_HEADER;
|
||||
import static org.springframework.cloud.gateway.filter.factory.SecureHeadersWebFilterFactory.REFERRER_POLICY_HEADER;
|
||||
import static org.springframework.cloud.gateway.filter.factory.SecureHeadersWebFilterFactory.STRICT_TRANSPORT_SECURITY_HEADER;
|
||||
import static org.springframework.cloud.gateway.filter.factory.SecureHeadersWebFilterFactory.X_CONTENT_TYPE_OPTIONS_HEADER;
|
||||
import static org.springframework.cloud.gateway.filter.factory.SecureHeadersWebFilterFactory.X_DOWNLOAD_OPTIONS_HEADER;
|
||||
import static org.springframework.cloud.gateway.filter.factory.SecureHeadersWebFilterFactory.X_FRAME_OPTIONS_HEADER;
|
||||
import static org.springframework.cloud.gateway.filter.factory.SecureHeadersWebFilterFactory.X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER;
|
||||
import static org.springframework.cloud.gateway.filter.factory.SecureHeadersWebFilterFactory.X_XSS_PROTECTION_HEADER;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.assertStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class SecureHeadersWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void secureHeadersFilterWorks() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/headers")
|
||||
.header("Host", "www.secureheaders.org")
|
||||
.exchange();
|
||||
|
||||
SecureHeadersProperties defaults = new SecureHeadersProperties();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
assertStatus(response, HttpStatus.OK);
|
||||
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
|
||||
assertThat(httpHeaders.getFirst(X_XSS_PROTECTION_HEADER)).isEqualTo(defaults.getXssProtectionHeader());
|
||||
assertThat(httpHeaders.getFirst(STRICT_TRANSPORT_SECURITY_HEADER)).isEqualTo(defaults.getStrictTransportSecurity());
|
||||
assertThat(httpHeaders.getFirst(X_FRAME_OPTIONS_HEADER)).isEqualTo(defaults.getFrameOptions());
|
||||
assertThat(httpHeaders.getFirst(X_CONTENT_TYPE_OPTIONS_HEADER)).isEqualTo(defaults.getContentTypeOptions());
|
||||
assertThat(httpHeaders.getFirst(REFERRER_POLICY_HEADER)).isEqualTo(defaults.getReferrerPolicy());
|
||||
assertThat(httpHeaders.getFirst(CONTENT_SECURITY_POLICY_HEADER)).isEqualTo(defaults.getContentSecurityPolicy());
|
||||
assertThat(httpHeaders.getFirst(X_DOWNLOAD_OPTIONS_HEADER)).isEqualTo(defaults.getDownloadOptions());
|
||||
assertThat(httpHeaders.getFirst(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER)).isEqualTo(defaults.getPermittedCrossDomainPolicies());
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.assertStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class SetPathWebFilterFactoryIntegrationTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void setPathFilterDefaultValuesWork() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/foo/get")
|
||||
.header("Host", "www.setpath.org")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
assertStatus(response, HttpStatus.OK);
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
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.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.gateway.filter.factory.SetPathWebFilterFactory.TEMPLATE_KEY;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SetPathWebFilterFactoryTests {
|
||||
|
||||
@Test
|
||||
public void rewritePathFilterWorks() {
|
||||
HashMap<String, String> variables = new HashMap<>();
|
||||
testRewriteFilter("/baz/bar", "/foo/bar", "/baz/bar", variables);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPathFilterWithTemplateVarsWorks() {
|
||||
HashMap<String, String> variables = new HashMap<>();
|
||||
variables.put("id", "123");
|
||||
testRewriteFilter("/bar/baz/{id}", "/foo/123", "/bar/baz/123", variables);
|
||||
}
|
||||
|
||||
private void testRewriteFilter(String template, String actualPath, String expectedPath, HashMap<String, String> variables) {
|
||||
WebFilter filter = new SetPathWebFilterFactory().apply(tuple().of(TEMPLATE_KEY, template));
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("http://localhost"+ actualPath)
|
||||
.build();
|
||||
|
||||
ServerWebExchange exchange = new MockServerWebExchange(request);
|
||||
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, variables);
|
||||
|
||||
WebFilterChain filterChain = mock(WebFilterChain.class);
|
||||
|
||||
ArgumentCaptor<ServerWebExchange> captor = ArgumentCaptor.forClass(ServerWebExchange.class);
|
||||
when(filterChain.filter(captor.capture())).thenReturn(Mono.empty());
|
||||
|
||||
filter.filter(exchange, filterChain);
|
||||
|
||||
ServerWebExchange webExchange = captor.getValue();
|
||||
|
||||
Assertions.assertThat(webExchange.getRequest().getURI().getPath()).isEqualTo(expectedPath);
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class SetResponseWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void setResponseHeaderFilterWorks() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/headers")
|
||||
.header("Host", "www.setreresponseheader.org")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
|
||||
assertThat(httpHeaders).containsKey("X-Request-Foo");
|
||||
assertThat(httpHeaders.get("X-Request-Foo")).containsExactly("Bar");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.assertStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class SetStatusWebFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void setStatusIntWorks() {
|
||||
setStatusStringTest("www.setstatusint.org", HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setStatusStringWorks() {
|
||||
setStatusStringTest("www.setstatusstring.org", HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
private void setStatusStringTest(String host, HttpStatus status) {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/headers")
|
||||
.header("Host", host)
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
assertStatus(response, status);
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package org.springframework.cloud.gateway.filter.ratelimit;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter.Response;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
* see https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L36-L62
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class RedisRateLimiterTests extends BaseWebClientTests {
|
||||
|
||||
@Autowired
|
||||
private RedisRateLimiter rateLimiter;
|
||||
|
||||
@Test
|
||||
public void requestRateLimiterWebFilterFactoryWorks() throws Exception {
|
||||
String id = UUID.randomUUID().toString();
|
||||
|
||||
int replenishRate = 10;
|
||||
int burstCapacity = 2 * replenishRate;
|
||||
|
||||
// Bursts work
|
||||
for (int i = 0; i < burstCapacity; i++) {
|
||||
Response response = rateLimiter.isAllowed(id, replenishRate, burstCapacity);
|
||||
assertThat(response.isAllowed()).as("Burst # %s is allowed", i).isTrue();
|
||||
}
|
||||
|
||||
Response response = rateLimiter.isAllowed(id, replenishRate, burstCapacity);
|
||||
if (response.isAllowed()) { //TODO: sometimes there is an off by one error
|
||||
response = rateLimiter.isAllowed(id, replenishRate, burstCapacity);
|
||||
}
|
||||
assertThat(response.isAllowed()).as("Burst # %s is not allowed", burstCapacity).isFalse();
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
// # After the burst is done, check the steady state
|
||||
for (int i = 0; i < replenishRate; i++) {
|
||||
response = rateLimiter.isAllowed(id, replenishRate, burstCapacity);
|
||||
assertThat(response.isAllowed()).as("steady state # %s is allowed", i).isTrue();
|
||||
}
|
||||
|
||||
response = rateLimiter.isAllowed(id, replenishRate, burstCapacity);
|
||||
assertThat(response.isAllowed()).as("steady state # %s is allowed", replenishRate).isFalse();
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(BaseWebClientTests.DefaultTestConfig.class)
|
||||
public static class TestConfig {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory.DATETIME_KEY;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.getExchange;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.minusHours;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.minusHoursMillis;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.plusHours;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.plusHoursMillis;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AfterRoutePredicateFactoryTests {
|
||||
|
||||
@Test
|
||||
public void beforeStringWorks() {
|
||||
String dateString = minusHours(1);
|
||||
|
||||
final boolean result = runPredicate(dateString);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterStringWorks() {
|
||||
String dateString = plusHours(1);
|
||||
|
||||
final boolean result = runPredicate(dateString);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beforeEpochWorks() {
|
||||
String dateString = minusHoursMillis(1);
|
||||
|
||||
final boolean result = runPredicate(dateString);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterEpochWorks() {
|
||||
String dateString = plusHoursMillis(1);
|
||||
|
||||
final boolean result = runPredicate(dateString);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
private boolean runPredicate(String dateString) {
|
||||
return new AfterRoutePredicateFactory().apply(tuple().of(DATETIME_KEY, dateString)).test(getExchange());
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory.DATETIME_KEY;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.getExchange;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.minusHours;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.minusHoursMillis;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.plusHours;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.plusHoursMillis;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class BeforeRoutePredicateFactoryTests {
|
||||
|
||||
@Test
|
||||
public void beforeStringWorks() {
|
||||
String dateString = minusHours(1);
|
||||
|
||||
boolean result = runPredicate(dateString);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterStringWorks() {
|
||||
String dateString = plusHours(1);
|
||||
|
||||
boolean result = runPredicate(dateString);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beforeEpochWorks() {
|
||||
String dateString = minusHoursMillis(1);
|
||||
|
||||
final boolean result = runPredicate(dateString);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterEpochWorks() {
|
||||
String dateString = plusHoursMillis(1);
|
||||
|
||||
final boolean result = runPredicate(dateString);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
private boolean runPredicate(String dateString) {
|
||||
return new BeforeRoutePredicateFactory().apply(tuple().of(DATETIME_KEY, dateString)).test(getExchange());
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory.DATETIME1_KEY;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory.DATETIME2_KEY;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class BetweenRoutePredicateFactoryTests {
|
||||
|
||||
@Test
|
||||
public void beforeStringWorks() {
|
||||
String dateString1 = plusHours(1);
|
||||
String dateString2 = plusHours(2);
|
||||
|
||||
final boolean result = runPredicate(dateString1, dateString2);
|
||||
|
||||
assertThat(result).as("Now is not before %s", dateString1).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void betweenStringWorks() {
|
||||
String dateString1 = minusHours(1);
|
||||
String dateString2 = plusHours(1);
|
||||
|
||||
final boolean result = runPredicate(dateString1, dateString2);
|
||||
|
||||
assertThat(result).as("Now is not between %s and %s", dateString1, dateString2).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterStringWorks() {
|
||||
String dateString1 = minusHours(2);
|
||||
String dateString2 = minusHours(1);
|
||||
|
||||
final boolean result = runPredicate(dateString1, dateString2);
|
||||
|
||||
assertThat(result).as("Now is not after %s", dateString2).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beforeEpochWorks() {
|
||||
String dateString1 = plusHoursMillis(1);
|
||||
String dateString2 = plusHoursMillis(2);
|
||||
|
||||
final boolean result = runPredicate(dateString1, dateString2);
|
||||
|
||||
assertThat(result).as("Now is not before %s", dateString1).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void betweenEpochWorks() {
|
||||
String dateString1 = minusHoursMillis(1);
|
||||
String dateString2 = plusHoursMillis(1);
|
||||
|
||||
final boolean result = runPredicate(dateString1, dateString2);
|
||||
|
||||
assertThat(result).as("Now is not between %s and %s", dateString1, dateString2).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterEpochWorks() {
|
||||
String dateString1 = minusHoursMillis(2);
|
||||
String dateString2 = minusHoursMillis(1);
|
||||
|
||||
final boolean result = runPredicate(dateString1, dateString2);
|
||||
|
||||
assertThat(result).as("Now is not after %s", dateString1).isFalse();
|
||||
}
|
||||
|
||||
boolean runPredicate(String dateString1, String dateString2) {
|
||||
return new BetweenRoutePredicateFactory().apply(tuple()
|
||||
.of(DATETIME1_KEY, dateString1, DATETIME2_KEY, dateString2)).test(getExchange());
|
||||
}
|
||||
|
||||
static String minusHoursMillis(int hours) {
|
||||
final int millis = hours * 1000 * 60 * 60;
|
||||
return String.valueOf(System.currentTimeMillis() - millis);
|
||||
}
|
||||
|
||||
static String plusHoursMillis(int hours) {
|
||||
final int millis = hours * 1000 * 60 * 60;
|
||||
return String.valueOf(System.currentTimeMillis() + millis);
|
||||
}
|
||||
|
||||
static String minusHours(int hours) {
|
||||
return ZonedDateTime.now().minusHours(hours).format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
|
||||
}
|
||||
|
||||
static String plusHours(int hours) {
|
||||
return ZonedDateTime.now().plusHours(hours).format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
|
||||
}
|
||||
|
||||
static ServerWebExchange getExchange() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("http://example.com").build();
|
||||
return new MockServerWebExchange(request);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.assertStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class HostRoutePredicateFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void hostRouteWorks() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/get")
|
||||
.header("Host", "www.example.org")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
assertStatus(response, HttpStatus.OK);
|
||||
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
|
||||
assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER))
|
||||
.isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName());
|
||||
assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER))
|
||||
.isEqualTo("host_example_to_httpbin");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.assertStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class MethodRoutePredicateFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void hostRouteWorks() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/get")
|
||||
.header("Host", "www.method.org")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
assertStatus(response, HttpStatus.OK);
|
||||
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
|
||||
assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER))
|
||||
.isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName());
|
||||
assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER))
|
||||
.isEqualTo("method_test");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.assertStatus;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class PathRoutePredicateFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void pathRouteWorks() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
.uri("/get")
|
||||
.exchange();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
assertStatus(response, HttpStatus.OK);
|
||||
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
|
||||
assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER))
|
||||
.isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName());
|
||||
assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER))
|
||||
.isEqualTo("default_path_to_httpbin");
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(DURATION);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicates.host;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicates.method;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicates.path;
|
||||
|
||||
/**
|
||||
* @author Biju Kunjummen
|
||||
*/
|
||||
public class RoutePredicatesTest {
|
||||
|
||||
@Test
|
||||
public void testHostPredicates() {
|
||||
assertThat(host("**").test(mockHostExchange("test.abc.com"))).isTrue();
|
||||
assertThat(host("**.abc.**").test(mockHostExchange("test.abc.com"))).isTrue();
|
||||
assertThat(host("**.co?").test(mockHostExchange("test.abc.com"))).isTrue();
|
||||
assertThat(host("**.co").test(mockHostExchange("test.abc.com"))).isFalse();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPathPredicates() {
|
||||
assertThat(path("/**").test(mockPathExchange("/path1/path2"))).isTrue();
|
||||
assertThat(path("/path1/*").test(mockPathExchange("/path1/path2"))).isTrue();
|
||||
assertThat(path("/path1/path?").test(mockPathExchange("/path1/path2"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodPredicates() {
|
||||
assertThat(method("GET").test(mockMethodExchange("GET"))).isTrue();
|
||||
assertThat(method("POST").test(mockMethodExchange("POST"))).isTrue();
|
||||
assertThat(method("PUT").test(mockMethodExchange("PUT"))).isTrue();
|
||||
}
|
||||
|
||||
private ServerWebExchange mockHostExchange(String host) {
|
||||
MockServerHttpRequest mockRequest = MockServerHttpRequest.get("/")
|
||||
.header("Host", host).build();
|
||||
return mockRequest.toExchange();
|
||||
}
|
||||
|
||||
private ServerWebExchange mockPathExchange(String path) {
|
||||
MockServerHttpRequest mockRequest = MockServerHttpRequest.get(path).build();
|
||||
return mockRequest.toExchange();
|
||||
}
|
||||
|
||||
private ServerWebExchange mockMethodExchange(String method) {
|
||||
MockServerHttpRequest mockRequest = MockServerHttpRequest
|
||||
.method(HttpMethod.resolve(method), "/").build();
|
||||
return mockRequest.toExchange();
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.test;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.AddRequestParameterWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.HystrixWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.RedirectToWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveNonProxyHeadersWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveRequestHeaderWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactoryIntegrationTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.SecureHeadersWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetPathWebFilterFactoryIntegrationTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetPathWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetResponseWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetStatusWebFilterFactoryTests;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiterTests;
|
||||
import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactoryTests;
|
||||
import org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactoryTests;
|
||||
import org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests;
|
||||
import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactoryTests;
|
||||
import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactoryTests;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactoryTests;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assume.assumeThat;
|
||||
|
||||
/**
|
||||
* To run this suite in an IDE, set env var GATEWAY_ADHOC_ENABLED=true in test runner.
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({GatewayIntegrationTests.class,
|
||||
FormIntegrationTests.class,
|
||||
PostTests.class,
|
||||
RedisRateLimiterTests.class,
|
||||
// route filter tests
|
||||
AddRequestHeaderWebFilterFactoryTests.class,
|
||||
AddRequestParameterWebFilterFactoryTests.class,
|
||||
HystrixWebFilterFactoryTests.class,
|
||||
RedirectToWebFilterFactoryTests.class,
|
||||
RemoveNonProxyHeadersWebFilterFactoryTests.class,
|
||||
RemoveRequestHeaderWebFilterFactoryTests.class,
|
||||
RewritePathWebFilterFactoryIntegrationTests.class,
|
||||
SecureHeadersWebFilterFactoryTests.class,
|
||||
SetPathWebFilterFactoryIntegrationTests.class,
|
||||
SetPathWebFilterFactoryTests.class,
|
||||
SetResponseWebFilterFactoryTests.class,
|
||||
SetStatusWebFilterFactoryTests.class,
|
||||
RewritePathWebFilterFactoryTests.class,
|
||||
// RoutePredicateFactory tests
|
||||
AfterRoutePredicateFactoryTests.class,
|
||||
BeforeRoutePredicateFactoryTests.class,
|
||||
BetweenRoutePredicateFactoryTests.class,
|
||||
HostRoutePredicateFactoryTests.class,
|
||||
MethodRoutePredicateFactoryTests.class,
|
||||
PathRoutePredicateFactoryTests.class,
|
||||
})
|
||||
public class AdhocTestSuite {
|
||||
|
||||
@ClassRule
|
||||
public static AdhocEnabled adhocEnabled = new AdhocEnabled();
|
||||
|
||||
static class AdhocEnabled implements TestRule {
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
assumeThat("Adhoc Tests ignored",
|
||||
System.getenv("GATEWAY_ADHOC_ENABLED"),
|
||||
is(equalTo("true")));
|
||||
|
||||
return base;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user