diff --git a/pom.xml b/pom.xml index 049fc897..7817d339 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,7 @@ 2.2.0.BUILD-SNAPSHOT 1.0.0.BUILD-SNAPSHOT 0.6 + 1.0.1.RELEASE @@ -134,6 +135,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 44df08a8..fc931075 100644 --- a/spring-cloud-gateway-core/pom.xml +++ b/spring-cloud-gateway-core/pom.xml @@ -112,6 +112,11 @@ reactor-test test + + io.projectreactor.tools + blockhound-junit-platform + test + org.assertj assertj-core 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 c6fb8f1c..996ffff1 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..e8352af7 --- /dev/null +++ b/spring-cloud-gateway-core/src/test/java/reactor/blockhound/integration/CustomBlockHoundIntegration.java @@ -0,0 +1,68 @@ +/* + * 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"); + + // SECURITY RELATED + + // For HTTPS traffic + builder.allowBlockingCallsInside("io.netty.handler.ssl.SslHandler", + "channelActive"); + 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.AbstractUserDetailsReactiveAuthenticationManager", + "lambda$authenticate$4"); + } + +} 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..42145c49 --- /dev/null +++ b/spring-cloud-gateway-core/src/test/java/reactor/blockhound/integration/CustomBlockHoundIntegrationTest.java @@ -0,0 +1,37 @@ +/* + * 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.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +/** + * @author Tim Ysewyn + */ +public class CustomBlockHoundIntegrationTest { + + @Test + public void shouldThrowErrorForBlockingCallWithCustomBlockHoundIntegration() { + Assertions.assertThrows(RuntimeException.class, () -> 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