Parse predicate if needed. Allow extra args in predicate. Create Header Predicate
This commit is contained in:
@@ -8,6 +8,7 @@ import org.springframework.cloud.gateway.actuate.GatewayEndpoint;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter;
|
||||
import org.springframework.cloud.gateway.handler.GatewayFilteringWebHandler;
|
||||
import org.springframework.cloud.gateway.handler.predicate.HeaderPredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.HostPredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.GatewayWebHandler;
|
||||
@@ -76,6 +77,11 @@ public class GatewayAutoConfiguration {
|
||||
return new MethodPredicateFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HeaderPredicateFactory headerPredicateFactory() {
|
||||
return new HeaderPredicateFactory();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
protected static class GatewayActuatorConfiguration {
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import javax.validation.Valid;
|
||||
@@ -32,108 +29,4 @@ public class GatewayProperties {
|
||||
this.routes = routes;
|
||||
}
|
||||
|
||||
public static class Route {
|
||||
@NotEmpty
|
||||
private String id;
|
||||
|
||||
@NotEmpty
|
||||
@Valid
|
||||
private List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
@NotNull
|
||||
private URI downstreamUrl;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<Predicate> getPredicates() {
|
||||
return predicates;
|
||||
}
|
||||
|
||||
public void setPredicates(List<Predicate> predicates) {
|
||||
this.predicates = predicates;
|
||||
}
|
||||
|
||||
public URI getDownstreamUrl() {
|
||||
return downstreamUrl;
|
||||
}
|
||||
|
||||
public void setDownstreamUrl(URI downstreamUrl) {
|
||||
this.downstreamUrl = downstreamUrl;
|
||||
}
|
||||
|
||||
@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(predicates, route.predicates) &&
|
||||
Objects.equals(downstreamUrl, route.downstreamUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, predicates, downstreamUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Route{" +
|
||||
"id='" + id + '\'' +
|
||||
", predicates=" + predicates +
|
||||
", downstreamUrl=" + downstreamUrl +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class Predicate {
|
||||
@NotNull
|
||||
private String name;
|
||||
@NotNull
|
||||
private String value;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Predicate predicate = (Predicate) o;
|
||||
return Objects.equals(name, predicate.name) &&
|
||||
Objects.equals(value, predicate.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Predicate{" +
|
||||
"name='" + name + '\'' +
|
||||
", value='" + value + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.validation.ValidationException;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import static org.springframework.util.StringUtils.tokenizeToStringArray;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class PredicateDefinition {
|
||||
@NotNull
|
||||
private String name;
|
||||
@NotNull
|
||||
private String value;
|
||||
|
||||
private String[] args;
|
||||
|
||||
public PredicateDefinition() {
|
||||
}
|
||||
|
||||
public PredicateDefinition(String text) {
|
||||
String[] parts = text.split("=");
|
||||
if (parts.length != 2) {
|
||||
throw new ValidationException("Unable to parse Predicate text '" + text + "'" +
|
||||
", must be of the form name=value");
|
||||
}
|
||||
setName(parts[0]);
|
||||
|
||||
String[] args = tokenizeToStringArray(parts[1], ",");
|
||||
|
||||
setValue(args[0]);
|
||||
|
||||
if (args.length > 1) {
|
||||
setArgs(Arrays.copyOfRange(args, 1, args.length));
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public void setArgs(String[] args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@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(value, that.value) &&
|
||||
Arrays.equals(args, that.args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, value, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("PredicateDefinition{");
|
||||
sb.append("name='").append(name).append('\'');
|
||||
sb.append(", value='").append(value).append('\'');
|
||||
sb.append(", args=").append(Arrays.toString(args));
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class Route {
|
||||
@NotEmpty
|
||||
private String id;
|
||||
|
||||
@NotEmpty
|
||||
@Valid
|
||||
private List<PredicateDefinition> predicates = new ArrayList<>();
|
||||
|
||||
@NotNull
|
||||
private URI downstreamUrl;
|
||||
|
||||
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 URI getDownstreamUrl() {
|
||||
return downstreamUrl;
|
||||
}
|
||||
|
||||
public void setDownstreamUrl(URI downstreamUrl) {
|
||||
this.downstreamUrl = downstreamUrl;
|
||||
}
|
||||
|
||||
@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(predicates, route.predicates) &&
|
||||
Objects.equals(downstreamUrl, route.downstreamUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, predicates, downstreamUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Route{" +
|
||||
"id='" + id + '\'' +
|
||||
", predicates=" + predicates +
|
||||
", downstreamUrl=" + downstreamUrl +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import java.net.URI;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties.Route;
|
||||
import org.springframework.cloud.gateway.config.Route;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
@@ -7,7 +7,8 @@ import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties.Route;
|
||||
import org.springframework.cloud.gateway.config.Route;
|
||||
import org.springframework.cloud.gateway.config.PredicateDefinition;
|
||||
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicateFactory;
|
||||
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -113,10 +114,10 @@ public class ServerWebExchangePredicateHandlerMapping extends AbstractHandlerMap
|
||||
|
||||
|
||||
private Predicate<ServerWebExchange> combinePredicates(Route route) {
|
||||
List<GatewayProperties.Predicate> predicates = route.getPredicates();
|
||||
List<PredicateDefinition> predicates = route.getPredicates();
|
||||
Predicate<ServerWebExchange> predicate = lookup(predicates.get(0));
|
||||
|
||||
for (GatewayProperties.Predicate andPredicate : predicates.subList(1, predicates.size())) {
|
||||
for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
|
||||
Predicate<ServerWebExchange> found = lookup(andPredicate);
|
||||
predicate = predicate.and(found);
|
||||
}
|
||||
@@ -124,12 +125,12 @@ public class ServerWebExchangePredicateHandlerMapping extends AbstractHandlerMap
|
||||
return predicate;
|
||||
}
|
||||
|
||||
private Predicate<ServerWebExchange> lookup(GatewayProperties.Predicate predicate) {
|
||||
private Predicate<ServerWebExchange> lookup(PredicateDefinition predicate) {
|
||||
GatewayPredicateFactory found = this.predicateFactories.get(predicate.getName());
|
||||
if (found == null) {
|
||||
throw new IllegalArgumentException("Unable to find GatewayPredicateFactory with name " + predicate.getName());
|
||||
}
|
||||
return found.create(predicate.getValue());
|
||||
return found.create(predicate.getValue(), predicate.getArgs());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@@ -11,5 +11,5 @@ public interface GatewayPredicateFactory {
|
||||
|
||||
String getName();
|
||||
|
||||
Predicate<ServerWebExchange> create(String value);
|
||||
Predicate<ServerWebExchange> create(String value, String[] args);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class HeaderPredicateFactory implements GatewayPredicateFactory {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Header";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> create(String header, String[] args) {
|
||||
//TODO: caching can happen here
|
||||
return exchange -> {
|
||||
Assert.isTrue(args != null && args.length == 1,
|
||||
"args must have one entry");
|
||||
|
||||
String regexp = args[0];
|
||||
List<String> values = exchange.getRequest().getHeaders().get(header);
|
||||
for (String value : values) {
|
||||
if (value.matches(regexp)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ public class HostPredicateFactory implements GatewayPredicateFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> create(String pattern) {
|
||||
public Predicate<ServerWebExchange> create(String pattern, String[] args) {
|
||||
//TODO: caching can happen here
|
||||
return exchange -> {
|
||||
String host = exchange.getRequest().getHeaders().getFirst("Host");
|
||||
|
||||
@@ -16,7 +16,7 @@ public class MethodPredicateFactory implements GatewayPredicateFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> create(String method) {
|
||||
public Predicate<ServerWebExchange> create(String method, String[] args) {
|
||||
//TODO: caching can happen here
|
||||
return exchange -> {
|
||||
HttpMethod requestMethod = exchange.getRequest().getMethod();
|
||||
|
||||
@@ -37,7 +37,7 @@ public class UrlPredicateFactory implements GatewayPredicateFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> create(String pattern) {
|
||||
public Predicate<ServerWebExchange> create(String pattern, String[] args) {
|
||||
return exchange -> {
|
||||
String lookupPath = getPathHelper().getLookupPathForRequest(exchange);
|
||||
return getPathMatcher().match(pattern, lookupPath);
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.embedded.LocalServerPort;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties.Route;
|
||||
import org.springframework.cloud.gateway.config.Route;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.handler.ServerWebExchangePredicateHandlerMapping;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -88,6 +88,7 @@ public class GatewayIntegrationTests {
|
||||
Mono<ClientResponse> result = webClient.exchange(
|
||||
GET("http://localhost:" + port + "/headers")
|
||||
.header("Host", "www.foo.org")
|
||||
.header("X-Request-Id", "123")
|
||||
.build()
|
||||
);
|
||||
|
||||
|
||||
@@ -7,19 +7,16 @@ spring:
|
||||
- id: host_example_to_httpbin
|
||||
downstreamUrl: http://httpbin.org:80
|
||||
predicates:
|
||||
- name: Host
|
||||
value: '**.example.org'
|
||||
- Host=**.example.org
|
||||
|
||||
# =====================================
|
||||
- id: host_foo_path_headers_to_httpbin
|
||||
downstreamUrl: http://httpbin.org:80
|
||||
predicates:
|
||||
- name: Host
|
||||
value: '**.foo.org'
|
||||
- name: Url
|
||||
value: /headers
|
||||
- name: Method
|
||||
value: GET
|
||||
- Host=**.foo.org
|
||||
- Url=/headers
|
||||
- Method=GET
|
||||
- Header=X-Request-Id, \d+
|
||||
|
||||
# =====================================
|
||||
- id: default_path_to_httpbin
|
||||
|
||||
Reference in New Issue
Block a user