From 0fdd053b7a208cd067cd8691f03a94b54de8a328 Mon Sep 17 00:00:00 2001 From: luyanbo Date: Wed, 28 Feb 2024 19:11:41 +0800 Subject: [PATCH 01/11] use logback,commons-lang3 --- .../sentinel-cluster-server-envoy-rls/pom.xml | 2 +- sentinel-dashboard/pom.xml | 16 +++------------- .../auth/DefaultLoginAuthenticationFilter.java | 3 +-- .../dashboard/config/DashboardConfig.java | 4 ++-- .../dashboard/controller/AuthController.java | 2 +- .../gateway/GatewayApiControllerTest.java | 2 +- .../gateway/GatewayFlowRuleControllerTest.java | 2 +- .../rule/zookeeper/ZookeeperConfigUtil.java | 3 +-- 8 files changed, 11 insertions(+), 23 deletions(-) diff --git a/sentinel-cluster/sentinel-cluster-server-envoy-rls/pom.xml b/sentinel-cluster/sentinel-cluster-server-envoy-rls/pom.xml index 30a713a806..97b17f23e0 100644 --- a/sentinel-cluster/sentinel-cluster-server-envoy-rls/pom.xml +++ b/sentinel-cluster/sentinel-cluster-server-envoy-rls/pom.xml @@ -85,7 +85,7 @@ kr.motd.maven os-maven-plugin - 1.6.2 + 1.7.1 diff --git a/sentinel-dashboard/pom.xml b/sentinel-dashboard/pom.xml index f93c5523f9..47184ade5d 100644 --- a/sentinel-dashboard/pom.xml +++ b/sentinel-dashboard/pom.xml @@ -61,15 +61,9 @@ - log4j - log4j - 1.2.17 - - - - commons-lang - commons-lang - 2.6 + org.apache.commons + commons-lang3 + 3.14.0 @@ -158,10 +152,6 @@ org.springframework.boot spring-boot-maven-plugin ${spring.boot.version} - - true - com.alibaba.csp.sentinel.dashboard.DashboardApplication - diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultLoginAuthenticationFilter.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultLoginAuthenticationFilter.java index 774dab36f3..501e763428 100644 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultLoginAuthenticationFilter.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultLoginAuthenticationFilter.java @@ -15,8 +15,7 @@ */ package com.alibaba.csp.sentinel.dashboard.auth; -import org.apache.commons.lang.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.util.AntPathMatcher; diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java index 92e0608ec3..83c0838222 100644 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java @@ -18,8 +18,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.math.NumberUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.springframework.lang.NonNull; /** diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java index 8b01aed232..eb584a019e 100644 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java @@ -19,7 +19,7 @@ import com.alibaba.csp.sentinel.dashboard.auth.SimpleWebAuthServiceImpl; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; import com.alibaba.csp.sentinel.dashboard.domain.Result; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiControllerTest.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiControllerTest.java index f59955f0a4..5f576c7d8d 100644 --- a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiControllerTest.java +++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiControllerTest.java @@ -29,7 +29,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; -import org.apache.commons.lang.time.DateUtils; +import org.apache.commons.lang3.time.DateUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleControllerTest.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleControllerTest.java index 974c2c1d57..26250b4c92 100644 --- a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleControllerTest.java +++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleControllerTest.java @@ -29,7 +29,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; -import org.apache.commons.lang.time.DateUtils; +import org.apache.commons.lang3.time.DateUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfigUtil.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfigUtil.java index 30605f2051..4c622f3951 100644 --- a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfigUtil.java +++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfigUtil.java @@ -15,8 +15,7 @@ */ package com.alibaba.csp.sentinel.dashboard.rule.zookeeper; - -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; public class ZookeeperConfigUtil { public static final String RULE_ROOT_PATH = "/sentinel_rule_config"; From f4ba61f6867a1146ef38310acbb5f349248b352c Mon Sep 17 00:00:00 2001 From: luyanbo Date: Thu, 29 Feb 2024 01:18:48 +0800 Subject: [PATCH 02/11] sync sentinel-spring-webmvc-v6x-adapter, add basic sentinel-dynamic-setting-extension,sentinel-webflow-extension --- pom.xml | 10 + .../pom.xml | 9 +- .../AbstractSentinelInterceptor.java | 47 ++- .../SentinelWebPrefixInterceptor.java | 2 +- .../DefaultBlockExceptionHandler.java | 87 +++++- .../param/HttpServletRequestItemParser.java | 93 ++++++ sentinel-extension/pom.xml | 2 + .../pom.xml | 71 +++++ .../setting/SentinelAdapterConstants.java | 33 ++ .../adapter/AdapterSettingManager.java | 18 ++ .../adapter/SentinelAdapterSettingEntity.java | 114 +++++++ .../setting/fallback/BlockFallbackConfig.java | 233 ++++++++++++++ .../fallback/BlockFallbackConfigManager.java | 42 +++ .../fallback/BlockFallbackConstants.java | 90 ++++++ .../setting/fallback/BlockFallbackUtils.java | 47 +++ .../slots/block/flow/param/ParamFlowRule.java | 62 +++- .../sentinel-webflow-extension/pom.xml | 64 ++++ .../webflow/SentinelWebFlowConstants.java | 70 +++++ .../webflow/param/ParamRegexCache.java | 58 ++++ .../webflow/param/RequestItemParser.java | 86 ++++++ .../webflow/param/WebParamParser.java | 256 ++++++++++++++++ .../sentinel/webflow/rule/WebFlowRule.java | 223 ++++++++++++++ .../webflow/rule/WebFlowRuleConverter.java | 126 ++++++++ .../webflow/rule/WebFlowRuleManager.java | 287 ++++++++++++++++++ .../sentinel/webflow/rule/WebParamItem.java | 101 ++++++ 25 files changed, 2209 insertions(+), 22 deletions(-) create mode 100644 sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/param/HttpServletRequestItemParser.java create mode 100644 sentinel-extension/sentinel-dynamic-setting-extension/pom.xml create mode 100644 sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/SentinelAdapterConstants.java create mode 100644 sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/adapter/AdapterSettingManager.java create mode 100644 sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/adapter/SentinelAdapterSettingEntity.java create mode 100644 sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfig.java create mode 100644 sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfigManager.java create mode 100644 sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConstants.java create mode 100644 sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackUtils.java create mode 100644 sentinel-extension/sentinel-webflow-extension/pom.xml create mode 100644 sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/SentinelWebFlowConstants.java create mode 100644 sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/ParamRegexCache.java create mode 100644 sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/RequestItemParser.java create mode 100644 sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/WebParamParser.java create mode 100644 sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRule.java create mode 100644 sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleConverter.java create mode 100644 sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManager.java create mode 100644 sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebParamItem.java diff --git a/pom.xml b/pom.xml index ef3e6381be..5cde17a3af 100755 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,16 @@ sentinel-extension ${project.version} + + com.alibaba.csp + sentinel-dynamic-setting-extension + ${project.version} + + + com.alibaba.csp + sentinel-webflow-extension + ${project.version} + com.alibaba.csp sentinel-annotation-aspectj diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/pom.xml b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/pom.xml index 65c09272d6..97cbd83e1d 100644 --- a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/pom.xml +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/pom.xml @@ -65,7 +65,6 @@ com.alibaba fastjson - test junit @@ -81,6 +80,14 @@ org.slf4j slf4j-api + + com.alibaba.csp + sentinel-webflow-extension + + + com.alibaba.csp + sentinel-dynamic-setting-extension + diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/AbstractSentinelInterceptor.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/AbstractSentinelInterceptor.java index 271d1442da..20b51ff66c 100644 --- a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/AbstractSentinelInterceptor.java +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/AbstractSentinelInterceptor.java @@ -15,18 +15,28 @@ */ package com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x; -import com.alibaba.csp.sentinel.*; +import com.alibaba.csp.sentinel.webflow.param.WebParamParser; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.EntryType; +import com.alibaba.csp.sentinel.ResourceTypeConstants; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.Tracer; import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.config.BaseWebMvcConfig; +import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.param.HttpServletRequestItemParser; import com.alibaba.csp.sentinel.context.ContextUtil; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.util.AssertUtil; import com.alibaba.csp.sentinel.util.StringUtil; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; + import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; +import java.util.Map; + /** * Since request may be reprocessed in flow if any forwarding or including or other action * happened (see {@link jakarta.servlet.ServletRequest#getDispatcherType()}) we will only @@ -52,10 +62,18 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor private final BaseWebMvcConfig baseWebMvcConfig; + protected final WebParamParser webParamParser; + public AbstractSentinelInterceptor(BaseWebMvcConfig config) { + this(config, new WebParamParser(new HttpServletRequestItemParser())); + } + + public AbstractSentinelInterceptor(BaseWebMvcConfig config, WebParamParser webParamParser) { AssertUtil.notNull(config, "BaseWebMvcConfig should not be null"); AssertUtil.assertNotBlank(config.getRequestAttributeName(), "requestAttributeName should not be blank"); + AssertUtil.assertNotNull(webParamParser, "webParamParser should not be null"); this.baseWebMvcConfig = config; + this.webParamParser = webParamParser; } /** @@ -80,20 +98,25 @@ private Integer increaseReference(HttpServletRequest request, String rcKey, int @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - String resourceName = ""; + String resourceName = getResourceName(request); + if (StringUtil.isEmpty(resourceName)) { + return true; + } + if (increaseReference(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) { + return true; + } try { - resourceName = getResourceName(request); - if (StringUtil.isEmpty(resourceName)) { - return true; - } - if (increaseReference(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) { - return true; - } // Parse the request origin using registered origin parser. String origin = parseOrigin(request); String contextName = getContextName(request); ContextUtil.enter(contextName, origin); - Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN); + +// Map params = webParamParser.parseParameterFor(resourceName, request, null); + + // Note that AsyncEntry is REQUIRED here (for async Servlet scenarios). + // TODO: identify whether request is actually ASYNC here. + Entry entry = SphU.asyncEntry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN); + request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry); return true; } catch (BlockException e) { diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebPrefixInterceptor.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebPrefixInterceptor.java index 021f2d903b..6ac2568311 100644 --- a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebPrefixInterceptor.java +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebPrefixInterceptor.java @@ -36,4 +36,4 @@ protected String getResourceName(HttpServletRequest request) { } return resourceName; } -} \ No newline at end of file +} diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/callback/DefaultBlockExceptionHandler.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/callback/DefaultBlockExceptionHandler.java index 5181da74e3..61bb75c036 100644 --- a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/callback/DefaultBlockExceptionHandler.java +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/callback/DefaultBlockExceptionHandler.java @@ -15,12 +15,22 @@ */ package com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback; +import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.config.WebServletLocalConfig; +import com.alibaba.csp.sentinel.setting.adapter.AdapterSettingManager; +import com.alibaba.csp.sentinel.setting.fallback.BlockFallbackConfig.WebBlockFallbackBehavior; +import com.alibaba.csp.sentinel.setting.fallback.BlockFallbackUtils; import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.util.StringUtil; + import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; import java.io.PrintWriter; +import static com.alibaba.csp.sentinel.setting.SentinelAdapterConstants.WEB_FALLBACK_CONTENT_TYPE_JSON; +import static com.alibaba.csp.sentinel.setting.SentinelAdapterConstants.WEB_FALLBACK_CONTENT_TYPE_TEXT; + /** * Default handler for the blocked request. * @@ -30,13 +40,82 @@ public class DefaultBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, String resourceName, BlockException ex) - throws Exception { - // Return 429 (Too Many Requests) by default. - response.setStatus(429); + throws Exception { + StringBuffer url = request.getRequestURL(); + + if ("GET".equals(request.getMethod()) && StringUtil.isNotBlank(request.getQueryString())) { + url.append("?").append(request.getQueryString()); + } + + WebBlockFallbackBehavior b = BlockFallbackUtils.getFallbackBehavior(resourceName, ex); + if (b != null) { + writeBlockPageWith(response, b); + } else { + if (StringUtil.isBlank(WebServletLocalConfig.getBlockPage())) { + writeBlockPage(response, WebServletLocalConfig.getBlockPageHttpStatus()); + } else { + String redirectUrl = WebServletLocalConfig.getBlockPage() + "?http_referer=" + url.toString(); + // Redirect to the customized block page. + response.sendRedirect(redirectUrl); + } + } + } + + private void writeBlockPageWith(HttpServletResponse response, WebBlockFallbackBehavior b) throws IOException { + int status = 429; + if (b.getWebRespStatusCode() != null && b.getWebRespStatusCode() > 0) { + status = b.getWebRespStatusCode(); + } + + Integer contentType = b.getWebRespContentType(); + if (contentType != null && contentType == WEB_FALLBACK_CONTENT_TYPE_JSON) { + setContentTypeToJson(response); + } + if (contentType != null && contentType == WEB_FALLBACK_CONTENT_TYPE_TEXT) { + setContentTypeToText(response); + } + response.setStatus(status); + setCharsetToUtf8(response); + PrintWriter out = response.getWriter(); + out.print(b.getWebRespMessage()); + out.flush(); + out.close(); + } + + private void writeBlockPage(HttpServletResponse response, int httpStatus) throws IOException { + response.setStatus(httpStatus); + setCharsetToUtf8(response); + + Integer contentType = AdapterSettingManager.getWebRespContentType(); + if (contentType != null && contentType.equals(WEB_FALLBACK_CONTENT_TYPE_JSON)) { + setContentTypeToJson(response); + } + if (contentType != null && contentType == WEB_FALLBACK_CONTENT_TYPE_TEXT) { + setContentTypeToText(response); + } + String respMessage = valueOrDefault(AdapterSettingManager.getWebRespMessage(), DEFAULT_BLOCK_MSG); PrintWriter out = response.getWriter(); - out.print("Blocked by Sentinel (flow limiting)"); + out.print(respMessage); out.flush(); out.close(); } + + private static void setContentTypeToJson(HttpServletResponse response) { + response.setContentType("application/json"); + } + + private static void setContentTypeToText(HttpServletResponse response) { + response.setContentType("text/plain"); + } + + private static void setCharsetToUtf8(HttpServletResponse response) { + response.setCharacterEncoding("utf-8"); + } + + private static R valueOrDefault(R nullable, /*@NonNull*/ R defaultValue) { + return nullable == null ? defaultValue : nullable; + } + + private static final String DEFAULT_BLOCK_MSG = "Blocked by Sentinel (flow limiting)"; } diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/param/HttpServletRequestItemParser.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/param/HttpServletRequestItemParser.java new file mode 100644 index 0000000000..f03b117ce5 --- /dev/null +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/param/HttpServletRequestItemParser.java @@ -0,0 +1,93 @@ +package com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.param; + +import com.alibaba.csp.sentinel.webflow.param.RequestItemParser; +import com.alibaba.fastjson.JSONObject; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.servlet.HandlerMapping; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Map; + +/** + * Web item parser for {@code HttpServletRequest}. + * + * @since 1.8.8 + */ +public class HttpServletRequestItemParser implements RequestItemParser { + + @Override + public String getPath(HttpServletRequest request) { + return request.getPathInfo(); + } + + @Override + public String getRemoteAddress(HttpServletRequest request) { + return request.getRemoteAddr(); + } + + @Override + public String getHeader(HttpServletRequest request, String key) { + return request.getHeader(key); + } + + @Override + public String getUrlParam(HttpServletRequest request, String paramName) { + return request.getParameter(paramName); + } + + @Override + public String getCookieValue(HttpServletRequest request, String cookieName) { + Cookie[] cookies = request.getCookies(); + if (cookies == null || cookieName == null) { + return null; + } + for (Cookie cookie : cookies) { + if (cookie != null && cookieName.equals(cookie.getName())) { + return cookie.getValue(); + } + } + return null; + } + + @Override + public String getBodyValue(HttpServletRequest request, String bodyName) { + BufferedReader br = null; + try { + br = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8")); + String str; + StringBuilder wholeStr = new StringBuilder(); + while ((str = br.readLine()) != null) { + wholeStr.append(str); + } + JSONObject jsonObject = JSONObject.parseObject(wholeStr.toString()); + return jsonObject.get(bodyName).toString(); + }catch (Throwable ignored){ + + }finally { + if(br != null){ + try { + br.close(); + } catch (IOException ignored) { + + } + } + } + + return null; + } + + @Override + public String getPathValue(HttpServletRequest request, String pathName) { + try { + Map pathVariables = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + return (String) pathVariables.get(pathName); + }catch (Throwable ignored){ + + } + return null; + } + +} diff --git a/sentinel-extension/pom.xml b/sentinel-extension/pom.xml index 20a8fcee37..a52200ae00 100755 --- a/sentinel-extension/pom.xml +++ b/sentinel-extension/pom.xml @@ -19,6 +19,8 @@ sentinel-datasource-redis sentinel-annotation-aspectj sentinel-parameter-flow-control + sentinel-webflow-extension + sentinel-dynamic-setting-extension sentinel-datasource-spring-cloud-config sentinel-datasource-consul sentinel-datasource-etcd diff --git a/sentinel-extension/sentinel-dynamic-setting-extension/pom.xml b/sentinel-extension/sentinel-dynamic-setting-extension/pom.xml new file mode 100644 index 0000000000..83a47b87e3 --- /dev/null +++ b/sentinel-extension/sentinel-dynamic-setting-extension/pom.xml @@ -0,0 +1,71 @@ + + + + sentinel-extension + com.alibaba.csp + 1.8.7 + + 4.0.0 + + sentinel-dynamic-setting-extension + jar + + + 5.1.8.RELEASE + 2.1.3.RELEASE + + + + + com.alibaba.csp + sentinel-core + + + com.alibaba.csp + sentinel-parameter-flow-control + + + com.alibaba + fastjson + + + com.alibaba.csp + sentinel-transport-common + + + com.alibaba.csp + sentinel-webflow-extension + + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + test + + + org.springframework.boot + spring-boot-starter-test + ${spring.boot.version} + test + + + junit + junit + test + + + + + + + + org.assertj + assertj-core + test + + + + + diff --git a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/SentinelAdapterConstants.java b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/SentinelAdapterConstants.java new file mode 100644 index 0000000000..583627498a --- /dev/null +++ b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/SentinelAdapterConstants.java @@ -0,0 +1,33 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.setting; + +/** + * @author Eric Zhao + */ +public final class SentinelAdapterConstants { + + public static final int WEB_FALLBACK_TEXT_RESPONSE = 0; + public static final int WEB_FALLBACK_REDIRECT = 1; + + public static final int WEB_FALLBACK_CONTENT_TYPE_TEXT = 0; + public static final int WEB_FALLBACK_CONTENT_TYPE_JSON = 1; + + public static final String WEB_BLOCK_PAGE_URL_CONF_KEY = "csp.sentinel.web.servlet.block.page"; + public static final String WEB_BLOCK_PAGE_HTTP_STATUS_CONF_KEY = "csp.sentinel.web.servlet.block.status"; + + private SentinelAdapterConstants() {} +} diff --git a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/adapter/AdapterSettingManager.java b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/adapter/AdapterSettingManager.java new file mode 100644 index 0000000000..0938a54ca3 --- /dev/null +++ b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/adapter/AdapterSettingManager.java @@ -0,0 +1,18 @@ +package com.alibaba.csp.sentinel.setting.adapter; + +/** + * @author Eric Zhao + */ +public class AdapterSettingManager { + private static volatile SentinelAdapterSettingEntity currentSetting = null; + + public static String getWebRespMessage() { + return currentSetting == null ? null : currentSetting.getWebRespMessage(); + } + + public static Integer getWebRespContentType() { + return currentSetting == null ? null : currentSetting.getWebRespContentType(); + } + + private AdapterSettingManager() {} +} diff --git a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/adapter/SentinelAdapterSettingEntity.java b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/adapter/SentinelAdapterSettingEntity.java new file mode 100644 index 0000000000..ff8cb84718 --- /dev/null +++ b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/adapter/SentinelAdapterSettingEntity.java @@ -0,0 +1,114 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.setting.adapter; + +/** + * @author Eric Zhao + */ +public class SentinelAdapterSettingEntity { + + private Integer webFallbackMode; + /** + * Items for text response mode. + */ + private Integer webRespStatusCode; + /** + * Items for text response mode. + * + * @since 1.8.2 + */ + private String webRespMessage; + /** + * Items for text response mode. + * + * @since 1.8.2 + */ + private Integer webRespContentType; + /** + * Items for redirect mode. + */ + private String webRedirectUrl; + /** + * Items for Web servlet URL cleaner (pre-configured). + */ + private String webUrlPrefixCleanItems; + + public Integer getWebFallbackMode() { + return webFallbackMode; + } + + public SentinelAdapterSettingEntity setWebFallbackMode(Integer webFallbackMode) { + this.webFallbackMode = webFallbackMode; + return this; + } + + public Integer getWebRespStatusCode() { + return webRespStatusCode; + } + + public SentinelAdapterSettingEntity setWebRespStatusCode(Integer webRespStatusCode) { + this.webRespStatusCode = webRespStatusCode; + return this; + } + + public String getWebRespMessage() { + return webRespMessage; + } + + public SentinelAdapterSettingEntity setWebRespMessage(String webRespMessage) { + this.webRespMessage = webRespMessage; + return this; + } + + public Integer getWebRespContentType() { + return webRespContentType; + } + + public SentinelAdapterSettingEntity setWebRespContentType(Integer webRespContentType) { + this.webRespContentType = webRespContentType; + return this; + } + + public String getWebRedirectUrl() { + return webRedirectUrl; + } + + public SentinelAdapterSettingEntity setWebRedirectUrl(String webRedirectUrl) { + this.webRedirectUrl = webRedirectUrl; + return this; + } + + public String getWebUrlPrefixCleanItems() { + return webUrlPrefixCleanItems; + } + + public SentinelAdapterSettingEntity setWebUrlPrefixCleanItems(String webUrlPrefixCleanItems) { + this.webUrlPrefixCleanItems = webUrlPrefixCleanItems; + return this; + } + + @Override + public String toString() { + return "SentinelAdapterSettingEntity{" + + "webFallbackMode=" + webFallbackMode + + ", webRespStatusCode=" + webRespStatusCode + + ", webRespMessage='" + webRespMessage + '\'' + + ", webRespContentType=" + webRespContentType + + ", webRedirectUrl='" + webRedirectUrl + '\'' + + ", webUrlPrefixCleanItems='" + webUrlPrefixCleanItems + '\'' + + '}'; + } +} diff --git a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfig.java b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfig.java new file mode 100644 index 0000000000..74449b6ffd --- /dev/null +++ b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfig.java @@ -0,0 +1,233 @@ +/* + * Copyright 1999-2020 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.setting.fallback; + +import java.util.Map; +import java.util.Set; + +/** + * @author Eric Zhao + * @author guanyu + */ +public class BlockFallbackConfig { + + private Integer targetResourceType; + /** + * (targetResource, [targetBlockType...]) + */ + private Map> targetMap; + + private T fallbackBehavior; + + public Integer getTargetResourceType() { + return targetResourceType; + } + + public BlockFallbackConfig setTargetResourceType(Integer targetResourceType) { + this.targetResourceType = targetResourceType; + return this; + } + + public Map> getTargetMap() { + return targetMap; + } + + public BlockFallbackConfig setTargetMap( + Map> targetMap) { + this.targetMap = targetMap; + return this; + } + + public T getFallbackBehavior() { + return fallbackBehavior; + } + + public BlockFallbackConfig setFallbackBehavior(T fallbackBehavior) { + this.fallbackBehavior = fallbackBehavior; + return this; + } + + @Override + public String toString() { + return "BlockFallbackConfig{" + + "targetResourceType=" + targetResourceType + + ", targetMap=" + targetMap + + ", fallbackBehavior=" + fallbackBehavior + + '}'; + } + + public static class WebBlockFallbackBehavior { + + private Integer webFallbackMode; + /** + * Items for text response mode. + */ + private Integer webRespStatusCode; + /** + * Items for text response mode. + */ + private String webRespMessage; + /** + * Items for text response mode. + */ + private Integer webRespContentType; + + /** + * Items for redirect mode. + */ + private String webRedirectUrl; + + public Integer getWebFallbackMode() { + return webFallbackMode; + } + + public WebBlockFallbackBehavior setWebFallbackMode(Integer webFallbackMode) { + this.webFallbackMode = webFallbackMode; + return this; + } + + public Integer getWebRespStatusCode() { + return webRespStatusCode; + } + + public WebBlockFallbackBehavior setWebRespStatusCode(Integer webRespStatusCode) { + this.webRespStatusCode = webRespStatusCode; + return this; + } + + public String getWebRespMessage() { + return webRespMessage; + } + + public WebBlockFallbackBehavior setWebRespMessage(String webRespMessage) { + this.webRespMessage = webRespMessage; + return this; + } + + public Integer getWebRespContentType() { + return webRespContentType; + } + + public WebBlockFallbackBehavior setWebRespContentType(Integer webRespContentType) { + this.webRespContentType = webRespContentType; + return this; + } + + public String getWebRedirectUrl() { + return webRedirectUrl; + } + + public WebBlockFallbackBehavior setWebRedirectUrl(String webRedirectUrl) { + this.webRedirectUrl = webRedirectUrl; + return this; + } + + @Override + public String toString() { + return "WebBlockFallbackBehavior{" + + "webFallbackMode=" + webFallbackMode + + ", webRespStatusCode=" + webRespStatusCode + + ", webRespMessage='" + webRespMessage + '\'' + + ", webRespContentType=" + webRespContentType + + ", webRedirectUrl='" + webRedirectUrl + '\'' + + '}'; + } + } + + public static class RpcBlockFallbackBehavior { + + /** + * Items for rpc response mode. + */ + private Integer rpcFallbackMode; + + /** + * Items for cache rpc instance. + */ + private Integer rpcFallbackCacheMode; + + /** + * Items for response fallback class name. + */ + private String rpcRespFallbackClassName; + + /** + * Items for exception error message. + */ + private String rpcFallbackExceptionMessage; + + /** + * Items for return object body. + */ + private String rpcRespContentBody; + + public Integer getRpcFallbackMode() { + return rpcFallbackMode; + } + + public RpcBlockFallbackBehavior setRpcFallbackMode(Integer rpcFallbackMode) { + this.rpcFallbackMode = rpcFallbackMode; + return this; + } + + public Integer getRpcFallbackCacheMode() { + return rpcFallbackCacheMode; + } + + public RpcBlockFallbackBehavior setRpcFallbackCacheMode(Integer rpcFallbackCacheMode) { + this.rpcFallbackCacheMode = rpcFallbackCacheMode; + return this; + } + + public String getRpcRespFallbackClassName() { + return rpcRespFallbackClassName; + } + + public RpcBlockFallbackBehavior setRpcRespFallbackClassName(String rpcRespFallbackClassName) { + this.rpcRespFallbackClassName = rpcRespFallbackClassName; + return this; + } + + public String getRpcFallbackExceptionMessage() { + return rpcFallbackExceptionMessage; + } + + public RpcBlockFallbackBehavior setRpcFallbackExceptionMessage(String rpcFallbackExceptionMessage) { + this.rpcFallbackExceptionMessage = rpcFallbackExceptionMessage; + return this; + } + + public String getRpcRespContentBody() { + return rpcRespContentBody; + } + + public RpcBlockFallbackBehavior setRpcRespContentBody(String rpcRespContentBody) { + this.rpcRespContentBody = rpcRespContentBody; + return this; + } + + @Override + public String toString() { + return "RpcBlockFallbackBehavior{" + + "rpcFallbackMode=" + rpcFallbackMode + + ", rpcFallbackCacheMode='" + rpcFallbackCacheMode + '\'' + + ", rpcRespFallbackClassName='" + rpcRespFallbackClassName + '\'' + + ", rpcRespContentBody='" + rpcRespContentBody + '\'' + + ", rpcFallbackExceptionMessage=" + rpcFallbackExceptionMessage + + '}'; + } + } +} diff --git a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfigManager.java b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfigManager.java new file mode 100644 index 0000000000..de0d41c5e8 --- /dev/null +++ b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfigManager.java @@ -0,0 +1,42 @@ +package com.alibaba.csp.sentinel.setting.fallback; + +import java.util.HashMap; +import java.util.Map; + +public class BlockFallbackConfigManager { + + /** + * (resource, (targetType, config)) + */ + private volatile Map>> webFallbackConfigMap + = new HashMap>>(); + + private static class InstanceHolder { + private static final BlockFallbackConfigManager INSTANCE = new BlockFallbackConfigManager(); + } + + public static BlockFallbackConfigManager getInstance() { + return BlockFallbackConfigManager.InstanceHolder.INSTANCE; + } + + public BlockFallbackConfig getWebFallbackConfig(String resource, + Class exceptionClazz) { + if (resource == null || exceptionClazz == null) { + return null; + } + Map> m = webFallbackConfigMap.get(resource); + if (m == null || m.isEmpty()) { + return null; + } + Integer blockType = BlockFallbackConstants.parseBlockType(exceptionClazz); + // Unknown block type: we can try the block fallback for all types (if present). + if (blockType == null) { + return m.get(BlockFallbackConstants.BLOCK_TYPE_ALL); + } + BlockFallbackConfig c = m.get(blockType); + if (c == null) { + c = m.get(BlockFallbackConstants.BLOCK_TYPE_ALL); + } + return c; + } +} diff --git a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConstants.java b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConstants.java new file mode 100644 index 0000000000..a0a04da7d2 --- /dev/null +++ b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConstants.java @@ -0,0 +1,90 @@ +/* + * Copyright 1999-2020 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.setting.fallback; + +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; +import com.alibaba.csp.sentinel.slots.block.flow.FlowException; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException; +import com.alibaba.csp.sentinel.slots.system.SystemBlockException; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Eric Zhao + * @author guanyu + */ +public final class BlockFallbackConstants { + + public static final int TARGET_RES_TYPE_WEB = 1; + public static final int TARGET_RES_TYPE_RPC = 2; + + public static final int RPC_BLOCK_CUSTOM_RETURN_OBJ = 0; + public static final int RPC_BLOCK_CUSTOM_EXCEPTION = 1; + + public static final int CACHE_RPC_FALLBACK_CONFIG = 1; + + public static final int RPC_FALLBACK_MANUAL_OPERATE_MODE = 0; + public static final int RPC_FALLBACK_AUTO_OPERATE_MODE = 1; + + public static final int BLOCK_TYPE_ALL = 0; + public static final int BLOCK_TYPE_FLOW = 1; + public static final int BLOCK_TYPE_CIRCUIT_BREAKING = 2; + public static final int BLOCK_TYPE_SYSTEM = 3; + public static final int BLOCK_TYPE_PARAM_FLOW = 4; + public static final int BLOCK_TYPE_MANUAL_DEGRADE = 5; + public static final int BLOCK_TYPE_ISOLATION = 6; + + public static final int BLOCK_TYPE_WEB_FLOW = 11; + + public static final String SYSTEM_RESOURCE = "__system_block_exception_resource__"; + + /** + * (resourceType, fallbackBehaviorClass) + */ + private static final Map> TARGET_RES_TYPE_MAP = new HashMap>() {{ + put(TARGET_RES_TYPE_WEB, BlockFallbackConfig.WebBlockFallbackBehavior.class); + put(TARGET_RES_TYPE_RPC, BlockFallbackConfig.RpcBlockFallbackBehavior.class); + }}; + + private static final Map, Integer> BLOCK_TYPE_MAP = new HashMap, Integer>() {{ + put(FlowException.class, BlockFallbackConstants.BLOCK_TYPE_FLOW); + put(DegradeException.class, BlockFallbackConstants.BLOCK_TYPE_CIRCUIT_BREAKING); + put(SystemBlockException.class, BlockFallbackConstants.BLOCK_TYPE_SYSTEM); + put(ParamFlowException.class, BlockFallbackConstants.BLOCK_TYPE_PARAM_FLOW); + }}; + + public static Integer parseBlockType(Class clazz) { + if (clazz == null) { + return null; + } + return BLOCK_TYPE_MAP.get(clazz); + } + + public static boolean isResourceTypeSupported(int t) { + return TARGET_RES_TYPE_MAP.containsKey(t); + } + + public static boolean isResourceTypeSupportedAndMatch(int t, Object behavior) { + Class clazz = TARGET_RES_TYPE_MAP.get(t); + if (clazz == null) { + return false; + } + return clazz.isAssignableFrom(behavior.getClass()); + } + + private BlockFallbackConstants() {} +} diff --git a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackUtils.java b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackUtils.java new file mode 100644 index 0000000000..fd423d1ddc --- /dev/null +++ b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright 1999-2020 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.setting.fallback; + +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.system.SystemBlockException; + +/** + * @author Eric Zhao + * @author guanyu + * @since 1.8.2 + */ +public final class BlockFallbackUtils { + + public static BlockFallbackConfig.WebBlockFallbackBehavior getFallbackBehavior(String resource, BlockException ex) { + if (ex == null) { + return null; + } + BlockFallbackConfig c; + // 系统保护规则特殊处理,取这个约定好的名称 + if (ex instanceof SystemBlockException) { + c = BlockFallbackConfigManager.getInstance().getWebFallbackConfig(BlockFallbackConstants.SYSTEM_RESOURCE, + ex.getClass()); + } else { + c = BlockFallbackConfigManager.getInstance().getWebFallbackConfig(resource, ex.getClass()); + } + if (c == null) { + return null; + } + return c.getFallbackBehavior(); + } + + private BlockFallbackUtils() {} +} diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java index 3d2186a984..b49b336a57 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java @@ -23,6 +23,7 @@ import com.alibaba.csp.sentinel.slots.block.AbstractRule; import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.util.function.Tuple2; /** * Rules for "hot-spot" frequent parameter flow control. @@ -39,6 +40,11 @@ public ParamFlowRule(String resourceName) { setResource(resourceName); } + public ParamFlowRule(String resourceName, Long id) { + setResource(resourceName); + setId(id); + } + /** * The threshold type of flow control (0: thread count, 1: QPS). */ @@ -49,6 +55,11 @@ public ParamFlowRule(String resourceName) { */ private Integer paramIdx; + /** + * Parameter Key. + */ + private String paramKey; + /** * The threshold count. */ @@ -136,6 +147,15 @@ public ParamFlowRule setParamIdx(Integer paramIdx) { return this; } + public String getParamKey() { + return paramKey; + } + + public ParamFlowRule setParamKey(String paramKey) { + this.paramKey = paramKey; + return this; + } + public double getCount() { return count; } @@ -194,7 +214,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } - ParamFlowRule that = (ParamFlowRule)o; + ParamFlowRule that = (ParamFlowRule) o; if (grade != that.grade) { return false; } if (Double.compare(that.count, count) != 0) { return false; } @@ -205,6 +225,7 @@ public boolean equals(Object o) { if (clusterMode != that.clusterMode) { return false; } if (!Objects.equals(paramIdx, that.paramIdx)) { return false; } if (!Objects.equals(paramFlowItemList, that.paramFlowItemList)) { return false; } + if (!Objects.equals(paramKey, that.paramKey)) { return false; } return Objects.equals(clusterConfig, that.clusterConfig); } @@ -215,13 +236,14 @@ public int hashCode() { result = 31 * result + grade; result = 31 * result + (paramIdx != null ? paramIdx.hashCode() : 0); temp = Double.doubleToLongBits(count); - result = 31 * result + (int)(temp ^ (temp >>> 32)); + result = 31 * result + (int) (temp ^ (temp >>> 32)); result = 31 * result + controlBehavior; result = 31 * result + maxQueueingTimeMs; result = 31 * result + burstCount; - result = 31 * result + (int)(durationInSec ^ (durationInSec >>> 32)); + result = 31 * result + (int) (durationInSec ^ (durationInSec >>> 32)); result = 31 * result + (paramFlowItemList != null ? paramFlowItemList.hashCode() : 0); result = 31 * result + (clusterMode ? 1 : 0); + result = 31 * result + (paramKey != null ? paramKey.hashCode() : 0); result = 31 * result + (clusterConfig != null ? clusterConfig.hashCode() : 0); return result; } @@ -229,7 +251,8 @@ public int hashCode() { @Override public String toString() { return "ParamFlowRule{" + - "grade=" + grade + + "resource=" + getResource() + + ", grade=" + grade + ", paramIdx=" + paramIdx + ", count=" + count + ", controlBehavior=" + controlBehavior + @@ -239,6 +262,37 @@ public String toString() { ", paramFlowItemList=" + paramFlowItemList + ", clusterMode=" + clusterMode + ", clusterConfig=" + clusterConfig + + ", paramKey=" + paramKey + '}'; } + + /** + * Parsed exclusion items of parameters. Only for internal use. + * Pair: (matchObj, (value, fallbackValue)) + */ + private Map> exclusionItems = new HashMap>(); + + Map> getParsedParamItems() { + return exclusionItems; + } + + ParamFlowRule setParsedParamItems(Map> parsedItems) { + this.exclusionItems = parsedItems; + return this; + } + + public Double retrieveThresholdOfItem(Object value, boolean clusterFallback) { + if (value == null || exclusionItems == null) { + return null; + } + Tuple2 pair = exclusionItems.get(value); + if (pair == null) { + return null; + } + return clusterFallback ? pair.r2 : pair.r1; + } + + public Double retrieveThresholdOfItem(Object value) { + return retrieveThresholdOfItem(value, false); + } } diff --git a/sentinel-extension/sentinel-webflow-extension/pom.xml b/sentinel-extension/sentinel-webflow-extension/pom.xml new file mode 100644 index 0000000000..ec921a9c4d --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + sentinel-extension + com.alibaba.csp + 1.8.7 + + + sentinel-webflow-extension + jar + + + 5.1.8.RELEASE + 3.1.0 + + + + + com.alibaba.csp + sentinel-core + + + com.alibaba.csp + sentinel-parameter-flow-control + + + + javax.servlet + javax.servlet-api + ${servlet.api.version} + provided + + + org.springframework + spring-webmvc + ${spring.version} + provided + + + com.alibaba + fastjson + test + + + + junit + junit + test + + + org.mockito + mockito-core + test + + + org.assertj + assertj-core + test + + + diff --git a/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/SentinelWebFlowConstants.java b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/SentinelWebFlowConstants.java new file mode 100644 index 0000000000..bcb1ec3725 --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/SentinelWebFlowConstants.java @@ -0,0 +1,70 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 + * + * http://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 com.alibaba.csp.sentinel.webflow; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * @author gaunyu + * @since 1.10.0 + */ +public final class SentinelWebFlowConstants { + + public static final int RESOURCE_MODE_INTERFACE_ID = 0; + public static final int RESOURCE_MODE_CUSTOM_API_NAME = 1; + + public static final int PARAM_PARSE_STRATEGY_CLIENT_IP = 0; + public static final int PARAM_PARSE_STRATEGY_HOST = 1; + public static final int PARAM_PARSE_STRATEGY_HEADER = 2; + public static final int PARAM_PARSE_STRATEGY_URL_PARAM = 3; + public static final int PARAM_PARSE_STRATEGY_COOKIE = 4; + public static final int PARAM_PARSE_STRATEGY_BODY_PARAM = 5; + public static final int PARAM_PARSE_STRATEGY_PATH_PARAM = 6; + + public static final int URL_MATCH_STRATEGY_EXACT = 0; + public static final int URL_MATCH_STRATEGY_PREFIX = 1; + public static final int URL_MATCH_STRATEGY_REGEX = 2; + + public static final int PARAM_MATCH_STRATEGY_EXACT = 0; + public static final int PARAM_MATCH_STRATEGY_PREFIX = 1; + public static final int PARAM_MATCH_STRATEGY_REGEX = 2; + public static final int PARAM_MATCH_STRATEGY_CONTAINS = 3; + + public static final String WEB_FLOW_CONTEXT_DEFAULT = "sentinel_web_flow_context_default"; + public static final String WEB_FLOW_CONTEXT_PREFIX = "sentinel_web_flow_context$$"; + public static final String WEB_FLOW_CONTEXT_ROUTE_PREFIX = "sentinel_web_flow_context$$route$$"; + + public static final String WEB_FLOW_NOT_MATCH_PARAM = "$NM"; + public static final String WEB_FLOW_DEFAULT_PARAM = "$D"; + + public static final String WEB_PARAM_UNKNOWN_PARSE_STRATEGY_KEY = "UNKNOWN"; + public static final Map PARSE_STRATEGY_KEY_MAP = Collections.unmodifiableMap( + new HashMap(8) {{ + put(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_CLIENT_IP, "CLIENT_IP"); + put(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_HOST, "HOST"); + put(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_HEADER, "HEADER"); + put(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_URL_PARAM, "URL_PARAM"); + put(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_COOKIE, "COOKIE"); + put(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_BODY_PARAM, "BODY"); + put(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_PATH_PARAM, "PATH"); + }}); + + public static final String WEB_FLOW_NON_PARAM_DEFAULT_KEY = "DEFAULT_KEY"; + + private SentinelWebFlowConstants() {} +} diff --git a/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/ParamRegexCache.java b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/ParamRegexCache.java new file mode 100644 index 0000000000..57727b461a --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/ParamRegexCache.java @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.webflow.param; + +import com.alibaba.csp.sentinel.log.RecordLog; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +/** + * @author guanyu + * @since 1.10.0 + */ +public final class ParamRegexCache { + + private static final Map REGEX_CACHE = new ConcurrentHashMap(); + + public static Pattern getRegexPattern(String pattern) { + if (pattern == null) { + return null; + } + return REGEX_CACHE.get(pattern); + } + + public static boolean addRegexPattern(String pattern) { + if (pattern == null) { + return false; + } + try { + Pattern regex = Pattern.compile(pattern); + REGEX_CACHE.put(pattern, regex); + return true; + } catch (Exception ex) { + RecordLog.warn("[WebFlowRegexCache] Failed to compile the regex: " + pattern, ex); + return false; + } + } + + public static void clear() { + REGEX_CACHE.clear(); + } + + private ParamRegexCache() {} +} diff --git a/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/RequestItemParser.java b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/RequestItemParser.java new file mode 100644 index 0000000000..88d0eaa5af --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/RequestItemParser.java @@ -0,0 +1,86 @@ +/* + * Copyright 1999-2024 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.webflow.param; + +/** + * @author guanyu + * @since 1.8.8 + */ +public interface RequestItemParser { + + /** + * Get API path from the request. + * + * @param request valid request + * @return API path + */ + String getPath(T request); + + /** + * Get remote address from the request. + * + * @param request valid request + * @return remote address + */ + String getRemoteAddress(T request); + + /** + * Get the header associated with the header key. + * + * @param request valid request + * @param key valid header key (ignoring case considerations) + * @return the header + */ + String getHeader(T request, String key); + + /** + * Get the parameter value associated with the parameter name. + * + * @param request valid request + * @param paramName valid parameter name + * @return the parameter value + */ + String getUrlParam(T request, String paramName); + + /** + * Get the cookie value associated with the cookie name. + * + * @param request valid request + * @param cookieName valid cookie name + * @return the cookie value + * @since 1.7.0 + */ + String getCookieValue(T request, String cookieName); + + /** + * Get the body value associated with the body name. + * + * @param request valid request + * @param bodyName valid body name + * @return the cookie value + * @since 1.12.0 + */ + String getBodyValue(T request, String bodyName); + + /** + * Get the path value associated with the parameter name. + * + * @param request valid request + * @param pathName valid parameter name + * @return the path value + */ + String getPathValue(T request, String pathName); +} diff --git a/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/WebParamParser.java b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/WebParamParser.java new file mode 100644 index 0000000000..c9d76ad131 --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/WebParamParser.java @@ -0,0 +1,256 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.webflow.param; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import com.alibaba.csp.sentinel.util.AssertUtil; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.csp.sentinel.util.function.Predicate; +import com.alibaba.csp.sentinel.webflow.rule.WebFlowRule; +import com.alibaba.csp.sentinel.webflow.rule.WebFlowRuleConverter; +import com.alibaba.csp.sentinel.webflow.rule.WebFlowRuleManager; +import com.alibaba.csp.sentinel.webflow.SentinelWebFlowConstants; +import com.alibaba.csp.sentinel.webflow.rule.WebParamItem; + +/** + * @author guanyu + * @author Eric Zhao + * @since 1.10.0 + */ +public class WebParamParser { + + private static final String UNKNOWN = "unknown"; + private static final String SEPARATOR = ","; + + private static final String HEADER_X_FORWARDED_FOR = "X-Forwarded-For"; + private static final String HEADER_PROXY_CLIENT_IP = "Proxy-Client-IP"; + private static final String HEADER_WL_PROXY_CLIENT_IP = "WL-Proxy-Client-IP"; + private static final String HEADER_X_REAL_IP = "X-Real-IP"; + private static final String HEADER_HTTP_CLIENT_IP = "HTTP_CLIENT_IP"; + + private final RequestItemParser requestItemParser; + + public WebParamParser(RequestItemParser requestItemParser) { + AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null"); + this.requestItemParser = requestItemParser; + } + + /** + * Parse parameters for given resource from the request entity on condition of the rule predicate. + * + * @param resource valid resource name + * @param request valid request + * @param rulePredicate rule predicate indicating the rules to refer, can be null + * @return the parameter array + */ + public Map parseParameterFor(String resource, T request, Predicate rulePredicate) { + if (StringUtil.isEmpty(resource) || request == null) { + return new HashMap(); + } + Set webFlowRules = new HashSet(); + Set predSet = new HashSet(); + // TODO: optimize the logic here. + boolean hasNonParamRule = false; + for (WebFlowRule rule : WebFlowRuleManager.getRulesForResource(resource)) { + if (rule.getParamItem() != null) { + webFlowRules.add(rule); + predSet.add(rulePredicate == null || rulePredicate.test(rule)); + } else { + hasNonParamRule = true; + } + } + + if (!hasNonParamRule && webFlowRules.isEmpty()) { + return new HashMap(); + } + if (predSet.size() > 1 || predSet.contains(false)) { + return new HashMap(); + } + Map argMap = new HashMap(); + for (WebFlowRule rule : webFlowRules) { + WebParamItem paramItem = rule.getParamItem(); + if (paramItem == null) { + continue; + } + String paramValue = parseInternal(paramItem, request); + // Use the cached param key to reduce memory footprint. + String paramKey = WebFlowRuleConverter.getCachedConvertedParamKey(paramItem); + argMap.put(paramKey, paramValue); + } + if (hasNonParamRule) { + // TODO: review the logic here + argMap.put("default", SentinelWebFlowConstants.WEB_FLOW_NON_PARAM_DEFAULT_KEY); + } + return argMap; + } + + public String parseInternal(WebParamItem item, T request) { + switch (item.getParseStrategy()) { + case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_CLIENT_IP: + return parseClientIp(item, request); + case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_HOST: + return parseHost(item, request); + case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_HEADER: + return parseHeader(item, request); + case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_URL_PARAM: + return parseUrlParameter(item, request); + case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_COOKIE: + return parseCookie(item, request); + case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_BODY_PARAM: + return parseBody(item, request); + case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_PATH_PARAM: + return parsePath(item, request); + default: + return null; + } + } + + + public String parsePath(/*@Valid*/ WebParamItem item, T request) { + String pathName = item.getFieldName(); + String pattern = item.getPattern(); + String param = requestItemParser.getPathValue(request, pathName); + if (StringUtil.isEmpty(pattern)) { + return param; + } + // Match value according to regex pattern or exact mode. + return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern); + } + + + public String parseClientIp(/*@Valid*/ WebParamItem item, T request) { + // X-Forwarded-For请求头获取多级代理转发流程:client、proxy1、proxy2 + String ipAddress = requestItemParser.getHeader(request, HEADER_X_FORWARDED_FOR); + // 若 X-Forwarded-For 为空尝试自定义头中的IP + if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { + ipAddress = requestItemParser.getHeader(request, HEADER_PROXY_CLIENT_IP); + } + // 尝试 Apache HTTP 代理服务器请求头 + if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { + ipAddress = requestItemParser.getHeader(request, HEADER_PROXY_CLIENT_IP); + } + // 尝试 Apache 代理时 WebLogic 插件的请求头 + if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { + ipAddress = requestItemParser.getHeader(request, HEADER_WL_PROXY_CLIENT_IP); + } + // 尝试 Nginx 代理使用的请求头 + if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { + ipAddress = requestItemParser.getHeader(request, HEADER_X_REAL_IP); + } + // 高级匿名代理等情况使用的请求头 + if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { + ipAddress = requestItemParser.getHeader(request, HEADER_HTTP_CLIENT_IP); + } + // TODO: 如果是如果 localhost (127.0.0.1),是否需要取本地真实 IP + if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { + ipAddress = requestItemParser.getRemoteAddress(request); + } + + // 通过多级反向代理的情况,X-Forwarded-For是一串IP,第一个IP为真实IP,IP按照','分割 + if (!StringUtil.isEmpty(ipAddress) && ipAddress.contains(SEPARATOR)) { + // TODO: optimize here + ipAddress = ipAddress.split(SEPARATOR)[0].trim(); + } + + String pattern = item.getPattern(); + if (StringUtil.isEmpty(pattern)) { + return ipAddress; + } + return parseWithMatchStrategyInternal(item.getMatchStrategy(), ipAddress, pattern); + } + + public String parseHeader(/*@Valid*/ WebParamItem item, T request) { + String headerKey = item.getFieldName(); + String pattern = item.getPattern(); + // TODO: what if the header has multiple values? + String headerValue = requestItemParser.getHeader(request, headerKey); + if (StringUtil.isEmpty(pattern)) { + return headerValue; + } + // Match value according to regex pattern or exact mode. + return parseWithMatchStrategyInternal(item.getMatchStrategy(), headerValue, pattern); + } + + public String parseHost(/*@Valid*/ WebParamItem item, T request) { + String pattern = item.getPattern(); + String host = requestItemParser.getHeader(request, "Host"); + if (StringUtil.isEmpty(pattern)) { + return host; + } + // Match value according to regex pattern or exact mode. + return parseWithMatchStrategyInternal(item.getMatchStrategy(), host, pattern); + } + + public String parseUrlParameter(/*@Valid*/ WebParamItem item, T request) { + String paramName = item.getFieldName(); + String pattern = item.getPattern(); + String param = requestItemParser.getUrlParam(request, paramName); + if (StringUtil.isEmpty(pattern)) { + return param; + } + // Match value according to regex pattern or exact mode. + return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern); + } + + public String parseCookie(/*@Valid*/ WebParamItem item, T request) { + String cookieName = item.getFieldName(); + String pattern = item.getPattern(); + String param = requestItemParser.getCookieValue(request, cookieName); + if (StringUtil.isEmpty(pattern)) { + return param; + } + // Match value according to regex pattern or exact mode. + return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern); + } + + public String parseBody(/*@Valid*/ WebParamItem item, T request) { + String bodyName = item.getFieldName(); + String pattern = item.getPattern(); + String param = requestItemParser.getBodyValue(request, bodyName); + if (StringUtil.isEmpty(pattern)) { + return param; + } + // Match value according to regex pattern or exact mode. + return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern); + } + + + static String parseWithMatchStrategyInternal(Integer matchStrategy, String value, String pattern) { + if (value == null || matchStrategy == null) { + return null; + } + switch (matchStrategy) { + case SentinelWebFlowConstants.PARAM_MATCH_STRATEGY_EXACT: + return value.equals(pattern) ? value : SentinelWebFlowConstants.WEB_FLOW_NOT_MATCH_PARAM; + case SentinelWebFlowConstants.PARAM_MATCH_STRATEGY_CONTAINS: + return value.contains(pattern) ? value : SentinelWebFlowConstants.WEB_FLOW_NOT_MATCH_PARAM; + case SentinelWebFlowConstants.PARAM_MATCH_STRATEGY_REGEX: + Pattern regex = ParamRegexCache.getRegexPattern(pattern); + if (regex == null) { + return value; + } + return regex.matcher(value).matches() ? value : SentinelWebFlowConstants.WEB_FLOW_NOT_MATCH_PARAM; + default: + return value; + } + } + +} diff --git a/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRule.java b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRule.java new file mode 100644 index 0000000000..75ffb79805 --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRule.java @@ -0,0 +1,223 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.webflow.rule; + +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig; +import com.alibaba.csp.sentinel.webflow.SentinelWebFlowConstants; + +/** + * @author guanyu + * @since 1.10.0 + */ +public class WebFlowRule { + + private Long id; + + private String resource; + private int resourceMode = SentinelWebFlowConstants.RESOURCE_MODE_INTERFACE_ID; + + private int grade = RuleConstant.FLOW_GRADE_QPS; + private Double count; + private long intervalMs = 1000; + + private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT; + private int burst; + /** + * For throttle (rate limiting with queueing). + */ + private int maxQueueingTimeoutMs = 500; + + /** + * For parameter flow control. If not set, the web flow rule will be + * converted to normal flow rule. + */ + private WebParamItem paramItem; + + /** + * Indicating whether the rule is for cluster mode. + */ + private boolean clusterMode = false; + /** + * Cluster mode specific config for parameter flow rule. + */ + private ParamFlowClusterConfig clusterConfig; + + public WebFlowRule() {} + + public WebFlowRule(String resource) { + this.resource = resource; + } + + public Long getId() { + return id; + } + + public WebFlowRule setId(Long id) { + this.id = id; + return this; + } + + public String getResource() { + return resource; + } + + public WebFlowRule setResource(String resource) { + this.resource = resource; + return this; + } + + public int getResourceMode() { + return resourceMode; + } + + public WebFlowRule setResourceMode(int resourceMode) { + this.resourceMode = resourceMode; + return this; + } + + public int getControlBehavior() { + return controlBehavior; + } + + public WebFlowRule setControlBehavior(int controlBehavior) { + this.controlBehavior = controlBehavior; + return this; + } + + public Double getCount() { + return count; + } + + public WebFlowRule setCount(Double count) { + this.count = count; + return this; + } + + public long getIntervalMs() { + return intervalMs; + } + + public WebFlowRule setIntervalMs(long intervalMs) { + this.intervalMs = intervalMs; + return this; + } + + public WebParamItem getParamItem() { + return paramItem; + } + + public WebFlowRule setParamItem(WebParamItem paramItem) { + this.paramItem = paramItem; + return this; + } + + public int getMaxQueueingTimeoutMs() { + return maxQueueingTimeoutMs; + } + + public WebFlowRule setMaxQueueingTimeoutMs(int maxQueueingTimeoutMs) { + this.maxQueueingTimeoutMs = maxQueueingTimeoutMs; + return this; + } + + public int getGrade() { + return grade; + } + + public void setGrade(int grade) { + this.grade = grade; + } + + public int getBurst() { + return burst; + } + + public WebFlowRule setBurst(int burst) { + this.burst = burst; + return this; + } + + public boolean isClusterMode() { + return clusterMode; + } + + public void setClusterMode(boolean clusterMode) { + this.clusterMode = clusterMode; + } + + public ParamFlowClusterConfig getClusterConfig() { + return clusterConfig; + } + + public void setClusterConfig(ParamFlowClusterConfig clusterConfig) { + this.clusterConfig = clusterConfig; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + WebFlowRule that = (WebFlowRule) o; + + if (resourceMode != that.resourceMode) { return false; } + if (grade != that.grade) { return false; } + if (Double.compare(that.count, count) != 0) { return false; } + if (intervalMs != that.intervalMs) { return false; } + if (controlBehavior != that.controlBehavior) { return false; } + if (burst != that.burst) { return false; } + if (maxQueueingTimeoutMs != that.maxQueueingTimeoutMs) { return false; } + if (resource != null ? !resource.equals(that.resource) : that.resource != null) { return false; } + return paramItem != null ? paramItem.equals(that.paramItem) : that.paramItem == null; + } + + @Override + public int hashCode() { + int result; + long temp; + result = resource != null ? resource.hashCode() : 0; + result = 31 * result + resourceMode; + result = 31 * result + grade; + temp = Double.doubleToLongBits(count); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + (int) (intervalMs ^ (intervalMs >>> 32)); + result = 31 * result + controlBehavior; + result = 31 * result + burst; + result = 31 * result + maxQueueingTimeoutMs; + result = 31 * result + (paramItem != null ? paramItem.hashCode() : 0); + result = 31 * result + (clusterMode ? 1 : 0); + result = 31 * result + (clusterConfig != null ? clusterConfig.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "WebFlowRule{" + + "resource='" + resource + '\'' + + ", resourceMode=" + resourceMode + + ", grade=" + grade + + ", count=" + count + + ", intervalMs=" + intervalMs + + ", controlBehavior=" + controlBehavior + + ", burst=" + burst + + ", maxQueueingTimeoutMs=" + maxQueueingTimeoutMs + + ", paramItem=" + paramItem + + ", clusterMode=" + clusterMode + + ", clusterConfig=" + clusterConfig + + '}'; + } +} diff --git a/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleConverter.java b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleConverter.java new file mode 100644 index 0000000000..1af45da1f3 --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleConverter.java @@ -0,0 +1,126 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.webflow.rule; + +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; +import com.alibaba.csp.sentinel.webflow.SentinelWebFlowConstants; + +/** + * @author guanyu + * @since 1.10.0 + */ +public final class WebFlowRuleConverter { + + public static final String WEB_PARAM_RULE_KEY_PREFIX = "$WEB_PF"; + + static FlowRule toFlowRule(/*@Valid*/ WebFlowRule rule) { + return new FlowRule(rule.getResource()) + .setControlBehavior(rule.getControlBehavior()) + .setCount(rule.getCount()) + .setGrade(rule.getGrade()) + .setMaxQueueingTimeMs(rule.getMaxQueueingTimeoutMs()); + } + + static ParamFlowItem generateNonMatchPassParamItem() { + return new ParamFlowItem().setClassType(String.class.getName()) + .setCount(10000000) + .setObject(SentinelWebFlowConstants.WEB_FLOW_NOT_MATCH_PARAM); + } + + static ParamFlowItem generateNonMatchBlockParamItem() { + return new ParamFlowItem().setClassType(String.class.getName()) + .setCount(0) + .setObject(SentinelWebFlowConstants.WEB_FLOW_NOT_MATCH_PARAM); + } + + static ParamFlowRule applyNonParamToParamRule(/*@Valid*/ WebFlowRule webFlowRule) { + return new ParamFlowRule(webFlowRule.getResource(), webFlowRule.getId()) + .setCount(webFlowRule.getCount()) + .setGrade(webFlowRule.getGrade()) + .setDurationInSec(webFlowRule.getIntervalMs() / 1000) + .setBurstCount(webFlowRule.getBurst()) + .setControlBehavior(webFlowRule.getControlBehavior()) + .setMaxQueueingTimeMs(webFlowRule.getMaxQueueingTimeoutMs()) + .setParamKey(SentinelWebFlowConstants.WEB_FLOW_NON_PARAM_DEFAULT_KEY); + } + + /** + * Convert a web flow rule to parameter flow rule, then apply the generated + * parameter index to {@link WebParamItem} of the rule. + * + * @param webFlowRule a valid gateway rule that should contain valid parameter items + * @return converted parameter flow rule + */ + static ParamFlowRule applyToParamRule(/*@Valid*/ WebFlowRule webFlowRule) { + ParamFlowRule paramRule = new ParamFlowRule(webFlowRule.getResource(), webFlowRule.getId()) + .setCount(webFlowRule.getCount()) + .setGrade(webFlowRule.getGrade()) + .setDurationInSec(webFlowRule.getIntervalMs() / 1000) + .setBurstCount(webFlowRule.getBurst()) + .setControlBehavior(webFlowRule.getControlBehavior()) + .setMaxQueueingTimeMs(webFlowRule.getMaxQueueingTimeoutMs()); + WebParamItem webParamItem = webFlowRule.getParamItem(); + // Apply the parse strategy to param key. + String convertedParamKey = generateParamKeyForWebRule(webFlowRule); + paramRule.setParamKey(convertedParamKey); + // Cache the param key inside the web param item. + webParamItem.setConvertedParamKey(convertedParamKey); + + // Apply for pattern-based parameters. + String valuePattern = webParamItem.getPattern(); + if (valuePattern != null) { + paramRule.getParamFlowItemList().add(generateNonMatchPassParamItem()); + } + //cluster mode + if (webFlowRule.isClusterMode()) { + paramRule.setClusterConfig(webFlowRule.getClusterConfig()); + } + return paramRule; + } + + public static String generateParamKeyForWebRule(WebFlowRule rule) { + if (rule == null || rule.getId() == null) { + return null; + } + WebParamItem item = rule.getParamItem(); + if (item == null) { + return WEB_PARAM_RULE_KEY_PREFIX + '|' + rule.getId(); + } + String paramKey = SentinelWebFlowConstants.PARSE_STRATEGY_KEY_MAP.get(item.getParseStrategy()); + if (paramKey == null) { + paramKey = SentinelWebFlowConstants.WEB_PARAM_UNKNOWN_PARSE_STRATEGY_KEY; + } + return WEB_PARAM_RULE_KEY_PREFIX + '|' + paramKey + '|' + rule.getId(); + } + + /** + * Get cached converted param key inside the original {@link WebParamItem}. + * + * @param item valid param item of a {@link WebFlowRule} + * @return cached key if present, otherwise null + */ + public static String getCachedConvertedParamKey(WebParamItem item) { + if (item == null) { + return null; + } + // 只有经历过 applyToParamRule 的 WebFlowRule 才会持有对应的 key cache. + return item.getConvertedParamKey(); + } + + private WebFlowRuleConverter() {} +} diff --git a/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManager.java b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManager.java new file mode 100644 index 0000000000..05baaf00ad --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManager.java @@ -0,0 +1,287 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 + * + * http://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 com.alibaba.csp.sentinel.webflow.rule; + +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.property.DynamicSentinelProperty; +import com.alibaba.csp.sentinel.property.PropertyListener; +import com.alibaba.csp.sentinel.property.SentinelProperty; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleUtil; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetric; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage; +import com.alibaba.csp.sentinel.util.AssertUtil; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.csp.sentinel.webflow.SentinelWebFlowConstants; +import com.alibaba.csp.sentinel.webflow.param.ParamRegexCache; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author guanyu + * @since 1.10.0 + */ +public final class WebFlowRuleManager { + + /** + * Web flow rule map: (resource, [rules...]) + */ + private static final Map> WEB_FLOW_RULE_MAP = new ConcurrentHashMap>(); + + private static final Map> CONVERTED_WEB_FLOW_RULE_MAP = new ConcurrentHashMap>(); + + private static final WebFlowRulePropertyListener LISTENER = new WebFlowRulePropertyListener(); + + private static final Set FIELD_REQUIRED_SET = new HashSet( + Arrays.asList(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_URL_PARAM, + SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_HEADER, + SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_COOKIE, + SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_BODY_PARAM, + SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_PATH_PARAM) + ); + private static SentinelProperty> currentProperty = new DynamicSentinelProperty>(); + + static { + currentProperty.addListener(LISTENER); + } + + private WebFlowRuleManager() { + } + + public static void register2Property(SentinelProperty> property) { + AssertUtil.notNull(property, "property cannot be null"); + synchronized (LISTENER) { + RecordLog.info("[WebFlowRuleManager] Registering new property to web flow rule manager"); + currentProperty.removeListener(LISTENER); + property.addListener(LISTENER); + currentProperty = property; + } + } + + /** + * Load all provided web flow rules into memory, while + * previous rules will be replaced. + * + * @param rules rule set + * @return true if updated, otherwise false + */ + public static boolean loadRules(Set rules) { + return currentProperty.updateValue(rules); + } + + public static Set getRules() { + Set rules = new HashSet(); + for (Set ruleSet : WEB_FLOW_RULE_MAP.values()) { + rules.addAll(ruleSet); + } + return rules; + } + + public static Set getRulesForResource(String resourceName) { + if (StringUtil.isBlank(resourceName)) { + return new HashSet(); + } + Set set = WEB_FLOW_RULE_MAP.get(resourceName); + if (set == null) { + return new HashSet(); + } + return new HashSet(set); + } + + /** + *

Get all converted parameter rules.

+ *

Note: caller SHOULD NOT modify the list and rules.

+ * + * @param resourceName valid resource name + * @return converted parameter rules + */ + public static List getConvertedParamRules(String resourceName) { + if (StringUtil.isBlank(resourceName)) { + return new ArrayList(); + } + return CONVERTED_WEB_FLOW_RULE_MAP.get(resourceName); + } + + public static boolean hasRules(String resourceName) { + List rules = CONVERTED_WEB_FLOW_RULE_MAP.get(resourceName); + return rules != null && !rules.isEmpty(); + } + + public static boolean isValidRule(WebFlowRule rule) { + if (rule == null || StringUtil.isBlank(rule.getResource()) || rule.getResourceMode() < 0 || rule.getControlBehavior() < 0) { + return false; + } + if (rule.getCount() == null || rule.getCount() < 0) { + return false; + } + if (rule.getControlBehavior() == RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER + && rule.getMaxQueueingTimeoutMs() < 0) { + return false; + } + if (rule.getIntervalMs() <= 0) { + return false; + } + WebParamItem item = rule.getParamItem(); + if (item != null) { + return isValidParamItem(item); + } + return true; + } + + static boolean isValidParamItem(/*@NonNull*/ WebParamItem item) { + if (item.getParseStrategy() < 0) { + return false; + } + // Check required field name for item types. + if (FIELD_REQUIRED_SET.contains(item.getParseStrategy()) && StringUtil.isBlank(item.getFieldName())) { + return false; + } + return StringUtil.isEmpty(item.getPattern()) || item.getMatchStrategy() >= 0; + } + + private static final class WebFlowRulePropertyListener implements PropertyListener> { + + @Override + public void configUpdate(Set conf) { + applyWebFlowRuleInternal(conf); + RecordLog.info("[WebFlowRuleManager] Web flow rules received: " + WEB_FLOW_RULE_MAP); + } + + @Override + public void configLoad(Set conf) { + applyWebFlowRuleInternal(conf); + RecordLog.info("[WebFlowRuleManager] Web flow rules loaded: " + WEB_FLOW_RULE_MAP); + } + + /*private int getIdxInternal(Map idxMap, String resourceName) { + // Prepare index map. + if (!idxMap.containsKey(resourceName)) { + idxMap.put(resourceName, 0); + } + return idxMap.get(resourceName); + }*/ + + private void cacheRegexPattern(/*@NonNull*/ WebParamItem item) { + String pattern = item.getPattern(); + if (StringUtil.isNotEmpty(pattern) && + item.getMatchStrategy() == SentinelWebFlowConstants.PARAM_MATCH_STRATEGY_REGEX) { + if (ParamRegexCache.getRegexPattern(pattern) == null) { + ParamRegexCache.addRegexPattern(pattern); + } + } + } + + private synchronized void applyWebFlowRuleInternal(Set conf) { + if (conf == null || conf.isEmpty()) { + applyToConvertedParamMap(new HashSet()); + WEB_FLOW_RULE_MAP.clear(); + return; + } + Map> webFlowRuleMap = new ConcurrentHashMap>(); + Map idxMap = new HashMap(); + Set paramFlowRules = new HashSet(); + Map> noParamMap = new HashMap>(); + + for (WebFlowRule rule : conf) { + if (!isValidRule(rule)) { + RecordLog.warn("[WebFlowRuleManager] Ignoring invalid rule when loading new rules: " + rule); + continue; + } + String resourceName = rule.getResource(); + if (rule.getParamItem() == null) { + // Cache the rules with no parameter config, then skip. + List noParamList = noParamMap.get(resourceName); + if (noParamList == null) { + noParamList = new ArrayList(); + noParamMap.put(resourceName, noParamList); + } + noParamList.add(rule); + } else { + /*int idx = getIdxInternal(idxMap, resourceName); + if (paramFlowRules.add(WebFlowRuleConverter.applyToParamRule(rule, idx))) { + idxMap.put(rule.getResource(), idx + 1); + }*/ + // Convert to parameter flow rule. + paramFlowRules.add(WebFlowRuleConverter.applyToParamRule(rule)); + cacheRegexPattern(rule.getParamItem()); + } + // Apply to the web param rule map. + Set ruleSet = webFlowRuleMap.get(resourceName); + if (ruleSet == null) { + ruleSet = new HashSet(); + webFlowRuleMap.put(resourceName, ruleSet); + } + ruleSet.add(rule); + } + // Handle non-param mode rules. + for (Map.Entry> e : noParamMap.entrySet()) { + List rules = e.getValue(); + if (rules == null || rules.isEmpty()) { + continue; + } + for (WebFlowRule rule : rules) { + // Always use the default param key. + paramFlowRules.add(WebFlowRuleConverter.applyNonParamToParamRule(rule)); + } + } + + applyToConvertedParamMap(paramFlowRules); + + WEB_FLOW_RULE_MAP.clear(); + WEB_FLOW_RULE_MAP.putAll(webFlowRuleMap); + } + + private void applyToConvertedParamMap(Set paramFlowRules) { + Map> newRuleMap = ParamFlowRuleUtil.buildParamRuleMap( + new ArrayList(paramFlowRules)); + if (newRuleMap == null || newRuleMap.isEmpty()) { + // No parameter flow rules, so clear all the metrics. + for (String resource : CONVERTED_WEB_FLOW_RULE_MAP.keySet()) { + ParameterMetricStorage.clearParamMetricForResource(resource); + } + RecordLog.info("[WebFlowRuleManager] No web param rules, clearing parameter metrics of previous rules"); + CONVERTED_WEB_FLOW_RULE_MAP.clear(); + return; + } + + // Clear unused parameter metrics. + for (Map.Entry> entry : CONVERTED_WEB_FLOW_RULE_MAP.entrySet()) { + String resource = entry.getKey(); + if (!newRuleMap.containsKey(resource)) { + ParameterMetricStorage.clearParamMetricForResource(resource); + continue; + } + List newRuleList = newRuleMap.get(resource); + List oldRuleList = new ArrayList(entry.getValue()); + oldRuleList.removeAll(newRuleList); + for (ParamFlowRule rule : oldRuleList) { + ParameterMetric metric = ParameterMetricStorage.getParamMetricForResource(resource); + if (null != metric) { + metric.clearForRule(rule); + } + } + } + + // Apply to converted rule map. + CONVERTED_WEB_FLOW_RULE_MAP.clear(); + CONVERTED_WEB_FLOW_RULE_MAP.putAll(newRuleMap); + + RecordLog.info("[WebFlowRuleManager] Converted internal param rules: " + CONVERTED_WEB_FLOW_RULE_MAP); + } + } +} diff --git a/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebParamItem.java b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebParamItem.java new file mode 100644 index 0000000000..02626654b5 --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/rule/WebParamItem.java @@ -0,0 +1,101 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.webflow.rule; + +import com.alibaba.csp.sentinel.webflow.SentinelWebFlowConstants; + +/** + * @author guanyu + * @since 1.10.0 + */ +public class WebParamItem { + + /** + * Strategy for parsing item (e.g. client IP, arbitrary headers and URL parameters). + */ + private int parseStrategy; + /** + * Field to get (only required for arbitrary headers or URL parameters mode). + */ + private String fieldName; + /** + * Matching pattern. If not set, all values will be kept in LRU map. + */ + private String pattern; + /** + * Matching strategy for item value. + */ + private int matchStrategy = SentinelWebFlowConstants.PARAM_MATCH_STRATEGY_EXACT; + + public int getParseStrategy() { + return parseStrategy; + } + + public WebParamItem setParseStrategy(int parseStrategy) { + this.parseStrategy = parseStrategy; + return this; + } + + public String getFieldName() { + return fieldName; + } + + public WebParamItem setFieldName(String fieldName) { + this.fieldName = fieldName; + return this; + } + + public String getPattern() { + return pattern; + } + + public WebParamItem setPattern(String pattern) { + this.pattern = pattern; + return this; + } + + public int getMatchStrategy() { + return matchStrategy; + } + + public WebParamItem setMatchStrategy(int matchStrategy) { + this.matchStrategy = matchStrategy; + return this; + } + + @Override + public String toString() { + return "WebParamItem{" + + "parseStrategy=" + parseStrategy + + ", fieldName='" + fieldName + '\'' + + ", pattern='" + pattern + '\'' + + ", matchStrategy=" + matchStrategy + + '}'; + } + + // Internal fields + + private String convertedParamKey; + + public String getConvertedParamKey() { + return convertedParamKey; + } + + WebParamItem setConvertedParamKey(String convertedParamKey) { + this.convertedParamKey = convertedParamKey; + return this; + } +} From 7c2717cc3180d33bc9d85fd18186cb239fc19ded Mon Sep 17 00:00:00 2001 From: luyanbo Date: Thu, 29 Feb 2024 13:43:18 +0800 Subject: [PATCH 03/11] build before test --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ff46df178..6fc9f0ca81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,9 @@ jobs: java-version: 17 distribution: 'temurin' + - name: Build with Maven + run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -DminimumPriority=1 + - name: Maven Test With Spring 6.x run: mvn --batch-mode test -Dsurefire.jdk-toolchain-version=${{ matrix.java }} if: ${{ matrix.java >= 17 }} @@ -43,11 +46,8 @@ jobs: run: mvn --batch-mode test -Dsurefire.jdk-toolchain-version=${{ matrix.java }} -Dskip.spring.v6x.test=true if: ${{ matrix.java < 17 }} - - name: Build with Maven - run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -DminimumPriority=1 - - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} - slug: alibaba/Sentinel + From 628007a56c96a1b9f321d19ee5bc95d8008b06d4 Mon Sep 17 00:00:00 2001 From: luyanbo Date: Thu, 29 Feb 2024 14:15:48 +0800 Subject: [PATCH 04/11] fix ci --- .github/workflows/ci.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6fc9f0ca81..95d590b8d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,19 +25,16 @@ jobs: - name: Setup Java for test uses: actions/setup-java@v4 with: - java-version: ${{ matrix.java }} + java-version: "${{ matrix.java }}" distribution: 'temurin' cache: 'maven' - name: Setup Java for mvn uses: actions/setup-java@v4 with: - java-version: 17 + java-version: "17" distribution: 'temurin' - - name: Build with Maven - run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -DminimumPriority=1 - - name: Maven Test With Spring 6.x run: mvn --batch-mode test -Dsurefire.jdk-toolchain-version=${{ matrix.java }} if: ${{ matrix.java >= 17 }} From 49f0b1634dc3ba4397e4eba10b0ed21fc0278809 Mon Sep 17 00:00:00 2001 From: luyanbo Date: Fri, 1 Mar 2024 16:13:58 +0800 Subject: [PATCH 05/11] add DefaultBlockExceptionHandlerTest --- .../DefaultBlockExceptionHandlerTest.java | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/callback/DefaultBlockExceptionHandlerTest.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/callback/DefaultBlockExceptionHandlerTest.java index fca59ffe62..5f1409f5f2 100644 --- a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/callback/DefaultBlockExceptionHandlerTest.java +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/callback/DefaultBlockExceptionHandlerTest.java @@ -1,12 +1,22 @@ package com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback; +import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.config.WebServletLocalConfig; +import com.alibaba.csp.sentinel.setting.fallback.BlockFallbackConfig; +import com.alibaba.csp.sentinel.setting.fallback.BlockFallbackUtils; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.flow.FlowException; +import org.hamcrest.text.IsEqualIgnoringCase; import org.junit.Test; +import org.mockito.MockedStatic; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import static com.alibaba.csp.sentinel.setting.SentinelAdapterConstants.WEB_FALLBACK_CONTENT_TYPE_JSON; +import static com.alibaba.csp.sentinel.setting.SentinelAdapterConstants.WEB_FALLBACK_CONTENT_TYPE_TEXT; +import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mockStatic; public class DefaultBlockExceptionHandlerTest { @@ -22,4 +32,54 @@ public void handle_writeBlockPage() throws Exception { assertEquals(429, resp.getStatus()); } -} \ No newline at end of file + @Test + public void handle_writeBlockPageWith() throws Exception { + DefaultBlockExceptionHandler h = new DefaultBlockExceptionHandler(); + MockHttpServletRequest req = new MockHttpServletRequest("GET", "/a/b/c"); + req.setQueryString("a=1&b=2"); + MockHttpServletResponse resp = new MockHttpServletResponse(); + String resourceName = "/a/b/c"; + BlockException ex = new FlowException("msg"); + + try (MockedStatic mockedBlockFallbackUtils = mockStatic(BlockFallbackUtils.class)) { + BlockFallbackConfig.WebBlockFallbackBehavior behavior = new BlockFallbackConfig.WebBlockFallbackBehavior(); + behavior.setWebRespStatusCode(444); + behavior.setWebRespContentType(WEB_FALLBACK_CONTENT_TYPE_JSON); + behavior.setWebRespMessage("webRespMessage"); + mockedBlockFallbackUtils.when(() -> BlockFallbackUtils.getFallbackBehavior(resourceName, ex)) + .thenReturn(behavior); + + h.handle(req, resp, resourceName, ex); + assertEquals(444, resp.getStatus()); + assertThat(resp.getContentType(), containsString("application/json")); + assertThat(resp.getCharacterEncoding(), IsEqualIgnoringCase.equalToIgnoringCase("utf-8")); + assertEquals("webRespMessage", resp.getContentAsString()); + + behavior.setWebRespContentType(WEB_FALLBACK_CONTENT_TYPE_TEXT); + h.handle(req, resp, resourceName, ex); + assertEquals(444, resp.getStatus()); + assertThat(resp.getContentType(), containsString("text/plain")); + assertThat(resp.getCharacterEncoding(), IsEqualIgnoringCase.equalToIgnoringCase("utf-8")); + assertEquals("webRespMessage", resp.getContentAsString()); + } + } + + @Test + public void handle_sendRedirect() throws Exception { + DefaultBlockExceptionHandler h = new DefaultBlockExceptionHandler(); + MockHttpServletRequest req = new MockHttpServletRequest("GET", "/a/b/c"); + req.setQueryString("a=1&b=2"); + MockHttpServletResponse resp = new MockHttpServletResponse(); + String resourceName = "/a/b/c"; + BlockException ex = new FlowException("msg"); + + try (MockedStatic mocked = mockStatic(WebServletLocalConfig.class)) { + mocked.when(WebServletLocalConfig::getBlockPage) + .thenReturn("/blocked"); + + h.handle(req, resp, resourceName, ex); + assertEquals(302, resp.getStatus()); + assertEquals("/blocked?http_referer=http://localhost/a/b/c?a=1&b=2", resp.getRedirectedUrl()); + } + } +} From a3a066b8e194236ff4b4ffc15816075c9f8c2e22 Mon Sep 17 00:00:00 2001 From: luyanbo Date: Fri, 1 Mar 2024 16:24:35 +0800 Subject: [PATCH 06/11] add test for sentinel-webflow-extension --- .../webflow/param/ParamRegexCacheTest.java | 49 ++++++++ .../rule/WebFlowRuleConverterTest.java | 62 ++++++++++ .../webflow/rule/WebFlowRuleManagerTest.java | 111 ++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/param/ParamRegexCacheTest.java create mode 100644 sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleConverterTest.java create mode 100644 sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManagerTest.java diff --git a/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/param/ParamRegexCacheTest.java b/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/param/ParamRegexCacheTest.java new file mode 100644 index 0000000000..3cfe0da377 --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/param/ParamRegexCacheTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 + * + * http://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 com.alibaba.csp.sentinel.webflow.param; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author guanyu + */ +public class ParamRegexCacheTest { + + @Before + public void setUp() { + ParamRegexCache.clear(); + } + + @After + public void tearDown() { + ParamRegexCache.clear(); + } + + @Test + public void testAddAndGetRegexPattern() { + // Test for invalid pattern. + assertThat(ParamRegexCache.addRegexPattern("\\")).isFalse(); + assertThat(ParamRegexCache.addRegexPattern(null)).isFalse(); + // Test for good pattern. + String goodPattern = "\\d+"; + assertThat(ParamRegexCache.addRegexPattern(goodPattern)).isTrue(); + assertThat(ParamRegexCache.getRegexPattern(goodPattern)).isNotNull(); + } +} diff --git a/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleConverterTest.java b/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleConverterTest.java new file mode 100644 index 0000000000..d6b7b9f888 --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleConverterTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.webflow.rule; + +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; +import com.alibaba.csp.sentinel.webflow.SentinelWebFlowConstants; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @author guanyu + */ +public class WebFlowRuleConverterTest { + + @Test + public void testConvertToFlowRule() { + WebFlowRule rule = new WebFlowRule("routeId1") + .setId(1L) + .setCount(10d) + .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER) + .setMaxQueueingTimeoutMs(1000); + FlowRule flowRule = WebFlowRuleConverter.toFlowRule(rule); + assertEquals(rule.getResource(), flowRule.getResource()); + assertEquals(rule.getCount(), flowRule.getCount(), 0.01); + assertEquals(rule.getControlBehavior(), flowRule.getControlBehavior()); + assertEquals(rule.getMaxQueueingTimeoutMs(), flowRule.getMaxQueueingTimeMs()); + } + + @Test + public void testConvertAndApplyToParamRule() { + WebFlowRule routeRule1 = new WebFlowRule("routeId1") + .setId(1L) + .setCount(2d) + .setBurst(2) + .setParamItem(new WebParamItem() + .setParseStrategy(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_CLIENT_IP) + ); + ParamFlowRule paramRule = WebFlowRuleConverter.applyToParamRule(routeRule1); + assertEquals(routeRule1.getResource(), paramRule.getResource()); + assertEquals(routeRule1.getCount(), paramRule.getCount(), 0.01); + assertEquals(routeRule1.getControlBehavior(), paramRule.getControlBehavior()); + assertEquals(routeRule1.getBurst(), paramRule.getBurstCount()); + assertNull(paramRule.getParamIdx()); + assertNotNull(paramRule.getParamKey()); + } +} \ No newline at end of file diff --git a/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManagerTest.java b/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManagerTest.java new file mode 100644 index 0000000000..17f3a5abb5 --- /dev/null +++ b/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManagerTest.java @@ -0,0 +1,111 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.webflow.rule; + +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; +import com.alibaba.csp.sentinel.webflow.SentinelWebFlowConstants; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.*; + +/** + * @author gaunyu + */ +public class WebFlowRuleManagerTest { + + @Test + public void testLoadAndGetGatewayRules() { + Set rules = new HashSet(); + String ahasRoute = "ahas_route"; + WebFlowRule rule1 = new WebFlowRule(ahasRoute) + .setCount(500d); + WebFlowRule rule2 = new WebFlowRule(ahasRoute) + .setCount(20d) + .setBurst(5) + .setParamItem(new WebParamItem() + .setParseStrategy(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_CLIENT_IP) + ); + WebFlowRule rule3 = new WebFlowRule("complex_route_ZZZ") + .setCount(10d) + .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER) + .setMaxQueueingTimeoutMs(600) + .setParamItem(new WebParamItem() + .setParseStrategy(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_HEADER) + .setFieldName("X-Sentinel-Flag") + ); + rules.add(rule1); + rules.add(rule2); + rules.add(rule3); + WebFlowRuleManager.loadRules(rules); + + List convertedRules = WebFlowRuleManager.getConvertedParamRules(ahasRoute); + assertNotNull(convertedRules); + assertTrue(WebFlowRuleManager.getRulesForResource(ahasRoute).contains(rule1)); + assertTrue(WebFlowRuleManager.getRulesForResource(ahasRoute).contains(rule2)); + } + + @Test + public void testIsValidRule() { + WebFlowRule bad1 = new WebFlowRule(); + WebFlowRule bad2 = new WebFlowRule("abc"); + WebFlowRule bad3 = new WebFlowRule("abc") + .setParamItem(new WebParamItem() + .setParseStrategy(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_URL_PARAM)); + WebFlowRule bad4 = new WebFlowRule("abc") + .setParamItem(new WebParamItem() + .setParseStrategy(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_URL_PARAM) + .setFieldName("p") + .setPattern("def") + .setMatchStrategy(-1) + ); + WebFlowRule good1 = new WebFlowRule("abc") + .setCount(1d); + WebFlowRule good2 = new WebFlowRule("abc") + .setCount(1d) + .setParamItem(new WebParamItem().setParseStrategy(0)); + WebFlowRule good3 = new WebFlowRule("abc") + .setCount(1d) + .setParamItem(new WebParamItem() + .setMatchStrategy(SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_HEADER) + .setFieldName("Origin") + .setPattern("def")); + assertFalse(WebFlowRuleManager.isValidRule(bad1)); + assertFalse(WebFlowRuleManager.isValidRule(bad2)); + assertFalse(WebFlowRuleManager.isValidRule(bad3)); + assertFalse(WebFlowRuleManager.isValidRule(bad4)); + + assertTrue(WebFlowRuleManager.isValidRule(good1)); + assertTrue(WebFlowRuleManager.isValidRule(good2)); + assertTrue(WebFlowRuleManager.isValidRule(good3)); + } + + @Before + public void setUp() { + WebFlowRuleManager.loadRules(new HashSet()); + } + + @After + public void tearDown() { + WebFlowRuleManager.loadRules(new HashSet()); + } +} \ No newline at end of file From 090b72896e0475b49ddeb1c616b731b74dc9400a Mon Sep 17 00:00:00 2001 From: luyanbo Date: Fri, 1 Mar 2024 16:27:17 +0800 Subject: [PATCH 07/11] add test for sentinel-spring-webmvc-v6x-adapter --- .../HttpServletRequestItemParserTest.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/param/HttpServletRequestItemParserTest.java diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/param/HttpServletRequestItemParserTest.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/param/HttpServletRequestItemParserTest.java new file mode 100644 index 0000000000..ead0e943a5 --- /dev/null +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/param/HttpServletRequestItemParserTest.java @@ -0,0 +1,94 @@ +package com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.param; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.ArrayUtils; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.servlet.HandlerMapping; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +public class HttpServletRequestItemParserTest { + + @Test + public void getPath() { + HttpServletRequest req = Mockito.mock(HttpServletRequest.class); + when(req.getPathInfo()).thenReturn("/a/b/c"); + + HttpServletRequestItemParser reqItem = new HttpServletRequestItemParser(); + String path = reqItem.getPath(req); + assertEquals("/a/b/c", path); + } + + @Test + public void getRemoteAddress() { + HttpServletRequest req = Mockito.mock(HttpServletRequest.class); + when(req.getRemoteAddr()).thenReturn("1.2.3.4"); + + HttpServletRequestItemParser reqItem = new HttpServletRequestItemParser(); + String addr = reqItem.getRemoteAddress(req); + assertEquals("1.2.3.4", addr); + } + + @Test + public void getHeader() { + HttpServletRequest req = Mockito.mock(HttpServletRequest.class); + when(req.getHeader("test-header")).thenReturn("test-value"); + + HttpServletRequestItemParser reqItem = new HttpServletRequestItemParser(); + String headerValue = reqItem.getHeader(req, "test-header"); + assertEquals("test-value", headerValue); + } + + @Test + public void getUrlParam() { + HttpServletRequest req = Mockito.mock(HttpServletRequest.class); + when(req.getParameter("testK")).thenReturn("testV"); + + HttpServletRequestItemParser reqItem = new HttpServletRequestItemParser(); + String paramValue = reqItem.getUrlParam(req, "testK"); + assertEquals("testV", paramValue); + } + + @Test + public void getCookieValue() { + HttpServletRequest req = Mockito.mock(HttpServletRequest.class); + Cookie[] cookies = new Cookie[0]; + cookies = ArrayUtils.add(cookies, new Cookie("testK", "testV")); + cookies = ArrayUtils.add(cookies, new Cookie("testK1", "testV1")); + when(req.getCookies()).thenReturn(cookies); + + HttpServletRequestItemParser reqItem = new HttpServletRequestItemParser(); + String paramValue = reqItem.getCookieValue(req, "testK"); + assertEquals("testV", paramValue); + } + + @Test + public void getBodyValue() { + MockHttpServletRequest req = new MockHttpServletRequest(); + req.setContent("{\"testK\": \"testV\"}".getBytes()); + + HttpServletRequestItemParser reqItem = new HttpServletRequestItemParser(); + String bodyValue = reqItem.getBodyValue(req, "testK"); + assertEquals("testV", bodyValue); + } + + @Test + public void getPathValue() { + MockHttpServletRequest req = new MockHttpServletRequest(); + Map vars = new HashMap<>(); + vars.put("testK", "testV"); + vars.put("testK1", "testV1"); + req.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, vars); + + HttpServletRequestItemParser reqItem = new HttpServletRequestItemParser(); + String pathValue = reqItem.getPathValue(req, "testK"); + assertEquals("testV", pathValue); + } +} From 80a5693152f892a003306f1ad8ecf6ce12e231fc Mon Sep 17 00:00:00 2001 From: luyanbo Date: Fri, 1 Mar 2024 16:54:23 +0800 Subject: [PATCH 08/11] fix WebFlowRuleManagerTest,ParamFlowRuleUtil --- .../slots/block/flow/param/ParamFlowRuleUtil.java | 14 +++++++++----- .../webflow/rule/WebFlowRuleManagerTest.java | 12 ++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtil.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtil.java index 80c62130c8..00f40d05b1 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtil.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtil.java @@ -45,11 +45,15 @@ public final class ParamFlowRuleUtil { * @return true if valid, otherwise false */ public static boolean isValidRule(ParamFlowRule rule) { - return rule != null && !StringUtil.isBlank(rule.getResource()) && rule.getCount() >= 0 - && rule.getGrade() >= 0 && rule.getParamIdx() != null - && rule.getBurstCount() >= 0 && rule.getControlBehavior() >= 0 - && rule.getDurationInSec() > 0 && rule.getMaxQueueingTimeMs() >= 0 - && checkCluster(rule) & checkRegexField(rule); + boolean valid = rule != null && !StringUtil.isBlank(rule.getResource()) && rule.getCount() >= 0 + && rule.getGrade() >= 0 + && rule.getBurstCount() >= 0 && rule.getControlBehavior() >= 0 + && rule.getDurationInSec() > 0 && rule.getMaxQueueingTimeMs() >= 0 + && checkCluster(rule) && checkRegexField(rule); + if (valid) { + valid = rule.getParamIdx() != null || StringUtil.isNotEmpty(rule.getParamKey()); + } + return valid; } private static boolean checkCluster(/*@PreChecked*/ ParamFlowRule rule) { diff --git a/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManagerTest.java b/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManagerTest.java index 17f3a5abb5..a974f458b0 100644 --- a/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManagerTest.java +++ b/sentinel-extension/sentinel-webflow-extension/src/test/java/com/alibaba/csp/sentinel/webflow/rule/WebFlowRuleManagerTest.java @@ -36,10 +36,10 @@ public class WebFlowRuleManagerTest { @Test public void testLoadAndGetGatewayRules() { Set rules = new HashSet(); - String ahasRoute = "ahas_route"; - WebFlowRule rule1 = new WebFlowRule(ahasRoute) + String mseRoute = "mse_route"; + WebFlowRule rule1 = new WebFlowRule(mseRoute) .setCount(500d); - WebFlowRule rule2 = new WebFlowRule(ahasRoute) + WebFlowRule rule2 = new WebFlowRule(mseRoute) .setCount(20d) .setBurst(5) .setParamItem(new WebParamItem() @@ -58,10 +58,10 @@ public void testLoadAndGetGatewayRules() { rules.add(rule3); WebFlowRuleManager.loadRules(rules); - List convertedRules = WebFlowRuleManager.getConvertedParamRules(ahasRoute); + List convertedRules = WebFlowRuleManager.getConvertedParamRules(mseRoute); assertNotNull(convertedRules); - assertTrue(WebFlowRuleManager.getRulesForResource(ahasRoute).contains(rule1)); - assertTrue(WebFlowRuleManager.getRulesForResource(ahasRoute).contains(rule2)); + assertTrue(WebFlowRuleManager.getRulesForResource(mseRoute).contains(rule1)); + assertTrue(WebFlowRuleManager.getRulesForResource(mseRoute).contains(rule2)); } @Test From 88834909628bfe48b466cf3e30d47a140a8601e3 Mon Sep 17 00:00:00 2001 From: luyanbo Date: Fri, 1 Mar 2024 17:41:42 +0800 Subject: [PATCH 09/11] add webmvc_v6x/SentinelWebPrefixInterceptorTest --- .../SentinelWebPrefixInterceptorTest.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebPrefixInterceptorTest.java diff --git a/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebPrefixInterceptorTest.java b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebPrefixInterceptorTest.java new file mode 100644 index 0000000000..5a116768b0 --- /dev/null +++ b/sentinel-adapter/sentinel-spring-webmvc-v6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc_v6x/SentinelWebPrefixInterceptorTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 1999-2024 Alibaba Group Holding Ltd. + * + * 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 + * + * http://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 com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x; + +import com.alibaba.csp.sentinel.EntryType; +import com.alibaba.csp.sentinel.ResourceTypeConstants; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.config.SentinelWebMvcConfig; +import org.junit.Test; +import org.mockito.MockedStatic; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.web.servlet.HandlerMapping; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; + +public class SentinelWebPrefixInterceptorTest { + + @Test + public void testPreHandle_asyncEntry() throws Exception { + SentinelWebMvcConfig config = new SentinelWebMvcConfig(); + config.setRequestAttributeName(null); + SentinelWebPrefixInterceptor interceptor = new SentinelWebPrefixInterceptor(); + MockHttpServletRequest req = new MockHttpServletRequest("GET", "/a/b"); + req.setAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, "/a/b"); + MockHttpServletResponse resp = new MockHttpServletResponse(); + + try (MockedStatic mocked = mockStatic(SphU.class)) { + mocked.when(() -> SphU.asyncEntry( + eq("GET:/a/b"), + eq(ResourceTypeConstants.COMMON_WEB), + eq(EntryType.IN) + )).thenCallRealMethod(); + interceptor.preHandle(req, resp, null); + mocked.verify(() -> SphU.asyncEntry( + eq("GET:/a/b"), + eq(ResourceTypeConstants.COMMON_WEB), + eq(EntryType.IN) + )); + } + } +} From 961f63da0354a72f99f68bd4bfa21f227cee3f6a Mon Sep 17 00:00:00 2001 From: luyanbo Date: Fri, 1 Mar 2024 18:05:21 +0800 Subject: [PATCH 10/11] update param/WebParamParser --- .../webflow/param/WebParamParser.java | 199 +----------------- 1 file changed, 4 insertions(+), 195 deletions(-) diff --git a/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/WebParamParser.java b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/WebParamParser.java index c9d76ad131..4ee7ea8d5b 100644 --- a/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/WebParamParser.java +++ b/sentinel-extension/sentinel-webflow-extension/src/main/java/com/alibaba/csp/sentinel/webflow/param/WebParamParser.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2019 Alibaba Group Holding Ltd. + * Copyright 1999-2024 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,25 +15,15 @@ */ package com.alibaba.csp.sentinel.webflow.param; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; - import com.alibaba.csp.sentinel.util.AssertUtil; -import com.alibaba.csp.sentinel.util.StringUtil; -import com.alibaba.csp.sentinel.util.function.Predicate; -import com.alibaba.csp.sentinel.webflow.rule.WebFlowRule; -import com.alibaba.csp.sentinel.webflow.rule.WebFlowRuleConverter; -import com.alibaba.csp.sentinel.webflow.rule.WebFlowRuleManager; import com.alibaba.csp.sentinel.webflow.SentinelWebFlowConstants; -import com.alibaba.csp.sentinel.webflow.rule.WebParamItem; + +import java.util.regex.Pattern; /** * @author guanyu * @author Eric Zhao - * @since 1.10.0 + * @since 1.8.8 */ public class WebParamParser { @@ -53,186 +43,6 @@ public WebParamParser(RequestItemParser requestItemParser) { this.requestItemParser = requestItemParser; } - /** - * Parse parameters for given resource from the request entity on condition of the rule predicate. - * - * @param resource valid resource name - * @param request valid request - * @param rulePredicate rule predicate indicating the rules to refer, can be null - * @return the parameter array - */ - public Map parseParameterFor(String resource, T request, Predicate rulePredicate) { - if (StringUtil.isEmpty(resource) || request == null) { - return new HashMap(); - } - Set webFlowRules = new HashSet(); - Set predSet = new HashSet(); - // TODO: optimize the logic here. - boolean hasNonParamRule = false; - for (WebFlowRule rule : WebFlowRuleManager.getRulesForResource(resource)) { - if (rule.getParamItem() != null) { - webFlowRules.add(rule); - predSet.add(rulePredicate == null || rulePredicate.test(rule)); - } else { - hasNonParamRule = true; - } - } - - if (!hasNonParamRule && webFlowRules.isEmpty()) { - return new HashMap(); - } - if (predSet.size() > 1 || predSet.contains(false)) { - return new HashMap(); - } - Map argMap = new HashMap(); - for (WebFlowRule rule : webFlowRules) { - WebParamItem paramItem = rule.getParamItem(); - if (paramItem == null) { - continue; - } - String paramValue = parseInternal(paramItem, request); - // Use the cached param key to reduce memory footprint. - String paramKey = WebFlowRuleConverter.getCachedConvertedParamKey(paramItem); - argMap.put(paramKey, paramValue); - } - if (hasNonParamRule) { - // TODO: review the logic here - argMap.put("default", SentinelWebFlowConstants.WEB_FLOW_NON_PARAM_DEFAULT_KEY); - } - return argMap; - } - - public String parseInternal(WebParamItem item, T request) { - switch (item.getParseStrategy()) { - case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_CLIENT_IP: - return parseClientIp(item, request); - case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_HOST: - return parseHost(item, request); - case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_HEADER: - return parseHeader(item, request); - case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_URL_PARAM: - return parseUrlParameter(item, request); - case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_COOKIE: - return parseCookie(item, request); - case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_BODY_PARAM: - return parseBody(item, request); - case SentinelWebFlowConstants.PARAM_PARSE_STRATEGY_PATH_PARAM: - return parsePath(item, request); - default: - return null; - } - } - - - public String parsePath(/*@Valid*/ WebParamItem item, T request) { - String pathName = item.getFieldName(); - String pattern = item.getPattern(); - String param = requestItemParser.getPathValue(request, pathName); - if (StringUtil.isEmpty(pattern)) { - return param; - } - // Match value according to regex pattern or exact mode. - return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern); - } - - - public String parseClientIp(/*@Valid*/ WebParamItem item, T request) { - // X-Forwarded-For请求头获取多级代理转发流程:client、proxy1、proxy2 - String ipAddress = requestItemParser.getHeader(request, HEADER_X_FORWARDED_FOR); - // 若 X-Forwarded-For 为空尝试自定义头中的IP - if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = requestItemParser.getHeader(request, HEADER_PROXY_CLIENT_IP); - } - // 尝试 Apache HTTP 代理服务器请求头 - if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = requestItemParser.getHeader(request, HEADER_PROXY_CLIENT_IP); - } - // 尝试 Apache 代理时 WebLogic 插件的请求头 - if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = requestItemParser.getHeader(request, HEADER_WL_PROXY_CLIENT_IP); - } - // 尝试 Nginx 代理使用的请求头 - if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = requestItemParser.getHeader(request, HEADER_X_REAL_IP); - } - // 高级匿名代理等情况使用的请求头 - if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = requestItemParser.getHeader(request, HEADER_HTTP_CLIENT_IP); - } - // TODO: 如果是如果 localhost (127.0.0.1),是否需要取本地真实 IP - if (StringUtil.isBlank(ipAddress) || UNKNOWN.equalsIgnoreCase(ipAddress)) { - ipAddress = requestItemParser.getRemoteAddress(request); - } - - // 通过多级反向代理的情况,X-Forwarded-For是一串IP,第一个IP为真实IP,IP按照','分割 - if (!StringUtil.isEmpty(ipAddress) && ipAddress.contains(SEPARATOR)) { - // TODO: optimize here - ipAddress = ipAddress.split(SEPARATOR)[0].trim(); - } - - String pattern = item.getPattern(); - if (StringUtil.isEmpty(pattern)) { - return ipAddress; - } - return parseWithMatchStrategyInternal(item.getMatchStrategy(), ipAddress, pattern); - } - - public String parseHeader(/*@Valid*/ WebParamItem item, T request) { - String headerKey = item.getFieldName(); - String pattern = item.getPattern(); - // TODO: what if the header has multiple values? - String headerValue = requestItemParser.getHeader(request, headerKey); - if (StringUtil.isEmpty(pattern)) { - return headerValue; - } - // Match value according to regex pattern or exact mode. - return parseWithMatchStrategyInternal(item.getMatchStrategy(), headerValue, pattern); - } - - public String parseHost(/*@Valid*/ WebParamItem item, T request) { - String pattern = item.getPattern(); - String host = requestItemParser.getHeader(request, "Host"); - if (StringUtil.isEmpty(pattern)) { - return host; - } - // Match value according to regex pattern or exact mode. - return parseWithMatchStrategyInternal(item.getMatchStrategy(), host, pattern); - } - - public String parseUrlParameter(/*@Valid*/ WebParamItem item, T request) { - String paramName = item.getFieldName(); - String pattern = item.getPattern(); - String param = requestItemParser.getUrlParam(request, paramName); - if (StringUtil.isEmpty(pattern)) { - return param; - } - // Match value according to regex pattern or exact mode. - return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern); - } - - public String parseCookie(/*@Valid*/ WebParamItem item, T request) { - String cookieName = item.getFieldName(); - String pattern = item.getPattern(); - String param = requestItemParser.getCookieValue(request, cookieName); - if (StringUtil.isEmpty(pattern)) { - return param; - } - // Match value according to regex pattern or exact mode. - return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern); - } - - public String parseBody(/*@Valid*/ WebParamItem item, T request) { - String bodyName = item.getFieldName(); - String pattern = item.getPattern(); - String param = requestItemParser.getBodyValue(request, bodyName); - if (StringUtil.isEmpty(pattern)) { - return param; - } - // Match value according to regex pattern or exact mode. - return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern); - } - - static String parseWithMatchStrategyInternal(Integer matchStrategy, String value, String pattern) { if (value == null || matchStrategy == null) { return null; @@ -252,5 +62,4 @@ static String parseWithMatchStrategyInternal(Integer matchStrategy, String value return value; } } - } From 00a1e4bdd5c3fa9c493115b90c6e7e13a3487a39 Mon Sep 17 00:00:00 2001 From: luyanbo Date: Fri, 1 Mar 2024 18:28:30 +0800 Subject: [PATCH 11/11] add BlockFallbackConfigManager.loadConfig --- .../fallback/BlockFallbackConfigManager.java | 26 +++++++++++++++++-- .../setting/fallback/BlockFallbackUtils.java | 16 ++++-------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfigManager.java b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfigManager.java index de0d41c5e8..d52f22e51f 100644 --- a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfigManager.java +++ b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackConfigManager.java @@ -1,8 +1,15 @@ package com.alibaba.csp.sentinel.setting.fallback; +import com.alibaba.csp.sentinel.property.DynamicSentinelProperty; +import com.alibaba.csp.sentinel.property.SentinelProperty; + import java.util.HashMap; +import java.util.List; import java.util.Map; +/** + * @since 1.8.8 + */ public class BlockFallbackConfigManager { /** @@ -11,6 +18,9 @@ public class BlockFallbackConfigManager { private volatile Map>> webFallbackConfigMap = new HashMap>>(); + private SentinelProperty>> currentProperty + = new DynamicSentinelProperty>>(); + private static class InstanceHolder { private static final BlockFallbackConfigManager INSTANCE = new BlockFallbackConfigManager(); } @@ -19,8 +29,20 @@ public static BlockFallbackConfigManager getInstance() { return BlockFallbackConfigManager.InstanceHolder.INSTANCE; } - public BlockFallbackConfig getWebFallbackConfig(String resource, - Class exceptionClazz) { + /** + * Load new block fallback config, which will replace existing config. + * + * @param configList new config list to load + * @return whether the config was actually updated + */ + public boolean loadConfig(List> configList) { + return currentProperty.updateValue(configList); + } + + public BlockFallbackConfig getWebFallbackConfig( + String resource, + Class exceptionClazz + ) { if (resource == null || exceptionClazz == null) { return null; } diff --git a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackUtils.java b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackUtils.java index fd423d1ddc..b2a9a55798 100644 --- a/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackUtils.java +++ b/sentinel-extension/sentinel-dynamic-setting-extension/src/main/java/com/alibaba/csp/sentinel/setting/fallback/BlockFallbackUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2020 Alibaba Group Holding Ltd. + * Copyright 1999-2024 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,11 @@ package com.alibaba.csp.sentinel.setting.fallback; import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.slots.system.SystemBlockException; /** * @author Eric Zhao * @author guanyu - * @since 1.8.2 + * @since 1.8.8 */ public final class BlockFallbackUtils { @@ -30,18 +29,13 @@ public static BlockFallbackConfig.WebBlockFallbackBehavior getFallbackBehavior(S return null; } BlockFallbackConfig c; - // 系统保护规则特殊处理,取这个约定好的名称 - if (ex instanceof SystemBlockException) { - c = BlockFallbackConfigManager.getInstance().getWebFallbackConfig(BlockFallbackConstants.SYSTEM_RESOURCE, - ex.getClass()); - } else { - c = BlockFallbackConfigManager.getInstance().getWebFallbackConfig(resource, ex.getClass()); - } + c = BlockFallbackConfigManager.getInstance().getWebFallbackConfig(resource, ex.getClass()); if (c == null) { return null; } return c.getFallbackBehavior(); } - private BlockFallbackUtils() {} + private BlockFallbackUtils() { + } }