Remove 2.0.x modules

This commit is contained in:
Dave Syer
2017-06-30 14:31:19 +01:00
parent fc46950575
commit 7b48378e0f
116 changed files with 3 additions and 8741 deletions

80
pom.xml
View File

@@ -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>

View File

@@ -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>

View File

@@ -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 {
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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 { }
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
});
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}));
}
}

View File

@@ -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());
};
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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);
};
}
}

View File

@@ -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));
}
}
}

View File

@@ -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());
};
}
}

View File

@@ -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();
}));
}
}

View File

@@ -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());
};
}
}

View File

@@ -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());
};
}
}

View File

@@ -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);
};
}
}

View File

@@ -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();
});
}
}

View File

@@ -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());
};
}
}

View File

@@ -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();
}
}

View File

@@ -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);
};
}
}

View File

@@ -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());
};
}
}

View File

@@ -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);
};
}
}

View File

@@ -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);
}
}));
};
}
}

View File

@@ -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
}

View File

@@ -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());
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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) {
}
}

View File

@@ -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);
};
}
}

View File

@@ -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);
};
}
}

View File

@@ -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;
}
}

View File

@@ -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;
};
}
}

View File

@@ -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;
};
}
}

View File

@@ -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);
};
}
}

View File

@@ -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);
};
}
}

View File

@@ -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;
}
};
}
}

View File

@@ -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();
}
}

View File

@@ -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;
};
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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 +
'}';
}
}

View File

@@ -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();
}

View File

@@ -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 {
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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;
}
}
}

View File

@@ -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)");
}
}

View File

@@ -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(), "");
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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 }

View File

@@ -1,4 +0,0 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration

View File

@@ -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 { }
}

View File

@@ -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 { }
}

View File

@@ -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 { }
}

View File

@@ -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 { }
}

View File

@@ -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 { }
}

View File

@@ -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 { }
}

View File

@@ -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 { }
}

View File

@@ -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");
}
}
}

View File

@@ -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 { }
}

View File

@@ -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);
}
}

View File

@@ -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 { }
}

View File

@@ -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 { }
}

View File

@@ -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);
}
}

View File

@@ -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 { }
}

View File

@@ -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 { }
}

View File

@@ -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 {
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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 { }
}

View File

@@ -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 { }
}

View File

@@ -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 { }
}

View File

@@ -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();
}
}

View File

@@ -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