| /* |
| * |
| * Copyright 2020 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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 xdsrouting |
| |
| import ( |
| "fmt" |
| "regexp" |
| "strconv" |
| "strings" |
| |
| "google.golang.org/grpc/metadata" |
| ) |
| |
| type headerMatcherInterface interface { |
| match(metadata.MD) bool |
| equal(headerMatcherInterface) bool |
| String() string |
| } |
| |
| // mdValuesFromOutgoingCtx retrieves metadata from context. If there are |
| // multiple values, the values are concatenated with "," (comma and no space). |
| // |
| // All header matchers only match against the comma-concatenated string. |
| func mdValuesFromOutgoingCtx(md metadata.MD, key string) (string, bool) { |
| vs, ok := md[key] |
| if !ok { |
| return "", false |
| } |
| return strings.Join(vs, ","), true |
| } |
| |
| type headerExactMatcher struct { |
| key string |
| exact string |
| } |
| |
| func newHeaderExactMatcher(key, exact string) *headerExactMatcher { |
| return &headerExactMatcher{key: key, exact: exact} |
| } |
| |
| func (hem *headerExactMatcher) match(md metadata.MD) bool { |
| v, ok := mdValuesFromOutgoingCtx(md, hem.key) |
| if !ok { |
| return false |
| } |
| return v == hem.exact |
| } |
| |
| func (hem *headerExactMatcher) equal(m headerMatcherInterface) bool { |
| mm, ok := m.(*headerExactMatcher) |
| if !ok { |
| return false |
| } |
| return hem.key == mm.key && hem.exact == mm.exact |
| } |
| |
| func (hem *headerExactMatcher) String() string { |
| return fmt.Sprintf("headerExact:%v:%v", hem.key, hem.exact) |
| } |
| |
| type headerRegexMatcher struct { |
| key string |
| re *regexp.Regexp |
| } |
| |
| func newHeaderRegexMatcher(key string, re *regexp.Regexp) *headerRegexMatcher { |
| return &headerRegexMatcher{key: key, re: re} |
| } |
| |
| func (hrm *headerRegexMatcher) match(md metadata.MD) bool { |
| v, ok := mdValuesFromOutgoingCtx(md, hrm.key) |
| if !ok { |
| return false |
| } |
| return hrm.re.MatchString(v) |
| } |
| |
| func (hrm *headerRegexMatcher) equal(m headerMatcherInterface) bool { |
| mm, ok := m.(*headerRegexMatcher) |
| if !ok { |
| return false |
| } |
| return hrm.key == mm.key && hrm.re.String() == mm.re.String() |
| } |
| |
| func (hrm *headerRegexMatcher) String() string { |
| return fmt.Sprintf("headerRegex:%v:%v", hrm.key, hrm.re.String()) |
| } |
| |
| type headerRangeMatcher struct { |
| key string |
| start, end int64 // represents [start, end). |
| } |
| |
| func newHeaderRangeMatcher(key string, start, end int64) *headerRangeMatcher { |
| return &headerRangeMatcher{key: key, start: start, end: end} |
| } |
| |
| func (hrm *headerRangeMatcher) match(md metadata.MD) bool { |
| v, ok := mdValuesFromOutgoingCtx(md, hrm.key) |
| if !ok { |
| return false |
| } |
| if i, err := strconv.ParseInt(v, 10, 64); err == nil && i >= hrm.start && i < hrm.end { |
| return true |
| } |
| return false |
| } |
| |
| func (hrm *headerRangeMatcher) equal(m headerMatcherInterface) bool { |
| mm, ok := m.(*headerRangeMatcher) |
| if !ok { |
| return false |
| } |
| return hrm.key == mm.key && hrm.start == mm.start && hrm.end == mm.end |
| } |
| |
| func (hrm *headerRangeMatcher) String() string { |
| return fmt.Sprintf("headerRange:%v:[%d,%d)", hrm.key, hrm.start, hrm.end) |
| } |
| |
| type headerPresentMatcher struct { |
| key string |
| present bool |
| } |
| |
| func newHeaderPresentMatcher(key string, present bool) *headerPresentMatcher { |
| return &headerPresentMatcher{key: key, present: present} |
| } |
| |
| func (hpm *headerPresentMatcher) match(md metadata.MD) bool { |
| vs, ok := mdValuesFromOutgoingCtx(md, hpm.key) |
| present := ok && len(vs) > 0 |
| return present == hpm.present |
| } |
| |
| func (hpm *headerPresentMatcher) equal(m headerMatcherInterface) bool { |
| mm, ok := m.(*headerPresentMatcher) |
| if !ok { |
| return false |
| } |
| return hpm.key == mm.key && hpm.present == mm.present |
| } |
| |
| func (hpm *headerPresentMatcher) String() string { |
| return fmt.Sprintf("headerPresent:%v:%v", hpm.key, hpm.present) |
| } |
| |
| type headerPrefixMatcher struct { |
| key string |
| prefix string |
| } |
| |
| func newHeaderPrefixMatcher(key string, prefix string) *headerPrefixMatcher { |
| return &headerPrefixMatcher{key: key, prefix: prefix} |
| } |
| |
| func (hpm *headerPrefixMatcher) match(md metadata.MD) bool { |
| v, ok := mdValuesFromOutgoingCtx(md, hpm.key) |
| if !ok { |
| return false |
| } |
| return strings.HasPrefix(v, hpm.prefix) |
| } |
| |
| func (hpm *headerPrefixMatcher) equal(m headerMatcherInterface) bool { |
| mm, ok := m.(*headerPrefixMatcher) |
| if !ok { |
| return false |
| } |
| return hpm.key == mm.key && hpm.prefix == mm.prefix |
| } |
| |
| func (hpm *headerPrefixMatcher) String() string { |
| return fmt.Sprintf("headerPrefix:%v:%v", hpm.key, hpm.prefix) |
| } |
| |
| type headerSuffixMatcher struct { |
| key string |
| suffix string |
| } |
| |
| func newHeaderSuffixMatcher(key string, suffix string) *headerSuffixMatcher { |
| return &headerSuffixMatcher{key: key, suffix: suffix} |
| } |
| |
| func (hsm *headerSuffixMatcher) match(md metadata.MD) bool { |
| v, ok := mdValuesFromOutgoingCtx(md, hsm.key) |
| if !ok { |
| return false |
| } |
| return strings.HasSuffix(v, hsm.suffix) |
| } |
| |
| func (hsm *headerSuffixMatcher) equal(m headerMatcherInterface) bool { |
| mm, ok := m.(*headerSuffixMatcher) |
| if !ok { |
| return false |
| } |
| return hsm.key == mm.key && hsm.suffix == mm.suffix |
| } |
| |
| func (hsm *headerSuffixMatcher) String() string { |
| return fmt.Sprintf("headerSuffix:%v:%v", hsm.key, hsm.suffix) |
| } |
| |
| type invertMatcher struct { |
| m headerMatcherInterface |
| } |
| |
| func newInvertMatcher(m headerMatcherInterface) *invertMatcher { |
| return &invertMatcher{m: m} |
| } |
| |
| func (i *invertMatcher) match(md metadata.MD) bool { |
| return !i.m.match(md) |
| } |
| |
| func (i *invertMatcher) equal(m headerMatcherInterface) bool { |
| mm, ok := m.(*invertMatcher) |
| if !ok { |
| return false |
| } |
| return i.m.equal(mm.m) |
| } |
| |
| func (i *invertMatcher) String() string { |
| return fmt.Sprintf("invert{%s}", i.m) |
| } |