func matchCondition()

in plugins/wasm-go/extensions/traffic-tag/content.go [40:144]


func matchCondition(conditionGroup *ConditionGroup, log wrapper.Log) bool {
	for _, condition := range conditionGroup.Conditions {
		conditionKeyValue, err := getConditionValue(condition, log)
		if err != nil {
			log.Debugf("failed to get condition value: %s", err)
			if conditionGroup.Logic == "and" {
				return false
			}
			continue
		}

		switch condition.Operator {
		case Op_Equal:
			if conditionKeyValue == condition.Value[0] && conditionGroup.Logic == "or" {
				log.Debugf("condition match: %s == %s", conditionKeyValue, condition.Value[0])
				return true
			} else if conditionKeyValue != condition.Value[0] && conditionGroup.Logic == "and" {
				return false
			}
		case Op_NotEqual:
			if conditionKeyValue != condition.Value[0] && conditionGroup.Logic == "or" {
				log.Debugf("condition match: %s != %s", conditionKeyValue, condition.Value[0])
				return true
			} else if conditionKeyValue == condition.Value[0] && conditionGroup.Logic == "and" {
				return false
			}
		case Op_Prefix:
			if strings.HasPrefix(conditionKeyValue, condition.Value[0]) && conditionGroup.Logic == "or" {
				log.Debugf("condition match: %s prefix %s", conditionKeyValue, condition.Value[0])
				return true
			} else if !strings.HasPrefix(conditionKeyValue, condition.Value[0]) && conditionGroup.Logic == "and" {
				return false
			}
		case Op_Regex:
			if _, ok := regexCache[condition.Value[0]]; !ok {
				err := compileRegex(condition.Value[0])
				if err != nil {
					log.Warnf("failed to compile regex: %s", err)
					return false
				}
			}
			regex := regexCache[condition.Value[0]]

			if regex.MatchString(conditionKeyValue) && conditionGroup.Logic == "or" {
				log.Debugf("condition match: %s regex %s", conditionKeyValue, condition.Value[0])
				return true
			} else if !regex.MatchString(conditionKeyValue) && conditionGroup.Logic == "and" {
				log.Debugf("condition not match: %s regex %s", conditionKeyValue, condition.Value[0])
				return false
			}
		case Op_In:
			isMatch := false
			for _, v := range condition.Value {
				if v == conditionKeyValue {
					isMatch = true
					break
				}
			}
			if isMatch && conditionGroup.Logic == "or" {
				log.Debugf("condition match: %s in %v", conditionKeyValue, condition.Value)
				return true
			} else if !isMatch && conditionGroup.Logic == "and" {
				return false
			}
		case Op_NotIn:
			isMatch := false
			for _, v := range condition.Value {
				if v == conditionKeyValue {
					isMatch = true
					break
				}
			}
			if !isMatch && conditionGroup.Logic == "or" {
				log.Debugf("condition match: %s not in %v", conditionKeyValue, condition.Value)
				return true
			} else if isMatch && conditionGroup.Logic == "and" {
				return false
			}
		case Op_Percent:
			percentThresholdInt, err := strconv.Atoi(condition.Value[0])
			if err != nil {
				log.Infof("invalid percent threshold config: %s", err)
				return false
			}

			// hash(value) % 100 < percent
			hash := sha256.Sum256([]byte(conditionKeyValue))
			hashInt64 := int64(binary.BigEndian.Uint64(hash[:8]) % 100)
			log.Debugf("hashInt64: %d", hashInt64)

			if hashInt64 < int64(percentThresholdInt) && conditionGroup.Logic == "or" {
				log.Debugf("condition match: %d < %d", hashInt64, percentThresholdInt)
				return true
			} else if hashInt64 >= int64(percentThresholdInt) && conditionGroup.Logic == "and" {
				log.Debugf("condition not match: %d >= %d", hashInt64, percentThresholdInt)
				return false
			}

		default:
			log.Criticalf("invalid operator: %s", condition.Operator)
			return false
		}
	}
	return len(conditionGroup.Conditions) > 0 && conditionGroup.Logic == "and" // all conditions are matched
}