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