diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/AsyncPredicate.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/AsyncPredicate.java index 505e1e67..144a0890 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/AsyncPredicate.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/AsyncPredicate.java @@ -20,7 +20,6 @@ import java.util.function.Function; import java.util.function.Predicate; import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate; @@ -106,8 +105,8 @@ public interface AsyncPredicate extends Function> { @Override public Publisher apply(T t) { - return Flux.zip(left.apply(t), right.apply(t)) - .map(tuple -> tuple.getT1() && tuple.getT2()); + return Mono.from(left.apply(t)).flatMap( + result -> !result ? Mono.just(false) : Mono.from(right.apply(t))); } @Override @@ -133,8 +132,8 @@ public interface AsyncPredicate extends Function> { @Override public Publisher apply(T t) { - return Flux.zip(left.apply(t), right.apply(t)) - .map(tuple -> tuple.getT1() || tuple.getT2()); + return Mono.from(left.apply(t)).flatMap( + result -> result ? Mono.just(true) : Mono.from(right.apply(t))); } @Override diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/AsyncPredicateTest.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/AsyncPredicateTest.java new file mode 100644 index 00000000..25e10486 --- /dev/null +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/AsyncPredicateTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2013-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 org.springframework.cloud.gateway.handler; + +import java.util.function.Predicate; + +import org.junit.Assert; +import org.junit.Test; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +public class AsyncPredicateTest { + + @Test + public void andPredicateShouldNotTestRightOperatorIfLeftOperatorIsFalse() { + TestAsyncPredicate left = new TestAsyncPredicate<>(o -> false); + TestAsyncPredicate right = new TestAsyncPredicate<>(o -> true); + Publisher andTest = left.and(right).apply(new Object()); + + StepVerifier.create(andTest).expectNext(false).expectComplete().verify(); + + left.assertTested(); + right.assertUntested(); + } + + @Test + public void andPredicateShouldTestRightOperatorIfLeftOperatorIsTrue() { + TestAsyncPredicate left = new TestAsyncPredicate<>(o -> true); + TestAsyncPredicate right = new TestAsyncPredicate<>(o -> false); + Publisher andTest = left.and(right).apply(new Object()); + + StepVerifier.create(andTest).expectNext(false).expectComplete().verify(); + + left.assertTested(); + right.assertTested(); + } + + @Test + public void orPredicateShouldNotTestRightOperatorIfLeftOperatorIsTrue() { + TestAsyncPredicate left = new TestAsyncPredicate<>(o -> true); + TestAsyncPredicate right = new TestAsyncPredicate<>(o -> false); + Publisher orTest = left.or(right).apply(new Object()); + + StepVerifier.create(orTest).expectNext(true).expectComplete().verify(); + + left.assertTested(); + right.assertUntested(); + } + + @Test + public void orPredicateShouldTestRightOperatorIfLeftOperatorIsFalse() { + TestAsyncPredicate left = new TestAsyncPredicate<>(o -> false); + TestAsyncPredicate right = new TestAsyncPredicate<>(o -> true); + Publisher orTest = left.or(right).apply(new Object()); + + StepVerifier.create(orTest).expectNext(true).expectComplete().verify(); + + left.assertTested(); + right.assertTested(); + } + + /** + * An AsyncPredicate decorator that records if the apply method was called. + */ + private final static class TestAsyncPredicate implements AsyncPredicate { + + private final Predicate delegate; + + private boolean tested = false; + + private TestAsyncPredicate(Predicate predicate) { + this.delegate = predicate; + } + + @Override + public Publisher apply(T t) { + tested = true; + return Mono.just(delegate.test(t)); + } + + public void assertTested() { + Assert.assertTrue("predicate must have been tested", tested); + } + + public void assertUntested() { + Assert.assertFalse("predicate must not have been tested", tested); + } + + } + +}