- Separate converted parameter rules from ParamFlowManager. Now the converted rules will be kept in GatewayRuleManager directly. - Add a GatewayFlowSlot to do separate flow checking for generated rules. - Refactor rule converting mechanism: now gateway rules in normal mode (without parameter) will also be converted to a parameter flow rule. The index will be the last (the last position). In GatewayParamParser we put a constant value to the last position. Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -37,5 +37,15 @@ | |||
<artifactId>junit</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.assertj</groupId> | |||
<artifactId>assertj-core</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-core</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -40,6 +40,7 @@ public final class SentinelGatewayConstants { | |||
public static final String GATEWAY_CONTEXT_ROUTE_PREFIX = "sentinel_gateway_context$$route$$"; | |||
public static final String GATEWAY_NOT_MATCH_PARAM = "$$not_match"; | |||
public static final String GATEWAY_DEFAULT_PARAM = "$D"; | |||
private SentinelGatewayConstants() {} | |||
} |
@@ -53,10 +53,13 @@ public class GatewayParamParser<T> { | |||
} | |||
Set<GatewayFlowRule> gatewayRules = new HashSet<>(); | |||
Set<Boolean> predSet = new HashSet<>(); | |||
boolean hasNonParamRule = false; | |||
for (GatewayFlowRule rule : GatewayRuleManager.getRulesForResource(resource)) { | |||
if (rule.getParamItem() != null) { | |||
gatewayRules.add(rule); | |||
predSet.add(rulePredicate.test(rule)); | |||
} else { | |||
hasNonParamRule = true; | |||
} | |||
} | |||
if (gatewayRules.isEmpty()) { | |||
@@ -65,13 +68,17 @@ public class GatewayParamParser<T> { | |||
if (predSet.size() != 1 || predSet.contains(false)) { | |||
return new Object[0]; | |||
} | |||
Object[] arr = new Object[gatewayRules.size()]; | |||
int size = hasNonParamRule ? gatewayRules.size() + 1 : gatewayRules.size(); | |||
Object[] arr = new Object[size]; | |||
for (GatewayFlowRule rule : gatewayRules) { | |||
GatewayParamFlowItem paramItem = rule.getParamItem(); | |||
int idx = paramItem.getIndex(); | |||
String param = parseInternal(paramItem, request); | |||
arr[idx] = param; | |||
} | |||
if (hasNonParamRule) { | |||
arr[size - 1] = SentinelGatewayConstants.GATEWAY_DEFAULT_PARAM; | |||
} | |||
return arr; | |||
} | |||
@@ -32,6 +32,17 @@ final class GatewayRuleConverter { | |||
.setMaxQueueingTimeMs(rule.getMaxQueueingTimeoutMs()); | |||
} | |||
static ParamFlowRule applyNonParamToParamRule(/*@Valid*/ GatewayFlowRule gatewayRule, int idx) { | |||
return new ParamFlowRule(gatewayRule.getResource()) | |||
.setCount(gatewayRule.getCount()) | |||
.setGrade(gatewayRule.getGrade()) | |||
.setDurationInSec(gatewayRule.getIntervalSec()) | |||
.setBurstCount(gatewayRule.getBurst()) | |||
.setControlBehavior(gatewayRule.getControlBehavior()) | |||
.setMaxQueueingTimeMs(gatewayRule.getMaxQueueingTimeoutMs()) | |||
.setParamIdx(idx); | |||
} | |||
/** | |||
* Convert a gateway rule to parameter flow rule, then apply the generated | |||
* parameter index to {@link GatewayParamFlowItem} of the rule. | |||
@@ -29,10 +29,9 @@ 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.FlowRule; | |||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleUtil; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage; | |||
import com.alibaba.csp.sentinel.util.AssertUtil; | |||
import com.alibaba.csp.sentinel.util.StringUtil; | |||
@@ -42,7 +41,12 @@ import com.alibaba.csp.sentinel.util.StringUtil; | |||
*/ | |||
public final class GatewayRuleManager { | |||
private static final Map<String, Set<GatewayFlowRule>> RULE_MAP = new ConcurrentHashMap<>(); | |||
/** | |||
* Gateway flow rule map: (resource, [rules...]) | |||
*/ | |||
private static final Map<String, Set<GatewayFlowRule>> GATEWAY_RULE_MAP = new ConcurrentHashMap<>(); | |||
private static final Map<String, List<ParamFlowRule>> CONVERTED_PARAM_RULE_MAP = new ConcurrentHashMap<>(); | |||
private static final GatewayRulePropertyListener LISTENER = new GatewayRulePropertyListener(); | |||
private static SentinelProperty<Set<GatewayFlowRule>> currentProperty = new DynamicSentinelProperty<>(); | |||
@@ -74,46 +78,69 @@ public final class GatewayRuleManager { | |||
public static Set<GatewayFlowRule> getRules() { | |||
Set<GatewayFlowRule> rules = new HashSet<>(); | |||
for (Set<GatewayFlowRule> ruleSet : RULE_MAP.values()) { | |||
for (Set<GatewayFlowRule> ruleSet : GATEWAY_RULE_MAP.values()) { | |||
rules.addAll(ruleSet); | |||
} | |||
return rules; | |||
} | |||
public static Set<GatewayFlowRule> getRulesForResource(String resourceName) { | |||
AssertUtil.assertNotBlank(resourceName, "resourceName cannot be blank"); | |||
Set<GatewayFlowRule> set = RULE_MAP.get(resourceName); | |||
if (StringUtil.isBlank(resourceName)) { | |||
return new HashSet<>(); | |||
} | |||
Set<GatewayFlowRule> set = GATEWAY_RULE_MAP.get(resourceName); | |||
if (set == null) { | |||
return new HashSet<>(); | |||
} | |||
return new HashSet<>(set); | |||
} | |||
/** | |||
* <p>Get all converted parameter rules.</p> | |||
* <p>Note: caller SHOULD NOT modify the list and rules.</p> | |||
* | |||
* @param resourceName valid resource name | |||
* @return converted parameter rules | |||
*/ | |||
public static List<ParamFlowRule> getConvertedParamRules(String resourceName) { | |||
if (StringUtil.isBlank(resourceName)) { | |||
return new ArrayList<>(); | |||
} | |||
return CONVERTED_PARAM_RULE_MAP.get(resourceName); | |||
} | |||
private static final class GatewayRulePropertyListener implements PropertyListener<Set<GatewayFlowRule>> { | |||
@Override | |||
public void configUpdate(Set<GatewayFlowRule> conf) { | |||
applyGatewayRuleInternal(conf); | |||
RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + RULE_MAP); | |||
RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + GATEWAY_RULE_MAP); | |||
} | |||
@Override | |||
public void configLoad(Set<GatewayFlowRule> conf) { | |||
applyGatewayRuleInternal(conf); | |||
RecordLog.info("[GatewayRuleManager] Gateway flow rules loaded: " + RULE_MAP); | |||
RecordLog.info("[GatewayRuleManager] Gateway flow rules loaded: " + GATEWAY_RULE_MAP); | |||
} | |||
private int getIdxInternal(Map<String, Integer> idxMap, String resourceName) { | |||
// Prepare index map. | |||
if (!idxMap.containsKey(resourceName)) { | |||
idxMap.put(resourceName, 0); | |||
} | |||
return idxMap.get(resourceName); | |||
} | |||
private synchronized void applyGatewayRuleInternal(Set<GatewayFlowRule> conf) { | |||
if (conf == null || conf.isEmpty()) { | |||
FlowRuleManager.loadRules(new ArrayList<FlowRule>()); | |||
ParamFlowRuleManager.loadRules(new ArrayList<ParamFlowRule>()); | |||
RULE_MAP.clear(); | |||
applyToConvertedParamMap(new HashSet<ParamFlowRule>()); | |||
GATEWAY_RULE_MAP.clear(); | |||
return; | |||
} | |||
Map<String, Set<GatewayFlowRule>> gatewayRuleMap = new ConcurrentHashMap<>(); | |||
Map<String, Integer> idxMap = new HashMap<>(); | |||
List<FlowRule> flowRules = new ArrayList<>(); | |||
Set<ParamFlowRule> paramFlowRules = new HashSet<>(); | |||
Map<String, List<GatewayFlowRule>> noParamMap = new HashMap<>(); | |||
for (GatewayFlowRule rule : conf) { | |||
if (!isValidRule(rule)) { | |||
@@ -122,14 +149,15 @@ public final class GatewayRuleManager { | |||
} | |||
String resourceName = rule.getResource(); | |||
if (rule.getParamItem() == null) { | |||
// If param item is absent, it will be converted to normal flow rule. | |||
flowRules.add(GatewayRuleConverter.toFlowRule(rule)); | |||
} else { | |||
// Prepare index map. | |||
if (!idxMap.containsKey(resourceName)) { | |||
idxMap.put(resourceName, 0); | |||
// Cache the rules with no parameter config, then skip. | |||
List<GatewayFlowRule> noParamList = noParamMap.get(resourceName); | |||
if (noParamList == null) { | |||
noParamList = new ArrayList<>(); | |||
noParamMap.put(resourceName, noParamList); | |||
} | |||
int idx = idxMap.get(resourceName); | |||
noParamList.add(rule); | |||
} else { | |||
int idx = getIdxInternal(idxMap, resourceName); | |||
// Convert to parameter flow rule. | |||
if (paramFlowRules.add(GatewayRuleConverter.applyToParamRule(rule, idx))) { | |||
idxMap.put(rule.getResource(), idx + 1); | |||
@@ -143,11 +171,51 @@ public final class GatewayRuleManager { | |||
} | |||
ruleSet.add(rule); | |||
} | |||
FlowRuleManager.loadRules(flowRules); | |||
ParamFlowRuleManager.loadRules(new ArrayList<>(paramFlowRules)); | |||
// Handle non-param mode rules. | |||
for (Map.Entry<String, List<GatewayFlowRule>> e : noParamMap.entrySet()) { | |||
List<GatewayFlowRule> rules = e.getValue(); | |||
if (rules == null || rules.isEmpty()) { | |||
continue; | |||
} | |||
for (GatewayFlowRule rule : rules) { | |||
int idx = getIdxInternal(idxMap, e.getKey()); | |||
// Always use the same index (the last position). | |||
paramFlowRules.add(GatewayRuleConverter.applyNonParamToParamRule(rule, idx)); | |||
} | |||
} | |||
applyToConvertedParamMap(paramFlowRules); | |||
GATEWAY_RULE_MAP.clear(); | |||
GATEWAY_RULE_MAP.putAll(gatewayRuleMap); | |||
} | |||
private void applyToConvertedParamMap(Set<ParamFlowRule> paramFlowRules) { | |||
Map<String, List<ParamFlowRule>> newRuleMap = ParamFlowRuleUtil.buildParamRuleMap( | |||
new ArrayList<>(paramFlowRules)); | |||
if (newRuleMap == null || newRuleMap.isEmpty()) { | |||
// No parameter flow rules, so clear all the metrics. | |||
for (String resource : CONVERTED_PARAM_RULE_MAP.keySet()) { | |||
ParameterMetricStorage.clearParamMetricForResource(resource); | |||
} | |||
RecordLog.info("[GatewayRuleManager] No gateway rules, clearing parameter metrics of previous rules"); | |||
CONVERTED_PARAM_RULE_MAP.clear(); | |||
return; | |||
} | |||
// Clear unused parameter metrics. | |||
Set<String> previousResources = CONVERTED_PARAM_RULE_MAP.keySet(); | |||
for (String resource : previousResources) { | |||
if (!newRuleMap.containsKey(resource)) { | |||
ParameterMetricStorage.clearParamMetricForResource(resource); | |||
} | |||
} | |||
// Apply to converted rule map. | |||
CONVERTED_PARAM_RULE_MAP.clear(); | |||
CONVERTED_PARAM_RULE_MAP.putAll(newRuleMap); | |||
RULE_MAP.clear(); | |||
RULE_MAP.putAll(gatewayRuleMap); | |||
RecordLog.info("[GatewayRuleManager] Converted internal param rules: " + CONVERTED_PARAM_RULE_MAP); | |||
} | |||
} | |||
@@ -0,0 +1,75 @@ | |||
/* | |||
* 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.adapter.gateway.common.slot; | |||
import java.util.List; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; | |||
import com.alibaba.csp.sentinel.context.Context; | |||
import com.alibaba.csp.sentinel.node.DefaultNode; | |||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot; | |||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; | |||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.1 | |||
*/ | |||
public class GatewayFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> { | |||
@Override | |||
public void entry(Context context, ResourceWrapper resource, DefaultNode node, int count, | |||
boolean prioritized, Object... args) throws Throwable { | |||
checkGatewayParamFlow(resource, count, args); | |||
fireEntry(context, resource, node, count, prioritized, args); | |||
} | |||
private void checkGatewayParamFlow(ResourceWrapper resourceWrapper, int count, Object... args) | |||
throws BlockException { | |||
if (args == null) { | |||
return; | |||
} | |||
List<ParamFlowRule> rules = GatewayRuleManager.getConvertedParamRules(resourceWrapper.getName()); | |||
if (rules == null || rules.isEmpty()) { | |||
return; | |||
} | |||
for (ParamFlowRule rule : rules) { | |||
// Initialize the parameter metrics. | |||
ParameterMetricStorage.initParamMetricsFor(resourceWrapper, rule); | |||
if (!ParamFlowChecker.passCheck(resourceWrapper, rule, count, args)) { | |||
String triggeredParam = ""; | |||
if (args.length > rule.getParamIdx()) { | |||
Object value = args[rule.getParamIdx()]; | |||
triggeredParam = String.valueOf(value); | |||
} | |||
throw new ParamFlowException(resourceWrapper.getName(), triggeredParam, rule); | |||
} | |||
} | |||
} | |||
@Override | |||
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) { | |||
fireExit(context, resourceWrapper, count, args); | |||
} | |||
} |
@@ -0,0 +1,57 @@ | |||
/* | |||
* 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.adapter.gateway.common.slot; | |||
import com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain; | |||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain; | |||
import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder; | |||
import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot; | |||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot; | |||
import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot; | |||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | |||
import com.alibaba.csp.sentinel.slots.logger.LogSlot; | |||
import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot; | |||
import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot; | |||
import com.alibaba.csp.sentinel.slots.system.SystemSlot; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.1 | |||
*/ | |||
public class GatewaySlotChainBuilder implements SlotChainBuilder { | |||
@Override | |||
public ProcessorSlotChain build() { | |||
ProcessorSlotChain chain = new DefaultProcessorSlotChain(); | |||
// Prepare slot | |||
chain.addLast(new NodeSelectorSlot()); | |||
chain.addLast(new ClusterBuilderSlot()); | |||
// Stat slot | |||
chain.addLast(new LogSlot()); | |||
chain.addLast(new StatisticSlot()); | |||
// Rule checking slot | |||
chain.addLast(new AuthoritySlot()); | |||
chain.addLast(new SystemSlot()); | |||
chain.addLast(new GatewayFlowSlot()); | |||
chain.addLast(new ParamFlowSlot()); | |||
chain.addLast(new FlowSlot()); | |||
chain.addLast(new DegradeSlot()); | |||
return chain; | |||
} | |||
} |
@@ -0,0 +1 @@ | |||
com.alibaba.csp.sentinel.adapter.gateway.common.slot.GatewaySlotChainBuilder |
@@ -0,0 +1,205 @@ | |||
/* | |||
* 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.adapter.gateway.common.param; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; | |||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | |||
import com.alibaba.csp.sentinel.util.function.Predicate; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.ArgumentMatchers.*; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
/** | |||
* @author Eric Zhao | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public class GatewayParamParserTest { | |||
private final Predicate<GatewayFlowRule> routeIdPredicate = new Predicate<GatewayFlowRule>() { | |||
@Override | |||
public boolean test(GatewayFlowRule e) { | |||
return e.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID; | |||
} | |||
}; | |||
private final Predicate<GatewayFlowRule> apiNamePredicate = new Predicate<GatewayFlowRule>() { | |||
@Override | |||
public boolean test(GatewayFlowRule e) { | |||
return e.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME; | |||
} | |||
}; | |||
@Test | |||
public void testParseParametersNoParamItem() { | |||
RequestItemParser<Object> itemParser = mock(RequestItemParser.class); | |||
GatewayParamParser<Object> parser = new GatewayParamParser<>(itemParser); | |||
// Create a fake request. | |||
Object request = new Object(); | |||
// Prepare gateway rules. | |||
Set<GatewayFlowRule> rules = new HashSet<>(); | |||
String routeId1 = "my_test_route_A"; | |||
rules.add(new GatewayFlowRule(routeId1) | |||
.setCount(5) | |||
.setIntervalSec(1) | |||
); | |||
GatewayRuleManager.loadRules(rules); | |||
Object[] params = parser.parseParameterFor(routeId1, request, routeIdPredicate); | |||
assertThat(params.length).isZero(); | |||
} | |||
@Test | |||
public void testParseParametersWithItems() { | |||
RequestItemParser<Object> itemParser = mock(RequestItemParser.class); | |||
GatewayParamParser<Object> paramParser = new GatewayParamParser<>(itemParser); | |||
// Create a fake request. | |||
Object request = new Object(); | |||
// Prepare gateway rules. | |||
Set<GatewayFlowRule> rules = new HashSet<>(); | |||
final String routeId1 = "my_test_route_A"; | |||
final String api1 = "my_test_route_B"; | |||
final String headerName = "X-Sentinel-Flag"; | |||
final String paramName = "p"; | |||
GatewayFlowRule routeRuleNoParam = new GatewayFlowRule(routeId1) | |||
.setCount(10) | |||
.setIntervalSec(10); | |||
GatewayFlowRule routeRule1 = new GatewayFlowRule(routeId1) | |||
.setCount(2) | |||
.setIntervalSec(2) | |||
.setBurst(2) | |||
.setParamItem(new GatewayParamFlowItem() | |||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP) | |||
); | |||
GatewayFlowRule routeRule2 = new GatewayFlowRule(routeId1) | |||
.setCount(10) | |||
.setIntervalSec(1) | |||
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER) | |||
.setMaxQueueingTimeoutMs(600) | |||
.setParamItem(new GatewayParamFlowItem() | |||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER) | |||
.setFieldName(headerName) | |||
); | |||
GatewayFlowRule routeRule3 = new GatewayFlowRule(routeId1) | |||
.setCount(20) | |||
.setIntervalSec(1) | |||
.setBurst(5) | |||
.setParamItem(new GatewayParamFlowItem() | |||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM) | |||
.setFieldName(paramName) | |||
); | |||
GatewayFlowRule routeRule4 = new GatewayFlowRule(routeId1) | |||
.setCount(120) | |||
.setIntervalSec(10) | |||
.setBurst(30) | |||
.setParamItem(new GatewayParamFlowItem() | |||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HOST) | |||
); | |||
GatewayFlowRule apiRule1 = new GatewayFlowRule(api1) | |||
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME) | |||
.setCount(5) | |||
.setIntervalSec(1) | |||
.setParamItem(new GatewayParamFlowItem() | |||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM) | |||
.setFieldName(paramName) | |||
); | |||
rules.add(routeRule1); | |||
rules.add(routeRule2); | |||
rules.add(routeRule3); | |||
rules.add(routeRule4); | |||
rules.add(routeRuleNoParam); | |||
rules.add(apiRule1); | |||
GatewayRuleManager.loadRules(rules); | |||
final String expectedHost = "hello.test.sentinel"; | |||
final String expectedAddress = "66.77.88.99"; | |||
final String expectedHeaderValue1 = "Sentinel"; | |||
final String expectedUrlParamValue1 = "17"; | |||
mockClientHostAddress(itemParser, expectedAddress); | |||
Map<String, String> expectedHeaders = new HashMap<String, String>() {{ | |||
put(headerName, expectedHeaderValue1); put("Host", expectedHost); | |||
}}; | |||
mockHeaders(itemParser, expectedHeaders); | |||
mockSingleUrlParam(itemParser, paramName, expectedUrlParamValue1); | |||
Object[] params = paramParser.parseParameterFor(routeId1, request, routeIdPredicate); | |||
// Param length should be 5 (4 with parameters, 1 normal flow with generated constant) | |||
assertThat(params.length).isEqualTo(5); | |||
assertThat(params[routeRule1.getParamItem().getIndex()]).isEqualTo(expectedAddress); | |||
assertThat(params[routeRule2.getParamItem().getIndex()]).isEqualTo(expectedHeaderValue1); | |||
assertThat(params[routeRule3.getParamItem().getIndex()]).isEqualTo(expectedUrlParamValue1); | |||
assertThat(params[routeRule4.getParamItem().getIndex()]).isEqualTo(expectedHost); | |||
assertThat(params[params.length - 1]).isEqualTo(SentinelGatewayConstants.GATEWAY_DEFAULT_PARAM); | |||
assertThat(paramParser.parseParameterFor(api1, request, routeIdPredicate).length).isZero(); | |||
String expectedUrlParamValue2 = "fs"; | |||
mockSingleUrlParam(itemParser, paramName, expectedUrlParamValue2); | |||
params = paramParser.parseParameterFor(api1, request, apiNamePredicate); | |||
assertThat(params.length).isEqualTo(1); | |||
assertThat(params[apiRule1.getParamItem().getIndex()]).isEqualTo(expectedUrlParamValue2); | |||
} | |||
private void mockClientHostAddress(/*@Mock*/ RequestItemParser parser, String address) { | |||
when(parser.getRemoteAddress(any())).thenReturn(address); | |||
} | |||
private void mockHeaders(/*@Mock*/ RequestItemParser parser, Map<String, String> headerMap) { | |||
for (Map.Entry<String, String> e : headerMap.entrySet()) { | |||
when(parser.getHeader(any(), eq(e.getKey()))).thenReturn(e.getValue()); | |||
} | |||
} | |||
private void mockUrlParams(/*@Mock*/ RequestItemParser parser, Map<String, String> paramMap) { | |||
for (Map.Entry<String, String> e : paramMap.entrySet()) { | |||
when(parser.getUrlParam(any(), eq(e.getKey()))).thenReturn(e.getValue()); | |||
} | |||
} | |||
private void mockSingleUrlParam(/*@Mock*/ RequestItemParser parser, String key, String value) { | |||
when(parser.getUrlParam(any(), eq(key))).thenReturn(value); | |||
} | |||
private void mockSingleHeader(/*@Mock*/ RequestItemParser parser, String key, String value) { | |||
when(parser.getHeader(any(), eq(key))).thenReturn(value); | |||
} | |||
@Before | |||
public void setUp() { | |||
GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<ApiDefinition>()); | |||
GatewayRuleManager.loadRules(new HashSet<GatewayFlowRule>()); | |||
} | |||
@After | |||
public void tearDown() { | |||
GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<ApiDefinition>()); | |||
GatewayRuleManager.loadRules(new HashSet<GatewayFlowRule>()); | |||
} | |||
} |
@@ -16,12 +16,12 @@ | |||
package com.alibaba.csp.sentinel.adapter.gateway.common.rule; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants; | |||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | |||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
@@ -62,8 +62,8 @@ public class GatewayRuleManagerTest { | |||
rules.add(rule3); | |||
GatewayRuleManager.loadRules(rules); | |||
assertTrue(FlowRuleManager.hasConfig(ahasRoute)); | |||
assertTrue(ParamFlowRuleManager.hasRules(ahasRoute)); | |||
List<ParamFlowRule> convertedRules = GatewayRuleManager.getConvertedParamRules(ahasRoute); | |||
assertNotNull(convertedRules); | |||
assertEquals(0, (int)rule2.getParamItem().getIndex()); | |||
assertEquals(0, (int)rule3.getParamItem().getIndex()); | |||
assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule1)); | |||