dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/utils/matcher/matcher_header.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  /*
    19   *
    20   * Copyright 2020 gRPC authors.
    21   *
    22   */
    23  
    24  package matcher
    25  
    26  import (
    27  	"fmt"
    28  	"regexp"
    29  	"strconv"
    30  	"strings"
    31  )
    32  
    33  import (
    34  	"google.golang.org/grpc/metadata"
    35  )
    36  
    37  // HeaderMatcher is an interface for header matchers. These are
    38  // documented in (EnvoyProxy link here?). These matchers will match on different
    39  // aspects of HTTP header name/value pairs.
    40  type HeaderMatcher interface {
    41  	Match(metadata.MD) bool
    42  	String() string
    43  }
    44  
    45  // mdValuesFromOutgoingCtx retrieves metadata from context. If there are
    46  // multiple values, the values are concatenated with "," (comma and no space).
    47  //
    48  // All header matchers only match against the comma-concatenated string.
    49  func mdValuesFromOutgoingCtx(md metadata.MD, key string) (string, bool) {
    50  	vs, ok := md[key]
    51  	if !ok {
    52  		return "", false
    53  	}
    54  	return strings.Join(vs, ","), true
    55  }
    56  
    57  // HeaderExactMatcher matches on an exact match of the value of the header.
    58  type HeaderExactMatcher struct {
    59  	key    string
    60  	exact  string
    61  	invert bool
    62  }
    63  
    64  // NewHeaderExactMatcher returns a new HeaderExactMatcher.
    65  func NewHeaderExactMatcher(key, exact string, invert bool) *HeaderExactMatcher {
    66  	return &HeaderExactMatcher{key: key, exact: exact, invert: invert}
    67  }
    68  
    69  // Match returns whether the passed in HTTP Headers match according to the
    70  // HeaderExactMatcher.
    71  func (hem *HeaderExactMatcher) Match(md metadata.MD) bool {
    72  	v, ok := mdValuesFromOutgoingCtx(md, hem.key)
    73  	if !ok {
    74  		return false
    75  	}
    76  	return (v == hem.exact) != hem.invert
    77  }
    78  
    79  func (hem *HeaderExactMatcher) String() string {
    80  	return fmt.Sprintf("headerExact:%v:%v", hem.key, hem.exact)
    81  }
    82  
    83  // HeaderRegexMatcher matches on whether the entire request header value matches
    84  // the regex.
    85  type HeaderRegexMatcher struct {
    86  	key    string
    87  	re     *regexp.Regexp
    88  	invert bool
    89  }
    90  
    91  // NewHeaderRegexMatcher returns a new HeaderRegexMatcher.
    92  func NewHeaderRegexMatcher(key string, re *regexp.Regexp, invert bool) *HeaderRegexMatcher {
    93  	return &HeaderRegexMatcher{key: key, re: re, invert: invert}
    94  }
    95  
    96  // Match returns whether the passed in HTTP Headers match according to the
    97  // HeaderRegexMatcher.
    98  func (hrm *HeaderRegexMatcher) Match(md metadata.MD) bool {
    99  	v, ok := mdValuesFromOutgoingCtx(md, hrm.key)
   100  	if !ok {
   101  		return false
   102  	}
   103  	return FullMatchWithRegex(hrm.re, v) != hrm.invert
   104  }
   105  
   106  func (hrm *HeaderRegexMatcher) String() string {
   107  	return fmt.Sprintf("headerRegex:%v:%v", hrm.key, hrm.re.String())
   108  }
   109  
   110  // HeaderRangeMatcher matches on whether the request header value is within the
   111  // range. The header value must be an integer in base 10 notation.
   112  type HeaderRangeMatcher struct {
   113  	key        string
   114  	start, end int64 // represents [start, end).
   115  	invert     bool
   116  }
   117  
   118  // NewHeaderRangeMatcher returns a new HeaderRangeMatcher.
   119  func NewHeaderRangeMatcher(key string, start, end int64, invert bool) *HeaderRangeMatcher {
   120  	return &HeaderRangeMatcher{key: key, start: start, end: end, invert: invert}
   121  }
   122  
   123  // Match returns whether the passed in HTTP Headers match according to the
   124  // HeaderRangeMatcher.
   125  func (hrm *HeaderRangeMatcher) Match(md metadata.MD) bool {
   126  	v, ok := mdValuesFromOutgoingCtx(md, hrm.key)
   127  	if !ok {
   128  		return false
   129  	}
   130  	if i, err := strconv.ParseInt(v, 10, 64); err == nil && i >= hrm.start && i < hrm.end {
   131  		return !hrm.invert
   132  	}
   133  	return hrm.invert
   134  }
   135  
   136  func (hrm *HeaderRangeMatcher) String() string {
   137  	return fmt.Sprintf("headerRange:%v:[%d,%d)", hrm.key, hrm.start, hrm.end)
   138  }
   139  
   140  // HeaderPresentMatcher will match based on whether the header is present in the
   141  // whole request.
   142  type HeaderPresentMatcher struct {
   143  	key     string
   144  	present bool
   145  }
   146  
   147  // NewHeaderPresentMatcher returns a new HeaderPresentMatcher.
   148  func NewHeaderPresentMatcher(key string, present bool, invert bool) *HeaderPresentMatcher {
   149  	if invert {
   150  		present = !present
   151  	}
   152  	return &HeaderPresentMatcher{key: key, present: present}
   153  }
   154  
   155  // Match returns whether the passed in HTTP Headers match according to the
   156  // HeaderPresentMatcher.
   157  func (hpm *HeaderPresentMatcher) Match(md metadata.MD) bool {
   158  	vs, ok := mdValuesFromOutgoingCtx(md, hpm.key)
   159  	present := ok && len(vs) > 0 // TODO: Are we sure we need this len(vs) > 0?
   160  	return present == hpm.present
   161  }
   162  
   163  func (hpm *HeaderPresentMatcher) String() string {
   164  	return fmt.Sprintf("headerPresent:%v:%v", hpm.key, hpm.present)
   165  }
   166  
   167  // HeaderPrefixMatcher matches on whether the prefix of the header value matches
   168  // the prefix passed into this struct.
   169  type HeaderPrefixMatcher struct {
   170  	key    string
   171  	prefix string
   172  	invert bool
   173  }
   174  
   175  // NewHeaderPrefixMatcher returns a new HeaderPrefixMatcher.
   176  func NewHeaderPrefixMatcher(key string, prefix string, invert bool) *HeaderPrefixMatcher {
   177  	return &HeaderPrefixMatcher{key: key, prefix: prefix, invert: invert}
   178  }
   179  
   180  // Match returns whether the passed in HTTP Headers match according to the
   181  // HeaderPrefixMatcher.
   182  func (hpm *HeaderPrefixMatcher) Match(md metadata.MD) bool {
   183  	v, ok := mdValuesFromOutgoingCtx(md, hpm.key)
   184  	if !ok {
   185  		return false
   186  	}
   187  	return strings.HasPrefix(v, hpm.prefix) != hpm.invert
   188  }
   189  
   190  func (hpm *HeaderPrefixMatcher) String() string {
   191  	return fmt.Sprintf("headerPrefix:%v:%v", hpm.key, hpm.prefix)
   192  }
   193  
   194  // HeaderSuffixMatcher matches on whether the suffix of the header value matches
   195  // the suffix passed into this struct.
   196  type HeaderSuffixMatcher struct {
   197  	key    string
   198  	suffix string
   199  	invert bool
   200  }
   201  
   202  // NewHeaderSuffixMatcher returns a new HeaderSuffixMatcher.
   203  func NewHeaderSuffixMatcher(key string, suffix string, invert bool) *HeaderSuffixMatcher {
   204  	return &HeaderSuffixMatcher{key: key, suffix: suffix, invert: invert}
   205  }
   206  
   207  // Match returns whether the passed in HTTP Headers match according to the
   208  // HeaderSuffixMatcher.
   209  func (hsm *HeaderSuffixMatcher) Match(md metadata.MD) bool {
   210  	v, ok := mdValuesFromOutgoingCtx(md, hsm.key)
   211  	if !ok {
   212  		return false
   213  	}
   214  	return strings.HasSuffix(v, hsm.suffix) != hsm.invert
   215  }
   216  
   217  func (hsm *HeaderSuffixMatcher) String() string {
   218  	return fmt.Sprintf("headerSuffix:%v:%v", hsm.key, hsm.suffix)
   219  }
   220  
   221  // HeaderContainsMatcher matches on whether the header value contains the
   222  // value passed into this struct.
   223  type HeaderContainsMatcher struct {
   224  	key      string
   225  	contains string
   226  	invert   bool
   227  }
   228  
   229  // NewHeaderContainsMatcher returns a new HeaderContainsMatcher. key is the HTTP
   230  // Header key to match on, and contains is the value that the header should
   231  // should contain for a successful match. An empty contains string does not
   232  // work, use HeaderPresentMatcher in that case.
   233  func NewHeaderContainsMatcher(key string, contains string, invert bool) *HeaderContainsMatcher {
   234  	return &HeaderContainsMatcher{key: key, contains: contains, invert: invert}
   235  }
   236  
   237  // Match returns whether the passed in HTTP Headers match according to the
   238  // HeaderContainsMatcher.
   239  func (hcm *HeaderContainsMatcher) Match(md metadata.MD) bool {
   240  	v, ok := mdValuesFromOutgoingCtx(md, hcm.key)
   241  	if !ok {
   242  		return false
   243  	}
   244  	return strings.Contains(v, hcm.contains) != hcm.invert
   245  }
   246  
   247  func (hcm *HeaderContainsMatcher) String() string {
   248  	return fmt.Sprintf("headerContains:%v%v", hcm.key, hcm.contains)
   249  }