Skip to content

Commit 39786e4

Browse files
yuzawa-sanpoutsma
authored andcommitted
Improve attribute handling in RequestPredicates
This commit changes the way request attributes are handled in RequestPredicates. Previously, the AND/OR/NOT predicates copied all attributes in a map, and restored that when the delegate predicate(s) failed. Now, we only set the attributes when all delegates have succeeded. Closes gh-30028
1 parent ed172d6 commit 39786e4

File tree

3 files changed

+280
-116
lines changed

3 files changed

+280
-116
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java

Lines changed: 135 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.security.Principal;
2222
import java.util.Arrays;
2323
import java.util.Collections;
24-
import java.util.HashMap;
2524
import java.util.LinkedHashMap;
2625
import java.util.LinkedHashSet;
2726
import java.util.List;
@@ -296,11 +295,6 @@ private static void traceMatch(String prefix, Object desired, @Nullable Object a
296295
}
297296
}
298297

299-
private static void restoreAttributes(ServerRequest request, Map<String, Object> attributes) {
300-
request.attributes().clear();
301-
request.attributes().putAll(attributes);
302-
}
303-
304298
private static Map<String, String> mergePathVariables(Map<String, String> oldVariables,
305299
Map<String, String> newVariables) {
306300

@@ -432,6 +426,81 @@ public interface Visitor {
432426
}
433427

434428

429+
/**
430+
* A boolean result with a state-changing commit step to be conditionally
431+
* applied by a caller.
432+
*/
433+
static abstract class Evaluation {
434+
static final Evaluation TRUE = new NopEvaluation(true);
435+
static final Evaluation FALSE = new NopEvaluation(false);
436+
437+
private final boolean value;
438+
439+
Evaluation(boolean value) {
440+
this.value = value;
441+
}
442+
443+
abstract void doCommit();
444+
445+
446+
private static final class NopEvaluation extends Evaluation {
447+
private NopEvaluation(boolean value) {
448+
super(value);
449+
}
450+
451+
@Override
452+
void doCommit() {
453+
// pass
454+
}
455+
}
456+
}
457+
458+
459+
/**
460+
* Evaluates a {@link ServerRequest} and returns an {@link Evaluation}.
461+
*/
462+
private static abstract class Evaluator {
463+
private static Evaluator of(RequestPredicate requestPredicate) {
464+
if (requestPredicate instanceof EvaluatorRequestPredicate evaluatorRequestPredicate) {
465+
return evaluatorRequestPredicate;
466+
}
467+
// Wrap the RequestPredicate with an Evaluator
468+
return new RequestPredicateEvaluator(requestPredicate);
469+
}
470+
471+
abstract Evaluation apply(ServerRequest request);
472+
473+
474+
private static final class RequestPredicateEvaluator extends Evaluator {
475+
private final RequestPredicate requestPredicate;
476+
477+
private RequestPredicateEvaluator(RequestPredicate requestPredicate) {
478+
this.requestPredicate = requestPredicate;
479+
}
480+
481+
@Override
482+
Evaluation apply(ServerRequest request) {
483+
return this.requestPredicate.test(request) ? Evaluation.TRUE : Evaluation.FALSE;
484+
}
485+
}
486+
}
487+
488+
/**
489+
* A {@link RequestPredicate} which may modify the request.
490+
*/
491+
static abstract class EvaluatorRequestPredicate extends Evaluator implements RequestPredicate {
492+
@Override
493+
public final boolean test(ServerRequest request) {
494+
Evaluation result = apply(request);
495+
boolean value = result.value;
496+
if (value) {
497+
result.doCommit();
498+
}
499+
return value;
500+
}
501+
}
502+
503+
435504
private static class HttpMethodPredicate implements RequestPredicate {
436505

437506
private final Set<HttpMethod> httpMethods;
@@ -482,7 +551,7 @@ public String toString() {
482551
}
483552

484553

485-
private static class PathPatternPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
554+
private static class PathPatternPredicate extends EvaluatorRequestPredicate implements ChangePathPatternParserVisitor.Target {
486555

487556
private PathPattern pattern;
488557

@@ -492,29 +561,28 @@ public PathPatternPredicate(PathPattern pattern) {
492561
}
493562

494563
@Override
495-
public boolean test(ServerRequest request) {
564+
public Evaluation apply(ServerRequest request) {
496565
PathContainer pathContainer = request.requestPath().pathWithinApplication();
497566
PathPattern.PathMatchInfo info = this.pattern.matchAndExtract(pathContainer);
498567
traceMatch("Pattern", this.pattern.getPatternString(), request.path(), info != null);
499-
if (info != null) {
500-
mergeAttributes(request, info.getUriVariables(), this.pattern);
501-
return true;
502-
}
503-
else {
504-
return false;
568+
if (info == null) {
569+
return Evaluation.FALSE;
505570
}
506-
}
507-
508-
private static void mergeAttributes(ServerRequest request, Map<String, String> variables,
509-
PathPattern pattern) {
510-
Map<String, String> pathVariables = mergePathVariables(request.pathVariables(), variables);
511-
request.attributes().put(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE,
512-
Collections.unmodifiableMap(pathVariables));
513-
514-
pattern = mergePatterns(
515-
(PathPattern) request.attributes().get(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE),
516-
pattern);
517-
request.attributes().put(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE, pattern);
571+
return new Evaluation(true) {
572+
@Override
573+
void doCommit() {
574+
Map<String, Object> attributes = request.attributes();
575+
Map<String, String> pathVariables = mergePathVariables(request.pathVariables(),
576+
info.getUriVariables());
577+
attributes.put(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE,
578+
Collections.unmodifiableMap(pathVariables));
579+
580+
PathPattern newPattern = mergePatterns(
581+
(PathPattern) attributes.get(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE),
582+
PathPatternPredicate.this.pattern);
583+
attributes.put(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE, newPattern);
584+
}
585+
};
518586
}
519587

520588
@Override
@@ -756,28 +824,40 @@ public String toString() {
756824
* {@link RequestPredicate} for where both {@code left} and {@code right} predicates
757825
* must match.
758826
*/
759-
static class AndRequestPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
827+
static class AndRequestPredicate extends EvaluatorRequestPredicate implements ChangePathPatternParserVisitor.Target {
760828

761829
private final RequestPredicate left;
830+
private final Evaluator leftEvaluator;
762831

763832
private final RequestPredicate right;
833+
private final Evaluator rightEvaluator;
764834

765835
public AndRequestPredicate(RequestPredicate left, RequestPredicate right) {
766836
Assert.notNull(left, "Left RequestPredicate must not be null");
767837
Assert.notNull(right, "Right RequestPredicate must not be null");
768838
this.left = left;
839+
this.leftEvaluator = Evaluator.of(left);
769840
this.right = right;
841+
this.rightEvaluator = Evaluator.of(right);
770842
}
771843

772844
@Override
773-
public boolean test(ServerRequest request) {
774-
Map<String, Object> oldAttributes = new HashMap<>(request.attributes());
775-
776-
if (this.left.test(request) && this.right.test(request)) {
777-
return true;
845+
public Evaluation apply(ServerRequest request) {
846+
Evaluation leftResult = this.leftEvaluator.apply(request);
847+
if (!leftResult.value) {
848+
return leftResult;
849+
}
850+
Evaluation rightResult = this.rightEvaluator.apply(request);
851+
if (!rightResult.value) {
852+
return rightResult;
778853
}
779-
restoreAttributes(request, oldAttributes);
780-
return false;
854+
return new Evaluation(true) {
855+
@Override
856+
void doCommit() {
857+
leftResult.doCommit();
858+
rightResult.doCommit();
859+
}
860+
};
781861
}
782862

783863
@Override
@@ -814,23 +894,26 @@ public String toString() {
814894
/**
815895
* {@link RequestPredicate} that negates a delegate predicate.
816896
*/
817-
static class NegateRequestPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
897+
static class NegateRequestPredicate extends EvaluatorRequestPredicate implements ChangePathPatternParserVisitor.Target {
818898

819899
private final RequestPredicate delegate;
900+
private final Evaluator delegateEvaluator;
820901

821902
public NegateRequestPredicate(RequestPredicate delegate) {
822903
Assert.notNull(delegate, "Delegate must not be null");
823904
this.delegate = delegate;
905+
this.delegateEvaluator = Evaluator.of(delegate);
824906
}
825907

826908
@Override
827-
public boolean test(ServerRequest request) {
828-
Map<String, Object> oldAttributes = new HashMap<>(request.attributes());
829-
boolean result = !this.delegate.test(request);
830-
if (!result) {
831-
restoreAttributes(request, oldAttributes);
832-
}
833-
return result;
909+
public Evaluation apply(ServerRequest request) {
910+
Evaluation result = this.delegateEvaluator.apply(request);
911+
return new Evaluation(!result.value) {
912+
@Override
913+
void doCommit() {
914+
result.doCommit();
915+
}
916+
};
834917
}
835918

836919
@Override
@@ -858,34 +941,30 @@ public String toString() {
858941
* {@link RequestPredicate} where either {@code left} or {@code right} predicates
859942
* may match.
860943
*/
861-
static class OrRequestPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
944+
static class OrRequestPredicate extends EvaluatorRequestPredicate implements ChangePathPatternParserVisitor.Target {
862945

863946
private final RequestPredicate left;
947+
private final Evaluator leftEvaluator;
864948

865949
private final RequestPredicate right;
950+
private final Evaluator rightEvaluator;
866951

867952
public OrRequestPredicate(RequestPredicate left, RequestPredicate right) {
868953
Assert.notNull(left, "Left RequestPredicate must not be null");
869954
Assert.notNull(right, "Right RequestPredicate must not be null");
870955
this.left = left;
956+
this.leftEvaluator = Evaluator.of(left);
871957
this.right = right;
958+
this.rightEvaluator = Evaluator.of(right);
872959
}
873960

874961
@Override
875-
public boolean test(ServerRequest request) {
876-
Map<String, Object> oldAttributes = new HashMap<>(request.attributes());
877-
878-
if (this.left.test(request)) {
879-
return true;
880-
}
881-
else {
882-
restoreAttributes(request, oldAttributes);
883-
if (this.right.test(request)) {
884-
return true;
885-
}
962+
public Evaluation apply(ServerRequest request) {
963+
Evaluation leftResult = this.leftEvaluator.apply(request);
964+
if (leftResult.value) {
965+
return leftResult;
886966
}
887-
restoreAttributes(request, oldAttributes);
888-
return false;
967+
return this.rightEvaluator.apply(request);
889968
}
890969

891970
@Override

spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RequestPredicateAttributesTests.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
import org.springframework.core.codec.StringDecoder;
2525
import org.springframework.http.codec.DecoderHttpMessageReader;
26+
import org.springframework.web.reactive.function.server.RequestPredicates.Evaluation;
27+
import org.springframework.web.reactive.function.server.RequestPredicates.EvaluatorRequestPredicate;
2628
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest;
2729
import org.springframework.web.testfixture.server.MockServerWebExchange;
2830

@@ -182,7 +184,7 @@ public void orBothFail() {
182184
}
183185

184186

185-
private static class AddAttributePredicate implements RequestPredicate {
187+
private static class AddAttributePredicate extends EvaluatorRequestPredicate {
186188

187189
private boolean result;
188190

@@ -197,9 +199,13 @@ private AddAttributePredicate(boolean result, String key, String value) {
197199
}
198200

199201
@Override
200-
public boolean test(ServerRequest request) {
201-
request.attributes().put(key, value);
202-
return this.result;
202+
public Evaluation apply(ServerRequest request) {
203+
return new Evaluation(result) {
204+
@Override
205+
void doCommit() {
206+
request.attributes().put(key, value);
207+
}
208+
};
203209
}
204210
}
205211

0 commit comments

Comments
 (0)