Bumping versions
This commit is contained in:
@@ -120,35 +120,43 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
getAvailableEndpointsForClass(AbstractGatewayControllerEndpoint.class.getName()),
|
||||
getAvailableEndpointsForClass(GatewayControllerEndpoint.class.getName()));
|
||||
|
||||
return Flux.fromIterable(endpoints).map(p -> p)
|
||||
.flatMap(path -> this.routeLocator.getRoutes().map(r -> generateHref(r, path)).distinct().collectList()
|
||||
.flatMapMany(Flux::fromIterable))
|
||||
.distinct() // Ensure overall uniqueness
|
||||
.collectList();
|
||||
return Flux.fromIterable(endpoints)
|
||||
.map(p -> p)
|
||||
.flatMap(path -> this.routeLocator.getRoutes()
|
||||
.map(r -> generateHref(r, path))
|
||||
.distinct()
|
||||
.collectList()
|
||||
.flatMapMany(Flux::fromIterable))
|
||||
.distinct() // Ensure overall uniqueness
|
||||
.collectList();
|
||||
}
|
||||
|
||||
private List<GatewayEndpointInfo> mergeEndpoints(List<GatewayEndpointInfo> listA, List<GatewayEndpointInfo> listB) {
|
||||
Map<String, List<String>> mergedMap = new HashMap<>();
|
||||
|
||||
Stream.concat(listA.stream(), listB.stream()).forEach(e -> mergedMap
|
||||
.computeIfAbsent(e.getHref(), k -> new ArrayList<>()).addAll(Arrays.asList(e.getMethods())));
|
||||
Stream.concat(listA.stream(), listB.stream())
|
||||
.forEach(e -> mergedMap.computeIfAbsent(e.getHref(), k -> new ArrayList<>())
|
||||
.addAll(Arrays.asList(e.getMethods())));
|
||||
|
||||
return mergedMap.entrySet().stream().map(entry -> new GatewayEndpointInfo(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
return mergedMap.entrySet()
|
||||
.stream()
|
||||
.map(entry -> new GatewayEndpointInfo(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<GatewayEndpointInfo> getAvailableEndpointsForClass(String className) {
|
||||
try {
|
||||
MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader(className);
|
||||
Set<MethodMetadata> annotatedMethods = metadataReader.getAnnotationMetadata()
|
||||
.getAnnotatedMethods(RequestMapping.class.getName());
|
||||
.getAnnotatedMethods(RequestMapping.class.getName());
|
||||
|
||||
String gatewayActuatorPath = webEndpointProperties.getBasePath() + "/gateway";
|
||||
return annotatedMethods.stream().map(method -> new GatewayEndpointInfo(gatewayActuatorPath
|
||||
+ ((String[]) method.getAnnotationAttributes(RequestMapping.class.getName()).get("path"))[0],
|
||||
((RequestMethod[]) method.getAnnotationAttributes(RequestMapping.class.getName()).get("method"))[0]
|
||||
.name()))
|
||||
.collect(Collectors.toList());
|
||||
return annotatedMethods.stream()
|
||||
.map(method -> new GatewayEndpointInfo(gatewayActuatorPath
|
||||
+ ((String[]) method.getAnnotationAttributes(RequestMapping.class.getName()).get("path"))[0],
|
||||
((RequestMethod[]) method.getAnnotationAttributes(RequestMapping.class.getName())
|
||||
.get("method"))[0].name()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
catch (IOException exception) {
|
||||
log.warn(exception.getMessage());
|
||||
@@ -186,8 +194,9 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
}
|
||||
|
||||
private Map<String, Object> convertToMap(List<String> byMetadata) {
|
||||
return byMetadata.stream().map(keyValueStr -> keyValueStr.split(":"))
|
||||
.collect(Collectors.toMap(kv -> kv[0], kv -> kv.length > 1 ? kv[1] : null));
|
||||
return byMetadata.stream()
|
||||
.map(keyValueStr -> keyValueStr.split(":"))
|
||||
.collect(Collectors.toMap(kv -> kv[0], kv -> kv.length > 1 ? kv[1] : null));
|
||||
}
|
||||
|
||||
@GetMapping("/globalfilters")
|
||||
@@ -228,13 +237,14 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<ResponseEntity<Object>> save(@PathVariable String id, @RequestBody RouteDefinition route) {
|
||||
|
||||
return Mono.just(route).doOnNext(this::validateRouteDefinition)
|
||||
.flatMap(routeDefinition -> this.routeDefinitionWriter.save(Mono.just(routeDefinition).map(r -> {
|
||||
r.setId(id);
|
||||
log.debug("Saving route: " + route);
|
||||
return r;
|
||||
})).then(Mono.defer(() -> Mono.just(ResponseEntity.created(URI.create("/routes/" + id)).build()))))
|
||||
.switchIfEmpty(Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
return Mono.just(route)
|
||||
.doOnNext(this::validateRouteDefinition)
|
||||
.flatMap(routeDefinition -> this.routeDefinitionWriter.save(Mono.just(routeDefinition).map(r -> {
|
||||
r.setId(id);
|
||||
log.debug("Saving route: " + route);
|
||||
return r;
|
||||
})).then(Mono.defer(() -> Mono.just(ResponseEntity.created(URI.create("/routes/" + id)).build()))))
|
||||
.switchIfEmpty(Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
}
|
||||
|
||||
@PostMapping("/routes")
|
||||
@@ -246,11 +256,12 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
});
|
||||
|
||||
return Flux.fromIterable(routes)
|
||||
.flatMap(routeDefinition -> this.routeDefinitionWriter.save(Mono.just(routeDefinition).map(r -> {
|
||||
log.debug("Saving route: " + routeDefinition);
|
||||
return r;
|
||||
}))).then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
|
||||
.switchIfEmpty(Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
.flatMap(routeDefinition -> this.routeDefinitionWriter.save(Mono.just(routeDefinition).map(r -> {
|
||||
log.debug("Saving route: " + routeDefinition);
|
||||
return r;
|
||||
})))
|
||||
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
|
||||
.switchIfEmpty(Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
}
|
||||
|
||||
private void validateRouteId(RouteDefinition routeDefinition) {
|
||||
@@ -260,11 +271,17 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
}
|
||||
|
||||
private void validateRouteDefinition(RouteDefinition routeDefinition) {
|
||||
Set<String> unavailableFilterDefinitions = routeDefinition.getFilters().stream().filter(rd -> !isAvailable(rd))
|
||||
.map(FilterDefinition::getName).collect(Collectors.toSet());
|
||||
Set<String> unavailableFilterDefinitions = routeDefinition.getFilters()
|
||||
.stream()
|
||||
.filter(rd -> !isAvailable(rd))
|
||||
.map(FilterDefinition::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<String> unavailablePredicatesDefinitions = routeDefinition.getPredicates().stream()
|
||||
.filter(rd -> !isAvailable(rd)).map(PredicateDefinition::getName).collect(Collectors.toSet());
|
||||
Set<String> unavailablePredicatesDefinitions = routeDefinition.getPredicates()
|
||||
.stream()
|
||||
.filter(rd -> !isAvailable(rd))
|
||||
.map(PredicateDefinition::getName)
|
||||
.collect(Collectors.toSet());
|
||||
if (!unavailableFilterDefinitions.isEmpty()) {
|
||||
handleUnavailableDefinition(FilterDefinition.class.getSimpleName(), unavailableFilterDefinitions);
|
||||
}
|
||||
@@ -298,12 +315,12 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
|
||||
private boolean isAvailable(FilterDefinition filterDefinition) {
|
||||
return GatewayFilters.stream()
|
||||
.anyMatch(gatewayFilterFactory -> filterDefinition.getName().equals(gatewayFilterFactory.name()));
|
||||
.anyMatch(gatewayFilterFactory -> filterDefinition.getName().equals(gatewayFilterFactory.name()));
|
||||
}
|
||||
|
||||
private boolean isAvailable(PredicateDefinition predicateDefinition) {
|
||||
return routePredicates.stream()
|
||||
.anyMatch(routePredicate -> predicateDefinition.getName().equals(routePredicate.name()));
|
||||
.anyMatch(routePredicate -> predicateDefinition.getName().equals(routePredicate.name()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/routes/{id}")
|
||||
@@ -317,8 +334,9 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
@GetMapping("/routes/{id}/combinedfilters")
|
||||
public Mono<HashMap<String, Object>> combinedfilters(@PathVariable String id) {
|
||||
// TODO: missing global filters
|
||||
return this.routeLocator.getRoutes().filter(route -> route.getId().equals(id)).reduce(new HashMap<>(),
|
||||
this::putItem);
|
||||
return this.routeLocator.getRoutes()
|
||||
.filter(route -> route.getId().equals(id))
|
||||
.reduce(new HashMap<>(), this::putItem);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class GatewayLegacyControllerEndpoint extends AbstractGatewayControllerEn
|
||||
@GetMapping("/routes")
|
||||
public Mono<List<Map<String, Object>>> routes() {
|
||||
Mono<Map<String, RouteDefinition>> routeDefs = this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.collectMap(RouteDefinition::getId);
|
||||
.collectMap(RouteDefinition::getId);
|
||||
Mono<List<Route>> routes = this.routeLocator.getRoutes().collectList();
|
||||
return Mono.zip(routeDefs, routes).map(tuple -> {
|
||||
Map<String, RouteDefinition> defs = tuple.getT1();
|
||||
@@ -103,8 +103,11 @@ public class GatewayLegacyControllerEndpoint extends AbstractGatewayControllerEn
|
||||
@GetMapping("/routes/{id}")
|
||||
public Mono<ResponseEntity<RouteDefinition>> route(@PathVariable String id) {
|
||||
// TODO: missing RouteLocator
|
||||
return this.routeDefinitionLocator.getRouteDefinitions().filter(route -> route.getId().equals(id))
|
||||
.singleOrEmpty().map(ResponseEntity::ok).switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
|
||||
return this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.filter(route -> route.getId().equals(id))
|
||||
.singleOrEmpty()
|
||||
.map(ResponseEntity::ok)
|
||||
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public abstract class AbstractSslConfigurer<T, S> {
|
||||
try {
|
||||
if (ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0) {
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory
|
||||
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
char[] keyPassword = ssl.getKeyPassword() != null ? ssl.getKeyPassword().toCharArray() : null;
|
||||
|
||||
if (keyPassword == null && ssl.getKeyStorePassword() != null) {
|
||||
|
||||
@@ -106,8 +106,9 @@ class ConfigurableHintsRegistrationProcessor implements BeanFactoryInitializatio
|
||||
|
||||
private static void addGenericsForClass(Set<Class<?>> genericsToAdd, ResolvableType resolvableType) {
|
||||
if (resolvableType.getSuperType().hasGenerics()) {
|
||||
genericsToAdd.addAll(Arrays.stream(resolvableType.getSuperType().getGenerics()).map(ResolvableType::toClass)
|
||||
.collect(Collectors.toSet()));
|
||||
genericsToAdd.addAll(Arrays.stream(resolvableType.getSuperType().getGenerics())
|
||||
.map(ResolvableType::toClass)
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -356,7 +356,7 @@ public class GatewayAutoConfiguration {
|
||||
public GrpcSslConfigurer grpcSslConfigurer(HttpClientProperties properties)
|
||||
throws KeyStoreException, NoSuchAlgorithmException {
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(KeyStore.getInstance(KeyStore.getDefaultType()));
|
||||
|
||||
return new GrpcSslConfigurer(properties.getSsl());
|
||||
@@ -792,7 +792,7 @@ public class GatewayAutoConfiguration {
|
||||
HttpClient httpClient) {
|
||||
Supplier<WebsocketClientSpec.Builder> builderSupplier = () -> {
|
||||
WebsocketClientSpec.Builder builder = WebsocketClientSpec.builder()
|
||||
.handlePing(properties.getWebsocket().isProxyPing());
|
||||
.handlePing(properties.getWebsocket().isProxyPing());
|
||||
if (properties.getWebsocket().getMaxFramePayloadLength() != null) {
|
||||
builder.maxFramePayloadLength(properties.getWebsocket().getMaxFramePayloadLength());
|
||||
}
|
||||
@@ -886,19 +886,19 @@ class GatewayHints implements RuntimeHintsRegistrar {
|
||||
return;
|
||||
}
|
||||
hints.reflection()
|
||||
.registerType(TypeReference.of(FilterDefinition.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference.of(PredicateDefinition.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference.of(AbstractNameValueGatewayFilterFactory.NameValueConfig.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference.of(
|
||||
"org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator$DelegatingServiceInstance"),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
.registerType(TypeReference.of(FilterDefinition.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference.of(PredicateDefinition.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference.of(AbstractNameValueGatewayFilterFactory.NameValueConfig.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference
|
||||
.of("org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator$DelegatingServiceInstance"),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,8 +27,9 @@ public class GatewayEnvironmentPostProcessor implements EnvironmentPostProcessor
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) {
|
||||
env.getPropertySources().addFirst(new MapPropertySource("gateway-properties",
|
||||
Collections.singletonMap("spring.webflux.hiddenmethod.filter.enabled", "false")));
|
||||
env.getPropertySources()
|
||||
.addFirst(new MapPropertySource("gateway-properties",
|
||||
Collections.singletonMap("spring.webflux.hiddenmethod.filter.enabled", "false")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,8 +79,10 @@ public class GatewayMetricsProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("enabled", enabled).append("prefix", prefix).append("tags", tags)
|
||||
.toString();
|
||||
return new ToStringCreator(this).append("enabled", enabled)
|
||||
.append("prefix", prefix)
|
||||
.append("tags", tags)
|
||||
.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -105,9 +105,11 @@ public class GatewayProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("routes", routes).append("defaultFilters", defaultFilters)
|
||||
.append("streamingMediaTypes", streamingMediaTypes)
|
||||
.append("failOnRouteDefinitionError", failOnRouteDefinitionError).toString();
|
||||
return new ToStringCreator(this).append("routes", routes)
|
||||
.append("defaultFilters", defaultFilters)
|
||||
.append("streamingMediaTypes", streamingMediaTypes)
|
||||
.append("failOnRouteDefinitionError", failOnRouteDefinitionError)
|
||||
.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,10 @@ public class GatewayReactiveOAuth2AutoConfiguration {
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository,
|
||||
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder
|
||||
.builder().authorizationCode().refreshToken().build();
|
||||
.builder()
|
||||
.authorizationCode()
|
||||
.refreshToken()
|
||||
.build();
|
||||
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
|
||||
clientRegistrationRepository, authorizedClientRepository);
|
||||
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
||||
|
||||
@@ -87,7 +87,7 @@ class GatewayRedisAutoConfiguration {
|
||||
Jackson2JsonRedisSerializer<RouteDefinition> valueSerializer = new Jackson2JsonRedisSerializer<>(
|
||||
RouteDefinition.class);
|
||||
RedisSerializationContext.RedisSerializationContextBuilder<String, RouteDefinition> builder = RedisSerializationContext
|
||||
.newSerializationContext(keySerializer);
|
||||
.newSerializationContext(keySerializer);
|
||||
RedisSerializationContext<String, RouteDefinition> context = builder.value(valueSerializer).build();
|
||||
|
||||
return new ReactiveRedisTemplate<>(factory, context);
|
||||
|
||||
@@ -80,8 +80,8 @@ public class HttpClientFactory extends AbstractFactoryBean<HttpClient> {
|
||||
ConnectionProvider connectionProvider = buildConnectionProvider(properties);
|
||||
|
||||
HttpClient httpClient = HttpClient.create(connectionProvider)
|
||||
// TODO: move customizations to HttpClientCustomizers
|
||||
.httpResponseDecoder(this::httpResponseDecoder);
|
||||
// TODO: move customizations to HttpClientCustomizers
|
||||
.httpResponseDecoder(this::httpResponseDecoder);
|
||||
|
||||
if (serverProperties.getHttp2().isEnabled()) {
|
||||
httpClient = httpClient.protocol(HttpProtocol.HTTP11, HttpProtocol.H2);
|
||||
@@ -170,13 +170,15 @@ public class HttpClientFactory extends AbstractFactoryBean<HttpClient> {
|
||||
// create either Fixed or Elastic pool
|
||||
ConnectionProvider.Builder builder = ConnectionProvider.builder(pool.getName());
|
||||
if (pool.getType() == FIXED) {
|
||||
builder.maxConnections(pool.getMaxConnections()).pendingAcquireMaxCount(-1)
|
||||
.pendingAcquireTimeout(Duration.ofMillis(pool.getAcquireTimeout()));
|
||||
builder.maxConnections(pool.getMaxConnections())
|
||||
.pendingAcquireMaxCount(-1)
|
||||
.pendingAcquireTimeout(Duration.ofMillis(pool.getAcquireTimeout()));
|
||||
}
|
||||
else {
|
||||
// Elastic
|
||||
builder.maxConnections(Integer.MAX_VALUE).pendingAcquireTimeout(Duration.ofMillis(0))
|
||||
.pendingAcquireMaxCount(-1);
|
||||
builder.maxConnections(Integer.MAX_VALUE)
|
||||
.pendingAcquireTimeout(Duration.ofMillis(0))
|
||||
.pendingAcquireMaxCount(-1);
|
||||
}
|
||||
|
||||
if (pool.getMaxIdleTime() != null) {
|
||||
|
||||
@@ -499,10 +499,11 @@ public class HttpClientProperties {
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("useInsecureTrustManager", useInsecureTrustManager)
|
||||
.append("trustedX509Certificates", trustedX509Certificates)
|
||||
.append("handshakeTimeout", handshakeTimeout)
|
||||
.append("closeNotifyFlushTimeout", closeNotifyFlushTimeout)
|
||||
.append("closeNotifyReadTimeout", closeNotifyReadTimeout).toString();
|
||||
.append("trustedX509Certificates", trustedX509Certificates)
|
||||
.append("handshakeTimeout", handshakeTimeout)
|
||||
.append("closeNotifyFlushTimeout", closeNotifyFlushTimeout)
|
||||
.append("closeNotifyReadTimeout", closeNotifyReadTimeout)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -534,7 +535,8 @@ public class HttpClientProperties {
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("maxFramePayloadLength", maxFramePayloadLength)
|
||||
.append("proxyPing", proxyPing).toString();
|
||||
.append("proxyPing", proxyPing)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -68,9 +68,10 @@ public class HttpClientSslConfigurer extends AbstractSslConfigurer<HttpClient, H
|
||||
}
|
||||
});
|
||||
|
||||
sslContextSpec.sslContext(clientSslContext).handshakeTimeout(ssl.getHandshakeTimeout())
|
||||
.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
|
||||
.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
|
||||
sslContextSpec.sslContext(clientSslContext)
|
||||
.handshakeTimeout(ssl.getHandshakeTimeout())
|
||||
.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
|
||||
.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
DiscoveryLocatorProperties properties) {
|
||||
this(discoveryClient.getClass().getSimpleName(), properties);
|
||||
serviceInstances = discoveryClient.getServices()
|
||||
.flatMap(service -> discoveryClient.getInstances(service).collectList());
|
||||
.flatMap(service -> discoveryClient.getInstances(service).collectList());
|
||||
}
|
||||
|
||||
private DiscoveryClientRouteDefinitionLocator(String discoveryClientName, DiscoveryLocatorProperties properties) {
|
||||
@@ -96,36 +96,39 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
};
|
||||
}
|
||||
|
||||
return serviceInstances.filter(instances -> !instances.isEmpty()).flatMap(Flux::fromIterable)
|
||||
.filter(includePredicate).collectMap(ServiceInstance::getServiceId)
|
||||
// remove duplicates
|
||||
.flatMapMany(map -> Flux.fromIterable(map.values())).map(instance -> {
|
||||
RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance);
|
||||
return serviceInstances.filter(instances -> !instances.isEmpty())
|
||||
.flatMap(Flux::fromIterable)
|
||||
.filter(includePredicate)
|
||||
.collectMap(ServiceInstance::getServiceId)
|
||||
// remove duplicates
|
||||
.flatMapMany(map -> Flux.fromIterable(map.values()))
|
||||
.map(instance -> {
|
||||
RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance);
|
||||
|
||||
final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);
|
||||
final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);
|
||||
|
||||
for (PredicateDefinition original : this.properties.getPredicates()) {
|
||||
PredicateDefinition predicate = new PredicateDefinition();
|
||||
predicate.setName(original.getName());
|
||||
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
|
||||
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
|
||||
predicate.addArg(entry.getKey(), value);
|
||||
}
|
||||
routeDefinition.getPredicates().add(predicate);
|
||||
for (PredicateDefinition original : this.properties.getPredicates()) {
|
||||
PredicateDefinition predicate = new PredicateDefinition();
|
||||
predicate.setName(original.getName());
|
||||
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
|
||||
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
|
||||
predicate.addArg(entry.getKey(), value);
|
||||
}
|
||||
routeDefinition.getPredicates().add(predicate);
|
||||
}
|
||||
|
||||
for (FilterDefinition original : this.properties.getFilters()) {
|
||||
FilterDefinition filter = new FilterDefinition();
|
||||
filter.setName(original.getName());
|
||||
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
|
||||
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
|
||||
filter.addArg(entry.getKey(), value);
|
||||
}
|
||||
routeDefinition.getFilters().add(filter);
|
||||
for (FilterDefinition original : this.properties.getFilters()) {
|
||||
FilterDefinition filter = new FilterDefinition();
|
||||
filter.setName(original.getName());
|
||||
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
|
||||
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
|
||||
filter.addArg(entry.getKey(), value);
|
||||
}
|
||||
routeDefinition.getFilters().add(filter);
|
||||
}
|
||||
|
||||
return routeDefinition;
|
||||
});
|
||||
return routeDefinition;
|
||||
});
|
||||
}
|
||||
|
||||
protected RouteDefinition buildRouteDefinition(Expression urlExpr, ServiceInstance serviceInstance) {
|
||||
|
||||
@@ -116,10 +116,14 @@ public class DiscoveryLocatorProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("enabled", enabled).append("routeIdPrefix", routeIdPrefix)
|
||||
.append("includeExpression", includeExpression).append("urlExpression", urlExpression)
|
||||
.append("lowerCaseServiceId", lowerCaseServiceId).append("predicates", predicates)
|
||||
.append("filters", filters).toString();
|
||||
return new ToStringCreator(this).append("enabled", enabled)
|
||||
.append("routeIdPrefix", routeIdPrefix)
|
||||
.append("includeExpression", includeExpression)
|
||||
.append("urlExpression", urlExpression)
|
||||
.append("lowerCaseServiceId", lowerCaseServiceId)
|
||||
.append("predicates", predicates)
|
||||
.append("filters", filters)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,8 +72,9 @@ public class GatewayMetricsFilter implements GlobalFilter, Ordered {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
Sample sample = Timer.start(meterRegistry);
|
||||
|
||||
return chain.filter(exchange).doOnSuccess(aVoid -> endTimerRespectingCommit(exchange, sample))
|
||||
.doOnError(throwable -> endTimerRespectingCommit(exchange, sample));
|
||||
return chain.filter(exchange)
|
||||
.doOnSuccess(aVoid -> endTimerRespectingCommit(exchange, sample))
|
||||
.doOnError(throwable -> endTimerRespectingCommit(exchange, sample));
|
||||
}
|
||||
|
||||
private void endTimerRespectingCommit(ServerWebExchange exchange, Sample sample) {
|
||||
|
||||
@@ -76,7 +76,8 @@ public class LoadBalancerServiceInstanceCookieFilter implements GlobalFilter, Or
|
||||
ServerWebExchange newExchange = exchange.mutate().request(exchange.getRequest().mutate().headers((headers) -> {
|
||||
List<String> cookieHeaders = new ArrayList<>(headers.getOrEmpty(HttpHeaders.COOKIE));
|
||||
String serviceInstanceCookie = new HttpCookie(instanceIdCookieName,
|
||||
serviceInstanceResponse.getServer().getInstanceId()).toString();
|
||||
serviceInstanceResponse.getServer().getInstanceId())
|
||||
.toString();
|
||||
cookieHeaders.add(serviceInstanceCookie);
|
||||
headers.put(HttpHeaders.COOKIE, cookieHeaders);
|
||||
}).build()).build();
|
||||
|
||||
@@ -131,69 +131,69 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||
|
||||
Flux<HttpClientResponse> responseFlux = getHttpClientMono(route, exchange)
|
||||
.flatMapMany(httpClient -> httpClient.headers(headers -> {
|
||||
headers.add(httpHeaders);
|
||||
// Will either be set below, or later by Netty
|
||||
headers.remove(HttpHeaders.HOST);
|
||||
if (preserveHost) {
|
||||
String host = request.getHeaders().getFirst(HttpHeaders.HOST);
|
||||
headers.add(HttpHeaders.HOST, host);
|
||||
}
|
||||
}).request(method).uri(url).send((req, nettyOutbound) -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
nettyOutbound.withConnection(connection -> log.trace("outbound route: "
|
||||
+ connection.channel().id().asShortText() + ", inbound: " + exchange.getLogPrefix()));
|
||||
}
|
||||
return nettyOutbound.send(request.getBody().map(this::getByteBuf));
|
||||
}).responseConnection((res, connection) -> {
|
||||
.flatMapMany(httpClient -> httpClient.headers(headers -> {
|
||||
headers.add(httpHeaders);
|
||||
// Will either be set below, or later by Netty
|
||||
headers.remove(HttpHeaders.HOST);
|
||||
if (preserveHost) {
|
||||
String host = request.getHeaders().getFirst(HttpHeaders.HOST);
|
||||
headers.add(HttpHeaders.HOST, host);
|
||||
}
|
||||
}).request(method).uri(url).send((req, nettyOutbound) -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
nettyOutbound.withConnection(connection -> log.trace("outbound route: "
|
||||
+ connection.channel().id().asShortText() + ", inbound: " + exchange.getLogPrefix()));
|
||||
}
|
||||
return nettyOutbound.send(request.getBody().map(this::getByteBuf));
|
||||
}).responseConnection((res, connection) -> {
|
||||
|
||||
// Defer committing the response until all route filters have run
|
||||
// Put client response as ServerWebExchange attribute and write
|
||||
// response later NettyWriteResponseFilter
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);
|
||||
// Defer committing the response until all route filters have run
|
||||
// Put client response as ServerWebExchange attribute and write
|
||||
// response later NettyWriteResponseFilter
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
// put headers and status so filters can modify the response
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
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()));
|
||||
res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));
|
||||
|
||||
String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
|
||||
if (StringUtils.hasLength(contentTypeValue)) {
|
||||
exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, contentTypeValue);
|
||||
}
|
||||
String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
|
||||
if (StringUtils.hasLength(contentTypeValue)) {
|
||||
exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, contentTypeValue);
|
||||
}
|
||||
|
||||
setResponseStatus(res, response);
|
||||
setResponseStatus(res, response);
|
||||
|
||||
// make sure headers filters run after setting status so it is
|
||||
// available in response
|
||||
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(getHeadersFilters(), headers,
|
||||
exchange, Type.RESPONSE);
|
||||
// make sure headers filters run after setting status so it is
|
||||
// available in response
|
||||
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(getHeadersFilters(), headers, exchange,
|
||||
Type.RESPONSE);
|
||||
|
||||
if (!filteredResponseHeaders.containsKey(HttpHeaders.TRANSFER_ENCODING)
|
||||
&& filteredResponseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH)) {
|
||||
// It is not valid to have both the transfer-encoding header and
|
||||
// the content-length header.
|
||||
// Remove the transfer-encoding header in the response if the
|
||||
// content-length header is present.
|
||||
response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);
|
||||
}
|
||||
if (!filteredResponseHeaders.containsKey(HttpHeaders.TRANSFER_ENCODING)
|
||||
&& filteredResponseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH)) {
|
||||
// It is not valid to have both the transfer-encoding header and
|
||||
// the content-length header.
|
||||
// Remove the transfer-encoding header in the response if the
|
||||
// content-length header is present.
|
||||
response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);
|
||||
}
|
||||
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES, filteredResponseHeaders.keySet());
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES, filteredResponseHeaders.keySet());
|
||||
|
||||
response.getHeaders().addAll(filteredResponseHeaders);
|
||||
response.getHeaders().addAll(filteredResponseHeaders);
|
||||
|
||||
return Mono.just(res);
|
||||
}));
|
||||
return Mono.just(res);
|
||||
}));
|
||||
|
||||
Duration responseTimeout = getResponseTimeout(route);
|
||||
if (responseTimeout != null) {
|
||||
responseFlux = responseFlux
|
||||
.timeout(responseTimeout,
|
||||
Mono.error(new TimeoutException("Response took longer than timeout: " + responseTimeout)))
|
||||
.onErrorMap(TimeoutException.class,
|
||||
th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th));
|
||||
.timeout(responseTimeout,
|
||||
Mono.error(new TimeoutException("Response took longer than timeout: " + responseTimeout)))
|
||||
.onErrorMap(TimeoutException.class,
|
||||
th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th));
|
||||
}
|
||||
|
||||
return responseFlux.then(chain.filter(exchange));
|
||||
|
||||
@@ -111,15 +111,15 @@ public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
URI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
String serviceId = requestUri.getHost();
|
||||
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
|
||||
.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
|
||||
RequestDataContext.class, ResponseData.class, ServiceInstance.class);
|
||||
.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
|
||||
RequestDataContext.class, ResponseData.class, ServiceInstance.class);
|
||||
DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(new RequestDataContext(
|
||||
new RequestData(exchange.getRequest(), exchange.getAttributes()), getHint(serviceId)));
|
||||
return choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext(response -> {
|
||||
|
||||
if (!response.hasServer()) {
|
||||
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, response)));
|
||||
.onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, response)));
|
||||
throw NotFoundException.create(properties.isUse404(), "Unable to find instance for " + url.getHost());
|
||||
}
|
||||
|
||||
@@ -145,17 +145,18 @@ public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
|
||||
exchange.getAttributes().put(GATEWAY_LOADBALANCER_RESPONSE_ATTR, response);
|
||||
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, response));
|
||||
}).then(chain.filter(exchange))
|
||||
.doOnError(throwable -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
|
||||
CompletionContext.Status.FAILED, throwable, lbRequest,
|
||||
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR)))))
|
||||
.doOnSuccess(aVoid -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
|
||||
CompletionContext.Status.SUCCESS, lbRequest,
|
||||
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR),
|
||||
new ResponseData(exchange.getResponse(),
|
||||
new RequestData(exchange.getRequest(), exchange.getAttributes()))))));
|
||||
})
|
||||
.then(chain.filter(exchange))
|
||||
.doOnError(throwable -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
|
||||
CompletionContext.Status.FAILED, throwable, lbRequest,
|
||||
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR)))))
|
||||
.doOnSuccess(aVoid -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
|
||||
CompletionContext.Status.SUCCESS, lbRequest,
|
||||
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR),
|
||||
new ResponseData(exchange.getResponse(),
|
||||
new RequestData(exchange.getRequest(), exchange.getAttributes()))))));
|
||||
}
|
||||
|
||||
protected URI reconstructURI(ServiceInstance serviceInstance, URI original) {
|
||||
|
||||
@@ -85,8 +85,12 @@ public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {
|
||||
}
|
||||
|
||||
URI mergedUrl = UriComponentsBuilder.fromUri(uri)
|
||||
// .uri(routeUri)
|
||||
.scheme(routeUri.getScheme()).host(routeUri.getHost()).port(routeUri.getPort()).build(encoded).toUri();
|
||||
// .uri(routeUri)
|
||||
.scheme(routeUri.getScheme())
|
||||
.host(routeUri.getHost())
|
||||
.port(routeUri.getPort())
|
||||
.build(encoded)
|
||||
.toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@@ -106,17 +106,17 @@ public class WebClientHttpRoutingFilter implements GlobalFilter, Ordered {
|
||||
}
|
||||
|
||||
return headersSpec.exchangeToMono(Mono::just)
|
||||
// .log("webClient route")
|
||||
.flatMap(res -> {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.getHeaders().putAll(res.headers().asHttpHeaders());
|
||||
response.setStatusCode(res.statusCode());
|
||||
// Defer committing the response until all route filters have run
|
||||
// Put client response as ServerWebExchange attribute and write
|
||||
// response later NettyWriteResponseFilter
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
|
||||
return chain.filter(exchange);
|
||||
});
|
||||
// .log("webClient route")
|
||||
.flatMap(res -> {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.getHeaders().putAll(res.headers().asHttpHeaders());
|
||||
response.setStatusCode(res.statusCode());
|
||||
// Defer committing the response until all route filters have run
|
||||
// Put client response as ServerWebExchange attribute and write
|
||||
// response later NettyWriteResponseFilter
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
|
||||
return chain.filter(exchange);
|
||||
});
|
||||
}
|
||||
|
||||
private boolean requiresBody(HttpMethod method) {
|
||||
|
||||
@@ -58,8 +58,8 @@ public class WebClientWriteResponseFilter implements GlobalFilter, Ordered {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
|
||||
return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers()))
|
||||
// .log("webClient response")
|
||||
.doOnCancel(() -> cleanup(exchange));
|
||||
// .log("webClient response")
|
||||
.doOnCancel(() -> cleanup(exchange));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -244,28 +244,32 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebSocketSession proxySession) {
|
||||
Mono<Void> serverClose = proxySession.closeStatus().filter(__ -> session.isOpen())
|
||||
.map(this::adaptCloseStatus).flatMap(session::close);
|
||||
Mono<Void> proxyClose = session.closeStatus().filter(__ -> proxySession.isOpen())
|
||||
.map(this::adaptCloseStatus).flatMap(proxySession::close);
|
||||
Mono<Void> serverClose = proxySession.closeStatus()
|
||||
.filter(__ -> session.isOpen())
|
||||
.map(this::adaptCloseStatus)
|
||||
.flatMap(session::close);
|
||||
Mono<Void> proxyClose = session.closeStatus()
|
||||
.filter(__ -> proxySession.isOpen())
|
||||
.map(this::adaptCloseStatus)
|
||||
.flatMap(proxySession::close);
|
||||
// Use retain() for Reactor Netty
|
||||
Mono<Void> proxySessionSend = proxySession
|
||||
.send(session.receive().doOnNext(WebSocketMessage::retain).doOnNext(webSocketMessage -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("proxySession(send from client): " + proxySession.getId()
|
||||
+ ", corresponding session:" + session.getId() + ", packet: "
|
||||
+ webSocketMessage.getPayloadAsText());
|
||||
}
|
||||
}));
|
||||
.send(session.receive().doOnNext(WebSocketMessage::retain).doOnNext(webSocketMessage -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("proxySession(send from client): " + proxySession.getId()
|
||||
+ ", corresponding session:" + session.getId() + ", packet: "
|
||||
+ webSocketMessage.getPayloadAsText());
|
||||
}
|
||||
}));
|
||||
// .log("proxySessionSend", Level.FINE);
|
||||
Mono<Void> serverSessionSend = session.send(
|
||||
proxySession.receive().doOnNext(WebSocketMessage::retain).doOnNext(webSocketMessage -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("session(send from backend): " + session.getId()
|
||||
+ ", corresponding proxySession:" + proxySession.getId() + " packet: "
|
||||
+ webSocketMessage.getPayloadAsText());
|
||||
}
|
||||
}));
|
||||
Mono<Void> serverSessionSend = session
|
||||
.send(proxySession.receive().doOnNext(WebSocketMessage::retain).doOnNext(webSocketMessage -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("session(send from backend): " + session.getId()
|
||||
+ ", corresponding proxySession:" + proxySession.getId() + " packet: "
|
||||
+ webSocketMessage.getPayloadAsText());
|
||||
}
|
||||
}));
|
||||
// .log("sessionSend", Level.FINE);
|
||||
// Ensure closeStatus from one propagates to the other
|
||||
Mono.when(serverClose, proxyClose).subscribe();
|
||||
|
||||
@@ -315,8 +315,11 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("group", group).append("weights", weights)
|
||||
.append("normalizedWeights", normalizedWeights).append("rangeIndexes", rangeIndexes).toString();
|
||||
return new ToStringCreator(this).append("group", group)
|
||||
.append("weights", weights)
|
||||
.append("normalizedWeights", normalizedWeights)
|
||||
.append("rangeIndexes", rangeIndexes)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -106,17 +106,17 @@ public class CorsGatewayFilterApplicationListener implements ApplicationListener
|
||||
final CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
|
||||
findValue(corsMetadata, "allowCredentials")
|
||||
.ifPresent(value -> corsConfiguration.setAllowCredentials((Boolean) value));
|
||||
.ifPresent(value -> corsConfiguration.setAllowCredentials((Boolean) value));
|
||||
findValue(corsMetadata, "allowedHeaders")
|
||||
.ifPresent(value -> corsConfiguration.setAllowedHeaders(asList(value)));
|
||||
.ifPresent(value -> corsConfiguration.setAllowedHeaders(asList(value)));
|
||||
findValue(corsMetadata, "allowedMethods")
|
||||
.ifPresent(value -> corsConfiguration.setAllowedMethods(asList(value)));
|
||||
.ifPresent(value -> corsConfiguration.setAllowedMethods(asList(value)));
|
||||
findValue(corsMetadata, "allowedOriginPatterns")
|
||||
.ifPresent(value -> corsConfiguration.setAllowedOriginPatterns(asList(value)));
|
||||
.ifPresent(value -> corsConfiguration.setAllowedOriginPatterns(asList(value)));
|
||||
findValue(corsMetadata, "allowedOrigins")
|
||||
.ifPresent(value -> corsConfiguration.setAllowedOrigins(asList(value)));
|
||||
.ifPresent(value -> corsConfiguration.setAllowedOrigins(asList(value)));
|
||||
findValue(corsMetadata, "exposedHeaders")
|
||||
.ifPresent(value -> corsConfiguration.setExposedHeaders(asList(value)));
|
||||
.ifPresent(value -> corsConfiguration.setExposedHeaders(asList(value)));
|
||||
findValue(corsMetadata, "maxAge").ifPresent(value -> corsConfiguration.setMaxAge(asLong(value)));
|
||||
|
||||
return Optional.of(corsConfiguration);
|
||||
|
||||
@@ -37,8 +37,10 @@ public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatew
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(httpHeaders -> httpHeaders.add(config.getName(), value)).build();
|
||||
ServerHttpRequest request = exchange.getRequest()
|
||||
.mutate()
|
||||
.headers(httpHeaders -> httpHeaders.add(config.getName(), value))
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
@@ -46,7 +48,8 @@ public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatew
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(AddRequestHeaderGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getValue()).toString();
|
||||
.append(config.getName(), config.getValue())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,8 +69,11 @@ public class AddRequestHeadersIfNotPresentGatewayFilterFactory
|
||||
for (Map.Entry<String, List<String>> kv : aggregatedHeaders.entrySet()) {
|
||||
String headerName = kv.getKey();
|
||||
|
||||
boolean headerIsMissingOrBlank = exchange.getRequest().getHeaders().getOrEmpty(headerName).stream()
|
||||
.allMatch(h -> !StringUtils.hasText(h));
|
||||
boolean headerIsMissingOrBlank = exchange.getRequest()
|
||||
.getHeaders()
|
||||
.getOrEmpty(headerName)
|
||||
.stream()
|
||||
.allMatch(h -> !StringUtils.hasText(h));
|
||||
|
||||
if (headerIsMissingOrBlank) {
|
||||
if (requestBuilder == null) {
|
||||
@@ -78,9 +81,10 @@ public class AddRequestHeadersIfNotPresentGatewayFilterFactory
|
||||
}
|
||||
ServerWebExchange finalExchange = exchange;
|
||||
requestBuilder.headers(httpHeaders -> {
|
||||
List<String> replacedValues = kv.getValue().stream()
|
||||
.map(value -> ServerWebExchangeUtils.expand(finalExchange, value))
|
||||
.collect(Collectors.toList());
|
||||
List<String> replacedValues = kv.getValue()
|
||||
.stream()
|
||||
.map(value -> ServerWebExchangeUtils.expand(finalExchange, value))
|
||||
.collect(Collectors.toList());
|
||||
httpHeaders.addAll(headerName, replacedValues);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,8 +60,10 @@ public class AddRequestParameterGatewayFilterFactory extends AbstractNameValueGa
|
||||
|
||||
boolean encoded = containsEncodedParts(uri);
|
||||
try {
|
||||
URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build(encoded)
|
||||
.toUri();
|
||||
URI newUri = UriComponentsBuilder.fromUri(uri)
|
||||
.replaceQuery(query.toString())
|
||||
.build(encoded)
|
||||
.toUri();
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
|
||||
|
||||
@@ -75,7 +77,8 @@ public class AddRequestParameterGatewayFilterFactory extends AbstractNameValueGa
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(AddRequestParameterGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getValue()).toString();
|
||||
.append(config.getName(), config.getValue())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGate
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(AddResponseHeaderGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getValue()).toString();
|
||||
.append(config.getName(), config.getValue())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -77,23 +77,23 @@ public class CacheRequestBodyGatewayFilterFactory
|
||||
|
||||
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> {
|
||||
final ServerRequest serverRequest = ServerRequest
|
||||
.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders);
|
||||
.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders);
|
||||
return serverRequest.bodyToMono((config.getBodyClass())).doOnNext(objectValue -> {
|
||||
Object previousCachedBody = exchange.getAttributes()
|
||||
.put(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR, objectValue);
|
||||
.put(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR, objectValue);
|
||||
if (previousCachedBody != null) {
|
||||
// store previous cached body
|
||||
exchange.getAttributes().put(CACHED_ORIGINAL_REQUEST_BODY_BACKUP_ATTR, previousCachedBody);
|
||||
}
|
||||
}).then(Mono.defer(() -> {
|
||||
ServerHttpRequest cachedRequest = exchange
|
||||
.getAttribute(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR);
|
||||
.getAttribute(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR);
|
||||
Assert.notNull(cachedRequest, "cache request shouldn't be null");
|
||||
exchange.getAttributes().remove(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR);
|
||||
return chain.filter(exchange.mutate().request(cachedRequest).build()).doFinally(s -> {
|
||||
//
|
||||
Object backupCachedBody = exchange.getAttributes()
|
||||
.get(CACHED_ORIGINAL_REQUEST_BODY_BACKUP_ATTR);
|
||||
.get(CACHED_ORIGINAL_REQUEST_BODY_BACKUP_ATTR);
|
||||
if (backupCachedBody instanceof DataBuffer dataBuffer) {
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
}
|
||||
@@ -105,7 +105,8 @@ public class CacheRequestBodyGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(CacheRequestBodyGatewayFilterFactory.this)
|
||||
.append("Body class", config.getBodyClass()).toString();
|
||||
.append("Body class", config.getBodyClass())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,13 +93,14 @@ public class DedupeResponseHeaderGatewayFilterFactory
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return chain.filter(exchange)
|
||||
.then(Mono.fromRunnable(() -> dedupe(exchange.getResponse().getHeaders(), config)));
|
||||
.then(Mono.fromRunnable(() -> dedupe(exchange.getResponse().getHeaders(), config)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(DedupeResponseHeaderGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getStrategy()).toString();
|
||||
.append(config.getName(), config.getStrategy())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
|
||||
ServerWebExchangeUtils.setAlreadyRouted(exchange);
|
||||
return modifiedResponse.writeWith(exchange.getRequest().getBody())
|
||||
.then(chain.filter(exchange.mutate().response(modifiedResponse).build()));
|
||||
.then(chain.filter(exchange.mutate().response(modifiedResponse).build()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,7 +190,7 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
Resource protoFile = resourceLoader.getResource(config.getProtoFile());
|
||||
|
||||
descriptor = DescriptorProtos.FileDescriptorProto.parseFrom(descriptorFile.getInputStream())
|
||||
.getDescriptorForType();
|
||||
.getDescriptorForType();
|
||||
|
||||
Descriptors.MethodDescriptor methodDescriptor = getMethodDescriptor(config,
|
||||
descriptorFile.getInputStream());
|
||||
@@ -218,19 +218,25 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
|
||||
exchange.getResponse().getHeaders().set("Content-Type", "application/json");
|
||||
|
||||
return getDelegate().writeWith(deserializeJSONRequest().map(callGRPCServer()).map(serialiseGRPCResponse())
|
||||
.map(wrapGRPCResponse()).cast(DataBuffer.class).last());
|
||||
return getDelegate().writeWith(deserializeJSONRequest().map(callGRPCServer())
|
||||
.map(serialiseGRPCResponse())
|
||||
.map(wrapGRPCResponse())
|
||||
.cast(DataBuffer.class)
|
||||
.last());
|
||||
}
|
||||
|
||||
private ClientCall<DynamicMessage, DynamicMessage> createClientCallForType(Config config,
|
||||
Descriptors.ServiceDescriptor serviceDescriptor, Descriptors.Descriptor outputType) {
|
||||
MethodDescriptor.Marshaller<DynamicMessage> marshaller = ProtoUtils
|
||||
.marshaller(DynamicMessage.newBuilder(outputType).build());
|
||||
.marshaller(DynamicMessage.newBuilder(outputType).build());
|
||||
MethodDescriptor<DynamicMessage, DynamicMessage> methodDescriptor = MethodDescriptor
|
||||
.<DynamicMessage, DynamicMessage>newBuilder().setType(MethodDescriptor.MethodType.UNKNOWN)
|
||||
.setFullMethodName(MethodDescriptor.generateFullMethodName(serviceDescriptor.getFullName(),
|
||||
config.getMethod()))
|
||||
.setRequestMarshaller(marshaller).setResponseMarshaller(marshaller).build();
|
||||
.<DynamicMessage, DynamicMessage>newBuilder()
|
||||
.setType(MethodDescriptor.MethodType.UNKNOWN)
|
||||
.setFullMethodName(
|
||||
MethodDescriptor.generateFullMethodName(serviceDescriptor.getFullName(), config.getMethod()))
|
||||
.setRequestMarshaller(marshaller)
|
||||
.setResponseMarshaller(marshaller)
|
||||
.build();
|
||||
Channel channel = createChannel();
|
||||
return channel.newCall(methodDescriptor, CallOptions.DEFAULT);
|
||||
}
|
||||
@@ -238,7 +244,7 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
private Descriptors.MethodDescriptor getMethodDescriptor(Config config, InputStream descriptorFile)
|
||||
throws IOException, Descriptors.DescriptorValidationException {
|
||||
DescriptorProtos.FileDescriptorSet fileDescriptorSet = DescriptorProtos.FileDescriptorSet
|
||||
.parseFrom(descriptorFile);
|
||||
.parseFrom(descriptorFile);
|
||||
DescriptorProtos.FileDescriptorProto fileProto = fileDescriptorSet.getFile(0);
|
||||
Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fileProto,
|
||||
new Descriptors.FileDescriptor[0]);
|
||||
@@ -250,8 +256,10 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
|
||||
List<Descriptors.MethodDescriptor> methods = serviceDescriptor.getMethods();
|
||||
|
||||
return methods.stream().filter(method -> method.getName().equals(config.getMethod())).findFirst()
|
||||
.orElseThrow(() -> new NoSuchElementException("No Method found"));
|
||||
return methods.stream()
|
||||
.filter(method -> method.getName().equals(config.getMethod()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NoSuchElementException("No Method found"));
|
||||
}
|
||||
|
||||
private ManagedChannel createChannel() {
|
||||
@@ -296,7 +304,7 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
return jsonResponse -> {
|
||||
try {
|
||||
return new NettyDataBufferFactory(new PooledByteBufAllocator())
|
||||
.wrap(Objects.requireNonNull(new ObjectMapper().writeValueAsBytes(jsonResponse)));
|
||||
.wrap(Objects.requireNonNull(new ObjectMapper().writeValueAsBytes(jsonResponse)));
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
return new NettyDataBufferFactory(new PooledByteBufAllocator()).allocateBuffer();
|
||||
|
||||
@@ -64,8 +64,10 @@ public class MapRequestHeaderGatewayFilterFactory
|
||||
}
|
||||
List<String> headerValues = exchange.getRequest().getHeaders().get(config.getFromHeader());
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(i -> i.addAll(config.getToHeader(), headerValues)).build();
|
||||
ServerHttpRequest request = exchange.getRequest()
|
||||
.mutate()
|
||||
.headers(i -> i.addAll(config.getToHeader(), headerValues))
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class PrefixPathGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(PrefixPathGatewayFilterFactory.this).append("prefix", config.getPrefix())
|
||||
.toString();
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -102,8 +102,11 @@ public class RedirectToGatewayFilterFactory
|
||||
|
||||
String location;
|
||||
if (includeRequestParams) {
|
||||
location = UriComponentsBuilder.fromUri(uri).queryParams(exchange.getRequest().getQueryParams())
|
||||
.build().toUri().toString();
|
||||
location = UriComponentsBuilder.fromUri(uri)
|
||||
.queryParams(exchange.getRequest().getQueryParams())
|
||||
.build()
|
||||
.toUri()
|
||||
.toString();
|
||||
}
|
||||
else {
|
||||
location = uri.toString();
|
||||
@@ -126,7 +129,8 @@ public class RedirectToGatewayFilterFactory
|
||||
status = httpStatus.getStatus().toString();
|
||||
}
|
||||
return filterToStringCreator(RedirectToGatewayFilterFactory.this).append(status, uri)
|
||||
.append(INCLUDE_REQUEST_PARAMS_KEY, includeRequestParams).toString();
|
||||
.append(INCLUDE_REQUEST_PARAMS_KEY, includeRequestParams)
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,7 +139,8 @@ public class RemoveJsonAttributesResponseBodyGatewayFilterFactory extends
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("fieldList", fieldList)
|
||||
.append("deleteRecursively", deleteRecursively).toString();
|
||||
.append("deleteRecursively", deleteRecursively)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,8 +48,10 @@ public class RemoveRequestHeaderGatewayFilterFactory
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(httpHeaders -> httpHeaders.remove(config.getName())).build();
|
||||
ServerHttpRequest request = exchange.getRequest()
|
||||
.mutate()
|
||||
.headers(httpHeaders -> httpHeaders.remove(config.getName()))
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
@@ -57,7 +59,8 @@ public class RemoveRequestHeaderGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RemoveRequestHeaderGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
.append("name", config.getName())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -58,7 +58,9 @@ public class RemoveRequestParameterGatewayFilterFactory
|
||||
queryParams.remove(config.getName());
|
||||
|
||||
URI newUri = UriComponentsBuilder.fromUri(request.getURI())
|
||||
.replaceQueryParams(unmodifiableMultiValueMap(queryParams)).build().toUri();
|
||||
.replaceQueryParams(unmodifiableMultiValueMap(queryParams))
|
||||
.build()
|
||||
.toUri();
|
||||
|
||||
ServerHttpRequest updatedRequest = exchange.getRequest().mutate().uri(newUri).build();
|
||||
|
||||
@@ -68,7 +70,8 @@ public class RemoveRequestParameterGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RemoveRequestParameterGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
.append("name", config.getName())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,13 +48,14 @@ public class RemoveResponseHeaderGatewayFilterFactory
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return chain.filter(exchange)
|
||||
.then(Mono.fromRunnable(() -> exchange.getResponse().getHeaders().remove(config.getName())));
|
||||
.then(Mono.fromRunnable(() -> exchange.getResponse().getHeaders().remove(config.getName())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RemoveResponseHeaderGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
.append("name", config.getName())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -83,8 +83,9 @@ public class RequestHeaderSizeGatewayFilterFactory
|
||||
|
||||
if (!longHeaders.isEmpty()) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE);
|
||||
exchange.getResponse().getHeaders().add(errorHeaderName,
|
||||
getErrorMessage(longHeaders, config.getMaxSize()));
|
||||
exchange.getResponse()
|
||||
.getHeaders()
|
||||
.add(errorHeaderName, getErrorMessage(longHeaders, config.getMaxSize()));
|
||||
return exchange.getResponse().setComplete();
|
||||
|
||||
}
|
||||
@@ -95,7 +96,8 @@ public class RequestHeaderSizeGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RequestHeaderSizeGatewayFilterFactory.this)
|
||||
.append("maxSize", config.getMaxSize()).toString();
|
||||
.append("maxSize", config.getMaxSize())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -103,7 +105,7 @@ public class RequestHeaderSizeGatewayFilterFactory
|
||||
private static String getErrorMessage(HashMap<String, Long> longHeaders, DataSize maxSize) {
|
||||
StringBuilder msg = new StringBuilder(String.format(ERROR_PREFIX, maxSize));
|
||||
longHeaders
|
||||
.forEach((header, size) -> msg.append(String.format(ERROR, header, DataSize.of(size, DataUnit.BYTES))));
|
||||
.forEach((header, size) -> msg.append(String.format(ERROR, header, DataSize.of(size, DataUnit.BYTES))));
|
||||
return msg.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@ public class RequestHeaderToRequestUriGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RequestHeaderToRequestUriGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
.append("name", config.getName())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class RequestRateLimiterGatewayFilterFactory
|
||||
RateLimiter<Object> limiter = getOrDefault(config.rateLimiter, defaultRateLimiter);
|
||||
boolean denyEmpty = getOrDefault(config.denyEmptyKey, this.denyEmptyKey);
|
||||
HttpStatusHolder emptyKeyStatus = HttpStatusHolder
|
||||
.parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode));
|
||||
.parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode));
|
||||
|
||||
return (exchange, chain) -> resolver.resolve(exchange).defaultIfEmpty(EMPTY_KEY).flatMap(key -> {
|
||||
if (EMPTY_KEY.equals(key)) {
|
||||
|
||||
@@ -75,8 +75,10 @@ public class RequestSizeGatewayFilterFactory
|
||||
if (currentRequestSize > requestSizeConfig.getMaxSize().toBytes()) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
|
||||
if (!exchange.getResponse().isCommitted()) {
|
||||
exchange.getResponse().getHeaders().add("errorMessage",
|
||||
getErrorMessage(currentRequestSize, requestSizeConfig.getMaxSize().toBytes()));
|
||||
exchange.getResponse()
|
||||
.getHeaders()
|
||||
.add("errorMessage",
|
||||
getErrorMessage(currentRequestSize, requestSizeConfig.getMaxSize().toBytes()));
|
||||
}
|
||||
return exchange.getResponse().setComplete();
|
||||
}
|
||||
@@ -87,7 +89,8 @@ public class RequestSizeGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RequestSizeGatewayFilterFactory.this)
|
||||
.append("max", requestSizeConfig.getMaxSize()).toString();
|
||||
.append("max", requestSizeConfig.getMaxSize())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory<Retr
|
||||
};
|
||||
|
||||
statusCodeRepeat = Repeat.onlyIf(repeatPredicate)
|
||||
.doOnRepeat(context -> reset(context.applicationContext()));
|
||||
.doOnRepeat(context -> reset(context.applicationContext()));
|
||||
|
||||
BackoffConfig backoff = retryConfig.getBackoff();
|
||||
if (backoff != null) {
|
||||
@@ -157,7 +157,8 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory<Retr
|
||||
return false;
|
||||
};
|
||||
exceptionRetry = Retry.onlyIf(retryContextPredicate)
|
||||
.doOnRetry(context -> reset(context.applicationContext())).retryMax(retryConfig.getRetries());
|
||||
.doOnRetry(context -> reset(context.applicationContext()))
|
||||
.retryMax(retryConfig.getRetries());
|
||||
BackoffConfig backoff = retryConfig.getBackoff();
|
||||
if (backoff != null) {
|
||||
exceptionRetry = exceptionRetry.backoff(getBackoff(backoff));
|
||||
@@ -174,9 +175,12 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory<Retr
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RetryGatewayFilterFactory.this).append("routeId", retryConfig.getRouteId())
|
||||
.append("retries", retryConfig.getRetries()).append("series", retryConfig.getSeries())
|
||||
.append("statuses", retryConfig.getStatuses()).append("methods", retryConfig.getMethods())
|
||||
.append("exceptions", retryConfig.getExceptions()).toString();
|
||||
.append("retries", retryConfig.getRetries())
|
||||
.append("series", retryConfig.getSeries())
|
||||
.append("statuses", retryConfig.getStatuses())
|
||||
.append("methods", retryConfig.getMethods())
|
||||
.append("exceptions", retryConfig.getExceptions())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -234,14 +238,15 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory<Retr
|
||||
|
||||
// chain.filter returns a Mono<Void>
|
||||
Publisher<Void> publisher = chain.filter(exchange)
|
||||
// .log("retry-filter", Level.INFO)
|
||||
.doOnSuccess(aVoid -> updateIteration(exchange)).doOnError(throwable -> updateIteration(exchange));
|
||||
// .log("retry-filter", Level.INFO)
|
||||
.doOnSuccess(aVoid -> updateIteration(exchange))
|
||||
.doOnError(throwable -> updateIteration(exchange));
|
||||
|
||||
if (retry != null) {
|
||||
// retryWhen returns a Mono<Void>
|
||||
// retry needs to go before repeat
|
||||
publisher = ((Mono<Void>) publisher)
|
||||
.retryWhen(reactor.util.retry.Retry.withThrowable(retry.withApplicationContext(exchange)));
|
||||
.retryWhen(reactor.util.retry.Retry.withThrowable(retry.withApplicationContext(exchange)));
|
||||
}
|
||||
if (repeat != null) {
|
||||
// repeatWhen returns a Flux<Void>
|
||||
|
||||
@@ -79,7 +79,8 @@ public class RewritePathGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RewritePathGatewayFilterFactory.this)
|
||||
.append(config.getRegexp(), replacement).toString();
|
||||
.append(config.getRegexp(), replacement)
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ public class RewriteRequestParameterGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RewriteRequestParameterGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.replacement).toString();
|
||||
.append(config.getName(), config.replacement)
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,8 +65,10 @@ public class RewriteResponseHeaderGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RewriteResponseHeaderGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).append("regexp", config.getRegexp())
|
||||
.append("replacement", config.getReplacement()).toString();
|
||||
.append("name", config.getName())
|
||||
.append("regexp", config.getRegexp())
|
||||
.append("replacement", config.getReplacement())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class SetPathGatewayFilterFactory extends AbstractGatewayFilterFactory<Se
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SetPathGatewayFilterFactory.this).append("template", config.getTemplate())
|
||||
.toString();
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,8 +37,10 @@ public class SetRequestHeaderGatewayFilterFactory extends AbstractNameValueGatew
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(httpHeaders -> httpHeaders.set(config.name, value)).build();
|
||||
ServerHttpRequest request = exchange.getRequest()
|
||||
.mutate()
|
||||
.headers(httpHeaders -> httpHeaders.set(config.name, value))
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
@@ -46,7 +48,8 @@ public class SetRequestHeaderGatewayFilterFactory extends AbstractNameValueGatew
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SetRequestHeaderGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getValue()).toString();
|
||||
.append(config.getName(), config.getValue())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class SetRequestHostHeaderGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SetRequestHostHeaderGatewayFilterFactory.this).append(config.getHost())
|
||||
.toString();
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,13 +37,14 @@ public class SetResponseHeaderGatewayFilterFactory extends AbstractNameValueGate
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
|
||||
return chain.filter(exchange)
|
||||
.then(Mono.fromRunnable(() -> exchange.getResponse().getHeaders().set(config.name, value)));
|
||||
.then(Mono.fromRunnable(() -> exchange.getResponse().getHeaders().set(config.name, value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SetResponseHeaderGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getValue()).toString();
|
||||
.append(config.getName(), config.getValue())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -78,8 +78,9 @@ public class SetStatusGatewayFilterFactory extends AbstractGatewayFilterFactory<
|
||||
HttpStatusCode statusCode = exchange.getResponse().getStatusCode();
|
||||
boolean isStatusCodeUpdated = setResponseStatus(exchange, statusHolder);
|
||||
if (isStatusCodeUpdated && originalStatusHeaderName != null) {
|
||||
exchange.getResponse().getHeaders().set(originalStatusHeaderName,
|
||||
singletonList(statusCode.value()).toString());
|
||||
exchange.getResponse()
|
||||
.getHeaders()
|
||||
.set(originalStatusHeaderName, singletonList(statusCode.value()).toString());
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -87,7 +88,7 @@ public class SetStatusGatewayFilterFactory extends AbstractGatewayFilterFactory<
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SetStatusGatewayFilterFactory.this).append("status", config.getStatus())
|
||||
.toString();
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -90,9 +90,12 @@ public abstract class SpringCloudCircuitBreakerFilterFactory
|
||||
@Override
|
||||
public GatewayFilter apply(Config config) {
|
||||
ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(config.getId());
|
||||
Set<HttpStatus> statuses = config.getStatusCodes().stream().map(HttpStatusHolder::parse)
|
||||
.filter(statusHolder -> statusHolder.getHttpStatus() != null).map(HttpStatusHolder::getHttpStatus)
|
||||
.collect(Collectors.toSet());
|
||||
Set<HttpStatus> statuses = config.getStatusCodes()
|
||||
.stream()
|
||||
.map(HttpStatusHolder::parse)
|
||||
.filter(statusHolder -> statusHolder.getHttpStatus() != null)
|
||||
.map(HttpStatusHolder::getHttpStatus)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
@@ -118,8 +121,13 @@ public abstract class SpringCloudCircuitBreakerFilterFactory
|
||||
config.getFallbackUri().getPath());
|
||||
String fullFallbackUri = String.format("%s:%s", config.getFallbackUri().getScheme(),
|
||||
expandedFallbackUri);
|
||||
URI requestUrl = UriComponentsBuilder.fromUri(uri).host(null).port(null)
|
||||
.uri(URI.create(fullFallbackUri)).scheme(null).build(encoded).toUri();
|
||||
URI requestUrl = UriComponentsBuilder.fromUri(uri)
|
||||
.host(null)
|
||||
.port(null)
|
||||
.uri(URI.create(fullFallbackUri))
|
||||
.scheme(null)
|
||||
.build(encoded)
|
||||
.toUri();
|
||||
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
|
||||
addExceptionDetails(t, exchange);
|
||||
@@ -135,7 +143,9 @@ public abstract class SpringCloudCircuitBreakerFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SpringCloudCircuitBreakerFilterFactory.this)
|
||||
.append("name", config.getName()).append("fallback", config.fallbackUri).toString();
|
||||
.append("name", config.getName())
|
||||
.append("fallback", config.fallbackUri)
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public class StripPrefixGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(StripPrefixGatewayFilterFactory.this).append("parts", config.getParts())
|
||||
.toString();
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -58,13 +58,16 @@ public class TokenRelayGatewayFilterFactory
|
||||
public GatewayFilter apply(NameConfig config) {
|
||||
String defaultClientRegistrationId = (config == null) ? null : config.getName();
|
||||
return (exchange, chain) -> exchange.getPrincipal()
|
||||
// .log("token-relay-filter")
|
||||
.filter(principal -> principal instanceof Authentication).cast(Authentication.class)
|
||||
.flatMap(principal -> authorizationRequest(defaultClientRegistrationId, principal))
|
||||
.flatMap(this::authorizedClient).map(OAuth2AuthorizedClient::getAccessToken)
|
||||
.map(token -> withBearerAuth(exchange, token))
|
||||
// TODO: adjustable behavior if empty
|
||||
.defaultIfEmpty(exchange).flatMap(chain::filter);
|
||||
// .log("token-relay-filter")
|
||||
.filter(principal -> principal instanceof Authentication)
|
||||
.cast(Authentication.class)
|
||||
.flatMap(principal -> authorizationRequest(defaultClientRegistrationId, principal))
|
||||
.flatMap(this::authorizedClient)
|
||||
.map(OAuth2AuthorizedClient::getAccessToken)
|
||||
.map(token -> withBearerAuth(exchange, token))
|
||||
// TODO: adjustable behavior if empty
|
||||
.defaultIfEmpty(exchange)
|
||||
.flatMap(chain::filter);
|
||||
}
|
||||
|
||||
private Mono<OAuth2AuthorizeRequest> authorizationRequest(String defaultClientRegistrationId,
|
||||
@@ -73,8 +76,9 @@ public class TokenRelayGatewayFilterFactory
|
||||
if (clientRegistrationId == null && principal instanceof OAuth2AuthenticationToken) {
|
||||
clientRegistrationId = ((OAuth2AuthenticationToken) principal).getAuthorizedClientRegistrationId();
|
||||
}
|
||||
return Mono.justOrEmpty(clientRegistrationId).map(OAuth2AuthorizeRequest::withClientRegistrationId)
|
||||
.map(builder -> builder.principal(principal).build());
|
||||
return Mono.justOrEmpty(clientRegistrationId)
|
||||
.map(OAuth2AuthorizeRequest::withClientRegistrationId)
|
||||
.map(builder -> builder.principal(principal).build());
|
||||
}
|
||||
|
||||
private Mono<OAuth2AuthorizedClient> authorizedClient(OAuth2AuthorizeRequest request) {
|
||||
@@ -89,8 +93,9 @@ public class TokenRelayGatewayFilterFactory
|
||||
}
|
||||
|
||||
private ServerWebExchange withBearerAuth(ServerWebExchange exchange, OAuth2AccessToken accessToken) {
|
||||
return exchange.mutate().request(r -> r.headers(headers -> headers.setBearerAuth(accessToken.getTokenValue())))
|
||||
.build();
|
||||
return exchange.mutate()
|
||||
.request(r -> r.headers(headers -> headers.setBearerAuth(accessToken.getTokenValue())))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class ResponseCacheGatewayFilter implements GatewayFilter, Ordered {
|
||||
}
|
||||
else {
|
||||
return chain
|
||||
.filter(exchange.mutate().response(new CachingResponseDecorator(metadataKey, exchange)).build());
|
||||
.filter(exchange.mutate().response(new CachingResponseDecorator(metadataKey, exchange)).build());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ public class ResponseCacheManager {
|
||||
final CachedResponseMetadata metadata = new CachedResponseMetadata(response.getHeaders().getVary());
|
||||
final String key = resolveKey(exchange, metadata.varyOnHeaders());
|
||||
CachedResponse.Builder cachedResponseBuilder = CachedResponse.create(response.getStatusCode())
|
||||
.headers(response.getHeaders());
|
||||
.headers(response.getHeaders());
|
||||
CachedResponse toProcess = cachedResponseBuilder.build();
|
||||
afterCacheExchangeMutators.forEach(processor -> processor.accept(exchange, toProcess));
|
||||
|
||||
@@ -152,7 +152,7 @@ public class ResponseCacheManager {
|
||||
saveMetadataInCache(metadataKey, new CachedResponseMetadata(cachedResponse.headers().getVary()));
|
||||
|
||||
return response
|
||||
.writeWith(Flux.fromIterable(cachedResponse.body()).map(data -> response.bufferFactory().wrap(data)));
|
||||
.writeWith(Flux.fromIterable(cachedResponse.body()).map(data -> response.bufferFactory().wrap(data)));
|
||||
}
|
||||
|
||||
private CachedResponseMetadata retrieveMetadata(String metadataKey) {
|
||||
|
||||
@@ -41,9 +41,12 @@ class CookiesKeyValueGenerator implements KeyValueGenerator {
|
||||
String cookiesData = null;
|
||||
MultiValueMap<String, HttpCookie> cookies = request.getCookies();
|
||||
if (!CollectionUtils.isEmpty(cookies)) {
|
||||
cookiesData = cookies.values().stream().flatMap(Collection::stream)
|
||||
.map(c -> String.format("%s=%s", c.getName(), c.getValue())).sorted()
|
||||
.collect(Collectors.joining(valueSeparator));
|
||||
cookiesData = cookies.values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.map(c -> String.format("%s=%s", c.getName(), c.getValue()))
|
||||
.sorted()
|
||||
.collect(Collectors.joining(valueSeparator));
|
||||
}
|
||||
return cookiesData;
|
||||
}
|
||||
|
||||
@@ -47,8 +47,9 @@ class HeaderKeyValueGenerator implements KeyValueGenerator {
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
if (headers.get(header) != null) {
|
||||
StringBuilder keyVaryHeaders = new StringBuilder();
|
||||
keyVaryHeaders.append(header).append("=")
|
||||
.append(getHeaderValues(headers).sorted().collect(Collectors.joining(valueSeparator)));
|
||||
keyVaryHeaders.append(header)
|
||||
.append("=")
|
||||
.append(getHeaderValues(headers).sorted().collect(Collectors.joining(valueSeparator)));
|
||||
return keyVaryHeaders.toString();
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -33,8 +33,10 @@ public class SetCacheDirectivesByMaxAgeAfterCacheExchangeMutator implements Afte
|
||||
@Override
|
||||
public void accept(ServerWebExchange exchange, CachedResponse cachedResponse) {
|
||||
Optional<Integer> maxAge = Optional.ofNullable(exchange.getResponse().getHeaders().getCacheControl())
|
||||
.map(MAX_AGE_PATTERN::matcher).filter(Matcher::find).map(matcher -> matcher.group(1))
|
||||
.map(Integer::parseInt);
|
||||
.map(MAX_AGE_PATTERN::matcher)
|
||||
.filter(Matcher::find)
|
||||
.map(matcher -> matcher.group(1))
|
||||
.map(Integer::parseInt);
|
||||
|
||||
if (maxAge.isPresent()) {
|
||||
if (maxAge.get() > 0) {
|
||||
@@ -66,7 +68,8 @@ public class SetCacheDirectivesByMaxAgeAfterCacheExchangeMutator implements Afte
|
||||
List<String> cacheControlValues = Arrays.asList(cacheControl.split("\\s*,\\s*"));
|
||||
|
||||
String newCacheControl = cacheControlValues.stream()
|
||||
.filter(s -> !s.matches("must-revalidate|no-cache|no-store")).collect(Collectors.joining(","));
|
||||
.filter(s -> !s.matches("must-revalidate|no-cache|no-store"))
|
||||
.collect(Collectors.joining(","));
|
||||
exchange.getResponse().getHeaders().setCacheControl(newCacheControl);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,8 @@ public class SetMaxAgeHeaderAfterCacheExchangeMutator implements AfterCacheExcha
|
||||
if (value.contains(MAX_AGE_PREFIX)) {
|
||||
if (seconds == -1) {
|
||||
List<String> removedMaxAgeList = Arrays.stream(value.split(","))
|
||||
.filter(i -> !i.trim().startsWith(MAX_AGE_PREFIX)).collect(Collectors.toList());
|
||||
.filter(i -> !i.trim().startsWith(MAX_AGE_PREFIX))
|
||||
.collect(Collectors.toList());
|
||||
value = String.join(",", removedMaxAgeList);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -78,7 +78,7 @@ public class CachedBodyOutputMessage implements ReactiveHttpOutputMessage {
|
||||
public Flux<DataBuffer> getBody() {
|
||||
if (body == null) {
|
||||
return Flux
|
||||
.error(new IllegalStateException("The body is not set. " + "Did handling complete with success?"));
|
||||
.error(new IllegalStateException("The body is not set. " + "Did handling complete with success?"));
|
||||
}
|
||||
return this.body;
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ public class ModifyRequestBodyGatewayFilterFactory
|
||||
|
||||
// TODO: flux or mono
|
||||
Mono<?> modifiedBody = serverRequest.bodyToMono(inClass)
|
||||
.flatMap(originalBody -> config.getRewriteFunction().apply(exchange, originalBody))
|
||||
.switchIfEmpty(Mono.defer(() -> (Mono) config.getRewriteFunction().apply(exchange, null)));
|
||||
.flatMap(originalBody -> config.getRewriteFunction().apply(exchange, originalBody))
|
||||
.switchIfEmpty(Mono.defer(() -> (Mono) config.getRewriteFunction().apply(exchange, null)));
|
||||
|
||||
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, config.getOutClass());
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
@@ -88,19 +88,22 @@ public class ModifyRequestBodyGatewayFilterFactory
|
||||
}
|
||||
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
|
||||
return bodyInserter.insert(outputMessage, new BodyInserterContext())
|
||||
// .log("modify_request", Level.INFO)
|
||||
.then(Mono.defer(() -> {
|
||||
ServerHttpRequest decorator = decorate(exchange, headers, outputMessage);
|
||||
return chain.filter(exchange.mutate().request(decorator).build());
|
||||
})).onErrorResume((Function<Throwable, Mono<Void>>) throwable -> release(exchange,
|
||||
outputMessage, throwable));
|
||||
// .log("modify_request", Level.INFO)
|
||||
.then(Mono.defer(() -> {
|
||||
ServerHttpRequest decorator = decorate(exchange, headers, outputMessage);
|
||||
return chain.filter(exchange.mutate().request(decorator).build());
|
||||
}))
|
||||
.onErrorResume(
|
||||
(Function<Throwable, Mono<Void>>) throwable -> release(exchange, outputMessage, throwable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(ModifyRequestBodyGatewayFilterFactory.this)
|
||||
.append("Content type", config.getContentType()).append("In class", config.getInClass())
|
||||
.append("Out class", config.getOutClass()).toString();
|
||||
.append("Content type", config.getContentType())
|
||||
.append("In class", config.getInClass())
|
||||
.append("Out class", config.getOutClass())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -67,9 +67,9 @@ public class ModifyResponseBodyGatewayFilterFactory
|
||||
super(Config.class);
|
||||
this.messageReaders = messageReaders;
|
||||
this.messageBodyDecoders = messageBodyDecoders.stream()
|
||||
.collect(Collectors.toMap(MessageBodyDecoder::encodingType, identity()));
|
||||
.collect(Collectors.toMap(MessageBodyDecoder::encodingType, identity()));
|
||||
this.messageBodyEncoders = messageBodyEncoders.stream()
|
||||
.collect(Collectors.toMap(MessageBodyEncoder::encodingType, identity()));
|
||||
.collect(Collectors.toMap(MessageBodyEncoder::encodingType, identity()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -181,7 +181,9 @@ public class ModifyResponseBodyGatewayFilterFactory
|
||||
public String toString() {
|
||||
Object obj = (this.gatewayFilterFactory != null) ? this.gatewayFilterFactory : this;
|
||||
return filterToStringCreator(obj).append("New content type", config.getNewContentType())
|
||||
.append("In class", config.getInClass()).append("Out class", config.getOutClass()).toString();
|
||||
.append("In class", config.getInClass())
|
||||
.append("Out class", config.getOutClass())
|
||||
.toString();
|
||||
}
|
||||
|
||||
public void setFactory(GatewayFilterFactory<Config> gatewayFilterFactory) {
|
||||
@@ -221,8 +223,8 @@ public class ModifyResponseBodyGatewayFilterFactory
|
||||
|
||||
// TODO: flux or mono
|
||||
Mono modifiedBody = extractBody(exchange, clientResponse, inClass)
|
||||
.flatMap(originalBody -> config.getRewriteFunction().apply(exchange, originalBody))
|
||||
.switchIfEmpty(Mono.defer(() -> (Mono) config.getRewriteFunction().apply(exchange, null)));
|
||||
.flatMap(originalBody -> config.getRewriteFunction().apply(exchange, originalBody))
|
||||
.switchIfEmpty(Mono.defer(() -> (Mono) config.getRewriteFunction().apply(exchange, null)));
|
||||
|
||||
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);
|
||||
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange,
|
||||
@@ -266,11 +268,12 @@ public class ModifyResponseBodyGatewayFilterFactory
|
||||
for (String encoding : encodingHeaders) {
|
||||
MessageBodyDecoder decoder = messageBodyDecoders.get(encoding);
|
||||
if (decoder != null) {
|
||||
return clientResponse.bodyToMono(byte[].class).publishOn(Schedulers.parallel()).map(decoder::decode)
|
||||
.map(bytes -> exchange.getResponse().bufferFactory().wrap(bytes))
|
||||
.map(buffer -> prepareClientResponse(Mono.just(buffer),
|
||||
exchange.getResponse().getHeaders()))
|
||||
.flatMap(response -> response.bodyToMono(inClass));
|
||||
return clientResponse.bodyToMono(byte[].class)
|
||||
.publishOn(Schedulers.parallel())
|
||||
.map(decoder::decode)
|
||||
.map(bytes -> exchange.getResponse().bufferFactory().wrap(bytes))
|
||||
.map(buffer -> prepareClientResponse(Mono.just(buffer), exchange.getResponse().getHeaders()))
|
||||
.flatMap(response -> response.bodyToMono(inClass));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@ public class DefaultGatewayObservationConvention implements GatewayObservationCo
|
||||
}
|
||||
Route route = context.getServerWebExchange().getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
|
||||
keyValues = keyValues
|
||||
.and(ROUTE_URI.withValue(route.getUri().toString()),
|
||||
METHOD.withValue(context.getRequest().getMethod().name()))
|
||||
.and(ROUTE_ID.withValue(route.getId()));
|
||||
.and(ROUTE_URI.withValue(route.getUri().toString()),
|
||||
METHOD.withValue(context.getRequest().getMethod().name()))
|
||||
.and(ROUTE_ID.withValue(route.getId()));
|
||||
ServerHttpResponse response = context.getResponse();
|
||||
if (response != null && response.getStatusCode() != null) {
|
||||
keyValues = keyValues.and(STATUS.withValue(String.valueOf(response.getStatusCode().value())));
|
||||
|
||||
@@ -53,9 +53,10 @@ public class GatewayPropagatingSenderTracingObservationHandler
|
||||
|
||||
@Override
|
||||
public void onStart(GatewayContext context) {
|
||||
this.propagator.fields().stream()
|
||||
.filter(field -> !remoteFieldsLowerCase.contains(field.toLowerCase(Locale.ROOT)))
|
||||
.forEach(s -> Objects.requireNonNull(context.getCarrier()).remove(s));
|
||||
this.propagator.fields()
|
||||
.stream()
|
||||
.filter(field -> !remoteFieldsLowerCase.contains(field.toLowerCase(Locale.ROOT)))
|
||||
.forEach(s -> Objects.requireNonNull(context.getCarrier()).remove(s));
|
||||
super.onStart(context);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,8 +58,10 @@ public abstract class AbstractRateLimiter<C> extends AbstractStatefulConfigurabl
|
||||
|
||||
C routeConfig = newConfig();
|
||||
if (this.configurationService != null) {
|
||||
this.configurationService.with(routeConfig).name(this.configurationPropertyName).normalizedProperties(args)
|
||||
.bind();
|
||||
this.configurationService.with(routeConfig)
|
||||
.name(this.configurationPropertyName)
|
||||
.normalizedProperties(args)
|
||||
.bind();
|
||||
}
|
||||
getConfig().put(routeId, routeConfig);
|
||||
}
|
||||
@@ -71,7 +73,9 @@ public abstract class AbstractRateLimiter<C> extends AbstractStatefulConfigurabl
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("configurationPropertyName", configurationPropertyName)
|
||||
.append("config", getConfig()).append("configClass", getConfigClass()).toString();
|
||||
.append("config", getConfig())
|
||||
.append("configClass", getConfigClass())
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -351,7 +351,9 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("replenishRate", replenishRate)
|
||||
.append("burstCapacity", burstCapacity).append("requestedTokens", requestedTokens).toString();
|
||||
.append("burstCapacity", burstCapacity)
|
||||
.append("requestedTokens", requestedTokens)
|
||||
.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -87,21 +87,22 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
return Mono.deferContextual(contextView -> {
|
||||
exchange.getAttributes().put(GATEWAY_REACTOR_CONTEXT_ATTR, contextView);
|
||||
return lookupRoute(exchange)
|
||||
// .log("route-predicate-handler-mapping", Level.FINER) //name this
|
||||
.map((Function<Route, ?>) r -> {
|
||||
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);
|
||||
}
|
||||
// .log("route-predicate-handler-mapping", Level.FINER) //name this
|
||||
.map((Function<Route, ?>) r -> {
|
||||
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);
|
||||
}
|
||||
|
||||
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
|
||||
return webHandler;
|
||||
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
|
||||
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");
|
||||
}
|
||||
})));
|
||||
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
|
||||
return webHandler;
|
||||
})
|
||||
.switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
|
||||
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");
|
||||
}
|
||||
})));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -126,29 +127,29 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
|
||||
return this.routeLocator.getRoutes()
|
||||
// individually filter routes so that filterWhen error delaying is not a
|
||||
// problem
|
||||
.concatMap(route -> Mono.just(route).filterWhen(r -> {
|
||||
// add the current route we are testing
|
||||
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
|
||||
return r.getPredicate().apply(exchange);
|
||||
})
|
||||
// instead of immediately stopping main flux due to error, log and
|
||||
// swallow it
|
||||
.doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e))
|
||||
.onErrorResume(e -> Mono.empty()))
|
||||
// .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("Route matched: " + route.getId());
|
||||
}
|
||||
validateRoute(route, exchange);
|
||||
return route;
|
||||
});
|
||||
// individually filter routes so that filterWhen error delaying is not a
|
||||
// problem
|
||||
.concatMap(route -> Mono.just(route).filterWhen(r -> {
|
||||
// add the current route we are testing
|
||||
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
|
||||
return r.getPredicate().apply(exchange);
|
||||
})
|
||||
// instead of immediately stopping main flux due to error, log and
|
||||
// swallow it
|
||||
.doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e))
|
||||
.onErrorResume(e -> Mono.empty()))
|
||||
// .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("Route matched: " + route.getId());
|
||||
}
|
||||
validateRoute(route, exchange);
|
||||
return route;
|
||||
});
|
||||
|
||||
/*
|
||||
* TODO: trace logging if (logger.isTraceEnabled()) {
|
||||
|
||||
@@ -54,7 +54,7 @@ public class CloudFoundryRouteServiceRoutePredicateFactory extends AbstractRoute
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Object unused) {
|
||||
return headerPredicate(X_CF_FORWARDED_URL).and(headerPredicate(X_CF_PROXY_SIGNATURE))
|
||||
.and(headerPredicate(X_CF_PROXY_METADATA));
|
||||
.and(headerPredicate(X_CF_PROXY_METADATA));
|
||||
}
|
||||
|
||||
private Predicate<ServerWebExchange> headerPredicate(String header) {
|
||||
|
||||
@@ -59,8 +59,9 @@ public class HeaderRoutePredicateFactory extends AbstractRoutePredicateFactory<H
|
||||
return new GatewayPredicate() {
|
||||
@Override
|
||||
public boolean test(ServerWebExchange exchange) {
|
||||
List<String> values = exchange.getRequest().getHeaders().getOrDefault(config.header,
|
||||
Collections.emptyList());
|
||||
List<String> values = exchange.getRequest()
|
||||
.getHeaders()
|
||||
.getOrDefault(config.header, Collections.emptyList());
|
||||
if (values.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -90,9 +90,9 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<Pat
|
||||
return new GatewayPredicate() {
|
||||
@Override
|
||||
public boolean test(ServerWebExchange exchange) {
|
||||
PathContainer path = (PathContainer) exchange.getAttributes().computeIfAbsent(
|
||||
GATEWAY_PREDICATE_PATH_CONTAINER_ATTR,
|
||||
s -> parsePath(exchange.getRequest().getURI().getRawPath()));
|
||||
PathContainer path = (PathContainer) exchange.getAttributes()
|
||||
.computeIfAbsent(GATEWAY_PREDICATE_PATH_CONTAINER_ATTR,
|
||||
s -> parsePath(exchange.getRequest().getURI().getRawPath()));
|
||||
|
||||
PathPattern match = null;
|
||||
for (int i = 0; i < pathPatterns.size(); i++) {
|
||||
@@ -179,7 +179,8 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<Pat
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("patterns", patterns)
|
||||
.append(MATCH_TRAILING_SLASH, matchTrailingSlash).toString();
|
||||
.append(MATCH_TRAILING_SLASH, matchTrailingSlash)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -90,10 +90,11 @@ public class ReadBodyRoutePredicateFactory extends AbstractRoutePredicateFactory
|
||||
else {
|
||||
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
|
||||
(serverHttpRequest) -> ServerRequest
|
||||
.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders)
|
||||
.bodyToMono(inClass).doOnNext(objectValue -> exchange.getAttributes()
|
||||
.put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
|
||||
.map(objectValue -> config.getPredicate().test(objectValue)));
|
||||
.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders)
|
||||
.bodyToMono(inClass)
|
||||
.doOnNext(objectValue -> exchange.getAttributes()
|
||||
.put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
|
||||
.map(objectValue -> config.getPredicate().test(objectValue)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ public class XForwardedRemoteAddrRoutePredicateFactory
|
||||
RemoteAddrRoutePredicateFactory.Config wrappedConfig = new RemoteAddrRoutePredicateFactory.Config();
|
||||
wrappedConfig.setSources(config.getSources());
|
||||
wrappedConfig
|
||||
.setRemoteAddressResolver(XForwardedRemoteAddressResolver.maxTrustedIndex(config.getMaxTrustedIndex()));
|
||||
.setRemoteAddressResolver(XForwardedRemoteAddressResolver.maxTrustedIndex(config.getMaxTrustedIndex()));
|
||||
RemoteAddrRoutePredicateFactory remoteAddrRoutePredicateFactory = new RemoteAddrRoutePredicateFactory();
|
||||
Predicate<ServerWebExchange> wrappedPredicate = remoteAddrRoutePredicateFactory.apply(wrappedConfig);
|
||||
|
||||
|
||||
@@ -86,11 +86,11 @@ public class CachingRouteLocator
|
||||
try {
|
||||
if (this.cache.containsKey(CACHE_KEY) && event.isScoped()) {
|
||||
final Mono<List<Route>> scopedRoutes = fetch(event.getMetadata()).collect(Collectors.toList())
|
||||
.onErrorResume(s -> Mono.just(List.of()));
|
||||
.onErrorResume(s -> Mono.just(List.of()));
|
||||
|
||||
scopedRoutes.subscribe(scopedRoutesList -> {
|
||||
updateCache(Flux.concat(Flux.fromIterable(scopedRoutesList), getNonScopedRoutes(event))
|
||||
.sort(AnnotationAwareOrderComparator.INSTANCE));
|
||||
.sort(AnnotationAwareOrderComparator.INSTANCE));
|
||||
}, this::handleRefreshError);
|
||||
}
|
||||
else {
|
||||
@@ -104,8 +104,9 @@ public class CachingRouteLocator
|
||||
}
|
||||
|
||||
private synchronized void updateCache(Flux<Route> routes) {
|
||||
routes.materialize().collect(Collectors.toList()).subscribe(this::publishRefreshEvent,
|
||||
this::handleRefreshError);
|
||||
routes.materialize()
|
||||
.collect(Collectors.toList())
|
||||
.subscribe(this::publishRefreshEvent, this::handleRefreshError);
|
||||
}
|
||||
|
||||
private void publishRefreshEvent(List<Signal<Route>> signals) {
|
||||
@@ -115,7 +116,7 @@ public class CachingRouteLocator
|
||||
|
||||
private Flux<Route> getNonScopedRoutes(RefreshRoutesEvent scopedEvent) {
|
||||
return this.getRoutes()
|
||||
.filter(route -> !RouteLocator.matchMetadata(route.getMetadata(), scopedEvent.getMetadata()));
|
||||
.filter(route -> !RouteLocator.matchMetadata(route.getMetadata(), scopedEvent.getMetadata()));
|
||||
}
|
||||
|
||||
private void handleRefreshError(Throwable throwable) {
|
||||
|
||||
@@ -50,18 +50,18 @@ public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return this.delegates.flatMapSequential(RouteDefinitionLocator::getRouteDefinitions)
|
||||
.flatMap(routeDefinition -> {
|
||||
if (routeDefinition.getId() == null) {
|
||||
return randomId().map(id -> {
|
||||
routeDefinition.setId(id);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Id set on route definition: " + routeDefinition);
|
||||
}
|
||||
return routeDefinition;
|
||||
});
|
||||
}
|
||||
return Mono.just(routeDefinition);
|
||||
});
|
||||
.flatMap(routeDefinition -> {
|
||||
if (routeDefinition.getId() == null) {
|
||||
return randomId().map(id -> {
|
||||
routeDefinition.setId(id);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Id set on route definition: " + routeDefinition);
|
||||
}
|
||||
return routeDefinition;
|
||||
});
|
||||
}
|
||||
return Mono.just(routeDefinition);
|
||||
});
|
||||
}
|
||||
|
||||
protected Mono<String> randomId() {
|
||||
|
||||
@@ -53,24 +53,25 @@ public class RedisRouteDefinitionRepository implements RouteDefinitionRepository
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return reactiveRedisTemplate.scan(ScanOptions.scanOptions().match(createKey("*")).build())
|
||||
.flatMap(key -> reactiveRedisTemplate.opsForValue().get(key))
|
||||
.onErrorContinue((throwable, routeDefinition) -> {
|
||||
if (log.isErrorEnabled()) {
|
||||
log.error("get routes from redis error cause : {}", throwable.toString(), throwable);
|
||||
}
|
||||
});
|
||||
.flatMap(key -> reactiveRedisTemplate.opsForValue().get(key))
|
||||
.onErrorContinue((throwable, routeDefinition) -> {
|
||||
if (log.isErrorEnabled()) {
|
||||
log.error("get routes from redis error cause : {}", throwable.toString(), throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> save(Mono<RouteDefinition> route) {
|
||||
return route.flatMap(routeDefinition -> routeDefinitionReactiveValueOperations
|
||||
.set(createKey(routeDefinition.getId()), routeDefinition).flatMap(success -> {
|
||||
if (success) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return Mono.defer(() -> Mono.error(new RuntimeException(
|
||||
String.format("Could not add route to redis repository: %s", routeDefinition))));
|
||||
}));
|
||||
.set(createKey(routeDefinition.getId()), routeDefinition)
|
||||
.flatMap(success -> {
|
||||
if (success) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return Mono.defer(() -> Mono.error(new RuntimeException(
|
||||
String.format("Could not add route to redis repository: %s", routeDefinition))));
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -98,7 +98,7 @@ public class RouteDefinitionRouteLocator implements RouteLocator {
|
||||
@Override
|
||||
public Flux<Route> getRoutesByMetadata(Map<String, Object> metadata) {
|
||||
return getRoutes(this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.filter(routeDef -> RouteLocator.matchMetadata(routeDef.getMetadata(), metadata)));
|
||||
.filter(routeDef -> RouteLocator.matchMetadata(routeDef.getMetadata(), metadata)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -43,9 +43,10 @@ public interface RouteLocator {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return toCheck != null
|
||||
&& expectedMetadata.entrySet().stream().allMatch(keyValue -> toCheck.containsKey(keyValue.getKey())
|
||||
&& toCheck.get(keyValue.getKey()).equals(keyValue.getValue()));
|
||||
return toCheck != null && expectedMetadata.entrySet()
|
||||
.stream()
|
||||
.allMatch(keyValue -> toCheck.containsKey(keyValue.getKey())
|
||||
&& toCheck.get(keyValue.getKey()).equals(keyValue.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec addRequestHeader(String headerName, String headerValue) {
|
||||
return filter(getBean(AddRequestHeaderGatewayFilterFactory.class)
|
||||
.apply(c -> c.setName(headerName).setValue(headerValue)));
|
||||
.apply(c -> c.setName(headerName).setValue(headerValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,8 +195,10 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec addRequestHeadersIfNotPresent(String... headers) {
|
||||
return filter(getBean(AddRequestHeadersIfNotPresentGatewayFilterFactory.class).apply(c -> {
|
||||
KeyValue[] values = Arrays.stream(headers).map(header -> header.split(":"))
|
||||
.map(parts -> new KeyValue(parts[0], parts[1])).toArray(size -> new KeyValue[size]);
|
||||
KeyValue[] values = Arrays.stream(headers)
|
||||
.map(header -> header.split(":"))
|
||||
.map(parts -> new KeyValue(parts[0], parts[1]))
|
||||
.toArray(size -> new KeyValue[size]);
|
||||
c.setKeyValues(values);
|
||||
}));
|
||||
}
|
||||
@@ -220,7 +222,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec addResponseHeader(String headerName, String headerValue) {
|
||||
return filter(getBean(AddResponseHeaderGatewayFilterFactory.class)
|
||||
.apply(c -> c.setName(headerName).setValue(headerValue)));
|
||||
.apply(c -> c.setName(headerName).setValue(headerValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,7 +238,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec localResponseCache(Duration timeToLive, DataSize size) {
|
||||
return filter(getBean(LocalResponseCacheGatewayFilterFactory.class)
|
||||
.apply(c -> c.setTimeToLive(timeToLive).setSize(size)));
|
||||
.apply(c -> c.setTimeToLive(timeToLive).setSize(size)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,7 +250,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec dedupeResponseHeader(String headerName, String strategy) {
|
||||
return filter(getBean(DedupeResponseHeaderGatewayFilterFactory.class)
|
||||
.apply(c -> c.setStrategy(Strategy.valueOf(strategy)).setName(headerName)));
|
||||
.apply(c -> c.setStrategy(Strategy.valueOf(strategy)).setName(headerName)));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec circuitBreaker(Consumer<SpringCloudCircuitBreakerFilterFactory.Config> configConsumer) {
|
||||
@@ -272,7 +274,9 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec jsonToGRPC(String protoDescriptor, String protoFile, String service, String method) {
|
||||
return filter(getBean(JsonToGrpcGatewayFilterFactory.class).apply(c -> c.setMethod(method)
|
||||
.setProtoDescriptor(protoDescriptor).setProtoFile(protoFile).setService(service)));
|
||||
.setProtoDescriptor(protoDescriptor)
|
||||
.setProtoFile(protoFile)
|
||||
.setService(service)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,7 +287,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec mapRequestHeader(String fromHeader, String toHeader) {
|
||||
return filter(getBean(MapRequestHeaderGatewayFilterFactory.class)
|
||||
.apply(c -> c.setFromHeader(fromHeader).setToHeader(toHeader)));
|
||||
.apply(c -> c.setFromHeader(fromHeader).setToHeader(toHeader)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,7 +303,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
public <T, R> GatewayFilterSpec modifyRequestBody(Class<T> inClass, Class<R> outClass,
|
||||
RewriteFunction<T, R> rewriteFunction) {
|
||||
return filter(getBean(ModifyRequestBodyGatewayFilterFactory.class)
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction)));
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,7 +320,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
public <T, R> GatewayFilterSpec modifyRequestBody(ParameterizedTypeReference<T> inClass,
|
||||
ParameterizedTypeReference<R> outClass, RewriteFunction<T, R> rewriteFunction) {
|
||||
return filter(getBean(ModifyRequestBodyGatewayFilterFactory.class)
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction)));
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,7 +336,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
public <T, R> GatewayFilterSpec modifyRequestBody(Class<T> inClass, Class<R> outClass, String newContentType,
|
||||
RewriteFunction<T, R> rewriteFunction) {
|
||||
return filter(getBean(ModifyRequestBodyGatewayFilterFactory.class)
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction).setContentType(newContentType)));
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction).setContentType(newContentType)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,7 +354,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
public <T, R> GatewayFilterSpec modifyRequestBody(ParameterizedTypeReference<T> inClass,
|
||||
ParameterizedTypeReference<R> outClass, String newContentType, RewriteFunction<T, R> rewriteFunction) {
|
||||
return filter(getBean(ModifyRequestBodyGatewayFilterFactory.class)
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction).setContentType(newContentType)));
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction).setContentType(newContentType)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -400,7 +404,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
public <T, R> GatewayFilterSpec modifyResponseBody(Class<T> inClass, Class<R> outClass,
|
||||
RewriteFunction<T, R> rewriteFunction) {
|
||||
return filter(getBean(ModifyResponseBodyGatewayFilterFactory.class)
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction)));
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -418,8 +422,8 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
// TODO: setup custom spec
|
||||
public <T, R> GatewayFilterSpec modifyResponseBody(Class<T> inClass, Class<R> outClass, String newContentType,
|
||||
RewriteFunction<T, R> rewriteFunction) {
|
||||
return filter(getBean(ModifyResponseBodyGatewayFilterFactory.class).apply(
|
||||
c -> c.setRewriteFunction(inClass, outClass, rewriteFunction).setNewContentType(newContentType)));
|
||||
return filter(getBean(ModifyResponseBodyGatewayFilterFactory.class)
|
||||
.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction).setNewContentType(newContentType)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -603,7 +607,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec removeJsonAttributes(boolean deleteRecursively, String... attributes) {
|
||||
return filter(getBean(RemoveJsonAttributesResponseBodyGatewayFilterFactory.class)
|
||||
.apply(c -> c.setFieldList(Arrays.asList(attributes)).setDeleteRecursively(deleteRecursively)));
|
||||
.apply(c -> c.setFieldList(Arrays.asList(attributes)).setDeleteRecursively(deleteRecursively)));
|
||||
|
||||
}
|
||||
|
||||
@@ -665,7 +669,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec rewritePath(String regex, String replacement) {
|
||||
return filter(getBean(RewritePathGatewayFilterFactory.class)
|
||||
.apply(c -> c.setRegexp(regex).setReplacement(replacement)));
|
||||
.apply(c -> c.setRegexp(regex).setReplacement(replacement)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -741,7 +745,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec setRequestHeader(String headerName, String headerValue) {
|
||||
return filter(getBean(SetRequestHeaderGatewayFilterFactory.class)
|
||||
.apply(c -> c.setName(headerName).setValue(headerValue)));
|
||||
.apply(c -> c.setName(headerName).setValue(headerValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -753,7 +757,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec setResponseHeader(String headerName, String headerValue) {
|
||||
return filter(getBean(SetResponseHeaderGatewayFilterFactory.class)
|
||||
.apply(c -> c.setName(headerName).setValue(headerValue)));
|
||||
.apply(c -> c.setName(headerName).setValue(headerValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -766,7 +770,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec rewriteResponseHeader(String headerName, String regex, String replacement) {
|
||||
return filter(getBean(RewriteResponseHeaderGatewayFilterFactory.class)
|
||||
.apply(c -> c.setReplacement(replacement).setRegexp(regex).setName(headerName)));
|
||||
.apply(c -> c.setReplacement(replacement).setRegexp(regex).setName(headerName)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -781,9 +785,11 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec rewriteLocationResponseHeader(String stripVersionMode, String locationHeaderName,
|
||||
String hostValue, String protocolsRegex) {
|
||||
return filter(getBean(RewriteLocationResponseHeaderGatewayFilterFactory.class).apply(
|
||||
c -> c.setStripVersion(StripVersion.valueOf(stripVersionMode)).setLocationHeaderName(locationHeaderName)
|
||||
.setHostValue(hostValue).setProtocols(protocolsRegex)));
|
||||
return filter(getBean(RewriteLocationResponseHeaderGatewayFilterFactory.class)
|
||||
.apply(c -> c.setStripVersion(StripVersion.valueOf(stripVersionMode))
|
||||
.setLocationHeaderName(locationHeaderName)
|
||||
.setHostValue(hostValue)
|
||||
.setProtocols(protocolsRegex)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -794,7 +800,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
*/
|
||||
public GatewayFilterSpec rewriteRequestParameter(String name, String replacement) {
|
||||
return filter(getBean(RewriteRequestParameterGatewayFilterFactory.class)
|
||||
.apply(c -> c.setReplacement(replacement).setName(name)));
|
||||
.apply(c -> c.setReplacement(replacement).setName(name)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -97,7 +97,7 @@ public class PredicateSpec extends UriSpec {
|
||||
*/
|
||||
public BooleanSpec between(ZonedDateTime datetime1, ZonedDateTime datetime2) {
|
||||
return asyncPredicate(getBean(BetweenRoutePredicateFactory.class)
|
||||
.applyAsync(c -> c.setDatetime1(datetime1).setDatetime2(datetime2)));
|
||||
.applyAsync(c -> c.setDatetime1(datetime1).setDatetime2(datetime2)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +188,7 @@ public class PredicateSpec extends UriSpec {
|
||||
*/
|
||||
public BooleanSpec path(boolean matchTrailingSlash, String... patterns) {
|
||||
return asyncPredicate(getBean(PathRoutePredicateFactory.class)
|
||||
.applyAsync(c -> c.setPatterns(Arrays.asList(patterns)).setMatchTrailingSlash(matchTrailingSlash)));
|
||||
.applyAsync(c -> c.setPatterns(Arrays.asList(patterns)).setMatchTrailingSlash(matchTrailingSlash)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,7 +287,7 @@ public class PredicateSpec extends UriSpec {
|
||||
*/
|
||||
public BooleanSpec weight(String group, int weight) {
|
||||
return asyncPredicate(getBean(WeightRoutePredicateFactory.class)
|
||||
.applyAsync(c -> c.setGroup(group).setRouteId(routeBuilder.getId()).setWeight(weight)));
|
||||
.applyAsync(c -> c.setGroup(group).setRouteId(routeBuilder.getId()).setWeight(weight)));
|
||||
}
|
||||
|
||||
public BooleanSpec cloudFoundryRouteService() {
|
||||
|
||||
@@ -95,7 +95,7 @@ public class ConfigurationService implements ApplicationEventPublisherAware {
|
||||
}
|
||||
|
||||
List<ConfigurationPropertySource> propertySources = Collections
|
||||
.singletonList(new MapConfigurationPropertySource(properties));
|
||||
.singletonList(new MapConfigurationPropertySource(properties));
|
||||
|
||||
return new Binder(propertySources, null, conversionService).bindOrCreate(configurationPropertyName, bindable,
|
||||
handler);
|
||||
@@ -137,8 +137,8 @@ public class ConfigurationService implements ApplicationEventPublisherAware {
|
||||
@Override
|
||||
protected Map<String, Object> normalizeProperties() {
|
||||
if (this.service.beanFactory != null) {
|
||||
return this.configurable.shortcutType().normalize(this.properties, this.configurable,
|
||||
this.service.parser, this.service.beanFactory);
|
||||
return this.configurable.shortcutType()
|
||||
.normalize(this.properties, this.configurable, this.service.parser, this.service.beanFactory);
|
||||
}
|
||||
return super.normalizeProperties();
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ public final class ServerWebExchangeUtils {
|
||||
public static void putUriTemplateVariables(ServerWebExchange exchange, Map<String, String> uriVariables) {
|
||||
if (exchange.getAttributes().containsKey(URI_TEMPLATE_VARIABLES_ATTRIBUTE)) {
|
||||
Map<String, Object> existingVariables = (Map<String, Object>) exchange.getAttributes()
|
||||
.get(URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
.get(URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
HashMap<String, Object> newVariables = new HashMap<>();
|
||||
newVariables.putAll(existingVariables);
|
||||
newVariables.putAll(uriVariables);
|
||||
@@ -372,9 +372,11 @@ public final class ServerWebExchangeUtils {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
DataBufferFactory factory = response.bufferFactory();
|
||||
// Join all the DataBuffers so we have a single DataBuffer for the body
|
||||
return DataBufferUtils.join(exchange.getRequest().getBody()).defaultIfEmpty(factory.wrap(EMPTY_BYTES))
|
||||
.map(dataBuffer -> decorate(exchange, dataBuffer, cacheDecoratedRequest))
|
||||
.switchIfEmpty(Mono.just(exchange.getRequest())).flatMap(function);
|
||||
return DataBufferUtils.join(exchange.getRequest().getBody())
|
||||
.defaultIfEmpty(factory.wrap(EMPTY_BYTES))
|
||||
.map(dataBuffer -> decorate(exchange, dataBuffer, cacheDecoratedRequest))
|
||||
.switchIfEmpty(Mono.just(exchange.getRequest()))
|
||||
.flatMap(function);
|
||||
}
|
||||
|
||||
private static ServerHttpRequest decorate(ServerWebExchange exchange, DataBuffer dataBuffer,
|
||||
|
||||
@@ -128,8 +128,11 @@ public interface ShortcutConfigurable {
|
||||
Assert.isTrue(fieldOrder != null && fieldOrder.size() == 1,
|
||||
"Shortcut Configuration Type GATHER_LIST must have shortcutFieldOrder of size 1");
|
||||
String fieldName = fieldOrder.get(0);
|
||||
map.put(fieldName, args.values().stream().map(value -> getValue(parser, beanFactory, value))
|
||||
.collect(Collectors.toList()));
|
||||
map.put(fieldName,
|
||||
args.values()
|
||||
.stream()
|
||||
.map(value -> getValue(parser, beanFactory, value))
|
||||
.collect(Collectors.toList()));
|
||||
return map;
|
||||
}
|
||||
},
|
||||
@@ -158,8 +161,10 @@ public interface ShortcutConfigurable {
|
||||
}
|
||||
}
|
||||
String fieldName = fieldOrder.get(0);
|
||||
map.put(fieldName, values.stream().map(value -> getValue(parser, beanFactory, value))
|
||||
.collect(Collectors.toList()));
|
||||
map.put(fieldName,
|
||||
values.stream()
|
||||
.map(value -> getValue(parser, beanFactory, value))
|
||||
.collect(Collectors.toList()));
|
||||
return map;
|
||||
}
|
||||
};
|
||||
@@ -182,7 +187,8 @@ public interface ShortcutConfigurable {
|
||||
Boolean.class, true);
|
||||
if (restrictive) {
|
||||
delegate = SimpleEvaluationContext.forPropertyAccessors(new RestrictivePropertyAccessor())
|
||||
.withMethodResolvers((context, targetObject, name, argumentTypes) -> null).build();
|
||||
.withMethodResolvers((context, targetObject, name, argumentTypes) -> null)
|
||||
.build();
|
||||
}
|
||||
else {
|
||||
delegate = SimpleEvaluationContext.forReadOnlyDataBinding().build();
|
||||
|
||||
@@ -80,8 +80,10 @@ public class WeightConfig {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("group", group).append("routeId", routeId).append("weight", weight)
|
||||
.toString();
|
||||
return new ToStringCreator(this).append("group", group)
|
||||
.append("routeId", routeId)
|
||||
.append("weight", weight)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,8 +32,10 @@ public class PropertiesTagsProvider implements GatewayTagsProvider {
|
||||
private final Tags propertiesTags;
|
||||
|
||||
public PropertiesTagsProvider(Map<String, String> tagsMap) {
|
||||
this.propertiesTags = Tags.of(tagsMap.entrySet().stream().map(entry -> Tag.of(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toList()));
|
||||
this.propertiesTags = Tags.of(tagsMap.entrySet()
|
||||
.stream()
|
||||
.map(entry -> Tag.of(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -70,75 +70,109 @@ public class GatewayControllerEndpointTests {
|
||||
|
||||
@Test
|
||||
public void testEndpoints() {
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway").exchange().expectStatus().isOk()
|
||||
.expectBodyList(Map.class).consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
assertThat(responseBody).contains(Map.of("href", "/actuator/gateway/", "methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/globalfilters", "methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/refresh", "methods", List.of("POST")),
|
||||
Map.of("href", "/actuator/gateway/routedefinitions", "methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/routefilters", "methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/routepredicates", "methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/routes", "methods", List.of("POST", "GET")),
|
||||
Map.of("href", "/actuator/gateway/routes/test-service", "methods",
|
||||
List.of("POST", "DELETE", "GET")),
|
||||
Map.of("href", "/actuator/gateway/routes/route_with_metadata", "methods",
|
||||
List.of("POST", "DELETE", "GET")));
|
||||
});
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(Map.class)
|
||||
.consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
assertThat(responseBody).contains(Map.of("href", "/actuator/gateway/", "methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/globalfilters", "methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/refresh", "methods", List.of("POST")),
|
||||
Map.of("href", "/actuator/gateway/routedefinitions", "methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/routefilters", "methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/routepredicates", "methods", List.of("GET")),
|
||||
Map.of("href", "/actuator/gateway/routes", "methods", List.of("POST", "GET")),
|
||||
Map.of("href", "/actuator/gateway/routes/test-service", "methods",
|
||||
List.of("POST", "DELETE", "GET")),
|
||||
Map.of("href", "/actuator/gateway/routes/route_with_metadata", "methods",
|
||||
List.of("POST", "DELETE", "GET")));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefresh() {
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh").exchange().expectStatus()
|
||||
.isOk();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/refresh")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoutes() {
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes").exchange().expectStatus().isOk()
|
||||
.expectBodyList(Map.class).consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
});
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(Map.class)
|
||||
.consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSpecificRoute() {
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes/test-service").exchange()
|
||||
.expectStatus().isOk().expectBodyList(Map.class).consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotNull();
|
||||
assertThat(responseBody.size()).isEqualTo(1);
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
});
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-service")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(Map.class)
|
||||
.consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotNull();
|
||||
assertThat(responseBody.size()).isEqualTo(1);
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRouteReturnsMetadata() {
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes/route_with_metadata").exchange()
|
||||
.expectStatus().isOk().expectBody().jsonPath("$.metadata")
|
||||
.value(map -> assertThat((Map<String, Object>) map).hasSize(3)
|
||||
.containsEntry("optionName", "OptionValue").containsEntry("iAmNumber", 1)
|
||||
.containsEntry("compositeObject", Maps.newHashMap("name", "value")));
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/route_with_metadata")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.metadata")
|
||||
.value(map -> assertThat((Map<String, Object>) map).hasSize(3)
|
||||
.containsEntry("optionName", "OptionValue")
|
||||
.containsEntry("iAmNumber", 1)
|
||||
.containsEntry("compositeObject", Maps.newHashMap("name", "value")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRouteFilters() {
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routefilters").exchange().expectStatus()
|
||||
.isOk().expectBody(Map.class).consumeWith(result -> {
|
||||
Map<?, ?> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
});
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routefilters")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
Map<?, ?> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoutePredicates() {
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routepredicates").exchange().expectStatus()
|
||||
.isOk().expectBody(Map.class).consumeWith(result -> {
|
||||
Map<?, ?> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
});
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routepredicates")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
Map<?, ?> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -150,15 +184,24 @@ public class GatewayControllerEndpointTests {
|
||||
|
||||
testRouteDefinition.setPredicates(Arrays.asList(methodRoutePredicateDefinition));
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/test-route-to-be-delete")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route-to-be-delete")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
|
||||
testClient.delete().uri("http://localhost:" + port + "/actuator/gateway/routes/test-route-to-be-delete")
|
||||
.exchange().expectStatus().isOk().expectBody(ResponseEntity.class).consumeWith(result -> {
|
||||
HttpStatusCode httpStatus = result.getStatus();
|
||||
Assertions.assertEquals(HttpStatus.OK, httpStatus);
|
||||
});
|
||||
testClient.delete()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route-to-be-delete")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(ResponseEntity.class)
|
||||
.consumeWith(result -> {
|
||||
HttpStatusCode httpStatus = result.getStatus();
|
||||
Assertions.assertEquals(HttpStatus.OK, httpStatus);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -169,8 +212,8 @@ public class GatewayControllerEndpointTests {
|
||||
FilterDefinition prefixPathFilterDefinition = new FilterDefinition("PrefixPath=/test-path");
|
||||
FilterDefinition redirectToFilterDefinition = new FilterDefinition("RemoveResponseHeader=Sensitive-Header");
|
||||
FilterDefinition testFilterDefinition = new FilterDefinition("TestFilter");
|
||||
testRouteDefinition.setFilters(
|
||||
Arrays.asList(prefixPathFilterDefinition, redirectToFilterDefinition, testFilterDefinition));
|
||||
testRouteDefinition
|
||||
.setFilters(Arrays.asList(prefixPathFilterDefinition, redirectToFilterDefinition, testFilterDefinition));
|
||||
|
||||
PredicateDefinition hostRoutePredicateDefinition = new PredicateDefinition("Host=myhost.org");
|
||||
PredicateDefinition methodRoutePredicateDefinition = new PredicateDefinition("Method=GET");
|
||||
@@ -178,9 +221,13 @@ public class GatewayControllerEndpointTests {
|
||||
testRouteDefinition.setPredicates(
|
||||
Arrays.asList(hostRoutePredicateDefinition, methodRoutePredicateDefinition, testPredicateDefinition));
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -191,27 +238,43 @@ public class GatewayControllerEndpointTests {
|
||||
testRouteDefinition.setMetadata(Map.of("groupBy", group1));
|
||||
|
||||
String routeId1 = "route-1_" + UUID.randomUUID();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
|
||||
RouteDefinition testRouteDefinition2 = new RouteDefinition();
|
||||
testRouteDefinition2.setUri(URI.create("http://example.org"));
|
||||
String group2 = "group-2_" + UUID.randomUUID();
|
||||
testRouteDefinition2.setMetadata(Map.of("groupBy", group2));
|
||||
String routeId2 = "route-2_" + UUID.randomUUID();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition2)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition2))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange().expectStatus().isOk();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes").exchange().expectStatus().isOk()
|
||||
.expectBodyList(Map.class).consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).extracting("route_id").contains(routeId1).doesNotContain(routeId2);
|
||||
});
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(Map.class)
|
||||
.consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).extracting("route_id").contains(routeId1).doesNotContain(routeId2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -223,9 +286,13 @@ public class GatewayControllerEndpointTests {
|
||||
testRouteDefinition.setMetadata(Map.of("groupBy", group1));
|
||||
|
||||
String routeId1 = "route-1_" + UUID.randomUUID();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
|
||||
RouteDefinition testRouteDefinition2 = new RouteDefinition();
|
||||
testRouteDefinition2.setUri(URI.create("http://example.org"));
|
||||
@@ -233,37 +300,68 @@ public class GatewayControllerEndpointTests {
|
||||
String group2 = "group-2_" + UUID.randomUUID();
|
||||
testRouteDefinition2.setMetadata(Map.of("groupBy", group2));
|
||||
String routeId2 = "route-2_" + UUID.randomUUID();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition2)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition2))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange().expectStatus().isOk();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group2)
|
||||
.exchange().expectStatus().isOk();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group2)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes").exchange().expectStatus().isOk()
|
||||
.expectBodyList(Map.class).consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(Map.class)
|
||||
.consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
|
||||
List ids = responseBody.stream().map(route -> route.get("route_id"))
|
||||
.filter(id -> id.equals(routeId1) || id.equals(routeId2)).collect(Collectors.toList());
|
||||
assertThat(ids).containsExactly(routeId2, routeId1);
|
||||
});
|
||||
List ids = responseBody.stream()
|
||||
.map(route -> route.get("route_id"))
|
||||
.filter(id -> id.equals(routeId1) || id.equals(routeId2))
|
||||
.collect(Collectors.toList());
|
||||
assertThat(ids).containsExactly(routeId2, routeId1);
|
||||
});
|
||||
|
||||
testRouteDefinition2.setOrder(testRouteDefinition.getOrder() + 1);
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition2)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group2)
|
||||
.exchange().expectStatus().isOk();
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes").exchange().expectStatus().isOk()
|
||||
.expectBodyList(Map.class).consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
List ids = responseBody.stream().map(route -> route.get("route_id"))
|
||||
.filter(id -> id.equals(routeId1) || id.equals(routeId2)).collect(Collectors.toList());
|
||||
assertThat(ids).containsExactly(routeId1, routeId2);
|
||||
});
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition2))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group2)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(Map.class)
|
||||
.consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
List ids = responseBody.stream()
|
||||
.map(route -> route.get("route_id"))
|
||||
.filter(id -> id.equals(routeId1) || id.equals(routeId2))
|
||||
.collect(Collectors.toList());
|
||||
assertThat(ids).containsExactly(routeId1, routeId2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -274,24 +372,42 @@ public class GatewayControllerEndpointTests {
|
||||
testRouteDefinition.setMetadata(Map.of("groupBy", group1));
|
||||
|
||||
String routeId1 = "route-1_" + UUID.randomUUID();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange().expectStatus().isOk();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
|
||||
testClient.delete().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1).exchange()
|
||||
.expectStatus().isOk();
|
||||
testClient.delete()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange().expectStatus().isOk();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes").exchange().expectStatus().isOk()
|
||||
.expectBodyList(Map.class).consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).extracting("route_id").doesNotContain(routeId1);
|
||||
});
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(Map.class)
|
||||
.consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).extracting("route_id").doesNotContain(routeId1);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -303,26 +419,42 @@ public class GatewayControllerEndpointTests {
|
||||
testRouteDefinition.setFilters(List.of(new FilterDefinition("StripPrefix=wrong")));
|
||||
|
||||
String routeId1 = UUID.randomUUID().toString();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
|
||||
RouteDefinition testRouteDefinition2 = new RouteDefinition();
|
||||
testRouteDefinition2.setUri(URI.create("http://valid.route"));
|
||||
testRouteDefinition2.setMetadata(Map.of("groupBy", group1));
|
||||
String routeId2 = UUID.randomUUID().toString();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition2)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition2))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange().expectStatus().isOk();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes").exchange().expectStatus().isOk()
|
||||
.expectBodyList(Map.class).consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).extracting("route_id").doesNotContain(routeId1, routeId2);
|
||||
});
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(Map.class)
|
||||
.consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).extracting("route_id").doesNotContain(routeId1, routeId2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -339,12 +471,20 @@ public class GatewayControllerEndpointTests {
|
||||
testRouteDefinition2.setId(routeId2);
|
||||
testRouteDefinition2.setUri(URI.create("http://valid-group-1.route"));
|
||||
testRouteDefinition2.setMetadata(Map.of("groupBy", group1));
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition2)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId1)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId2)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition2))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
|
||||
RouteDefinition testRouteDefinition3 = new RouteDefinition();
|
||||
String routeId3 = UUID.randomUUID().toString();
|
||||
@@ -357,27 +497,47 @@ public class GatewayControllerEndpointTests {
|
||||
testRouteDefinition4.setId(routeId4);
|
||||
testRouteDefinition4.setUri(URI.create("http://valid-group-2.route"));
|
||||
testRouteDefinition4.setMetadata(Map.of("groupBy", group2));
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId3)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition3)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId4)
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition4)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId3)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition3))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/" + routeId4)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition4))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
|
||||
// When
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange().expectStatus().isOk();
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group2)
|
||||
.exchange().expectStatus().isOk();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group1)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/refresh?metadata=groupBy:" + group2)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
|
||||
// Then
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes").exchange().expectStatus().isOk()
|
||||
.expectBodyList(Map.class).consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).extracting("route_id").doesNotContain(routeId1, routeId2)
|
||||
.contains(routeId3, routeId4);
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(Map.class)
|
||||
.consumeWith(result -> {
|
||||
List<Map> responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).extracting("route_id")
|
||||
.doesNotContain(routeId1, routeId2)
|
||||
.contains(routeId3, routeId4);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -390,8 +550,8 @@ public class GatewayControllerEndpointTests {
|
||||
FilterDefinition prefixPathFilterDefinition = new FilterDefinition("PrefixPath=/test-path");
|
||||
FilterDefinition redirectToFilterDefinition = new FilterDefinition("RemoveResponseHeader=Sensitive-Header");
|
||||
FilterDefinition testFilterDefinition = new FilterDefinition("TestFilter");
|
||||
testRouteDefinition.setFilters(
|
||||
Arrays.asList(prefixPathFilterDefinition, redirectToFilterDefinition, testFilterDefinition));
|
||||
testRouteDefinition
|
||||
.setFilters(Arrays.asList(prefixPathFilterDefinition, redirectToFilterDefinition, testFilterDefinition));
|
||||
|
||||
PredicateDefinition hostRoutePredicateDefinition = new PredicateDefinition("Host=myhost.org");
|
||||
PredicateDefinition methodRoutePredicateDefinition = new PredicateDefinition("Method=GET");
|
||||
@@ -407,8 +567,8 @@ public class GatewayControllerEndpointTests {
|
||||
FilterDefinition prefixPathFilterDefinition2 = new FilterDefinition("PrefixPath=/test-path-2");
|
||||
FilterDefinition redirectToFilterDefinition2 = new FilterDefinition("RemoveResponseHeader=Sensitive-Header-2");
|
||||
FilterDefinition testFilterDefinition2 = new FilterDefinition("TestFilter");
|
||||
testRouteDefinition2.setFilters(
|
||||
Arrays.asList(prefixPathFilterDefinition2, redirectToFilterDefinition2, testFilterDefinition2));
|
||||
testRouteDefinition2
|
||||
.setFilters(Arrays.asList(prefixPathFilterDefinition2, redirectToFilterDefinition2, testFilterDefinition2));
|
||||
|
||||
PredicateDefinition hostRoutePredicateDefinition2 = new PredicateDefinition("Host=myhost-2.org");
|
||||
PredicateDefinition methodRoutePredicateDefinition2 = new PredicateDefinition("Method=GET");
|
||||
@@ -418,12 +578,20 @@ public class GatewayControllerEndpointTests {
|
||||
|
||||
List<RouteDefinition> multipleRouteDefs = List.of(testRouteDefinition, testRouteDefinition2);
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(multipleRouteDefs)).exchange()
|
||||
.expectStatus().isOk();
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routedefinitions")
|
||||
.accept(MediaType.APPLICATION_JSON).exchange().expectBody()
|
||||
.jsonPath("[?(@.id in ['%s','%s'])].id".formatted(routeId1, routeId2)).exists();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(multipleRouteDefs))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routedefinitions")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectBody()
|
||||
.jsonPath("[?(@.id in ['%s','%s'])].id".formatted(routeId1, routeId2))
|
||||
.exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -437,8 +605,8 @@ public class GatewayControllerEndpointTests {
|
||||
FilterDefinition prefixPathFilterDefinition = new FilterDefinition("PrefixPath=/test-path");
|
||||
FilterDefinition redirectToFilterDefinition = new FilterDefinition("RemoveResponseHeader=Sensitive-Header");
|
||||
FilterDefinition testFilterDefinition = new FilterDefinition("TestFilter");
|
||||
testRouteDefinition.setFilters(
|
||||
Arrays.asList(prefixPathFilterDefinition, redirectToFilterDefinition, testFilterDefinition));
|
||||
testRouteDefinition
|
||||
.setFilters(Arrays.asList(prefixPathFilterDefinition, redirectToFilterDefinition, testFilterDefinition));
|
||||
|
||||
PredicateDefinition hostRoutePredicateDefinition = new PredicateDefinition("Host=myhost.org");
|
||||
PredicateDefinition methodRoutePredicateDefinition = new PredicateDefinition("Method=GET");
|
||||
@@ -453,20 +621,28 @@ public class GatewayControllerEndpointTests {
|
||||
|
||||
List<RouteDefinition> multipleRouteDefs = List.of(testRouteDefinition, testRouteDefinition2);
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(multipleRouteDefs)).exchange()
|
||||
.expectStatus().is4xxClientError();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(multipleRouteDefs))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.is4xxClientError();
|
||||
|
||||
testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routedefinitions")
|
||||
.accept(MediaType.APPLICATION_JSON).exchange().expectBody()
|
||||
.jsonPath("[?(@.id in ['%s','%s'])].id".formatted(routeId1, routeId2)).doesNotExist();
|
||||
testClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routedefinitions")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectBody()
|
||||
.jsonPath("[?(@.id in ['%s','%s'])].id".formatted(routeId1, routeId2))
|
||||
.doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostValidShortcutRouteDefinition() {
|
||||
RouteDefinition testRouteDefinition = new RouteDefinition();
|
||||
testRouteDefinition.setId(
|
||||
"gatewaywithgrpcfiltertest-0-104014-8916263311295787431172436062-test-gateway-tls-client-mapping-0");
|
||||
testRouteDefinition
|
||||
.setId("gatewaywithgrpcfiltertest-0-104014-8916263311295787431172436062-test-gateway-tls-client-mapping-0");
|
||||
testRouteDefinition.setUri(URI.create("https://localhost:8095"));
|
||||
testRouteDefinition.setOrder(0);
|
||||
testRouteDefinition.setMetadata(Collections.emptyMap());
|
||||
@@ -488,9 +664,13 @@ public class GatewayControllerEndpointTests {
|
||||
hostRoutePredicateDefinition.addArg("_genkey_0", "/json/hello");
|
||||
testRouteDefinition.setPredicates(Arrays.asList(hostRoutePredicateDefinition));
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isCreated();
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -502,10 +682,16 @@ public class GatewayControllerEndpointTests {
|
||||
FilterDefinition filterDefinition = new FilterDefinition("NotExistingFilter=test-config");
|
||||
testRouteDefinition.setFilters(Collections.singletonList(filterDefinition));
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isBadRequest().expectBody().jsonPath("$.message")
|
||||
.isEqualTo("Invalid FilterDefinition: [NotExistingFilter]");
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.message")
|
||||
.isEqualTo("Invalid FilterDefinition: [NotExistingFilter]");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -514,10 +700,16 @@ public class GatewayControllerEndpointTests {
|
||||
RouteDefinition testRouteDefinition = new RouteDefinition();
|
||||
testRouteDefinition.setUri(URI.create("example.org"));
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/no-scheme-test-route")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isBadRequest().expectBody().jsonPath("$.message")
|
||||
.isEqualTo("The URI format [example.org] is incorrect, scheme can not be empty");
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/no-scheme-test-route")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.message")
|
||||
.isEqualTo("The URI format [example.org] is incorrect, scheme can not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -526,9 +718,16 @@ public class GatewayControllerEndpointTests {
|
||||
RouteDefinition testRouteDefinition = new RouteDefinition();
|
||||
testRouteDefinition.setUri(null);
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/no-scheme-test-route")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isBadRequest().expectBody().jsonPath("$.message").isEqualTo("The URI can not be empty");
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/no-scheme-test-route")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.message")
|
||||
.isEqualTo("The URI can not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -540,10 +739,16 @@ public class GatewayControllerEndpointTests {
|
||||
PredicateDefinition predicateDefinition = new PredicateDefinition("NotExistingPredicate=test-config");
|
||||
testRouteDefinition.setPredicates(Collections.singletonList(predicateDefinition));
|
||||
|
||||
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
|
||||
.expectStatus().isBadRequest().expectBody().jsonPath("$.message")
|
||||
.isEqualTo("Invalid PredicateDefinition: [NotExistingPredicate]");
|
||||
testClient.post()
|
||||
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters.fromValue(testRouteDefinition))
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isBadRequest()
|
||||
.expectBody()
|
||||
.jsonPath("$.message")
|
||||
.isEqualTo("Invalid PredicateDefinition: [NotExistingPredicate]");
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@@ -554,7 +759,8 @@ public class GatewayControllerEndpointTests {
|
||||
@Bean
|
||||
RouteLocator testRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
|
||||
return routeLocatorBuilder.routes()
|
||||
.route("test-service", r -> r.path("/test-service/**").uri("lb://test-service")).build();
|
||||
.route("test-service", r -> r.path("/test-service/**").uri("lb://test-service"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -58,7 +58,8 @@ class ConfigurableHintsRegistrationProcessorTests {
|
||||
@Test
|
||||
void shouldRegisterReflectionHintsForTypeAndSuperTypesAndGenerics() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SpringCloudCircuitBreakerResilience4JFilterFactory.class).getBeanDefinition();
|
||||
.rootBeanDefinition(SpringCloudCircuitBreakerResilience4JFilterFactory.class)
|
||||
.getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||
|
||||
BeanFactoryInitializationAotContribution contribution = processor.processAheadOfTime(beanFactory);
|
||||
|
||||
@@ -77,7 +77,7 @@ public class GatewayAutoConfigurationTests {
|
||||
try (ConfigurableApplicationContext ctx = SpringApplication.run(Config.class, "--spring.jmx.enabled=false",
|
||||
"--server.port=0")) {
|
||||
assertThat(ctx.getEnvironment().getProperty("spring.webflux.hiddenmethod.filter.enabled"))
|
||||
.isEqualTo("false");
|
||||
.isEqualTo("false");
|
||||
assertThat(ctx.getBeanNamesForType(HiddenHttpMethodFilter.class)).isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -85,83 +85,83 @@ public class GatewayAutoConfigurationTests {
|
||||
@Test
|
||||
public void nettyHttpClientDefaults() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
ServerPropertiesConfig.class))
|
||||
.withPropertyValues("debug=true").run(context -> {
|
||||
assertThat(context).hasSingleBean(HttpClient.class);
|
||||
HttpClient httpClient = context.getBean(HttpClient.class);
|
||||
CustomHttpClientFactory factory = context.getBean(CustomHttpClientFactory.class);
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
ServerPropertiesConfig.class))
|
||||
.withPropertyValues("debug=true")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(HttpClient.class);
|
||||
HttpClient httpClient = context.getBean(HttpClient.class);
|
||||
CustomHttpClientFactory factory = context.getBean(CustomHttpClientFactory.class);
|
||||
|
||||
assertThat(factory.connectionProvider).isNotNull();
|
||||
assertThat(factory.connectionProvider.maxConnections()).isEqualTo(Integer.MAX_VALUE); // elastic
|
||||
assertThat(factory.connectionProvider).isNotNull();
|
||||
assertThat(factory.connectionProvider.maxConnections()).isEqualTo(Integer.MAX_VALUE); // elastic
|
||||
|
||||
assertThat(factory.proxyProvider).isNull();
|
||||
assertThat(factory.isSslConfigured()).isFalse();
|
||||
assertThat(factory.proxyProvider).isNull();
|
||||
assertThat(factory.isSslConfigured()).isFalse();
|
||||
|
||||
assertThat(httpClient.configuration().isAcceptGzip()).isFalse();
|
||||
assertThat(httpClient.configuration().loggingHandler()).isNull();
|
||||
assertThat(httpClient.configuration().options())
|
||||
.doesNotContainKey(ChannelOption.CONNECT_TIMEOUT_MILLIS);
|
||||
});
|
||||
assertThat(httpClient.configuration().isAcceptGzip()).isFalse();
|
||||
assertThat(httpClient.configuration().loggingHandler()).isNull();
|
||||
assertThat(httpClient.configuration().options())
|
||||
.doesNotContainKey(ChannelOption.CONNECT_TIMEOUT_MILLIS);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nettyHttpClientConfigured() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, ServerPropertiesConfig.class))
|
||||
.withPropertyValues("spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager=true",
|
||||
"spring.cloud.gateway.httpclient.connect-timeout=10",
|
||||
"spring.cloud.gateway.httpclient.response-timeout=10s",
|
||||
"spring.cloud.gateway.httpclient.pool.eviction-interval=10s",
|
||||
"spring.cloud.gateway.httpclient.pool.type=fixed",
|
||||
"spring.cloud.gateway.httpclient.pool.metrics=true",
|
||||
"spring.cloud.gateway.httpclient.compression=true",
|
||||
"spring.cloud.gateway.httpclient.wiretap=true",
|
||||
// greater than integer max value
|
||||
"spring.cloud.gateway.httpclient.max-initial-line-length=2147483647",
|
||||
"spring.cloud.gateway.httpclient.proxy.host=myhost",
|
||||
"spring.cloud.gateway.httpclient.websocket.max-frame-payload-length=1024")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(HttpClient.class);
|
||||
HttpClient httpClient = context.getBean(HttpClient.class);
|
||||
CustomHttpClientFactory factory = context.getBean(CustomHttpClientFactory.class);
|
||||
HttpClientProperties properties = context.getBean(HttpClientProperties.class);
|
||||
assertThat(properties.getMaxInitialLineLength().toBytes()).isLessThanOrEqualTo(Integer.MAX_VALUE);
|
||||
assertThat(properties.isCompression()).isEqualTo(true);
|
||||
assertThat(properties.getPool().getEvictionInterval()).hasSeconds(10);
|
||||
assertThat(properties.getPool().isMetrics()).isEqualTo(true);
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, ServerPropertiesConfig.class))
|
||||
.withPropertyValues("spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager=true",
|
||||
"spring.cloud.gateway.httpclient.connect-timeout=10",
|
||||
"spring.cloud.gateway.httpclient.response-timeout=10s",
|
||||
"spring.cloud.gateway.httpclient.pool.eviction-interval=10s",
|
||||
"spring.cloud.gateway.httpclient.pool.type=fixed",
|
||||
"spring.cloud.gateway.httpclient.pool.metrics=true",
|
||||
"spring.cloud.gateway.httpclient.compression=true", "spring.cloud.gateway.httpclient.wiretap=true",
|
||||
// greater than integer max value
|
||||
"spring.cloud.gateway.httpclient.max-initial-line-length=2147483647",
|
||||
"spring.cloud.gateway.httpclient.proxy.host=myhost",
|
||||
"spring.cloud.gateway.httpclient.websocket.max-frame-payload-length=1024")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(HttpClient.class);
|
||||
HttpClient httpClient = context.getBean(HttpClient.class);
|
||||
CustomHttpClientFactory factory = context.getBean(CustomHttpClientFactory.class);
|
||||
HttpClientProperties properties = context.getBean(HttpClientProperties.class);
|
||||
assertThat(properties.getMaxInitialLineLength().toBytes()).isLessThanOrEqualTo(Integer.MAX_VALUE);
|
||||
assertThat(properties.isCompression()).isEqualTo(true);
|
||||
assertThat(properties.getPool().getEvictionInterval()).hasSeconds(10);
|
||||
assertThat(properties.getPool().isMetrics()).isEqualTo(true);
|
||||
|
||||
assertThat(httpClient.configuration().isAcceptGzip()).isTrue();
|
||||
assertThat(httpClient.configuration().loggingHandler()).isNotNull();
|
||||
assertThat(httpClient.configuration().options()).containsKey(ChannelOption.CONNECT_TIMEOUT_MILLIS);
|
||||
assertThat(httpClient.configuration().options().get(ChannelOption.CONNECT_TIMEOUT_MILLIS))
|
||||
.isEqualTo(10);
|
||||
assertThat(httpClient.configuration().isAcceptGzip()).isTrue();
|
||||
assertThat(httpClient.configuration().loggingHandler()).isNotNull();
|
||||
assertThat(httpClient.configuration().options()).containsKey(ChannelOption.CONNECT_TIMEOUT_MILLIS);
|
||||
assertThat(httpClient.configuration().options().get(ChannelOption.CONNECT_TIMEOUT_MILLIS))
|
||||
.isEqualTo(10);
|
||||
|
||||
assertThat(factory.connectionProvider).isNotNull();
|
||||
// fixed pool
|
||||
assertThat(factory.connectionProvider.maxConnections())
|
||||
.isEqualTo(ConnectionProvider.DEFAULT_POOL_MAX_CONNECTIONS);
|
||||
assertThat(factory.connectionProvider).isNotNull();
|
||||
// fixed pool
|
||||
assertThat(factory.connectionProvider.maxConnections())
|
||||
.isEqualTo(ConnectionProvider.DEFAULT_POOL_MAX_CONNECTIONS);
|
||||
|
||||
assertThat(factory.proxyProvider).isNotNull();
|
||||
assertThat(factory.proxyProvider.build().getAddress().get().getHostName()).isEqualTo("myhost");
|
||||
assertThat(factory.proxyProvider).isNotNull();
|
||||
assertThat(factory.proxyProvider.build().getAddress().get().getHostName()).isEqualTo("myhost");
|
||||
|
||||
assertThat(factory.isSslConfigured()).isTrue();
|
||||
assertThat(factory.isInsecureTrustManagerSet()).isTrue();
|
||||
assertThat(factory.isSslConfigured()).isTrue();
|
||||
assertThat(factory.isInsecureTrustManagerSet()).isTrue();
|
||||
|
||||
assertThat(context).hasSingleBean(ReactorNettyRequestUpgradeStrategy.class);
|
||||
ReactorNettyRequestUpgradeStrategy upgradeStrategy = context
|
||||
.getBean(ReactorNettyRequestUpgradeStrategy.class);
|
||||
assertThat(upgradeStrategy.getWebsocketServerSpec().maxFramePayloadLength()).isEqualTo(1024);
|
||||
assertThat(upgradeStrategy.getWebsocketServerSpec().handlePing()).isTrue();
|
||||
assertThat(context).hasSingleBean(ReactorNettyWebSocketClient.class);
|
||||
ReactorNettyWebSocketClient webSocketClient = context.getBean(ReactorNettyWebSocketClient.class);
|
||||
assertThat(webSocketClient.getWebsocketClientSpec().maxFramePayloadLength()).isEqualTo(1024);
|
||||
HttpClientCustomizedConfig config = context.getBean(HttpClientCustomizedConfig.class);
|
||||
assertThat(config.called.get()).isTrue();
|
||||
});
|
||||
assertThat(context).hasSingleBean(ReactorNettyRequestUpgradeStrategy.class);
|
||||
ReactorNettyRequestUpgradeStrategy upgradeStrategy = context
|
||||
.getBean(ReactorNettyRequestUpgradeStrategy.class);
|
||||
assertThat(upgradeStrategy.getWebsocketServerSpec().maxFramePayloadLength()).isEqualTo(1024);
|
||||
assertThat(upgradeStrategy.getWebsocketServerSpec().handlePing()).isTrue();
|
||||
assertThat(context).hasSingleBean(ReactorNettyWebSocketClient.class);
|
||||
ReactorNettyWebSocketClient webSocketClient = context.getBean(ReactorNettyWebSocketClient.class);
|
||||
assertThat(webSocketClient.getWebsocketClientSpec().maxFramePayloadLength()).isEqualTo(1024);
|
||||
HttpClientCustomizedConfig config = context.getBean(HttpClientCustomizedConfig.class);
|
||||
assertThat(config.called.get()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -185,39 +185,39 @@ public class GatewayAutoConfigurationTests {
|
||||
@Test
|
||||
public void tokenRelayBeansAreCreated() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
|
||||
ReactiveOAuth2ClientAutoConfiguration.class, GatewayReactiveOAuth2AutoConfiguration.class,
|
||||
GatewayAutoConfiguration.TokenRelayConfiguration.class))
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.client.provider[testprovider].authorization-uri=http://localhost",
|
||||
"spring.security.oauth2.client.provider[testprovider].token-uri=http://localhost/token",
|
||||
"spring.security.oauth2.client.registration[test].provider=testprovider",
|
||||
"spring.security.oauth2.client.registration[test].authorization-grant-type=authorization_code",
|
||||
"spring.security.oauth2.client.registration[test].redirect-uri=http://localhost/redirect",
|
||||
"spring.security.oauth2.client.registration[test].client-id=login-client")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(ReactiveOAuth2AuthorizedClientManager.class);
|
||||
assertThat(context).hasSingleBean(TokenRelayGatewayFilterFactory.class);
|
||||
});
|
||||
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
|
||||
ReactiveOAuth2ClientAutoConfiguration.class, GatewayReactiveOAuth2AutoConfiguration.class,
|
||||
GatewayAutoConfiguration.TokenRelayConfiguration.class))
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.client.provider[testprovider].authorization-uri=http://localhost",
|
||||
"spring.security.oauth2.client.provider[testprovider].token-uri=http://localhost/token",
|
||||
"spring.security.oauth2.client.registration[test].provider=testprovider",
|
||||
"spring.security.oauth2.client.registration[test].authorization-grant-type=authorization_code",
|
||||
"spring.security.oauth2.client.registration[test].redirect-uri=http://localhost/redirect",
|
||||
"spring.security.oauth2.client.registration[test].client-id=login-client")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(ReactiveOAuth2AuthorizedClientManager.class);
|
||||
assertThat(context).hasSingleBean(TokenRelayGatewayFilterFactory.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gatewayReactiveOAuth2AuthorizedClientManagerBacksOffForCustomBean() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
|
||||
ReactiveOAuth2ClientAutoConfiguration.class, GatewayReactiveOAuth2AutoConfiguration.class))
|
||||
.withUserConfiguration(TestReactiveOAuth2AuthorizedClientManagerConfig.class)
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.client.provider[testprovider].authorization-uri=http://localhost",
|
||||
"spring.security.oauth2.client.provider[testprovider].token-uri=http://localhost/token",
|
||||
"spring.security.oauth2.client.registration[test].provider=testprovider",
|
||||
"spring.security.oauth2.client.registration[test].authorization-grant-type=authorization_code",
|
||||
"spring.security.oauth2.client.registration[test].redirect-uri=http://localhost/redirect",
|
||||
"spring.security.oauth2.client.registration[test].client-id=login-client")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(ReactiveOAuth2AuthorizedClientManager.class);
|
||||
assertThat(context).hasBean("myReactiveOAuth2AuthorizedClientManager");
|
||||
});
|
||||
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
|
||||
ReactiveOAuth2ClientAutoConfiguration.class, GatewayReactiveOAuth2AutoConfiguration.class))
|
||||
.withUserConfiguration(TestReactiveOAuth2AuthorizedClientManagerConfig.class)
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.client.provider[testprovider].authorization-uri=http://localhost",
|
||||
"spring.security.oauth2.client.provider[testprovider].token-uri=http://localhost/token",
|
||||
"spring.security.oauth2.client.registration[test].provider=testprovider",
|
||||
"spring.security.oauth2.client.registration[test].authorization-grant-type=authorization_code",
|
||||
"spring.security.oauth2.client.registration[test].redirect-uri=http://localhost/redirect",
|
||||
"spring.security.oauth2.client.registration[test].client-id=login-client")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(ReactiveOAuth2AuthorizedClientManager.class);
|
||||
assertThat(context).hasBean("myReactiveOAuth2AuthorizedClientManager");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -235,14 +235,14 @@ public class GatewayAutoConfigurationTests {
|
||||
assertThat(ctx.getBeanNamesForType(GatewayLegacyControllerEndpoint.class)).hasSize(1);
|
||||
}
|
||||
}).hasRootCauseInstanceOf(IllegalStateException.class)
|
||||
.hasMessageContaining("No TokenRelayGatewayFilterFactory bean was found. Did you include");
|
||||
.hasMessageContaining("No TokenRelayGatewayFilterFactory bean was found. Did you include");
|
||||
}
|
||||
|
||||
@Test // gh-2159
|
||||
public void reactorNettyRequestUpgradeStrategyWebSocketSpecBuilderIsUniquePerRequest()
|
||||
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
ReactorNettyRequestUpgradeStrategy strategy = new GatewayAutoConfiguration.NettyConfiguration()
|
||||
.reactorNettyRequestUpgradeStrategy(new HttpClientProperties());
|
||||
.reactorNettyRequestUpgradeStrategy(new HttpClientProperties());
|
||||
|
||||
// Method "buildSpec" was introduced for Tests, but has only default visiblity
|
||||
Method buildSpec = ReactorNettyRequestUpgradeStrategy.class.getDeclaredMethod("buildSpec", String.class);
|
||||
@@ -258,7 +258,7 @@ public class GatewayAutoConfigurationTests {
|
||||
public void webSocketClientSpecBuilderIsUniquePerReactorNettyWebSocketClient()
|
||||
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
ReactorNettyWebSocketClient websocketClient = new GatewayAutoConfiguration.NettyConfiguration()
|
||||
.reactorNettyWebSocketClient(new HttpClientProperties(), HttpClient.create());
|
||||
.reactorNettyWebSocketClient(new HttpClientProperties(), HttpClient.create());
|
||||
|
||||
// Method "buildSpec" has only private visibility
|
||||
Method buildSpec = ReactorNettyWebSocketClient.class.getDeclaredMethod("buildSpec", String.class);
|
||||
@@ -274,53 +274,56 @@ public class GatewayAutoConfigurationTests {
|
||||
@Test
|
||||
public void gRPCFiltersConfiguredWhenHTTP2Enabled() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, ServerPropertiesConfig.class))
|
||||
.withPropertyValues("server.http2.enabled=true").run(context -> {
|
||||
assertThat(context).hasSingleBean(GRPCRequestHeadersFilter.class);
|
||||
assertThat(context).hasSingleBean(GRPCResponseHeadersFilter.class);
|
||||
HttpClient httpClient = context.getBean(HttpClient.class);
|
||||
assertThat(httpClient.configuration().protocols()).contains(HttpProtocol.HTTP11, HttpProtocol.H2);
|
||||
});
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, ServerPropertiesConfig.class))
|
||||
.withPropertyValues("server.http2.enabled=true")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(GRPCRequestHeadersFilter.class);
|
||||
assertThat(context).hasSingleBean(GRPCResponseHeadersFilter.class);
|
||||
HttpClient httpClient = context.getBean(HttpClient.class);
|
||||
assertThat(httpClient.configuration().protocols()).contains(HttpProtocol.HTTP11, HttpProtocol.H2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gRPCFiltersNotConfiguredWhenHTTP2Disabled() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, ServerPropertiesConfig.class))
|
||||
.withPropertyValues("server.http2.enabled=false").run(context -> {
|
||||
assertThat(context).doesNotHaveBean(GRPCRequestHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(GRPCResponseHeadersFilter.class);
|
||||
});
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, ServerPropertiesConfig.class))
|
||||
.withPropertyValues("server.http2.enabled=false")
|
||||
.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(GRPCRequestHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(GRPCResponseHeadersFilter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insecureTrustManagerNotEnabledByDefaultWhenHTTP2Enabled() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, ServerPropertiesConfig.class))
|
||||
.withPropertyValues("server.http2.enabled=true").run(context -> {
|
||||
assertThat(context).hasSingleBean(HttpClient.class);
|
||||
CustomHttpClientFactory factory = context.getBean(CustomHttpClientFactory.class);
|
||||
assertThat(factory.isInsecureTrustManagerSet()).isFalse();
|
||||
});
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, ServerPropertiesConfig.class))
|
||||
.withPropertyValues("server.http2.enabled=true")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(HttpClient.class);
|
||||
CustomHttpClientFactory factory = context.getBean(CustomHttpClientFactory.class);
|
||||
assertThat(factory.isInsecureTrustManagerSet()).isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customHttpClientWorks() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, CustomHttpClientConfig.class))
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(HttpClient.class);
|
||||
HttpClient httpClient = context.getBean(HttpClient.class);
|
||||
assertThat(httpClient).isInstanceOf(CustomHttpClient.class);
|
||||
});
|
||||
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, MetricsAutoConfiguration.class,
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, CustomHttpClientConfig.class))
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(HttpClient.class);
|
||||
HttpClient httpClient = context.getBean(HttpClient.class);
|
||||
assertThat(httpClient).isInstanceOf(CustomHttpClient.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -447,8 +450,8 @@ public class GatewayAutoConfigurationTests {
|
||||
@Bean
|
||||
public RouteLocator myRouteLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes()
|
||||
.route("test", r -> r.alwaysTrue().filters(GatewayFilterSpec::tokenRelay).uri("http://localhost"))
|
||||
.build();
|
||||
.route("test", r -> r.alwaysTrue().filters(GatewayFilterSpec::tokenRelay).uri("http://localhost"))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -119,14 +119,14 @@ public class GatewayMetricsAutoConfigurationTests {
|
||||
@Test
|
||||
public void observabilityBeansMissing() {
|
||||
assertThat(beanFactory.getBeanProvider(ObservedRequestHttpHeadersFilter.class).getIfAvailable(() -> null))
|
||||
.isNull();
|
||||
.isNull();
|
||||
assertThat(beanFactory.getBeanProvider(ObservedResponseHttpHeadersFilter.class).getIfAvailable(() -> null))
|
||||
.isNull();
|
||||
.isNull();
|
||||
assertThat(
|
||||
beanFactory.getBeanProvider(ObservationClosingWebExceptionHandler.class).getIfAvailable(() -> null))
|
||||
.isNull();
|
||||
.isNull();
|
||||
assertThat(beanFactory.getBeanProvider(GatewayPropagatingSenderTracingObservationHandler.class)
|
||||
.getIfAvailable(() -> null)).isNull();
|
||||
.getIfAvailable(() -> null)).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,23 +32,25 @@ public class LocalResponseCacheAutoConfigurationTests {
|
||||
@Test
|
||||
void onlyOneCacheManagerBeanCreated() {
|
||||
new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(LocalResponseCacheAutoConfiguration.class))
|
||||
.withPropertyValues("spring.cloud.gateway.filter.local-response-cache.enabled=true").run(context -> {
|
||||
context.containsBean(LocalResponseCacheAutoConfiguration.RESPONSE_CACHE_MANAGER_NAME);
|
||||
context.assertThat().hasSingleBean(GlobalLocalResponseCacheGatewayFilter.class);
|
||||
});
|
||||
.withConfiguration(AutoConfigurations.of(LocalResponseCacheAutoConfiguration.class))
|
||||
.withPropertyValues("spring.cloud.gateway.filter.local-response-cache.enabled=true")
|
||||
.run(context -> {
|
||||
context.containsBean(LocalResponseCacheAutoConfiguration.RESPONSE_CACHE_MANAGER_NAME);
|
||||
context.assertThat().hasSingleBean(GlobalLocalResponseCacheGatewayFilter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void twoCacheManagerBeans() {
|
||||
new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(CustomCacheManagerConfig.class,
|
||||
LocalResponseCacheAutoConfiguration.class))
|
||||
.withPropertyValues("spring.cloud.gateway.filter.local-response-cache.enabled=true").run(context -> {
|
||||
context.containsBean(LocalResponseCacheAutoConfiguration.RESPONSE_CACHE_MANAGER_NAME);
|
||||
context.containsBean("myCacheManager");
|
||||
context.assertThat().hasSingleBean(GlobalLocalResponseCacheGatewayFilter.class);
|
||||
});
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(CustomCacheManagerConfig.class, LocalResponseCacheAutoConfiguration.class))
|
||||
.withPropertyValues("spring.cloud.gateway.filter.local-response-cache.enabled=true")
|
||||
.run(context -> {
|
||||
context.containsBean(LocalResponseCacheAutoConfiguration.RESPONSE_CACHE_MANAGER_NAME);
|
||||
context.containsBean("myCacheManager");
|
||||
context.assertThat().hasSingleBean(GlobalLocalResponseCacheGatewayFilter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
||||
@@ -67,8 +67,9 @@ public class DisableBuiltInFiltersTests {
|
||||
@Test
|
||||
public void shouldInjectOnlyEnabledBuiltInFilters() {
|
||||
assertThat(gatewayFilters).hasSizeGreaterThan(0);
|
||||
assertThat(gatewayFilters).allSatisfy(filter -> assertThat(filter).isNotInstanceOfAny(
|
||||
AddRequestHeaderGatewayFilterFactory.class, MapRequestHeaderGatewayFilterFactory.class));
|
||||
assertThat(gatewayFilters)
|
||||
.allSatisfy(filter -> assertThat(filter).isNotInstanceOfAny(AddRequestHeaderGatewayFilterFactory.class,
|
||||
MapRequestHeaderGatewayFilterFactory.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class DisableBuiltInGlobalFiltersTests {
|
||||
public void shouldInjectOnlyEnabledBuiltInFilters() {
|
||||
assertThat(globalFilters).hasSizeGreaterThan(0);
|
||||
assertThat(globalFilters).allSatisfy(filter -> assertThat(filter)
|
||||
.isNotInstanceOfAny(RemoveCachedBodyFilter.class, RouteToRequestUrlFilter.class));
|
||||
.isNotInstanceOfAny(RemoveCachedBodyFilter.class, RouteToRequestUrlFilter.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class DisableBuiltInPredicatesTests {
|
||||
public void shouldInjectOnlyEnabledBuiltInPredicates() {
|
||||
assertThat(predicates).hasSizeGreaterThan(0);
|
||||
assertThat(predicates).allSatisfy(filter -> assertThat(filter)
|
||||
.isNotInstanceOfAny(AfterRoutePredicateFactory.class, BeforeRoutePredicateFactory.class));
|
||||
.isNotInstanceOfAny(AfterRoutePredicateFactory.class, BeforeRoutePredicateFactory.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ class OnEnabledComponentTests {
|
||||
private AnnotatedTypeMetadata mockMetaData(Class<?> value) {
|
||||
AnnotatedTypeMetadata metadata = mock(AnnotatedTypeMetadata.class);
|
||||
when(metadata.getAnnotationAttributes(eq(ConditionalOnEnabledFilter.class.getName())))
|
||||
.thenReturn(Collections.singletonMap("value", value));
|
||||
.thenReturn(Collections.singletonMap("value", value));
|
||||
return metadata;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,11 +49,15 @@ class OnEnabledFilterTests {
|
||||
FallbackHeadersGatewayFilterFactory.class, MapRequestHeaderGatewayFilterFactory.class,
|
||||
SpringCloudCircuitBreakerResilience4JFilterFactory.class);
|
||||
|
||||
List<String> resultNames = predicates.stream().map(onEnabledFilter::normalizeComponentName)
|
||||
.collect(Collectors.toList());
|
||||
List<String> resultNames = predicates.stream()
|
||||
.map(onEnabledFilter::normalizeComponentName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<String> expectedNames = Stream.of("add-request-header", "dedupe-response-header", "fallback-headers",
|
||||
"map-request-header", "circuit-breaker").map(s -> "filter." + s).collect(Collectors.toList());
|
||||
List<String> expectedNames = Stream
|
||||
.of("add-request-header", "dedupe-response-header", "fallback-headers", "map-request-header",
|
||||
"circuit-breaker")
|
||||
.map(s -> "filter." + s)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(resultNames).isEqualTo(expectedNames);
|
||||
}
|
||||
|
||||
@@ -45,11 +45,13 @@ class OnEnabledGlobalFilterTests {
|
||||
List<Class<? extends GlobalFilter>> predicates = Arrays.asList(ForwardPathFilter.class,
|
||||
AdaptCachedBodyGlobalFilter.class, WebsocketRoutingFilter.class);
|
||||
|
||||
List<String> resultNames = predicates.stream().map(onEnabledGlobalFilter::normalizeComponentName)
|
||||
.collect(Collectors.toList());
|
||||
List<String> resultNames = predicates.stream()
|
||||
.map(onEnabledGlobalFilter::normalizeComponentName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<String> expectedNames = Stream.of("forward-path", "adapt-cached-body", "websocket-routing")
|
||||
.map(s -> "global-filter." + s).collect(Collectors.toList());
|
||||
.map(s -> "global-filter." + s)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(resultNames).isEqualTo(expectedNames);
|
||||
}
|
||||
|
||||
@@ -47,11 +47,13 @@ class OnEnabledPredicateTests {
|
||||
CloudFoundryRouteServiceRoutePredicateFactory.class, ReadBodyRoutePredicateFactory.class,
|
||||
RemoteAddrRoutePredicateFactory.class);
|
||||
|
||||
List<String> resultNames = predicates.stream().map(onEnabledPredicate::normalizeComponentName)
|
||||
.collect(Collectors.toList());
|
||||
List<String> resultNames = predicates.stream()
|
||||
.map(onEnabledPredicate::normalizeComponentName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<String> expectedNames = Stream.of("after", "cloud-foundry-route-service", "read-body", "remote-addr")
|
||||
.map(s -> "predicate." + s).collect(Collectors.toList());
|
||||
.map(s -> "predicate." + s)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(resultNames).isEqualTo(expectedNames);
|
||||
}
|
||||
|
||||
@@ -46,28 +46,39 @@ public class CorsGlobalTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void testPreFlightCorsRequest() {
|
||||
ClientResponse clientResponse = webClient.options().uri("/abc/123/function").header("Origin", "domain.com")
|
||||
.header("Access-Control-Request-Method", "GET").exchangeToMono(Mono::just).block();
|
||||
ClientResponse clientResponse = webClient.options()
|
||||
.uri("/abc/123/function")
|
||||
.header("Origin", "domain.com")
|
||||
.header("Access-Control-Request-Method", "GET")
|
||||
.exchangeToMono(Mono::just)
|
||||
.block();
|
||||
HttpHeaders asHttpHeaders = clientResponse.headers().asHttpHeaders();
|
||||
Mono<String> bodyToMono = clientResponse.bodyToMono(String.class);
|
||||
// pre-flight request shouldn't return the response body
|
||||
assertThat(bodyToMono.block()).isNull();
|
||||
assertThat(asHttpHeaders.getAccessControlAllowOrigin())
|
||||
.as("Missing header value in response: " + HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN).isEqualTo("*");
|
||||
.as("Missing header value in response: " + HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.isEqualTo("*");
|
||||
assertThat(asHttpHeaders.getAccessControlAllowMethods())
|
||||
.as("Missing header value in response: " + HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)
|
||||
.isEqualTo(Arrays.asList(new HttpMethod[] { HttpMethod.GET }));
|
||||
.as("Missing header value in response: " + HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)
|
||||
.isEqualTo(Arrays.asList(new HttpMethod[] { HttpMethod.GET }));
|
||||
assertThat(clientResponse.statusCode()).as("Pre Flight call failed.").isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCorsRequest() {
|
||||
ResponseEntity<String> response = webClient.get().uri("/abc/123/function").header("Origin", "domain.com")
|
||||
.header(HttpHeaders.HOST, "www.path.org").retrieve().toEntity(String.class).block();
|
||||
ResponseEntity<String> response = webClient.get()
|
||||
.uri("/abc/123/function")
|
||||
.header("Origin", "domain.com")
|
||||
.header(HttpHeaders.HOST, "www.path.org")
|
||||
.retrieve()
|
||||
.toEntity(String.class)
|
||||
.block();
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getHeaders().getAccessControlAllowOrigin())
|
||||
.as("Missing header value in response: " + HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN).isEqualTo("*");
|
||||
.as("Missing header value in response: " + HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.isEqualTo("*");
|
||||
assertThat(response.getStatusCode()).as("CORS request failed.").isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,59 +48,79 @@ public class CorsPerRouteTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void testPreFlightCorsRequest() {
|
||||
testClient.options().uri("/abc").header("Origin", "domain.com").header("Access-Control-Request-Method", "GET")
|
||||
.exchange().expectBody(Map.class).consumeWith(result -> {
|
||||
assertThat(result.getResponseBody()).isNull();
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.OK);
|
||||
testClient.options()
|
||||
.uri("/abc")
|
||||
.header("Origin", "domain.com")
|
||||
.header("Access-Control-Request-Method", "GET")
|
||||
.exchange()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
assertThat(result.getResponseBody()).isNull();
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
HttpHeaders responseHeaders = result.getResponseHeaders();
|
||||
assertThat(responseHeaders.getAccessControlAllowOrigin())
|
||||
.as(missingHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("domain.com");
|
||||
assertThat(responseHeaders.getAccessControlAllowMethods())
|
||||
.as(missingHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS))
|
||||
.containsExactlyInAnyOrder(HttpMethod.GET, HttpMethod.POST);
|
||||
assertThat(responseHeaders.getAccessControlMaxAge()).as(missingHeader(ACCESS_CONTROL_MAX_AGE))
|
||||
.isEqualTo(30L);
|
||||
assertThat(responseHeaders.getAccessControlAllowCredentials())
|
||||
.as(missingHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS)).isEqualTo(true);
|
||||
});
|
||||
HttpHeaders responseHeaders = result.getResponseHeaders();
|
||||
assertThat(responseHeaders.getAccessControlAllowOrigin()).as(missingHeader(ACCESS_CONTROL_ALLOW_ORIGIN))
|
||||
.isEqualTo("domain.com");
|
||||
assertThat(responseHeaders.getAccessControlAllowMethods())
|
||||
.as(missingHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS))
|
||||
.containsExactlyInAnyOrder(HttpMethod.GET, HttpMethod.POST);
|
||||
assertThat(responseHeaders.getAccessControlMaxAge()).as(missingHeader(ACCESS_CONTROL_MAX_AGE))
|
||||
.isEqualTo(30L);
|
||||
assertThat(responseHeaders.getAccessControlAllowCredentials())
|
||||
.as(missingHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS))
|
||||
.isEqualTo(true);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreFlightCorsRequestJavaConfig() {
|
||||
testClient.options().uri("/route-test").header("Origin", "another-domain.com")
|
||||
.header("Host", "www.javaconfhost.org").header("Access-Control-Request-Method", "GET").exchange()
|
||||
.expectBody(Map.class).consumeWith(result -> {
|
||||
assertThat(result.getResponseBody()).isNull();
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.OK);
|
||||
testClient.options()
|
||||
.uri("/route-test")
|
||||
.header("Origin", "another-domain.com")
|
||||
.header("Host", "www.javaconfhost.org")
|
||||
.header("Access-Control-Request-Method", "GET")
|
||||
.exchange()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
assertThat(result.getResponseBody()).isNull();
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
HttpHeaders responseHeaders = result.getResponseHeaders();
|
||||
assertThat(responseHeaders.getAccessControlAllowOrigin())
|
||||
.as(missingHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("another-domain.com");
|
||||
assertThat(responseHeaders.getAccessControlAllowMethods())
|
||||
.as(missingHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS))
|
||||
.containsExactlyInAnyOrder(HttpMethod.GET);
|
||||
assertThat(responseHeaders.getAccessControlMaxAge()).as(missingHeader(ACCESS_CONTROL_MAX_AGE))
|
||||
.isEqualTo(50L);
|
||||
});
|
||||
HttpHeaders responseHeaders = result.getResponseHeaders();
|
||||
assertThat(responseHeaders.getAccessControlAllowOrigin()).as(missingHeader(ACCESS_CONTROL_ALLOW_ORIGIN))
|
||||
.isEqualTo("another-domain.com");
|
||||
assertThat(responseHeaders.getAccessControlAllowMethods())
|
||||
.as(missingHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS))
|
||||
.containsExactlyInAnyOrder(HttpMethod.GET);
|
||||
assertThat(responseHeaders.getAccessControlMaxAge()).as(missingHeader(ACCESS_CONTROL_MAX_AGE))
|
||||
.isEqualTo(50L);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreFlightForbiddenCorsRequest() {
|
||||
testClient.get().uri("/cors").header("Origin", "domain.com").header("Access-Control-Request-Method", "GET")
|
||||
.exchange().expectBody(Map.class).consumeWith(result -> {
|
||||
assertThat(result.getResponseBody()).isNull();
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
});
|
||||
testClient.get()
|
||||
.uri("/cors")
|
||||
.header("Origin", "domain.com")
|
||||
.header("Access-Control-Request-Method", "GET")
|
||||
.exchange()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
assertThat(result.getResponseBody()).isNull();
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCorsValidatedRequest() {
|
||||
testClient.get().uri("/cors/status/201").header("Origin", "https://test.com").exchange()
|
||||
.expectBody(String.class).consumeWith(result -> {
|
||||
assertThat(result.getResponseBody()).endsWith("201");
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.CREATED);
|
||||
});
|
||||
testClient.get()
|
||||
.uri("/cors/status/201")
|
||||
.header("Origin", "https://test.com")
|
||||
.exchange()
|
||||
.expectBody(String.class)
|
||||
.consumeWith(result -> {
|
||||
assertThat(result.getResponseBody()).endsWith("201");
|
||||
assertThat(result.getStatus()).isEqualTo(HttpStatus.CREATED);
|
||||
});
|
||||
}
|
||||
|
||||
private String missingHeader(String accessControlAllowOrigin) {
|
||||
@@ -118,13 +138,16 @@ public class CorsPerRouteTests extends BaseWebClientTests {
|
||||
@Bean
|
||||
public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes()
|
||||
.route("cors_route_java_test",
|
||||
r -> r.host("*.javaconfhost.org").and().path("/route-test/**")
|
||||
.filters(f -> f.stripPrefix(1).prefixPath("/httpbin"))
|
||||
.metadata(Map.of("cors", Map.of("allowedOrigins", "another-domain.com",
|
||||
"allowedMethods", HttpMethod.GET.name(), "maxAge", 50)))
|
||||
.uri(uri))
|
||||
.build();
|
||||
.route("cors_route_java_test",
|
||||
r -> r.host("*.javaconfhost.org")
|
||||
.and()
|
||||
.path("/route-test/**")
|
||||
.filters(f -> f.stripPrefix(1).prefixPath("/httpbin"))
|
||||
.metadata(Map.of("cors",
|
||||
Map.of("allowedOrigins", "another-domain.com", "allowedMethods",
|
||||
HttpMethod.GET.name(), "maxAge", 50)))
|
||||
.uri(uri))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user