Skip to content

Commit b0b68a4

Browse files
committed
Merge branch '4.3.x'
2 parents 8ef3612 + dd54ae7 commit b0b68a4

2 files changed

Lines changed: 151 additions & 1 deletion

File tree

spring-cloud-gateway-server-webflux/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private DiscoveryClientRouteDefinitionLocator(String discoveryClientName, Discov
7575
else {
7676
routeIdPrefix = discoveryClientName + "_";
7777
}
78-
evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build();
78+
evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().build();
7979
}
8080

8181
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* Copyright 2013-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.gateway.discovery;
18+
19+
import java.net.URI;
20+
import java.util.HashMap;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
import org.junit.jupiter.api.Test;
25+
import reactor.core.publisher.Flux;
26+
27+
import org.springframework.cloud.client.DefaultServiceInstance;
28+
import org.springframework.cloud.client.ServiceInstance;
29+
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
30+
import org.springframework.cloud.gateway.route.RouteDefinition;
31+
import org.springframework.expression.spel.SpelEvaluationException;
32+
33+
import static org.assertj.core.api.Assertions.assertThat;
34+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
35+
import static org.mockito.Mockito.mock;
36+
import static org.mockito.Mockito.when;
37+
38+
/**
39+
* Failures are not always the outer or innermost throwable: Reactor may wrap the signal, and Spring SpEL often
40+
* wraps an {@link org.springframework.expression.AccessException} inside {@link SpelEvaluationException}, so the
41+
* assertion walks the {@linkplain Throwable#getCause() cause chain}.
42+
*/
43+
public class DiscoveryClientRouteDefinitionLocatorServiceInstanceSpelTests {
44+
45+
@Test
46+
public void urlExpressionSpelInstanceMethodCallThrowsSpelEvaluationException() {
47+
DefaultServiceInstance instance = new DefaultServiceInstance("my-service-1", "my-service", "before-spel-host",
48+
8080, false);
49+
50+
ReactiveDiscoveryClient discoveryClient = mock(ReactiveDiscoveryClient.class);
51+
when(discoveryClient.getServices()).thenReturn(Flux.just("my-service"));
52+
when(discoveryClient.getInstances("my-service")).thenReturn(Flux.just(instance));
53+
54+
DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
55+
properties.setUrlExpression("setHost('after-spel-host') ?: ('lb://' + host)");
56+
57+
DiscoveryClientRouteDefinitionLocator locator = new DiscoveryClientRouteDefinitionLocator(discoveryClient,
58+
properties);
59+
60+
assertThatThrownBy(() -> locator.getRouteDefinitions().collectList().block())
61+
.satisfies(t -> assertThat(throwableChainContains(t, SpelEvaluationException.class))
62+
.as("SpEL should reject instance method calls; cause chain: %s", formatChain(t))
63+
.isTrue());
64+
65+
assertThat(instance.getHost()).isEqualTo("before-spel-host");
66+
}
67+
68+
@Test
69+
public void urlExpressionSpelWorksOnMetadata() {
70+
ServiceInstance instance = new ServiceInstance() {
71+
@Override
72+
public String getServiceId() {
73+
return "test";
74+
}
75+
76+
@Override
77+
public String getHost() {
78+
return "myhost";
79+
}
80+
81+
@Override
82+
public int getPort() {
83+
return 8080;
84+
}
85+
86+
@Override
87+
public boolean isSecure() {
88+
return false;
89+
}
90+
91+
@Override
92+
public URI getUri() {
93+
return URI.create("http://localhost:8080/my-service-1");
94+
}
95+
96+
@Override
97+
public Map<String, String> getMetadata() {
98+
Map<String, String> metadata = new HashMap<>();
99+
metadata.put("override-host", "http://host-override");
100+
return metadata;
101+
}
102+
};
103+
104+
105+
ReactiveDiscoveryClient discoveryClient = mock(ReactiveDiscoveryClient.class);
106+
when(discoveryClient.getServices()).thenReturn(Flux.just("my-service"));
107+
when(discoveryClient.getInstances("my-service")).thenReturn(Flux.just(instance));
108+
109+
DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
110+
properties.setUrlExpression("metadata['override-host'] ?: host");
111+
112+
DiscoveryClientRouteDefinitionLocator locator = new DiscoveryClientRouteDefinitionLocator(discoveryClient,
113+
properties);
114+
115+
List<RouteDefinition> routeDefinitions = locator.getRouteDefinitions().collectList().block();
116+
assertThat(routeDefinitions).hasSize(1);
117+
assertThat(routeDefinitions.get(0).getUri().getHost()).isEqualTo("host-override");
118+
119+
}
120+
121+
private static boolean throwableChainContains(Throwable throwable, Class<?> type) {
122+
if (throwable == null) {
123+
return false;
124+
}
125+
if (type.isInstance(throwable)) {
126+
return true;
127+
}
128+
if (throwableChainContains(throwable.getCause(), type)) {
129+
return true;
130+
}
131+
for (Throwable suppressed : throwable.getSuppressed()) {
132+
if (throwableChainContains(suppressed, type)) {
133+
return true;
134+
}
135+
}
136+
return false;
137+
}
138+
139+
private static String formatChain(Throwable throwable) {
140+
StringBuilder sb = new StringBuilder();
141+
for (Throwable current = throwable; current != null; current = current.getCause()) {
142+
sb.append(current.getClass().getName()).append(": ").append(current.getMessage()).append(" -> ");
143+
}
144+
if (sb.length() >= 4) {
145+
sb.setLength(sb.length() - 4);
146+
}
147+
return sb.toString();
148+
}
149+
150+
}

0 commit comments

Comments
 (0)