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
19package resolver
20
21import (
22	"fmt"
23	"regexp"
24	"strconv"
25	"strings"
26
27	"google.golang.org/grpc/metadata"
28)
29
30type headerMatcherInterface interface {
31	match(metadata.MD) bool
32	String() string
33}
34
35// mdValuesFromOutgoingCtx retrieves metadata from context. If there are
36// multiple values, the values are concatenated with "," (comma and no space).
37//
38// All header matchers only match against the comma-concatenated string.
39func mdValuesFromOutgoingCtx(md metadata.MD, key string) (string, bool) {
40	vs, ok := md[key]
41	if !ok {
42		return "", false
43	}
44	return strings.Join(vs, ","), true
45}
46
47type headerExactMatcher struct {
48	key   string
49	exact string
50}
51
52func newHeaderExactMatcher(key, exact string) *headerExactMatcher {
53	return &headerExactMatcher{key: key, exact: exact}
54}
55
56func (hem *headerExactMatcher) match(md metadata.MD) bool {
57	v, ok := mdValuesFromOutgoingCtx(md, hem.key)
58	if !ok {
59		return false
60	}
61	return v == hem.exact
62}
63
64func (hem *headerExactMatcher) String() string {
65	return fmt.Sprintf("headerExact:%v:%v", hem.key, hem.exact)
66}
67
68type headerRegexMatcher struct {
69	key string
70	re  *regexp.Regexp
71}
72
73func newHeaderRegexMatcher(key string, re *regexp.Regexp) *headerRegexMatcher {
74	return &headerRegexMatcher{key: key, re: re}
75}
76
77func (hrm *headerRegexMatcher) match(md metadata.MD) bool {
78	v, ok := mdValuesFromOutgoingCtx(md, hrm.key)
79	if !ok {
80		return false
81	}
82	return hrm.re.MatchString(v)
83}
84
85func (hrm *headerRegexMatcher) String() string {
86	return fmt.Sprintf("headerRegex:%v:%v", hrm.key, hrm.re.String())
87}
88
89type headerRangeMatcher struct {
90	key        string
91	start, end int64 // represents [start, end).
92}
93
94func newHeaderRangeMatcher(key string, start, end int64) *headerRangeMatcher {
95	return &headerRangeMatcher{key: key, start: start, end: end}
96}
97
98func (hrm *headerRangeMatcher) match(md metadata.MD) bool {
99	v, ok := mdValuesFromOutgoingCtx(md, hrm.key)
100	if !ok {
101		return false
102	}
103	if i, err := strconv.ParseInt(v, 10, 64); err == nil && i >= hrm.start && i < hrm.end {
104		return true
105	}
106	return false
107}
108
109func (hrm *headerRangeMatcher) String() string {
110	return fmt.Sprintf("headerRange:%v:[%d,%d)", hrm.key, hrm.start, hrm.end)
111}
112
113type headerPresentMatcher struct {
114	key     string
115	present bool
116}
117
118func newHeaderPresentMatcher(key string, present bool) *headerPresentMatcher {
119	return &headerPresentMatcher{key: key, present: present}
120}
121
122func (hpm *headerPresentMatcher) match(md metadata.MD) bool {
123	vs, ok := mdValuesFromOutgoingCtx(md, hpm.key)
124	present := ok && len(vs) > 0
125	return present == hpm.present
126}
127
128func (hpm *headerPresentMatcher) String() string {
129	return fmt.Sprintf("headerPresent:%v:%v", hpm.key, hpm.present)
130}
131
132type headerPrefixMatcher struct {
133	key    string
134	prefix string
135}
136
137func newHeaderPrefixMatcher(key string, prefix string) *headerPrefixMatcher {
138	return &headerPrefixMatcher{key: key, prefix: prefix}
139}
140
141func (hpm *headerPrefixMatcher) match(md metadata.MD) bool {
142	v, ok := mdValuesFromOutgoingCtx(md, hpm.key)
143	if !ok {
144		return false
145	}
146	return strings.HasPrefix(v, hpm.prefix)
147}
148
149func (hpm *headerPrefixMatcher) String() string {
150	return fmt.Sprintf("headerPrefix:%v:%v", hpm.key, hpm.prefix)
151}
152
153type headerSuffixMatcher struct {
154	key    string
155	suffix string
156}
157
158func newHeaderSuffixMatcher(key string, suffix string) *headerSuffixMatcher {
159	return &headerSuffixMatcher{key: key, suffix: suffix}
160}
161
162func (hsm *headerSuffixMatcher) match(md metadata.MD) bool {
163	v, ok := mdValuesFromOutgoingCtx(md, hsm.key)
164	if !ok {
165		return false
166	}
167	return strings.HasSuffix(v, hsm.suffix)
168}
169
170func (hsm *headerSuffixMatcher) String() string {
171	return fmt.Sprintf("headerSuffix:%v:%v", hsm.key, hsm.suffix)
172}
173
174type invertMatcher struct {
175	m headerMatcherInterface
176}
177
178func newInvertMatcher(m headerMatcherInterface) *invertMatcher {
179	return &invertMatcher{m: m}
180}
181
182func (i *invertMatcher) match(md metadata.MD) bool {
183	return !i.m.match(md)
184}
185
186func (i *invertMatcher) String() string {
187	return fmt.Sprintf("invert{%s}", i.m)
188}
189