mirror of https://github.com/grpc/grpc-go.git
244 lines
7.4 KiB
Go
244 lines
7.4 KiB
Go
/*
|
|
*
|
|
* 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 matcher
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"google.golang.org/grpc/internal/grpcutil"
|
|
"google.golang.org/grpc/metadata"
|
|
)
|
|
|
|
// HeaderMatcher is an interface for header matchers. These are
|
|
// documented in (EnvoyProxy link here?). These matchers will match on different
|
|
// aspects of HTTP header name/value pairs.
|
|
type HeaderMatcher interface {
|
|
Match(metadata.MD) 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
|
|
}
|
|
|
|
// HeaderExactMatcher matches on an exact match of the value of the header.
|
|
type HeaderExactMatcher struct {
|
|
key string
|
|
exact string
|
|
invert bool
|
|
}
|
|
|
|
// NewHeaderExactMatcher returns a new HeaderExactMatcher.
|
|
func NewHeaderExactMatcher(key, exact string, invert bool) *HeaderExactMatcher {
|
|
return &HeaderExactMatcher{key: key, exact: exact, invert: invert}
|
|
}
|
|
|
|
// Match returns whether the passed in HTTP Headers match according to the
|
|
// HeaderExactMatcher.
|
|
func (hem *HeaderExactMatcher) Match(md metadata.MD) bool {
|
|
v, ok := mdValuesFromOutgoingCtx(md, hem.key)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return (v == hem.exact) != hem.invert
|
|
}
|
|
|
|
func (hem *HeaderExactMatcher) String() string {
|
|
return fmt.Sprintf("headerExact:%v:%v", hem.key, hem.exact)
|
|
}
|
|
|
|
// HeaderRegexMatcher matches on whether the entire request header value matches
|
|
// the regex.
|
|
type HeaderRegexMatcher struct {
|
|
key string
|
|
re *regexp.Regexp
|
|
invert bool
|
|
}
|
|
|
|
// NewHeaderRegexMatcher returns a new HeaderRegexMatcher.
|
|
func NewHeaderRegexMatcher(key string, re *regexp.Regexp, invert bool) *HeaderRegexMatcher {
|
|
return &HeaderRegexMatcher{key: key, re: re, invert: invert}
|
|
}
|
|
|
|
// Match returns whether the passed in HTTP Headers match according to the
|
|
// HeaderRegexMatcher.
|
|
func (hrm *HeaderRegexMatcher) Match(md metadata.MD) bool {
|
|
v, ok := mdValuesFromOutgoingCtx(md, hrm.key)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return grpcutil.FullMatchWithRegex(hrm.re, v) != hrm.invert
|
|
}
|
|
|
|
func (hrm *HeaderRegexMatcher) String() string {
|
|
return fmt.Sprintf("headerRegex:%v:%v", hrm.key, hrm.re.String())
|
|
}
|
|
|
|
// HeaderRangeMatcher matches on whether the request header value is within the
|
|
// range. The header value must be an integer in base 10 notation.
|
|
type HeaderRangeMatcher struct {
|
|
key string
|
|
start, end int64 // represents [start, end).
|
|
invert bool
|
|
}
|
|
|
|
// NewHeaderRangeMatcher returns a new HeaderRangeMatcher.
|
|
func NewHeaderRangeMatcher(key string, start, end int64, invert bool) *HeaderRangeMatcher {
|
|
return &HeaderRangeMatcher{key: key, start: start, end: end, invert: invert}
|
|
}
|
|
|
|
// Match returns whether the passed in HTTP Headers match according to the
|
|
// HeaderRangeMatcher.
|
|
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 !hrm.invert
|
|
}
|
|
return hrm.invert
|
|
}
|
|
|
|
func (hrm *HeaderRangeMatcher) String() string {
|
|
return fmt.Sprintf("headerRange:%v:[%d,%d)", hrm.key, hrm.start, hrm.end)
|
|
}
|
|
|
|
// HeaderPresentMatcher will match based on whether the header is present in the
|
|
// whole request.
|
|
type HeaderPresentMatcher struct {
|
|
key string
|
|
present bool
|
|
}
|
|
|
|
// NewHeaderPresentMatcher returns a new HeaderPresentMatcher.
|
|
func NewHeaderPresentMatcher(key string, present bool, invert bool) *HeaderPresentMatcher {
|
|
if invert {
|
|
present = !present
|
|
}
|
|
return &HeaderPresentMatcher{key: key, present: present}
|
|
}
|
|
|
|
// Match returns whether the passed in HTTP Headers match according to the
|
|
// HeaderPresentMatcher.
|
|
func (hpm *HeaderPresentMatcher) Match(md metadata.MD) bool {
|
|
vs, ok := mdValuesFromOutgoingCtx(md, hpm.key)
|
|
present := ok && len(vs) > 0 // TODO: Are we sure we need this len(vs) > 0?
|
|
return present == hpm.present
|
|
}
|
|
|
|
func (hpm *HeaderPresentMatcher) String() string {
|
|
return fmt.Sprintf("headerPresent:%v:%v", hpm.key, hpm.present)
|
|
}
|
|
|
|
// HeaderPrefixMatcher matches on whether the prefix of the header value matches
|
|
// the prefix passed into this struct.
|
|
type HeaderPrefixMatcher struct {
|
|
key string
|
|
prefix string
|
|
invert bool
|
|
}
|
|
|
|
// NewHeaderPrefixMatcher returns a new HeaderPrefixMatcher.
|
|
func NewHeaderPrefixMatcher(key string, prefix string, invert bool) *HeaderPrefixMatcher {
|
|
return &HeaderPrefixMatcher{key: key, prefix: prefix, invert: invert}
|
|
}
|
|
|
|
// Match returns whether the passed in HTTP Headers match according to the
|
|
// HeaderPrefixMatcher.
|
|
func (hpm *HeaderPrefixMatcher) Match(md metadata.MD) bool {
|
|
v, ok := mdValuesFromOutgoingCtx(md, hpm.key)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return strings.HasPrefix(v, hpm.prefix) != hpm.invert
|
|
}
|
|
|
|
func (hpm *HeaderPrefixMatcher) String() string {
|
|
return fmt.Sprintf("headerPrefix:%v:%v", hpm.key, hpm.prefix)
|
|
}
|
|
|
|
// HeaderSuffixMatcher matches on whether the suffix of the header value matches
|
|
// the suffix passed into this struct.
|
|
type HeaderSuffixMatcher struct {
|
|
key string
|
|
suffix string
|
|
invert bool
|
|
}
|
|
|
|
// NewHeaderSuffixMatcher returns a new HeaderSuffixMatcher.
|
|
func NewHeaderSuffixMatcher(key string, suffix string, invert bool) *HeaderSuffixMatcher {
|
|
return &HeaderSuffixMatcher{key: key, suffix: suffix, invert: invert}
|
|
}
|
|
|
|
// Match returns whether the passed in HTTP Headers match according to the
|
|
// HeaderSuffixMatcher.
|
|
func (hsm *HeaderSuffixMatcher) Match(md metadata.MD) bool {
|
|
v, ok := mdValuesFromOutgoingCtx(md, hsm.key)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return strings.HasSuffix(v, hsm.suffix) != hsm.invert
|
|
}
|
|
|
|
func (hsm *HeaderSuffixMatcher) String() string {
|
|
return fmt.Sprintf("headerSuffix:%v:%v", hsm.key, hsm.suffix)
|
|
}
|
|
|
|
// HeaderContainsMatcher matches on whether the header value contains the
|
|
// value passed into this struct.
|
|
type HeaderContainsMatcher struct {
|
|
key string
|
|
contains string
|
|
invert bool
|
|
}
|
|
|
|
// NewHeaderContainsMatcher returns a new HeaderContainsMatcher. key is the HTTP
|
|
// Header key to match on, and contains is the value that the header should
|
|
// should contain for a successful match. An empty contains string does not
|
|
// work, use HeaderPresentMatcher in that case.
|
|
func NewHeaderContainsMatcher(key string, contains string, invert bool) *HeaderContainsMatcher {
|
|
return &HeaderContainsMatcher{key: key, contains: contains, invert: invert}
|
|
}
|
|
|
|
// Match returns whether the passed in HTTP Headers match according to the
|
|
// HeaderContainsMatcher.
|
|
func (hcm *HeaderContainsMatcher) Match(md metadata.MD) bool {
|
|
v, ok := mdValuesFromOutgoingCtx(md, hcm.key)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return strings.Contains(v, hcm.contains) != hcm.invert
|
|
}
|
|
|
|
func (hcm *HeaderContainsMatcher) String() string {
|
|
return fmt.Sprintf("headerContains:%v%v", hcm.key, hcm.contains)
|
|
}
|