[type: BUG] fix fallback issue (#5496)
* [ISSUE #5494] fix fallback issue
* [ISSUE #5494]
* avoid redirect loop
* add unit test
---------
Co-authored-by: ray <zhongzhenchao@ingbaobei.com>
Co-authored-by: moremind <hefengen@apache.org>
Co-authored-by: xiaoyu <xiaoyu@apache.org>
Co-authored-by: loongs-zhang <zhangzicheng@apache.org>
diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/utils/UriUtils.java b/shenyu-common/src/main/java/org/apache/shenyu/common/utils/UriUtils.java
index 3a1fde3..6adf4df 100644
--- a/shenyu-common/src/main/java/org/apache/shenyu/common/utils/UriUtils.java
+++ b/shenyu-common/src/main/java/org/apache/shenyu/common/utils/UriUtils.java
@@ -43,6 +43,18 @@
}
/**
+ * create URI {@link URI}.
+ *
+ * @param scheme scheme eg:http
+ * @param authority registry or server eg: 127.0.0.1:8080
+ * @param path path eg:/ fallback
+ * @return created {@link URI} from uri
+ */
+ public static URI createUri(final String scheme, final String authority, final String path) {
+ return createUri(scheme + "://" + authority + repairData(path));
+ }
+
+ /**
* Repair data string.
*
* @param name the name
diff --git a/shenyu-common/src/test/java/org/apache/shenyu/common/utils/UriUtilsTest.java b/shenyu-common/src/test/java/org/apache/shenyu/common/utils/UriUtilsTest.java
index 3047e68..b4a686a 100644
--- a/shenyu-common/src/test/java/org/apache/shenyu/common/utils/UriUtilsTest.java
+++ b/shenyu-common/src/test/java/org/apache/shenyu/common/utils/UriUtilsTest.java
@@ -41,6 +41,9 @@
uri = UriUtils.createUri("");
assertNull(uri);
+
+ uri = UriUtils.createUri("https", "example.com", "/http");
+ assertEquals("https://example.com/http", uri.toString());
}
@Test
diff --git a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/fallback/FallbackHandler.java b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/fallback/FallbackHandler.java
index 5d4aeab..cfd4c93 100644
--- a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/fallback/FallbackHandler.java
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/fallback/FallbackHandler.java
@@ -58,12 +58,18 @@
// client HttpStatusCodeException, return the client response directly
if (t instanceof HttpStatusCodeException || Objects.isNull(uri)) {
return withoutFallback(exchange, t);
- }
+ }
if (uri.toString().startsWith(PREFIX)) {
- String fallbackUri = uri.toString().substring(PREFIX.length());
+ String fallbackPath = uri.toString().substring(PREFIX.length());
DispatcherHandler dispatcherHandler =
SpringBeanUtils.getInstance().getBean(DispatcherHandler.class);
- ServerHttpRequest request = exchange.getRequest().mutate().uri(URI.create(fallbackUri)).build();
+ URI previsouUri = exchange.getRequest().getURI();
+ // avoid redirect loop, return error.
+ if (UriUtils.getPathWithParams(previsouUri).equals(fallbackPath)) {
+ return withoutFallback(exchange, t);
+ }
+ URI fallbackUri = UriUtils.createUri(previsouUri.getScheme(), previsouUri.getAuthority(), fallbackPath);
+ ServerHttpRequest request = exchange.getRequest().mutate().uri(fallbackUri).build();
ServerWebExchange mutated = exchange.mutate().request(request).build();
return dispatcherHandler.handle(mutated);
}
diff --git a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/fallback/FallbackHandlerTest.java b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/fallback/FallbackHandlerTest.java
index 9b3de9a..b810d5b 100644
--- a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/fallback/FallbackHandlerTest.java
+++ b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/fallback/FallbackHandlerTest.java
@@ -35,6 +35,7 @@
import java.net.InetSocketAddress;
import java.net.URI;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -50,6 +51,8 @@
private TestFallbackHandler testFallbackHandler;
+ private TestFallbackAvoidRedirectLoopHandler testFallbackAvoidRedirectLoopHandler;
+
@BeforeEach
public void setUp() {
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
@@ -62,6 +65,7 @@
.build());
when(handler.handle(any())).thenReturn(Mono.empty());
this.testFallbackHandler = new TestFallbackHandler();
+ this.testFallbackAvoidRedirectLoopHandler = new TestFallbackAvoidRedirectLoopHandler();
}
/**
@@ -76,15 +80,32 @@
* The fallback test.
*/
@Test
- public void fallbackTest() {
+ public void httpFallbackPrefixTest() {
StepVerifier.create(testFallbackHandler.fallback(exchange, null, mock(RuntimeException.class))).expectSubscription().verifyComplete();
StepVerifier.create(testFallbackHandler.fallback(exchange, URI.create("http://127.0.0.1:8090/SHENYU"), mock(RuntimeException.class))).expectSubscription().verifyComplete();
}
+ /**
+ * The fallback test.
+ */
+ @Test
+ public void fallbackPrefixTest() {
+ StepVerifier.create(testFallbackHandler.fallback(exchange, URI.create("fallback:/SHENYU"), mock(RuntimeException.class))).expectSubscription().verifyComplete();
+ assertThrows(RuntimeException.class, () -> StepVerifier.create(testFallbackAvoidRedirectLoopHandler.fallback(exchange,
+ URI.create("fallback:/SHENYU/SHENYU"), mock(RuntimeException.class))).expectSubscription().verifyComplete());
+ }
+
static class TestFallbackHandler implements FallbackHandler {
@Override
public Mono<Void> withoutFallback(final ServerWebExchange exchange, final Throwable throwable) {
return Mono.empty();
}
}
+
+ static class TestFallbackAvoidRedirectLoopHandler implements FallbackHandler {
+ @Override
+ public Mono<Void> withoutFallback(final ServerWebExchange exchange, final Throwable throwable) {
+ throw new RuntimeException(throwable.getCause());
+ }
+ }
}