github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/xdsclient/xdsresource/matcher.go (about)

     1  /*
     2   *
     3   * Copyright 2020 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * 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  package xdsresource
    19  
    20  import (
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/hxx258456/ccgo/grpc/internal/grpcrand"
    25  	"github.com/hxx258456/ccgo/grpc/internal/grpcutil"
    26  	iresolver "github.com/hxx258456/ccgo/grpc/internal/resolver"
    27  	"github.com/hxx258456/ccgo/grpc/internal/xds/matcher"
    28  	"github.com/hxx258456/ccgo/grpc/metadata"
    29  )
    30  
    31  // RouteToMatcher converts a route to a Matcher to match incoming RPC's against.
    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  		default:
    63  			return nil, fmt.Errorf("illegal route: missing header_match_specifier")
    64  		}
    65  		headerMatchers = append(headerMatchers, matcherT)
    66  	}
    67  
    68  	var fractionMatcher *fractionMatcher
    69  	if r.Fraction != nil {
    70  		fractionMatcher = newFractionMatcher(*r.Fraction)
    71  	}
    72  	return newCompositeMatcher(pm, headerMatchers, fractionMatcher), nil
    73  }
    74  
    75  // CompositeMatcher is a matcher that holds onto many matchers and aggregates
    76  // the matching results.
    77  type CompositeMatcher struct {
    78  	pm  pathMatcher
    79  	hms []matcher.HeaderMatcher
    80  	fm  *fractionMatcher
    81  }
    82  
    83  func newCompositeMatcher(pm pathMatcher, hms []matcher.HeaderMatcher, fm *fractionMatcher) *CompositeMatcher {
    84  	return &CompositeMatcher{pm: pm, hms: hms, fm: fm}
    85  }
    86  
    87  // Match returns true if all matchers return true.
    88  func (a *CompositeMatcher) Match(info iresolver.RPCInfo) bool {
    89  	if a.pm != nil && !a.pm.match(info.Method) {
    90  		return false
    91  	}
    92  
    93  	// Call headerMatchers even if md is nil, because routes may match
    94  	// non-presence of some headers.
    95  	var md metadata.MD
    96  	if info.Context != nil {
    97  		md, _ = metadata.FromOutgoingContext(info.Context)
    98  		if extraMD, ok := grpcutil.ExtraMetadata(info.Context); ok {
    99  			md = metadata.Join(md, extraMD)
   100  			// Remove all binary headers. They are hard to match with. May need
   101  			// to add back if asked by users.
   102  			for k := range md {
   103  				if strings.HasSuffix(k, "-bin") {
   104  					delete(md, k)
   105  				}
   106  			}
   107  		}
   108  	}
   109  	for _, m := range a.hms {
   110  		if !m.Match(md) {
   111  			return false
   112  		}
   113  	}
   114  
   115  	if a.fm != nil && !a.fm.match() {
   116  		return false
   117  	}
   118  	return true
   119  }
   120  
   121  func (a *CompositeMatcher) String() string {
   122  	var ret string
   123  	if a.pm != nil {
   124  		ret += a.pm.String()
   125  	}
   126  	for _, m := range a.hms {
   127  		ret += m.String()
   128  	}
   129  	if a.fm != nil {
   130  		ret += a.fm.String()
   131  	}
   132  	return ret
   133  }
   134  
   135  type fractionMatcher struct {
   136  	fraction int64 // real fraction is fraction/1,000,000.
   137  }
   138  
   139  func newFractionMatcher(fraction uint32) *fractionMatcher {
   140  	return &fractionMatcher{fraction: int64(fraction)}
   141  }
   142  
   143  // RandInt63n overwrites grpcrand for control in tests.
   144  var RandInt63n = grpcrand.Int63n
   145  
   146  func (fm *fractionMatcher) match() bool {
   147  	t := RandInt63n(1000000)
   148  	return t <= fm.fraction
   149  }
   150  
   151  func (fm *fractionMatcher) String() string {
   152  	return fmt.Sprintf("fraction:%v", fm.fraction)
   153  }
   154  
   155  type domainMatchType int
   156  
   157  const (
   158  	domainMatchTypeInvalid domainMatchType = iota
   159  	domainMatchTypeUniversal
   160  	domainMatchTypePrefix
   161  	domainMatchTypeSuffix
   162  	domainMatchTypeExact
   163  )
   164  
   165  // Exact > Suffix > Prefix > Universal > Invalid.
   166  func (t domainMatchType) betterThan(b domainMatchType) bool {
   167  	return t > b
   168  }
   169  
   170  func matchTypeForDomain(d string) domainMatchType {
   171  	if d == "" {
   172  		return domainMatchTypeInvalid
   173  	}
   174  	if d == "*" {
   175  		return domainMatchTypeUniversal
   176  	}
   177  	if strings.HasPrefix(d, "*") {
   178  		return domainMatchTypeSuffix
   179  	}
   180  	if strings.HasSuffix(d, "*") {
   181  		return domainMatchTypePrefix
   182  	}
   183  	if strings.Contains(d, "*") {
   184  		return domainMatchTypeInvalid
   185  	}
   186  	return domainMatchTypeExact
   187  }
   188  
   189  func match(domain, host string) (domainMatchType, bool) {
   190  	switch typ := matchTypeForDomain(domain); typ {
   191  	case domainMatchTypeInvalid:
   192  		return typ, false
   193  	case domainMatchTypeUniversal:
   194  		return typ, true
   195  	case domainMatchTypePrefix:
   196  		// abc.*
   197  		return typ, strings.HasPrefix(host, strings.TrimSuffix(domain, "*"))
   198  	case domainMatchTypeSuffix:
   199  		// *.123
   200  		return typ, strings.HasSuffix(host, strings.TrimPrefix(domain, "*"))
   201  	case domainMatchTypeExact:
   202  		return typ, domain == host
   203  	default:
   204  		return domainMatchTypeInvalid, false
   205  	}
   206  }
   207  
   208  // FindBestMatchingVirtualHost returns the virtual host whose domains field best
   209  // matches host
   210  //
   211  // The domains field support 4 different matching pattern types:
   212  //  - Exact match
   213  //  - Suffix match (e.g. “*ABC”)
   214  //  - Prefix match (e.g. “ABC*)
   215  //  - Universal match (e.g. “*”)
   216  //
   217  // The best match is defined as:
   218  //  - A match is better if it’s matching pattern type is better
   219  //    - Exact match > suffix match > prefix match > universal match
   220  //  - If two matches are of the same pattern type, the longer match is better
   221  //    - This is to compare the length of the matching pattern, e.g. “*ABCDE” >
   222  //    “*ABC”
   223  func FindBestMatchingVirtualHost(host string, vHosts []*VirtualHost) *VirtualHost { // Maybe move this crap to client
   224  	var (
   225  		matchVh   *VirtualHost
   226  		matchType = domainMatchTypeInvalid
   227  		matchLen  int
   228  	)
   229  	for _, vh := range vHosts {
   230  		for _, domain := range vh.Domains {
   231  			typ, matched := match(domain, host)
   232  			if typ == domainMatchTypeInvalid {
   233  				// The rds response is invalid.
   234  				return nil
   235  			}
   236  			if matchType.betterThan(typ) || matchType == typ && matchLen >= len(domain) || !matched {
   237  				// The previous match has better type, or the previous match has
   238  				// better length, or this domain isn't a match.
   239  				continue
   240  			}
   241  			matchVh = vh
   242  			matchType = typ
   243  			matchLen = len(domain)
   244  		}
   245  	}
   246  	return matchVh
   247  }
   248  
   249  // FindBestMatchingVirtualHostServer returns the virtual host whose domains field best
   250  // matches authority.
   251  func FindBestMatchingVirtualHostServer(authority string, vHosts []VirtualHostWithInterceptors) *VirtualHostWithInterceptors {
   252  	var (
   253  		matchVh   *VirtualHostWithInterceptors
   254  		matchType = domainMatchTypeInvalid
   255  		matchLen  int
   256  	)
   257  	for _, vh := range vHosts {
   258  		for _, domain := range vh.Domains {
   259  			typ, matched := match(domain, authority)
   260  			if typ == domainMatchTypeInvalid {
   261  				// The rds response is invalid.
   262  				return nil
   263  			}
   264  			if matchType.betterThan(typ) || matchType == typ && matchLen >= len(domain) || !matched {
   265  				// The previous match has better type, or the previous match has
   266  				// better length, or this domain isn't a match.
   267  				continue
   268  			}
   269  			matchVh = &vh
   270  			matchType = typ
   271  			matchLen = len(domain)
   272  		}
   273  	}
   274  	return matchVh
   275  }