...
1
18
19
20
21 package matcher
22
23 import (
24 "errors"
25 "fmt"
26 "regexp"
27 "strings"
28
29 v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
30 "google.golang.org/grpc/internal/grpcutil"
31 )
32
33
34
35
36 type StringMatcher struct {
37
38
39 exactMatch *string
40 prefixMatch *string
41 suffixMatch *string
42 regexMatch *regexp.Regexp
43 containsMatch *string
44
45
46 ignoreCase bool
47 }
48
49
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
70
71
72
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
123
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
149
150 func (sm StringMatcher) ExactMatch() string {
151 if sm.exactMatch != nil {
152 return *sm.exactMatch
153 }
154 return ""
155 }
156
157
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 }
185
View as plain text