From 6c548c75c28bfcf7a58c12ba194924c7cfd86890 Mon Sep 17 00:00:00 2001 From: tuzhe Date: Sun, 1 Sep 2024 16:28:59 +0800 Subject: [PATCH] =?UTF-8?q?Solon=20Cloud=20Gateway=20=E6=B7=BB=E5=8A=A0=20?= =?UTF-8?q?RewritePaht=E8=B7=AF=E7=94=B1=E8=BF=87=E6=BB=A4=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/route/RouteFactoryManager.java | 2 +- .../filter/RewritePathFilterFactory.java | 71 ++++++++++++ .../exFilter/RewritePathFilterTest.java | 108 ++++++++++++++++++ 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 solon-projects/solon-cloud/solon-cloud-gateway/src/main/java/org/noear/solon/cloud/gateway/route/filter/RewritePathFilterFactory.java create mode 100644 solon-projects/solon-cloud/solon-cloud-gateway/src/test/java/features/exFilter/RewritePathFilterTest.java diff --git a/solon-projects/solon-cloud/solon-cloud-gateway/src/main/java/org/noear/solon/cloud/gateway/route/RouteFactoryManager.java b/solon-projects/solon-cloud/solon-cloud-gateway/src/main/java/org/noear/solon/cloud/gateway/route/RouteFactoryManager.java index 2eb9a5c139..dc2bb08cff 100644 --- a/solon-projects/solon-cloud/solon-cloud-gateway/src/main/java/org/noear/solon/cloud/gateway/route/RouteFactoryManager.java +++ b/solon-projects/solon-cloud/solon-cloud-gateway/src/main/java/org/noear/solon/cloud/gateway/route/RouteFactoryManager.java @@ -19,7 +19,6 @@ import org.noear.solon.Utils; import org.noear.solon.cloud.gateway.exchange.ExFilter; import org.noear.solon.cloud.gateway.exchange.ExPredicate; import org.noear.solon.cloud.gateway.route.filter.*; -import org.noear.solon.cloud.gateway.route.predicate.HeaderPredicateFactory; import org.noear.solon.cloud.gateway.route.predicate.*; import org.noear.solon.core.util.RankEntity; import org.noear.solon.lang.Nullable; @@ -61,6 +60,7 @@ public class RouteFactoryManager { addFactory(new AddResponseHeaderFilterFactory()); addFactory(new PrefixPathFilterFactory()); addFactory(new RedirectToFilterFactory()); + addFactory(new RewritePathFilterFactory ()); addFactory(new RemoveRequestHeaderFilterFactory()); addFactory(new RemoveResponseHeaderFilterFactory()); diff --git a/solon-projects/solon-cloud/solon-cloud-gateway/src/main/java/org/noear/solon/cloud/gateway/route/filter/RewritePathFilterFactory.java b/solon-projects/solon-cloud/solon-cloud-gateway/src/main/java/org/noear/solon/cloud/gateway/route/filter/RewritePathFilterFactory.java new file mode 100644 index 0000000000..7cc10c44df --- /dev/null +++ b/solon-projects/solon-cloud/solon-cloud-gateway/src/main/java/org/noear/solon/cloud/gateway/route/filter/RewritePathFilterFactory.java @@ -0,0 +1,71 @@ +package org.noear.solon.cloud.gateway.route.filter; + +import org.noear.solon.Utils; +import org.noear.solon.cloud.gateway.exchange.ExContext; +import org.noear.solon.cloud.gateway.exchange.ExFilter; +import org.noear.solon.cloud.gateway.exchange.ExFilterChain; +import org.noear.solon.cloud.gateway.route.RouteFilterFactory; +import org.noear.solon.rx.Completable; + +import java.util.regex.Pattern; + +/** + * 重写路径路由过滤器 + * 当配置信息为 RewritePath=/red/(?.*), /$\{segment}时, + * 该处理器会将路径进行如下替换 /red/(?.*) => /$\{segment} + * 例: + * http://ip:port/red/hello => http://ip2:port2/hello + * + * @author TuZhe + * @since 2.9 + **/ +public class RewritePathFilterFactory implements RouteFilterFactory { + + @Override + public String prefix() { + return "RewritePath"; //魔法值, 不建议 + } + + @Override + public ExFilter create(String config) { + return new RewritePathFilter (config); + } + + public static class RewritePathFilter implements ExFilter { + private final String replacement; + private final Pattern pattern; + + // if [RewritePath=/red/hello, /hello] then config = [/red/hello, /hello]] + public RewritePathFilter(String config) { + if(Utils.isBlank (config)) { + throw new IllegalArgumentException ("RewritePathFilter config cannot be blank"); + } + + String[] parts = config.split (","); //应该统一符号常量 + if(parts.length != 2) { + throw new IllegalArgumentException ("RewritePathFilter config is wrong: " + config); + //throw new IllegalArgumentException ("RewritePath config is incorrect, such as /red/(?.*), /$\\{segment} "); + } + String regex = parts[0].trim (); + String rawReplacement = parts[1].trim (); + + if(!regex.startsWith ("/") || !rawReplacement.startsWith ("/")) { + throw new IllegalArgumentException ("RewritePathFilter config is wrong, path must be start with slash, config is : " + config); + } + pattern = Pattern.compile (regex); + replacement = rawReplacement.replace ("$\\", "$"); + } + + @Override + public Completable doFilter(ExContext ctx, ExFilterChain chain) { + //此处 newRequest() 并非 new XXX()!!! + String path = ctx.newRequest ().getPath (); + //TODO 这里需要优化,因为每次都要匹配,考虑使用缓存, + // 但针对于 /path/{PathVariable}路径,后续会无限膨胀,占用内存的同时,命中率极低 + String newPath = pattern.matcher (path).replaceAll (replacement); + ctx.newRequest ().path (newPath); + return chain.doFilter (ctx); + } + } + +} diff --git a/solon-projects/solon-cloud/solon-cloud-gateway/src/test/java/features/exFilter/RewritePathFilterTest.java b/solon-projects/solon-cloud/solon-cloud-gateway/src/test/java/features/exFilter/RewritePathFilterTest.java new file mode 100644 index 0000000000..2cb7679a23 --- /dev/null +++ b/solon-projects/solon-cloud/solon-cloud-gateway/src/test/java/features/exFilter/RewritePathFilterTest.java @@ -0,0 +1,108 @@ +/* + * Copyright 2017-2024 noear.org and 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 features.exFilter; + +import features.ExContextEmpty; +import org.junit.jupiter.api.Test; +import org.noear.solon.cloud.gateway.exchange.ExFilter; +import org.noear.solon.cloud.gateway.exchange.ExNewRequest; +import org.noear.solon.cloud.gateway.route.RouteFactoryManager; +import org.noear.solon.rx.Completable; +import org.noear.solon.rx.impl.CompletableSubscriberSimple; + +/** + * @author noear 2024/8/21 created + */ +public class RewritePathFilterTest { + @Test + public void testValidConfig() { + ExFilter filter = RouteFactoryManager.buildFilter ( + "RewritePath=/red/?(?.*), /$\\{segment}"); + + assert filter != null; + + ExNewRequest newRequest = new ExNewRequest (); + newRequest.path ("/red/test"); + filter.doFilter (new ExContextEmpty () { + @Override + public ExNewRequest newRequest() { + return newRequest; + } + }, ctx -> Completable.complete ()).subscribe (new CompletableSubscriberSimple ()); + + assert "/test".equals (newRequest.getPath ()); + } + + + @Test + public void testValidConfig1() { + ExFilter filter = RouteFactoryManager.buildFilter ( + "RewritePath=/(?.*), /$\\{segment}"); + + assert filter != null; + + ExNewRequest newRequest = new ExNewRequest (); + newRequest.path ("/test"); + filter.doFilter (new ExContextEmpty () { + @Override + public ExNewRequest newRequest() { + return newRequest; + } + }, ctx -> Completable.complete ()).subscribe (new CompletableSubscriberSimple ()); + + assert "/test".equals (newRequest.getPath ()); + } + + + @Test + public void testValidConfig2() { + ExFilter filter = RouteFactoryManager.buildFilter ( + "RewritePath=/red/blue/(?.*), /$\\{segment}"); + + assert filter != null; + + ExNewRequest newRequest = new ExNewRequest (); + newRequest.path ("/red/blue/test"); + filter.doFilter (new ExContextEmpty () { + @Override + public ExNewRequest newRequest() { + return newRequest; + } + }, ctx -> Completable.complete ()).subscribe (new CompletableSubscriberSimple ()); + + assert "/test".equals (newRequest.getPath ()); + } + + + @Test + public void testValidConfig3() { + ExFilter filter = RouteFactoryManager.buildFilter ( + "RewritePath=/red/blue/?(?.*), /$\\{segment}"); + + assert filter != null; + + ExNewRequest newRequest = new ExNewRequest (); + newRequest.path ("/red/blue/test"); + filter.doFilter (new ExContextEmpty () { + @Override + public ExNewRequest newRequest() { + return newRequest; + } + }, ctx -> Completable.complete ()).subscribe (new CompletableSubscriberSimple ()); + + assert "/test".equals (newRequest.getPath ()); + } +} -- Gitee