gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/internal/xds/matcher/string_matcher.go (about) 1 /* 2 * 3 * Copyright 2021 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 // Package matcher contains types that need to be shared between code under 20 // google.golang.org/grpc/xds/... and the rest of gRPC. 21 package matcher 22 23 import ( 24 "errors" 25 "fmt" 26 "regexp" 27 "strings" 28 29 v3matcherpb "gitee.com/ks-custle/core-gm/go-control-plane/envoy/type/matcher/v3" 30 "gitee.com/ks-custle/core-gm/grpc/internal/grpcutil" 31 ) 32 33 // StringMatcher contains match criteria for matching a string, and is an 34 // internal representation of the `StringMatcher` proto defined at 35 // https://github.com/envoyproxy/envoy/blob/main/api/envoy/type/matcher/v3/string.proto. 36 type StringMatcher struct { 37 // Since these match fields are part of a `oneof` in the corresponding xDS 38 // proto, only one of them is expected to be set. 39 exactMatch *string 40 prefixMatch *string 41 suffixMatch *string 42 regexMatch *regexp.Regexp 43 containsMatch *string 44 // If true, indicates the exact/prefix/suffix/contains matching should be 45 // case insensitive. This has no effect on the regex match. 46 ignoreCase bool 47 } 48 49 // Match returns true if input matches the criteria in the given StringMatcher. 50 func (sm StringMatcher) Match(input string) bool { 51 if sm.ignoreCase { 52 input = strings.ToLower(input) 53 } 54 switch { 55 case sm.exactMatch != nil: 56 return input == *sm.exactMatch 57 case sm.prefixMatch != nil: 58 return strings.HasPrefix(input, *sm.prefixMatch) 59 case sm.suffixMatch != nil: 60 return strings.HasSuffix(input, *sm.suffixMatch) 61 case sm.regexMatch != nil: 62 return grpcutil.FullMatchWithRegex(sm.regexMatch, input) 63 case sm.containsMatch != nil: 64 return strings.Contains(input, *sm.containsMatch) 65 } 66 return false 67 } 68 69 // StringMatcherFromProto is a helper function to create a StringMatcher from 70 // the corresponding StringMatcher proto. 71 // 72 // Returns a non-nil error if matcherProto is invalid. 73 func StringMatcherFromProto(matcherProto *v3matcherpb.StringMatcher) (StringMatcher, error) { 74 if matcherProto == nil { 75 return StringMatcher{}, errors.New("input StringMatcher proto is nil") 76 } 77 78 matcher := StringMatcher{ignoreCase: matcherProto.GetIgnoreCase()} 79 switch mt := matcherProto.GetMatchPattern().(type) { 80 case *v3matcherpb.StringMatcher_Exact: 81 matcher.exactMatch = &mt.Exact 82 if matcher.ignoreCase { 83 *matcher.exactMatch = strings.ToLower(*matcher.exactMatch) 84 } 85 case *v3matcherpb.StringMatcher_Prefix: 86 if matcherProto.GetPrefix() == "" { 87 return StringMatcher{}, errors.New("empty prefix is not allowed in StringMatcher") 88 } 89 matcher.prefixMatch = &mt.Prefix 90 if matcher.ignoreCase { 91 *matcher.prefixMatch = strings.ToLower(*matcher.prefixMatch) 92 } 93 case *v3matcherpb.StringMatcher_Suffix: 94 if matcherProto.GetSuffix() == "" { 95 return StringMatcher{}, errors.New("empty suffix is not allowed in StringMatcher") 96 } 97 matcher.suffixMatch = &mt.Suffix 98 if matcher.ignoreCase { 99 *matcher.suffixMatch = strings.ToLower(*matcher.suffixMatch) 100 } 101 case *v3matcherpb.StringMatcher_SafeRegex: 102 regex := matcherProto.GetSafeRegex().GetRegex() 103 re, err := regexp.Compile(regex) 104 if err != nil { 105 return StringMatcher{}, fmt.Errorf("safe_regex matcher %q is invalid", regex) 106 } 107 matcher.regexMatch = re 108 case *v3matcherpb.StringMatcher_Contains: 109 if matcherProto.GetContains() == "" { 110 return StringMatcher{}, errors.New("empty contains is not allowed in StringMatcher") 111 } 112 matcher.containsMatch = &mt.Contains 113 if matcher.ignoreCase { 114 *matcher.containsMatch = strings.ToLower(*matcher.containsMatch) 115 } 116 default: 117 return StringMatcher{}, fmt.Errorf("unrecognized string matcher: %+v", matcherProto) 118 } 119 return matcher, nil 120 } 121 122 // StringMatcherForTesting is a helper function to create a StringMatcher based 123 // on the given arguments. Intended only for testing purposes. 124 func StringMatcherForTesting(exact, prefix, suffix, contains *string, regex *regexp.Regexp, ignoreCase bool) StringMatcher { 125 sm := StringMatcher{ 126 exactMatch: exact, 127 prefixMatch: prefix, 128 suffixMatch: suffix, 129 regexMatch: regex, 130 containsMatch: contains, 131 ignoreCase: ignoreCase, 132 } 133 if ignoreCase { 134 switch { 135 case sm.exactMatch != nil: 136 *sm.exactMatch = strings.ToLower(*exact) 137 case sm.prefixMatch != nil: 138 *sm.prefixMatch = strings.ToLower(*prefix) 139 case sm.suffixMatch != nil: 140 *sm.suffixMatch = strings.ToLower(*suffix) 141 case sm.containsMatch != nil: 142 *sm.containsMatch = strings.ToLower(*contains) 143 } 144 } 145 return sm 146 } 147 148 // ExactMatch returns the value of the configured exact match or an empty string 149 // if exact match criteria was not specified. 150 func (sm StringMatcher) ExactMatch() string { 151 if sm.exactMatch != nil { 152 return *sm.exactMatch 153 } 154 return "" 155 } 156 157 // Equal returns true if other and sm are equivalent to each other. 158 func (sm StringMatcher) Equal(other StringMatcher) bool { 159 if sm.ignoreCase != other.ignoreCase { 160 return false 161 } 162 163 if (sm.exactMatch != nil) != (other.exactMatch != nil) || 164 (sm.prefixMatch != nil) != (other.prefixMatch != nil) || 165 (sm.suffixMatch != nil) != (other.suffixMatch != nil) || 166 (sm.regexMatch != nil) != (other.regexMatch != nil) || 167 (sm.containsMatch != nil) != (other.containsMatch != nil) { 168 return false 169 } 170 171 switch { 172 case sm.exactMatch != nil: 173 return *sm.exactMatch == *other.exactMatch 174 case sm.prefixMatch != nil: 175 return *sm.prefixMatch == *other.prefixMatch 176 case sm.suffixMatch != nil: 177 return *sm.suffixMatch == *other.suffixMatch 178 case sm.regexMatch != nil: 179 return sm.regexMatch.String() == other.regexMatch.String() 180 case sm.containsMatch != nil: 181 return *sm.containsMatch == *other.containsMatch 182 } 183 return true 184 }