From d47437c91e798b5370e019660cd74c9c0c8e32fb Mon Sep 17 00:00:00 2001 From: Zach Reyes <39203661+zasweq@users.noreply.github.com> Date: Thu, 28 Oct 2021 12:16:33 -0400 Subject: [PATCH] xds: Fix invert functionality for header matcher (#4902) * Fix invert functionality for header matcher --- internal/xds/matcher/matcher_header.go | 79 ++++----- internal/xds/matcher/matcher_header_test.go | 186 ++++++++++++++++---- internal/xds/rbac/matchers.go | 17 +- xds/internal/xdsclient/matcher.go | 16 +- xds/internal/xdsclient/matcher_test.go | 12 +- 5 files changed, 208 insertions(+), 102 deletions(-) diff --git a/internal/xds/matcher/matcher_header.go b/internal/xds/matcher/matcher_header.go index c3944373cd7f..fd4833d3fff8 100644 --- a/internal/xds/matcher/matcher_header.go +++ b/internal/xds/matcher/matcher_header.go @@ -50,13 +50,14 @@ func mdValuesFromOutgoingCtx(md metadata.MD, key string) (string, bool) { // HeaderExactMatcher matches on an exact match of the value of the header. type HeaderExactMatcher struct { - key string - exact string + key string + exact string + invert bool } // NewHeaderExactMatcher returns a new HeaderExactMatcher. -func NewHeaderExactMatcher(key, exact string) *HeaderExactMatcher { - return &HeaderExactMatcher{key: key, exact: exact} +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 @@ -66,7 +67,7 @@ func (hem *HeaderExactMatcher) Match(md metadata.MD) bool { if !ok { return false } - return v == hem.exact + return (v == hem.exact) != hem.invert } func (hem *HeaderExactMatcher) String() string { @@ -76,13 +77,14 @@ func (hem *HeaderExactMatcher) String() string { // HeaderRegexMatcher matches on whether the entire request header value matches // the regex. type HeaderRegexMatcher struct { - key string - re *regexp.Regexp + key string + re *regexp.Regexp + invert bool } // NewHeaderRegexMatcher returns a new HeaderRegexMatcher. -func NewHeaderRegexMatcher(key string, re *regexp.Regexp) *HeaderRegexMatcher { - return &HeaderRegexMatcher{key: key, re: re} +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 @@ -92,7 +94,7 @@ func (hrm *HeaderRegexMatcher) Match(md metadata.MD) bool { if !ok { return false } - return grpcutil.FullMatchWithRegex(hrm.re, v) + return grpcutil.FullMatchWithRegex(hrm.re, v) != hrm.invert } func (hrm *HeaderRegexMatcher) String() string { @@ -104,11 +106,12 @@ func (hrm *HeaderRegexMatcher) String() string { 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) *HeaderRangeMatcher { - return &HeaderRangeMatcher{key: key, start: start, end: end} +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 @@ -119,9 +122,9 @@ func (hrm *HeaderRangeMatcher) Match(md metadata.MD) bool { return false } if i, err := strconv.ParseInt(v, 10, 64); err == nil && i >= hrm.start && i < hrm.end { - return true + return !hrm.invert } - return false + return hrm.invert } func (hrm *HeaderRangeMatcher) String() string { @@ -136,7 +139,10 @@ type HeaderPresentMatcher struct { } // NewHeaderPresentMatcher returns a new HeaderPresentMatcher. -func NewHeaderPresentMatcher(key string, present bool) *HeaderPresentMatcher { +func NewHeaderPresentMatcher(key string, present bool, invert bool) *HeaderPresentMatcher { + if invert { + present = !present + } return &HeaderPresentMatcher{key: key, present: present} } @@ -144,7 +150,7 @@ func NewHeaderPresentMatcher(key string, present bool) *HeaderPresentMatcher { // HeaderPresentMatcher. func (hpm *HeaderPresentMatcher) Match(md metadata.MD) bool { vs, ok := mdValuesFromOutgoingCtx(md, hpm.key) - present := ok && len(vs) > 0 + present := ok && len(vs) > 0 // TODO: Are we sure we need this len(vs) > 0? return present == hpm.present } @@ -157,11 +163,12 @@ func (hpm *HeaderPresentMatcher) String() string { type HeaderPrefixMatcher struct { key string prefix string + invert bool } // NewHeaderPrefixMatcher returns a new HeaderPrefixMatcher. -func NewHeaderPrefixMatcher(key string, prefix string) *HeaderPrefixMatcher { - return &HeaderPrefixMatcher{key: key, prefix: prefix} +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 @@ -171,7 +178,7 @@ func (hpm *HeaderPrefixMatcher) Match(md metadata.MD) bool { if !ok { return false } - return strings.HasPrefix(v, hpm.prefix) + return strings.HasPrefix(v, hpm.prefix) != hpm.invert } func (hpm *HeaderPrefixMatcher) String() string { @@ -183,11 +190,12 @@ func (hpm *HeaderPrefixMatcher) String() string { type HeaderSuffixMatcher struct { key string suffix string + invert bool } // NewHeaderSuffixMatcher returns a new HeaderSuffixMatcher. -func NewHeaderSuffixMatcher(key string, suffix string) *HeaderSuffixMatcher { - return &HeaderSuffixMatcher{key: key, suffix: suffix} +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 @@ -197,7 +205,7 @@ func (hsm *HeaderSuffixMatcher) Match(md metadata.MD) bool { if !ok { return false } - return strings.HasSuffix(v, hsm.suffix) + return strings.HasSuffix(v, hsm.suffix) != hsm.invert } func (hsm *HeaderSuffixMatcher) String() string { @@ -209,14 +217,15 @@ func (hsm *HeaderSuffixMatcher) String() string { 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) *HeaderContainsMatcher { - return &HeaderContainsMatcher{key: key, contains: contains} +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 @@ -226,29 +235,9 @@ func (hcm *HeaderContainsMatcher) Match(md metadata.MD) bool { if !ok { return false } - return strings.Contains(v, hcm.contains) + return strings.Contains(v, hcm.contains) != hcm.invert } func (hcm *HeaderContainsMatcher) String() string { return fmt.Sprintf("headerContains:%v%v", hcm.key, hcm.contains) } - -// InvertMatcher inverts the match result of the underlying header matcher. -type InvertMatcher struct { - m HeaderMatcher -} - -// NewInvertMatcher returns a new InvertMatcher. -func NewInvertMatcher(m HeaderMatcher) *InvertMatcher { - return &InvertMatcher{m: m} -} - -// Match returns whether the passed in HTTP Headers match according to the -// InvertMatcher. -func (i *InvertMatcher) Match(md metadata.MD) bool { - return !i.m.Match(md) -} - -func (i *InvertMatcher) String() string { - return fmt.Sprintf("invert{%s}", i.m) -} diff --git a/internal/xds/matcher/matcher_header_test.go b/internal/xds/matcher/matcher_header_test.go index 7e78065212cf..f567f3198242 100644 --- a/internal/xds/matcher/matcher_header_test.go +++ b/internal/xds/matcher/matcher_header_test.go @@ -31,6 +31,7 @@ func TestHeaderExactMatcherMatch(t *testing.T) { key, exact string md metadata.MD want bool + invert bool }{ { name: "one value one match", @@ -61,10 +62,34 @@ func TestHeaderExactMatcherMatch(t *testing.T) { md: metadata.Pairs("th", "abc"), want: false, }, + { + name: "invert header not present", + key: "th", + exact: "tv", + md: metadata.Pairs(":method", "GET"), + want: false, + invert: true, + }, + { + name: "invert header match", + key: "th", + exact: "tv", + md: metadata.Pairs("th", "tv"), + want: false, + invert: true, + }, + { + name: "invert header not match", + key: "th", + exact: "tv", + md: metadata.Pairs("th", "tvv"), + want: true, + invert: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - hem := NewHeaderExactMatcher(tt.key, tt.exact) + hem := NewHeaderExactMatcher(tt.key, tt.exact, tt.invert) if got := hem.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } @@ -78,6 +103,7 @@ func TestHeaderRegexMatcherMatch(t *testing.T) { key, regexStr string md metadata.MD want bool + invert bool }{ { name: "one value one match", @@ -121,10 +147,34 @@ func TestHeaderRegexMatcherMatch(t *testing.T) { md: metadata.Pairs("header", "aa"), want: true, }, + { + name: "invert header not present", + key: "th", + regexStr: "^t+v*$", + md: metadata.Pairs(":method", "GET"), + want: false, + invert: true, + }, + { + name: "invert header match", + key: "th", + regexStr: "^t+v*$", + md: metadata.Pairs("th", "tttvv"), + want: false, + invert: true, + }, + { + name: "invert header not match", + key: "th", + regexStr: "^t+v*$", + md: metadata.Pairs("th", "abc"), + want: true, + invert: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - hrm := NewHeaderRegexMatcher(tt.key, regexp.MustCompile(tt.regexStr)) + hrm := NewHeaderRegexMatcher(tt.key, regexp.MustCompile(tt.regexStr), tt.invert) if got := hrm.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } @@ -139,6 +189,7 @@ func TestHeaderRangeMatcherMatch(t *testing.T) { start, end int64 md metadata.MD want bool + invert bool }{ { name: "match", @@ -168,10 +219,34 @@ func TestHeaderRangeMatcherMatch(t *testing.T) { md: metadata.Pairs("th", "-5"), want: true, }, + { + name: "invert header not present", + key: "th", + start: 1, end: 10, + md: metadata.Pairs(":method", "GET"), + want: false, + invert: true, + }, + { + name: "invert header match", + key: "th", + start: 1, end: 10, + md: metadata.Pairs("th", "5"), + want: false, + invert: true, + }, + { + name: "invert header not match", + key: "th", + start: 1, end: 9, + md: metadata.Pairs("th", "10"), + want: true, + invert: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - hrm := NewHeaderRangeMatcher(tt.key, tt.start, tt.end) + hrm := NewHeaderRangeMatcher(tt.key, tt.start, tt.end, tt.invert) if got := hrm.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } @@ -186,6 +261,7 @@ func TestHeaderPresentMatcherMatch(t *testing.T) { present bool md metadata.MD want bool + invert bool }{ { name: "want present is present", @@ -215,10 +291,34 @@ func TestHeaderPresentMatcherMatch(t *testing.T) { md: metadata.Pairs("abc", "tv"), want: true, }, + { + name: "invert header not present", + key: "th", + present: true, + md: metadata.Pairs(":method", "GET"), + want: true, + invert: true, + }, + { + name: "invert header match", + key: "th", + present: true, + md: metadata.Pairs("th", "tv"), + want: false, + invert: true, + }, + { + name: "invert header not match", + key: "th", + present: true, + md: metadata.Pairs(":method", "GET"), + want: true, + invert: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - hpm := NewHeaderPresentMatcher(tt.key, tt.present) + hpm := NewHeaderPresentMatcher(tt.key, tt.present, tt.invert) if got := hpm.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } @@ -232,6 +332,7 @@ func TestHeaderPrefixMatcherMatch(t *testing.T) { key, prefix string md metadata.MD want bool + invert bool }{ { name: "one value one match", @@ -261,10 +362,34 @@ func TestHeaderPrefixMatcherMatch(t *testing.T) { md: metadata.Pairs("th", "abc"), want: false, }, + { + name: "invert header not present", + key: "th", + prefix: "tv", + md: metadata.Pairs(":method", "GET"), + want: false, + invert: true, + }, + { + name: "invert header match", + key: "th", + prefix: "tv", + md: metadata.Pairs("th", "tv123"), + want: false, + invert: true, + }, + { + name: "invert header not match", + key: "th", + prefix: "tv", + md: metadata.Pairs("th", "abc"), + want: true, + invert: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - hpm := NewHeaderPrefixMatcher(tt.key, tt.prefix) + hpm := NewHeaderPrefixMatcher(tt.key, tt.prefix, tt.invert) if got := hpm.Match(tt.md); got != tt.want { t.Errorf("match() = %v, want %v", got, tt.want) } @@ -278,6 +403,7 @@ func TestHeaderSuffixMatcherMatch(t *testing.T) { key, suffix string md metadata.MD want bool + invert bool }{ { name: "one value one match", @@ -307,40 +433,36 @@ func TestHeaderSuffixMatcherMatch(t *testing.T) { md: metadata.Pairs("th", "abc"), want: false, }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - hsm := NewHeaderSuffixMatcher(tt.key, tt.suffix) - if got := hsm.Match(tt.md); got != tt.want { - t.Errorf("match() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestInvertMatcherMatch(t *testing.T) { - tests := []struct { - name string - m HeaderMatcher - md metadata.MD - }{ { - name: "true->false", - m: NewHeaderExactMatcher("th", "tv"), - md: metadata.Pairs("th", "tv"), + name: "invert header not present", + key: "th", + suffix: "tv", + md: metadata.Pairs(":method", "GET"), + want: false, + invert: true, }, { - name: "false->true", - m: NewHeaderExactMatcher("th", "abc"), - md: metadata.Pairs("th", "tv"), + name: "invert header match", + key: "th", + suffix: "tv", + md: metadata.Pairs("th", "123tv"), + want: false, + invert: true, + }, + { + name: "invert header not match", + key: "th", + suffix: "tv", + md: metadata.Pairs("th", "abc"), + want: true, + invert: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := NewInvertMatcher(tt.m).Match(tt.md) - want := !tt.m.Match(tt.md) - if got != want { - t.Errorf("match() = %v, want %v", got, want) + hsm := NewHeaderSuffixMatcher(tt.key, tt.suffix, tt.invert) + if got := hsm.Match(tt.md); got != tt.want { + t.Errorf("match() = %v, want %v", got, tt.want) } }) } diff --git a/internal/xds/rbac/matchers.go b/internal/xds/rbac/matchers.go index 6129a292d23e..6f30c8016e2b 100644 --- a/internal/xds/rbac/matchers.go +++ b/internal/xds/rbac/matchers.go @@ -265,29 +265,26 @@ func newHeaderMatcher(headerMatcherConfig *v3route_componentspb.HeaderMatcher) ( var m internalmatcher.HeaderMatcher switch headerMatcherConfig.HeaderMatchSpecifier.(type) { case *v3route_componentspb.HeaderMatcher_ExactMatch: - m = internalmatcher.NewHeaderExactMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetExactMatch()) + m = internalmatcher.NewHeaderExactMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetExactMatch(), headerMatcherConfig.InvertMatch) case *v3route_componentspb.HeaderMatcher_SafeRegexMatch: regex, err := regexp.Compile(headerMatcherConfig.GetSafeRegexMatch().Regex) if err != nil { return nil, err } - m = internalmatcher.NewHeaderRegexMatcher(headerMatcherConfig.Name, regex) + m = internalmatcher.NewHeaderRegexMatcher(headerMatcherConfig.Name, regex, headerMatcherConfig.InvertMatch) case *v3route_componentspb.HeaderMatcher_RangeMatch: - m = internalmatcher.NewHeaderRangeMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetRangeMatch().Start, headerMatcherConfig.GetRangeMatch().End) + m = internalmatcher.NewHeaderRangeMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetRangeMatch().Start, headerMatcherConfig.GetRangeMatch().End, headerMatcherConfig.InvertMatch) case *v3route_componentspb.HeaderMatcher_PresentMatch: - m = internalmatcher.NewHeaderPresentMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPresentMatch()) + m = internalmatcher.NewHeaderPresentMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPresentMatch(), headerMatcherConfig.InvertMatch) case *v3route_componentspb.HeaderMatcher_PrefixMatch: - m = internalmatcher.NewHeaderPrefixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPrefixMatch()) + m = internalmatcher.NewHeaderPrefixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPrefixMatch(), headerMatcherConfig.InvertMatch) case *v3route_componentspb.HeaderMatcher_SuffixMatch: - m = internalmatcher.NewHeaderSuffixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetSuffixMatch()) + m = internalmatcher.NewHeaderSuffixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetSuffixMatch(), headerMatcherConfig.InvertMatch) case *v3route_componentspb.HeaderMatcher_ContainsMatch: - m = internalmatcher.NewHeaderContainsMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetContainsMatch()) + m = internalmatcher.NewHeaderContainsMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetContainsMatch(), headerMatcherConfig.InvertMatch) default: return nil, errors.New("unknown header matcher type") } - if headerMatcherConfig.InvertMatch { - m = internalmatcher.NewInvertMatcher(m) - } return &headerMatcher{matcher: m}, nil } diff --git a/xds/internal/xdsclient/matcher.go b/xds/internal/xdsclient/matcher.go index e663e02769f8..85fff30638e6 100644 --- a/xds/internal/xdsclient/matcher.go +++ b/xds/internal/xdsclient/matcher.go @@ -46,25 +46,23 @@ func RouteToMatcher(r *Route) (*CompositeMatcher, error) { headerMatchers := make([]matcher.HeaderMatcher, 0, len(r.Headers)) for _, h := range r.Headers { var matcherT matcher.HeaderMatcher + invert := h.InvertMatch != nil && *h.InvertMatch switch { case h.ExactMatch != nil && *h.ExactMatch != "": - matcherT = matcher.NewHeaderExactMatcher(h.Name, *h.ExactMatch) + matcherT = matcher.NewHeaderExactMatcher(h.Name, *h.ExactMatch, invert) case h.RegexMatch != nil: - matcherT = matcher.NewHeaderRegexMatcher(h.Name, h.RegexMatch) + matcherT = matcher.NewHeaderRegexMatcher(h.Name, h.RegexMatch, invert) case h.PrefixMatch != nil && *h.PrefixMatch != "": - matcherT = matcher.NewHeaderPrefixMatcher(h.Name, *h.PrefixMatch) + matcherT = matcher.NewHeaderPrefixMatcher(h.Name, *h.PrefixMatch, invert) case h.SuffixMatch != nil && *h.SuffixMatch != "": - matcherT = matcher.NewHeaderSuffixMatcher(h.Name, *h.SuffixMatch) + matcherT = matcher.NewHeaderSuffixMatcher(h.Name, *h.SuffixMatch, invert) case h.RangeMatch != nil: - matcherT = matcher.NewHeaderRangeMatcher(h.Name, h.RangeMatch.Start, h.RangeMatch.End) + matcherT = matcher.NewHeaderRangeMatcher(h.Name, h.RangeMatch.Start, h.RangeMatch.End, invert) case h.PresentMatch != nil: - matcherT = matcher.NewHeaderPresentMatcher(h.Name, *h.PresentMatch) + matcherT = matcher.NewHeaderPresentMatcher(h.Name, *h.PresentMatch, invert) default: return nil, fmt.Errorf("illegal route: missing header_match_specifier") } - if h.InvertMatch != nil && *h.InvertMatch { - matcherT = matcher.NewInvertMatcher(matcherT) - } headerMatchers = append(headerMatchers, matcherT) } diff --git a/xds/internal/xdsclient/matcher_test.go b/xds/internal/xdsclient/matcher_test.go index f750d07d6e4f..724fa8269582 100644 --- a/xds/internal/xdsclient/matcher_test.go +++ b/xds/internal/xdsclient/matcher_test.go @@ -40,7 +40,7 @@ func TestAndMatcherMatch(t *testing.T) { { name: "both match", pm: newPathExactMatcher("/a/b", false), - hm: matcher.NewHeaderExactMatcher("th", "tv"), + hm: matcher.NewHeaderExactMatcher("th", "tv", false), info: iresolver.RPCInfo{ Method: "/a/b", Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")), @@ -50,7 +50,7 @@ func TestAndMatcherMatch(t *testing.T) { { name: "both match with path case insensitive", pm: newPathExactMatcher("/A/B", true), - hm: matcher.NewHeaderExactMatcher("th", "tv"), + hm: matcher.NewHeaderExactMatcher("th", "tv", false), info: iresolver.RPCInfo{ Method: "/a/b", Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")), @@ -60,7 +60,7 @@ func TestAndMatcherMatch(t *testing.T) { { name: "only one match", pm: newPathExactMatcher("/a/b", false), - hm: matcher.NewHeaderExactMatcher("th", "tv"), + hm: matcher.NewHeaderExactMatcher("th", "tv", false), info: iresolver.RPCInfo{ Method: "/z/y", Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")), @@ -70,7 +70,7 @@ func TestAndMatcherMatch(t *testing.T) { { name: "both not match", pm: newPathExactMatcher("/z/y", false), - hm: matcher.NewHeaderExactMatcher("th", "abc"), + hm: matcher.NewHeaderExactMatcher("th", "abc", false), info: iresolver.RPCInfo{ Method: "/a/b", Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs("th", "tv")), @@ -80,7 +80,7 @@ func TestAndMatcherMatch(t *testing.T) { { name: "fake header", pm: newPathPrefixMatcher("/", false), - hm: matcher.NewHeaderExactMatcher("content-type", "fake"), + hm: matcher.NewHeaderExactMatcher("content-type", "fake", false), info: iresolver.RPCInfo{ Method: "/a/b", Context: grpcutil.WithExtraMetadata(context.Background(), metadata.Pairs( @@ -92,7 +92,7 @@ func TestAndMatcherMatch(t *testing.T) { { name: "binary header", pm: newPathPrefixMatcher("/", false), - hm: matcher.NewHeaderPresentMatcher("t-bin", true), + hm: matcher.NewHeaderPresentMatcher("t-bin", true, false), info: iresolver.RPCInfo{ Method: "/a/b", Context: grpcutil.WithExtraMetadata(