/* * * 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) }