From 4635961813a7f8b4cc4234d3e92970838c11fa72 Mon Sep 17 00:00:00 2001 From: Alexandre Picard-Lemieux Date: Wed, 4 Dec 2019 15:39:56 -0500 Subject: [PATCH 1/2] Adding ConditionalOnMissingBean for the name of the bean (#1452) --- .../cloud/gateway/config/GatewayAutoConfiguration.java | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index 2351873c..d05ea769 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -202,6 +202,7 @@ public class GatewayAutoConfiguration { @Bean @Primary + @ConditionalOnMissingBean(name = "cachedCompositeRouteLocator") // TODO: property to disable composite? public RouteLocator cachedCompositeRouteLocator(List routeLocators) { return new CachingRouteLocator( From 1d151215af1b402347803ce750a7b3636fa5133a Mon Sep 17 00:00:00 2001 From: Tim Ysewyn Date: Tue, 10 Dec 2019 19:52:21 +0100 Subject: [PATCH 2/2] Fixed blocking call for route discovery (#1342) * Made "reactive" call great again * Added BlockHound integration fixes gh-1104 --- pom.xml | 16 ++++ spring-cloud-gateway-core/pom.xml | 25 ++++++ ...DiscoveryClientRouteDefinitionLocator.java | 5 +- ...yGatewayFilterFactoryIntegrationTests.java | 15 ++-- .../CustomBlockHoundIntegration.java | 89 +++++++++++++++++++ .../CustomBlockHoundIntegrationTest.java | 36 ++++++++ ...ockhound.integration.BlockHoundIntegration | 1 + 7 files changed, 176 insertions(+), 11 deletions(-) create mode 100644 spring-cloud-gateway-core/src/test/java/reactor/blockhound/integration/CustomBlockHoundIntegration.java create mode 100644 spring-cloud-gateway-core/src/test/java/reactor/blockhound/integration/CustomBlockHoundIntegrationTest.java create mode 100644 spring-cloud-gateway-core/src/test/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration diff --git a/pom.xml b/pom.xml index b129a54b..594e5799 100644 --- a/pom.xml +++ b/pom.xml @@ -55,11 +55,15 @@ 2.1.5.BUILD-SNAPSHOT 2.1.5.BUILD-SNAPSHOT 0.6 + 1.0.1.RELEASE true true true + 5.5.2 + 2.22.2 + 2.22.2 @@ -85,6 +89,13 @@ pom import + + org.junit + junit-bom + ${junit-jupiter.version} + import + pom + org.springframework.cloud spring-cloud-starter-netflix-ribbon @@ -126,6 +137,11 @@ embedded-redis ${embedded-redis.version} + + io.projectreactor.tools + blockhound-junit-platform + ${blockhound.version} + diff --git a/spring-cloud-gateway-core/pom.xml b/spring-cloud-gateway-core/pom.xml index 74c3f2ec..0b1d12fb 100644 --- a/spring-cloud-gateway-core/pom.xml +++ b/spring-cloud-gateway-core/pom.xml @@ -102,6 +102,26 @@ spring-boot-starter-validation test + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.platform + junit-platform-launcher + test + + + org.junit.vintage + junit-vintage-engine + test + org.springframework.cloud spring-cloud-test-support @@ -112,6 +132,11 @@ reactor-test test + + io.projectreactor.tools + blockhound-junit-platform + test + org.assertj assertj-core diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java index 5cfeaac0..f0342457 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java @@ -23,6 +23,7 @@ import java.util.function.Predicate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Flux; +import reactor.core.scheduler.Schedulers; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; @@ -93,8 +94,8 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc }; } - return Flux.fromIterable(discoveryClient.getServices()) - .map(discoveryClient::getInstances) + return Flux.defer(() -> Flux.fromIterable(discoveryClient.getServices())) + .map(discoveryClient::getInstances).subscribeOn(Schedulers.elastic()) .filter(instances -> !instances.isEmpty()) .map(instances -> instances.get(0)).filter(includePredicate) .map(instance -> { diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java index 12934c4d..939dcae1 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java @@ -31,6 +31,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; @@ -193,19 +194,15 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest private String uri; @RequestMapping("/httpbin/sleep") - public ResponseEntity sleep(@RequestParam("key") String key, + public Mono> sleep(@RequestParam("key") String key, @RequestParam("millis") long millisToSleep) { AtomicInteger num = getCount(key); int retryCount = num.incrementAndGet(); log.warn("Retry count: " + retryCount); - try { - Thread.sleep(millisToSleep); - } - catch (InterruptedException e) { - } - return ResponseEntity.status(HttpStatus.OK) - .header("X-Retry-Count", String.valueOf(retryCount)) - .body("slept " + millisToSleep + " ms"); + return Mono.delay(Duration.ofMillis(millisToSleep)) + .thenReturn(ResponseEntity.status(HttpStatus.OK) + .header("X-Retry-Count", String.valueOf(retryCount)) + .body("slept " + millisToSleep + " ms")); } @RequestMapping("/httpbin/retryalwaysfail") diff --git a/spring-cloud-gateway-core/src/test/java/reactor/blockhound/integration/CustomBlockHoundIntegration.java b/spring-cloud-gateway-core/src/test/java/reactor/blockhound/integration/CustomBlockHoundIntegration.java new file mode 100644 index 00000000..ee6afa68 --- /dev/null +++ b/spring-cloud-gateway-core/src/test/java/reactor/blockhound/integration/CustomBlockHoundIntegration.java @@ -0,0 +1,89 @@ +/* + * Copyright 2019-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reactor.blockhound.integration; + +import reactor.blockhound.BlockHound; + +/** + * @author Tim Ysewyn + */ +public class CustomBlockHoundIntegration implements BlockHoundIntegration { + + @Override + public void applyTo(BlockHound.Builder builder) { + // Uses + // ch.qos.logback.classic.spi.PackagingDataCalculator#getImplementationVersion + builder.allowBlockingCallsInside( + "org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler", + "logError"); + builder.allowBlockingCallsInside("reactor.util.Loggers$Slf4JLogger", "debug"); + + // Uses org.springframework.util.JdkIdGenerator#generateId + // Uses UUID#randomUUID + builder.allowBlockingCallsInside( + "org.springframework.web.server.session.InMemoryWebSessionStore", + "lambda$createWebSession$0"); + + // Uses java.util.Random#nextInt + builder.allowBlockingCallsInside("org.springframework.util.MimeTypeUtils", + "generateMultipartBoundary"); + + // SPRING DATA REDIS RELATED + + // Uses Unsafe#park + builder.allowBlockingCallsInside( + "org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory", + "getReactiveConnection"); + + // NETTY RELATED + + // Uses Thread#sleep + builder.allowBlockingCallsInside("io.netty.channel.nio.NioEventLoop", + "handleLoopException"); + builder.allowBlockingCallsInside( + "io.netty.util.concurrent.SingleThreadEventExecutor", "confirmShutdown"); + + // Uses Unsafe#park + builder.allowBlockingCallsInside("io.netty.util.concurrent.GlobalEventExecutor", + "execute"); + + // SECURITY RELATED + + // For HTTPS traffic + builder.allowBlockingCallsInside("io.netty.handler.ssl.SslHandler", + "channelActive"); + builder.allowBlockingCallsInside("io.netty.handler.ssl.SslHandler", + "channelInactive"); + builder.allowBlockingCallsInside("io.netty.handler.ssl.SslHandler", "unwrap"); + builder.allowBlockingCallsInside("io.netty.handler.ssl.SslContext", + "newClientContextInternal"); + + // Uses + // org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientConnectionManagerFactory#newConnectionManager + // Uses javax.net.ssl.SSLContext#init + builder.allowBlockingCallsInside( + "org.springframework.cloud.netflix.ribbon.SpringClientFactory", + "getContext"); + + // Uses org.springframework.security.crypto.bcrypt.BCrypt#gensalt + // Uses java.security.SecureRandom#nextBytes + builder.allowBlockingCallsInside( + "org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager", + "lambda$authenticate$2"); + } + +} diff --git a/spring-cloud-gateway-core/src/test/java/reactor/blockhound/integration/CustomBlockHoundIntegrationTest.java b/spring-cloud-gateway-core/src/test/java/reactor/blockhound/integration/CustomBlockHoundIntegrationTest.java new file mode 100644 index 00000000..21766007 --- /dev/null +++ b/spring-cloud-gateway-core/src/test/java/reactor/blockhound/integration/CustomBlockHoundIntegrationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package reactor.blockhound.integration; + +import org.junit.Test; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +/** + * @author Tim Ysewyn + */ +public class CustomBlockHoundIntegrationTest { + + @Test(expected = RuntimeException.class) + public void shouldThrowErrorForBlockingCallWithCustomBlockHoundIntegration() { + Mono.fromCallable(() -> { + Thread.sleep(1); + return null; + }).subscribeOn(Schedulers.parallel()).block(); + } + +} diff --git a/spring-cloud-gateway-core/src/test/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration b/spring-cloud-gateway-core/src/test/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration new file mode 100644 index 00000000..a6bdb542 --- /dev/null +++ b/spring-cloud-gateway-core/src/test/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration @@ -0,0 +1 @@ +reactor.blockhound.integration.CustomBlockHoundIntegration