
com.tencent.polaris.api.utils.RuleUtils Maven / Gradle / Ivy
/*
* Tencent is pleased to support the open source community by making polaris-java available.
*
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.tencent.polaris.api.utils;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.pojo.TrieNode;
import com.tencent.polaris.logging.LoggerFactory;
import com.tencent.polaris.metadata.core.manager.MetadataContainerGroup;
import com.tencent.polaris.specification.api.v1.model.ModelProto;
import com.tencent.polaris.specification.api.v1.model.ModelProto.MatchString;
import com.tencent.polaris.specification.api.v1.model.ModelProto.MatchString.MatchStringType;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
public class RuleUtils {
private static final Logger LOG = LoggerFactory.getLogger(RuleUtils.class);
public static final String MATCH_ALL = "*";
public static final String CALLEE_APPLICATION_METADATA_PREFIX = "$caller_metadata.";
private static final Function DEFAULT_REGEX_PATTERN = new Function() {
@Override
public Pattern apply(String s) {
return Pattern.compile(s);
}
};
/**
* 是否全匹配的规则
*
* @param ruleMetaValue 规则匹配条件
* @return 是否全匹配,全匹配则忽略该规则
*/
public static boolean isMatchAllValue(MatchString ruleMetaValue) {
// see issue: https://github.com/Tencent/spring-cloud-tencent/issues/1214
// 如果 ValueType 类型不为 TEXT, 则不参与是否为全匹配场景判断, 直接快速返回 false
if (ruleMetaValue.getValueType() != MatchString.ValueType.TEXT) {
return false;
}
return isMatchAllValue(ruleMetaValue.getValue().getValue());
}
/**
* 是否全匹配的规则
*
* @param value 规则匹配键
* @return 是否全匹配,全匹配则忽略该规则
*/
public static boolean isMatchAllValue(String value) {
return StringUtils.isEmpty(value) || StringUtils.equals(value, MATCH_ALL);
}
public static boolean matchStringValue(MatchString matchString, String actualValue,
Function regexToPattern) {
MatchStringType matchType = matchString.getType();
String matchValue = matchString.getValue().getValue();
return matchStringValue(matchType, actualValue, matchValue, regexToPattern, false, null);
}
public static boolean matchStringValue(MatchStringType matchType, String actualValue, String matchValue) {
return matchStringValue(matchType, actualValue, matchValue, DEFAULT_REGEX_PATTERN, false, null);
}
public static boolean matchStringValue(MatchStringType matchType, String actualValue, String matchValue,
Function regexToPattern, boolean useTrieNode, Function> trieNodeFunction) {
actualValue = StringUtils.defaultString(actualValue);
matchValue = StringUtils.defaultString(matchValue);
if (RuleUtils.isMatchAllValue(matchValue)) {
return true;
}
switch (matchType) {
case EXACT: {
if (useTrieNode && trieNodeFunction != null) {
return TrieUtil.checkSimpleApi(trieNodeFunction.apply(matchValue), actualValue);
}
return StringUtils.equalsIgnoreCase(actualValue, matchValue);
}
case REGEX: {
//正则表达式匹配
Pattern pattern = regexToPattern.apply(matchValue);
return pattern.matcher(actualValue).find();
}
case NOT_EQUALS: {
if (useTrieNode && trieNodeFunction != null) {
return !TrieUtil.checkSimpleApi(trieNodeFunction.apply(matchValue), actualValue);
}
return !StringUtils.equalsIgnoreCase(actualValue, matchValue);
}
case IN: {
String[] tokens = matchValue.split(",");
for (String token : tokens) {
if (useTrieNode && trieNodeFunction != null) {
if (TrieUtil.checkSimpleApi(trieNodeFunction.apply(token), actualValue)) {
return true;
}
} else {
if (StringUtils.equalsIgnoreCase(token, actualValue)) {
return true;
}
}
}
return false;
}
case NOT_IN: {
String[] tokens = matchValue.split(",");
for (String token : tokens) {
if (useTrieNode && trieNodeFunction != null) {
if (TrieUtil.checkSimpleApi(trieNodeFunction.apply(token), actualValue)) {
return false;
}
} else {
if (StringUtils.equalsIgnoreCase(token, actualValue)) {
return false;
}
}
}
return true;
}
case RANGE: {
// 区间范围判断 [a, b], a <= matchV <= b
String[] tokens = matchValue.split("~");
if (tokens.length != 2) {
return false;
}
try {
// 区间范围中的左端值
long left = Long.parseLong(tokens[0]);
// 区间范围中的右端值
long right = Long.parseLong(tokens[1]);
long matchV = Long.parseLong(actualValue);
return matchV >= left && matchV <= right;
} catch (NumberFormatException ignore) {
LOG.error("[RuleUtils] actualValue {} is not a number in RANGE match type, return false",
actualValue);
return false;
}
}
}
return false;
}
// 匹配metadata
public static boolean matchMetadata(Map ruleMeta, Map destMeta) {
return matchMetadata(ruleMeta, destMeta, false, Collections.emptyMap(), Collections.emptyMap());
}
// 匹配metadata
public static boolean matchMetadata(Map ruleMeta, Map destMeta,
boolean isMatchSource, Map multiEnvRouterParamMap, Map variables) {
return matchMetadata(ruleMeta, destMeta, null, isMatchSource, multiEnvRouterParamMap, variables, null);
}
// 匹配metadata
public static boolean matchMetadata(Map ruleMeta, Map destMeta, MetadataContainerGroup metadataContainerGroup) {
return matchMetadata(ruleMeta, destMeta, metadataContainerGroup, true, Collections.emptyMap(), Collections.emptyMap(), null);
}
// 匹配metadata
public static boolean matchMetadata(Map ruleMeta, Map destMeta,
MetadataContainerGroup metadataContainerGroup, boolean isMatchSource,
Map multiEnvRouterParamMap, Map variables,
Function> trieNodeFunction) {
// 如果规则metadata为空, 返回成功
if (MapUtils.isEmpty(ruleMeta)) {
return true;
}
if (ruleMeta.containsKey(RuleUtils.MATCH_ALL)) {
return true;
}
// 如果规则metadata不为空, 待匹配规则为空, 直接返回失败
if (metadataContainerGroup == null && MapUtils.isEmpty(destMeta)) {
return false;
}
// metadata是否全部匹配
boolean allMetaMatched = true;
// dest中找到的metadata个数, 用于辅助判断是否能匹配成功
int matchNum = 0;
for (Map.Entry entry : ruleMeta.entrySet()) {
String ruleMetaKey = entry.getKey();
MatchString ruleMetaValue = entry.getValue();
if (RuleUtils.isMatchAllValue(ruleMetaValue)) {
matchNum++;
continue;
}
String destMetaValue = null;
if (metadataContainerGroup != null) {
if (StringUtils.substringMatch(ruleMetaKey, 0, CALLEE_APPLICATION_METADATA_PREFIX) && metadataContainerGroup.getApplicationMetadataContainer() != null) {
String originalRuleMetaKey = StringUtils.replace(ruleMetaKey, CALLEE_APPLICATION_METADATA_PREFIX, "");
destMetaValue = metadataContainerGroup.getApplicationMetadataContainer().getRawMetadataStringValue(originalRuleMetaKey);
} else if (StringUtils.substringMatch(ruleMetaKey, 0, "$") && metadataContainerGroup.getMessageMetadataContainer() != null) {
if (!ruleMetaKey.contains(".")) {
destMetaValue = metadataContainerGroup.getMessageMetadataContainer().getRawMetadataStringValue(ruleMetaKey);
} else {
int index = ruleMetaKey.indexOf(".");
if (index != -1 && index < ruleMetaKey.length() - 1) {
String newKey = ruleMetaKey.substring(0, index + 1);
String newMapKey = ruleMetaKey.substring(index + 1);
destMetaValue = metadataContainerGroup.getMessageMetadataContainer().getRawMetadataMapValue(newKey, newMapKey);
} else {
destMetaValue = metadataContainerGroup.getMessageMetadataContainer().getRawMetadataStringValue(ruleMetaKey);
}
}
} else if (metadataContainerGroup.getCustomMetadataContainer() != null) {
destMetaValue = metadataContainerGroup.getCustomMetadataContainer().getRawMetadataStringValue(ruleMetaKey);
}
}
if (StringUtils.isBlank(destMetaValue) && CollectionUtils.isNotEmpty(destMeta) && destMeta.containsKey(ruleMetaKey)) {
destMetaValue = destMeta.get(ruleMetaKey);
}
if (StringUtils.isNotBlank(destMetaValue)) {
matchNum++;
if (!ruleMetaValue.hasValue()
&& ruleMetaValue.getValueType() != MatchString.ValueType.PARAMETER) {
continue;
}
boolean useTrieNode = StringUtils.equals(ruleMetaKey, "$path") && ruleMetaValue.getType() != MatchStringType.REGEX;
allMetaMatched = isAllMetaMatched(isMatchSource, ruleMetaKey, ruleMetaValue, destMetaValue,
multiEnvRouterParamMap, variables, useTrieNode, trieNodeFunction);
}
if (!allMetaMatched) {
break;
}
}
// 如果一个metadata未找到, 匹配失败
if (matchNum == 0) {
allMetaMatched = false;
}
if (matchNum != ruleMeta.entrySet().size()) {
allMetaMatched = false;
}
return allMetaMatched;
}
private static boolean isAllMetaMatched(boolean isMatchSource, String ruleMetaKey,
MatchString ruleMetaValue, String destMetaValue,
Map multiEnvRouterParamMap,
Map variables,
boolean useTrieNode, Function> trieNodeFunction) {
if (RuleUtils.MATCH_ALL.equals(destMetaValue)) {
return true;
}
return matchValueByValueType(isMatchSource, ruleMetaKey, ruleMetaValue, destMetaValue,
multiEnvRouterParamMap, variables, useTrieNode, trieNodeFunction);
}
private static boolean matchValueByValueType(boolean isMatchSource, String ruleMetaKey,
MatchString ruleMetaValue, String destMetaValue,
Map multiEnvRouterParamMap,
Map variables,
boolean useTrieNode, Function> trieNodeFunction) {
boolean allMetaMatched = true;
switch (ruleMetaValue.getValueType()) {
case PARAMETER:
// 通过参数传入
if (isMatchSource) {
// 当匹配的是source,记录请求的 K V
multiEnvRouterParamMap.put(ruleMetaKey, destMetaValue);
} else {
// 当匹配的是 dest 方向时,ruleMetaKey 为 dest 标签的 key,destMetaValue 为实例标签的 value, 流量标签的变量值信息都在
// multiEnvRouterParamMap 中
// 例如, source 标签为 , dest 标签则为
// 因此,在参数场景下,需要根据 dest 中的标签的 value 值信息,反向去查询 source 对应标签的 value
if (!multiEnvRouterParamMap.containsKey(ruleMetaValue.getValue().getValue())) {
allMetaMatched = false;
} else {
String ruleValue = multiEnvRouterParamMap.get(ruleMetaValue.getValue().getValue());
// contains key
allMetaMatched = matchStringValue(ruleMetaValue.getType(), ruleValue, destMetaValue);
}
}
break;
case VARIABLE:
if (variables.containsKey(ruleMetaKey)) {
// 1.先从配置获取
String ruleValue = variables.get(ruleMetaKey);
allMetaMatched = matchStringValue(ruleMetaValue.getType(), destMetaValue, ruleValue);
} else {
// 2.从环境变量中获取 key从规则中获取
String key = ruleMetaValue.getValue().getValue();
if (!System.getenv().containsKey(key)) {
allMetaMatched = false;
} else {
String value = System.getenv(key);
allMetaMatched = matchStringValue(ruleMetaValue.getType(), destMetaValue, value);
}
if (!System.getenv().containsKey(key) || !System.getenv(key).equals(destMetaValue)) {
allMetaMatched = false;
}
}
break;
default:
allMetaMatched = matchStringValue(ruleMetaValue.getType(), destMetaValue,
ruleMetaValue.getValue().getValue(), DEFAULT_REGEX_PATTERN, useTrieNode, trieNodeFunction);
}
return allMetaMatched;
}
public static boolean matchService(ServiceKey serviceKey, String namespace, String service) {
String inputNamespace = "";
String inputService = "";
if (null != serviceKey) {
inputNamespace = serviceKey.getNamespace();
inputService = serviceKey.getService();
}
if (StringUtils.isNotBlank(namespace) && !StringUtils.equals(namespace, RuleUtils.MATCH_ALL) && !StringUtils
.equals(inputNamespace, namespace)) {
return false;
}
if (StringUtils.isNotBlank(service) && !StringUtils.equals(service, RuleUtils.MATCH_ALL) && !StringUtils
.equals(inputService, service)) {
return false;
}
return true;
}
public static boolean matchMethod(String path, String protocol, String method, ModelProto.API api,
Function regexToPattern, Function> trieNodeFunction) {
MatchStringType methodMatchStringType = MatchStringType.EXACT;
String apiMethod = api.getMethod();
if (StringUtils.isNotBlank(apiMethod) && apiMethod.startsWith("!")) {
methodMatchStringType = MatchStringType.NOT_EQUALS;
apiMethod = apiMethod.substring(1);
}
if (trieNodeFunction != null) {
return RuleUtils.matchStringValue(MatchString.MatchStringType.EXACT, protocol, api.getProtocol())
&& RuleUtils.matchStringValue(methodMatchStringType, method, apiMethod)
&& RuleUtils.matchStringValue(api.getPath().getType(), path, api.getPath().getValue().getValue(), regexToPattern, true, trieNodeFunction);
} else {
return RuleUtils.matchStringValue(MatchString.MatchStringType.EXACT, protocol, api.getProtocol())
&& RuleUtils.matchStringValue(methodMatchStringType, method, apiMethod)
&& RuleUtils.matchStringValue(api.getPath(), path, regexToPattern);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy