This is an automated email from the ASF dual-hosted git repository.
wy471x pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 4e44c60bcd [ISSUE #7452] skip caching when cache rule handle is null
(#7541)
4e44c60bcd is described below
commit 4e44c60bcd1ce8b38b41d6c22adc41926d5a74e8
Author: Emrys <[email protected]>
AuthorDate: Sun Oct 11 18:50:33 2026 +0800
[ISSUE #7452] skip caching when cache rule handle is null (#7541)
CachePluginDataHandler skips rules whose handle is null, so CachePlugin
could not find a CacheRuleHandle for such a rule and CacheHttpResponse
threw a NullPointerException on getTimeoutSeconds() while writing an
otherwise successful downstream response.
When no rule handle is cached, CachePlugin now passes the request to the
rest of the chain without reading or writing the cache, the same way the
Hystrix, Resilience4J, Sentinel and RateLimiter plugins handle a missing
handle (#6877).
Add regression tests for a rule without a handle and for a rule with a
handle.
Fixes #7452
---
.../apache/shenyu/plugin/cache/CachePlugin.java | 6 +-
.../shenyu/plugin/cache/CachePluginTest.java | 66 ++++++++++++++++++++++
2 files changed, 70 insertions(+), 2 deletions(-)
diff --git
a/shenyu-plugin/shenyu-plugin-cache/shenyu-plugin-cache-handler/src/main/java/org/apache/shenyu/plugin/cache/CachePlugin.java
b/shenyu-plugin/shenyu-plugin-cache/shenyu-plugin-cache-handler/src/main/java/org/apache/shenyu/plugin/cache/CachePlugin.java
index d89fe42789..93f114ac38 100644
---
a/shenyu-plugin/shenyu-plugin-cache/shenyu-plugin-cache-handler/src/main/java/org/apache/shenyu/plugin/cache/CachePlugin.java
+++
b/shenyu-plugin/shenyu-plugin-cache/shenyu-plugin-cache-handler/src/main/java/org/apache/shenyu/plugin/cache/CachePlugin.java
@@ -52,6 +52,10 @@ public class CachePlugin extends AbstractShenyuPlugin {
@Override
public Mono<Void> doExecute(final ServerWebExchange exchange, final
ShenyuPluginChain chain,
final SelectorData selector, final RuleData
rule) {
+ final CacheRuleHandle cacheRuleHandle = buildRuleHandle(rule);
+ if (Objects.isNull(cacheRuleHandle)) {
+ return chain.execute(exchange);
+ }
CacheUpstream cacheUpstream =
GsonUtils.getInstance().fromJson(selector.getHandle(), CacheUpstream.class);
ICache cache = getCache(selector.getId(), cacheUpstream);
if (Objects.nonNull(cache)) {
@@ -67,11 +71,9 @@ public class CachePlugin extends AbstractShenyuPlugin {
return
exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(bytes))
.doOnNext(data ->
exchange.getResponse().getHeaders().setContentLength(data.readableByteCount())));
}
- CacheRuleHandle cacheRuleHandle =
buildRuleHandle(rule);
return chain.execute(exchange.mutate().response(new
CacheHttpResponse(exchange, cacheRuleHandle, selector.getId())).build());
});
}
- CacheRuleHandle cacheRuleHandle = buildRuleHandle(rule);
return chain.execute(exchange.mutate().response(new
CacheHttpResponse(exchange, cacheRuleHandle, selector.getId())).build());
}
diff --git
a/shenyu-plugin/shenyu-plugin-cache/shenyu-plugin-cache-handler/src/test/java/org/apache/shenyu/plugin/cache/CachePluginTest.java
b/shenyu-plugin/shenyu-plugin-cache/shenyu-plugin-cache-handler/src/test/java/org/apache/shenyu/plugin/cache/CachePluginTest.java
index e2198a0414..0454ad33b1 100644
---
a/shenyu-plugin/shenyu-plugin-cache/shenyu-plugin-cache-handler/src/test/java/org/apache/shenyu/plugin/cache/CachePluginTest.java
+++
b/shenyu-plugin/shenyu-plugin-cache/shenyu-plugin-cache-handler/src/test/java/org/apache/shenyu/plugin/cache/CachePluginTest.java
@@ -146,4 +146,70 @@ public class CachePluginTest {
StepVerifier.create(result3).expectSubscription().verifyComplete();
}
+ @Test
+ public void testDoExecuteSkipsCacheWhenRuleHandleIsNull() {
+ final ConfigurableApplicationContext context =
mock(ConfigurableApplicationContext.class);
+ when(context.getBean(ShenyuResult.class)).thenReturn(new
DefaultShenyuResult());
+ SpringBeanUtils.getInstance().setApplicationContext(context);
+ final MockServerWebExchange exchange =
MockServerWebExchange.from(MockServerHttpRequest.get("/cache-null-handle-path").build());
+ final MemoryCache memoryCache = new MemoryCache();
+ Singleton.INST.single(ICache.class, memoryCache);
+ final RuleData ruleData = new RuleData();
+ ruleData.setSelectorId("cache-null-handle-selector");
+ ruleData.setId("cache-null-handle-rule");
+ final String key = CacheKeyUtils.INST.getKey(ruleData);
+ try {
+ CachePluginDataHandler.CACHED_HANDLE.get().removeHandle(key);
+ final SelectorData selectorData = new SelectorData();
+ selectorData.setId("cache-null-handle-selector");
+ final ShenyuPluginChain chain = mock(ShenyuPluginChain.class);
+ when(chain.execute(any())).thenAnswer(invocation -> {
+ final ServerWebExchange downstream = invocation.getArgument(0);
+
downstream.getResponse().getHeaders().setContentType(MediaType.TEXT_PLAIN);
+ return downstream.getResponse().writeWith(Mono.just(
+
downstream.getResponse().bufferFactory().wrap("body".getBytes(StandardCharsets.UTF_8))));
+ });
+ final Mono<Void> result = new CachePlugin().doExecute(exchange,
chain, selectorData, ruleData);
+ StepVerifier.create(result).verifyComplete();
+ Assertions.assertEquals("body",
exchange.getResponse().getBodyAsString().block());
+
Assertions.assertNull(memoryCache.getData(CacheUtils.dataKey(exchange)).block());
+ } finally {
+ CachePluginDataHandler.CACHED_HANDLE.get().removeHandle(key);
+ memoryCache.close();
+ }
+ }
+
+ @Test
+ public void testDoExecuteCachesResponseWhenRuleHandleIsPresent() {
+ final ConfigurableApplicationContext context =
mock(ConfigurableApplicationContext.class);
+ when(context.getBean(ShenyuResult.class)).thenReturn(new
DefaultShenyuResult());
+ SpringBeanUtils.getInstance().setApplicationContext(context);
+ final MockServerWebExchange exchange =
MockServerWebExchange.from(MockServerHttpRequest.get("/cache-with-handle-path").build());
+ final MemoryCache memoryCache = new MemoryCache();
+ Singleton.INST.single(ICache.class, memoryCache);
+ final RuleData ruleData = new RuleData();
+ ruleData.setSelectorId("cache-with-handle-selector");
+ ruleData.setId("cache-with-handle-rule");
+ final String key = CacheKeyUtils.INST.getKey(ruleData);
+ try {
+ CachePluginDataHandler.CACHED_HANDLE.get().cachedHandle(key, new
CacheRuleHandle());
+ final SelectorData selectorData = new SelectorData();
+ selectorData.setId("cache-with-handle-selector");
+ final ShenyuPluginChain chain = mock(ShenyuPluginChain.class);
+ when(chain.execute(any())).thenAnswer(invocation -> {
+ final ServerWebExchange downstream = invocation.getArgument(0);
+
downstream.getResponse().getHeaders().setContentType(MediaType.TEXT_PLAIN);
+ return downstream.getResponse().writeWith(Mono.just(
+
downstream.getResponse().bufferFactory().wrap("body".getBytes(StandardCharsets.UTF_8))));
+ });
+ final Mono<Void> result = new CachePlugin().doExecute(exchange,
chain, selectorData, ruleData);
+ StepVerifier.create(result).verifyComplete();
+ Assertions.assertEquals("body",
exchange.getResponse().getBodyAsString().block());
+
Assertions.assertArrayEquals("body".getBytes(StandardCharsets.UTF_8),
memoryCache.getData(CacheUtils.dataKey(exchange)).block());
+ } finally {
+ CachePluginDataHandler.CACHED_HANDLE.get().removeHandle(key);
+ memoryCache.close();
+ }
+ }
+
}