1
17
18 package xdsresource
19
20 import (
21 "fmt"
22 "strings"
23
24 "google.golang.org/grpc/internal/grpcrand"
25 "google.golang.org/grpc/internal/grpcutil"
26 iresolver "google.golang.org/grpc/internal/resolver"
27 "google.golang.org/grpc/internal/xds/matcher"
28 "google.golang.org/grpc/metadata"
29 )
30
31
32 func RouteToMatcher(r *Route) (*CompositeMatcher, error) {
33 var pm pathMatcher
34 switch {
35 case r.Regex != nil:
36 pm = newPathRegexMatcher(r.Regex)
37 case r.Path != nil:
38 pm = newPathExactMatcher(*r.Path, r.CaseInsensitive)
39 case r.Prefix != nil:
40 pm = newPathPrefixMatcher(*r.Prefix, r.CaseInsensitive)
41 default:
42 return nil, fmt.Errorf("illegal route: missing path_matcher")
43 }
44
45 headerMatchers := make([]matcher.HeaderMatcher, 0, len(r.Headers))
46 for _, h := range r.Headers {
47 var matcherT matcher.HeaderMatcher
48 invert := h.InvertMatch != nil && *h.InvertMatch
49 switch {
50 case h.ExactMatch != nil && *h.ExactMatch != "":
51 matcherT = matcher.NewHeaderExactMatcher(h.Name, *h.ExactMatch, invert)
52 case h.RegexMatch != nil:
53 matcherT = matcher.NewHeaderRegexMatcher(h.Name, h.RegexMatch, invert)
54 case h.PrefixMatch != nil && *h.PrefixMatch != "":
55 matcherT = matcher.NewHeaderPrefixMatcher(h.Name, *h.PrefixMatch, invert)
56 case h.SuffixMatch != nil && *h.SuffixMatch != "":
57 matcherT = matcher.NewHeaderSuffixMatcher(h.Name, *h.SuffixMatch, invert)
58 case h.RangeMatch != nil:
59 matcherT = matcher.NewHeaderRangeMatcher(h.Name, h.RangeMatch.Start, h.RangeMatch.End, invert)
60 case h.PresentMatch != nil:
61 matcherT = matcher.NewHeaderPresentMatcher(h.Name, *h.PresentMatch, invert)
62 case h.StringMatch != nil:
63 matcherT = matcher.NewHeaderStringMatcher(h.Name, *h.StringMatch, invert)
64 default:
65 return nil, fmt.Errorf("illegal route: missing header_match_specifier")
66 }
67 headerMatchers = append(headerMatchers, matcherT)
68 }
69
70 var fractionMatcher *fractionMatcher
71 if r.Fraction != nil {
72 fractionMatcher = newFractionMatcher(*r.Fraction)
73 }
74 return newCompositeMatcher(pm, headerMatchers, fractionMatcher), nil
75 }
76
77
78
79 type CompositeMatcher struct {
80 pm pathMatcher
81 hms []matcher.HeaderMatcher
82 fm *fractionMatcher
83 }
84
85 func newCompositeMatcher(pm pathMatcher, hms []matcher.HeaderMatcher, fm *fractionMatcher) *CompositeMatcher {
86 return &CompositeMatcher{pm: pm, hms: hms, fm: fm}
87 }
88
89
90 func (a *CompositeMatcher) Match(info iresolver.RPCInfo) bool {
91 if a.pm != nil && !a.pm.match(info.Method) {
92 return false
93 }
94
95
96
97 var md metadata.MD
98 if info.Context != nil {
99 md, _ = metadata.FromOutgoingContext(info.Context)
100 if extraMD, ok := grpcutil.ExtraMetadata(info.Context); ok {
101 md = metadata.Join(md, extraMD)
102
103
104 for k := range md {
105 if strings.HasSuffix(k, "-bin") {
106 delete(md, k)
107 }
108 }
109 }
110 }
111 for _, m := range a.hms {
112 if !m.Match(md) {
113 return false
114 }
115 }
116
117 if a.fm != nil && !a.fm.match() {
118 return false
119 }
120 return true
121 }
122
123 func (a *CompositeMatcher) String() string {
124 var ret string
125 if a.pm != nil {
126 ret += a.pm.String()
127 }
128 for _, m := range a.hms {
129 ret += m.String()
130 }
131 if a.fm != nil {
132 ret += a.fm.String()
133 }
134 return ret
135 }
136
137 type fractionMatcher struct {
138 fraction int64
139 }
140
141 func newFractionMatcher(fraction uint32) *fractionMatcher {
142 return &fractionMatcher{fraction: int64(fraction)}
143 }
144
145
146 var RandInt63n = grpcrand.Int63n
147
148 func (fm *fractionMatcher) match() bool {
149 t := RandInt63n(1000000)
150 return t <= fm.fraction
151 }
152
153 func (fm *fractionMatcher) String() string {
154 return fmt.Sprintf("fraction:%v", fm.fraction)
155 }
156
157 type domainMatchType int
158
159 const (
160 domainMatchTypeInvalid domainMatchType = iota
161 domainMatchTypeUniversal
162 domainMatchTypePrefix
163 domainMatchTypeSuffix
164 domainMatchTypeExact
165 )
166
167
168 func (t domainMatchType) betterThan(b domainMatchType) bool {
169 return t > b
170 }
171
172 func matchTypeForDomain(d string) domainMatchType {
173 if d == "" {
174 return domainMatchTypeInvalid
175 }
176 if d == "*" {
177 return domainMatchTypeUniversal
178 }
179 if strings.HasPrefix(d, "*") {
180 return domainMatchTypeSuffix
181 }
182 if strings.HasSuffix(d, "*") {
183 return domainMatchTypePrefix
184 }
185 if strings.Contains(d, "*") {
186 return domainMatchTypeInvalid
187 }
188 return domainMatchTypeExact
189 }
190
191 func match(domain, host string) (domainMatchType, bool) {
192 switch typ := matchTypeForDomain(domain); typ {
193 case domainMatchTypeInvalid:
194 return typ, false
195 case domainMatchTypeUniversal:
196 return typ, true
197 case domainMatchTypePrefix:
198
199 return typ, strings.HasPrefix(host, strings.TrimSuffix(domain, "*"))
200 case domainMatchTypeSuffix:
201
202 return typ, strings.HasSuffix(host, strings.TrimPrefix(domain, "*"))
203 case domainMatchTypeExact:
204 return typ, domain == host
205 default:
206 return domainMatchTypeInvalid, false
207 }
208 }
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228 func FindBestMatchingVirtualHost(host string, vHosts []*VirtualHost) *VirtualHost {
229 var (
230 matchVh *VirtualHost
231 matchType = domainMatchTypeInvalid
232 matchLen int
233 )
234 for _, vh := range vHosts {
235 for _, domain := range vh.Domains {
236 typ, matched := match(domain, host)
237 if typ == domainMatchTypeInvalid {
238
239 return nil
240 }
241 if matchType.betterThan(typ) || matchType == typ && matchLen >= len(domain) || !matched {
242
243
244 continue
245 }
246 matchVh = vh
247 matchType = typ
248 matchLen = len(domain)
249 }
250 }
251 return matchVh
252 }
253
254
255
256 func FindBestMatchingVirtualHostServer(authority string, vHosts []VirtualHostWithInterceptors) *VirtualHostWithInterceptors {
257 var (
258 matchVh *VirtualHostWithInterceptors
259 matchType = domainMatchTypeInvalid
260 matchLen int
261 )
262 for _, vh := range vHosts {
263 for _, domain := range vh.Domains {
264 typ, matched := match(domain, authority)
265 if typ == domainMatchTypeInvalid {
266
267 return nil
268 }
269 if matchType.betterThan(typ) || matchType == typ && matchLen >= len(domain) || !matched {
270
271
272 continue
273 }
274 matchVh = &vh
275 matchType = typ
276 matchLen = len(domain)
277 }
278 }
279 return matchVh
280 }
281
View as plain text