github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/internal/xds/matcher/matcher_header.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 19 package matcher 20 21 import ( 22 "fmt" 23 "regexp" 24 "strconv" 25 "strings" 26 27 "github.com/hxx258456/ccgo/grpc/internal/grpcutil" 28 "github.com/hxx258456/ccgo/grpc/metadata" 29 ) 30 31 // HeaderMatcher is an interface for header matchers. These are 32 // documented in (EnvoyProxy link here?). These matchers will match on different 33 // aspects of HTTP header name/value pairs. 34 type HeaderMatcher interface { 35 Match(metadata.MD) bool 36 String() string 37 } 38 39 // mdValuesFromOutgoingCtx retrieves metadata from context. If there are 40 // multiple values, the values are concatenated with "," (comma and no space). 41 // 42 // All header matchers only match against the comma-concatenated string. 43 func mdValuesFromOutgoingCtx(md metadata.MD, key string) (string, bool) { 44 vs, ok := md[key] 45 if !ok { 46 return "", false 47 } 48 return strings.Join(vs, ","), true 49 } 50 51 // HeaderExactMatcher matches on an exact match of the value of the header. 52 type HeaderExactMatcher struct { 53 key string 54 exact string 55 invert bool 56 } 57 58 // NewHeaderExactMatcher returns a new HeaderExactMatcher. 59 func NewHeaderExactMatcher(key, exact string, invert bool) *HeaderExactMatcher { 60 return &HeaderExactMatcher{key: key, exact: exact, invert: invert} 61 } 62 63 // Match returns whether the passed in HTTP Headers match according to the 64 // HeaderExactMatcher. 65 func (hem *HeaderExactMatcher) Match(md metadata.MD) bool { 66 v, ok := mdValuesFromOutgoingCtx(md, hem.key) 67 if !ok { 68 return false 69 } 70 return (v == hem.exact) != hem.invert 71 } 72 73 func (hem *HeaderExactMatcher) String() string { 74 return fmt.Sprintf("headerExact:%v:%v", hem.key, hem.exact) 75 } 76 77 // HeaderRegexMatcher matches on whether the entire request header value matches 78 // the regex. 79 type HeaderRegexMatcher struct { 80 key string 81 re *regexp.Regexp 82 invert bool 83 } 84 85 // NewHeaderRegexMatcher returns a new HeaderRegexMatcher. 86 func NewHeaderRegexMatcher(key string, re *regexp.Regexp, invert bool) *HeaderRegexMatcher { 87 return &HeaderRegexMatcher{key: key, re: re, invert: invert} 88 } 89 90 // Match returns whether the passed in HTTP Headers match according to the 91 // HeaderRegexMatcher. 92 func (hrm *HeaderRegexMatcher) Match(md metadata.MD) bool { 93 v, ok := mdValuesFromOutgoingCtx(md, hrm.key) 94 if !ok { 95 return false 96 } 97 return grpcutil.FullMatchWithRegex(hrm.re, v) != hrm.invert 98 } 99 100 func (hrm *HeaderRegexMatcher) String() string { 101 return fmt.Sprintf("headerRegex:%v:%v", hrm.key, hrm.re.String()) 102 } 103 104 // HeaderRangeMatcher matches on whether the request header value is within the 105 // range. The header value must be an integer in base 10 notation. 106 type HeaderRangeMatcher struct { 107 key string 108 start, end int64 // represents [start, end). 109 invert bool 110 } 111 112 // NewHeaderRangeMatcher returns a new HeaderRangeMatcher. 113 func NewHeaderRangeMatcher(key string, start, end int64, invert bool) *HeaderRangeMatcher { 114 return &HeaderRangeMatcher{key: key, start: start, end: end, invert: invert} 115 } 116 117 // Match returns whether the passed in HTTP Headers match according to the 118 // HeaderRangeMatcher. 119 func (hrm *HeaderRangeMatcher) Match(md metadata.MD) bool { 120 v, ok := mdValuesFromOutgoingCtx(md, hrm.key) 121 if !ok { 122 return false 123 } 124 if i, err := strconv.ParseInt(v, 10, 64); err == nil && i >= hrm.start && i < hrm.end { 125 return !hrm.invert 126 } 127 return hrm.invert 128 } 129 130 func (hrm *HeaderRangeMatcher) String() string { 131 return fmt.Sprintf("headerRange:%v:[%d,%d)", hrm.key, hrm.start, hrm.end) 132 } 133 134 // HeaderPresentMatcher will match based on whether the header is present in the 135 // whole request. 136 type HeaderPresentMatcher struct { 137 key string 138 present bool 139 } 140 141 // NewHeaderPresentMatcher returns a new HeaderPresentMatcher. 142 func NewHeaderPresentMatcher(key string, present bool, invert bool) *HeaderPresentMatcher { 143 if invert { 144 present = !present 145 } 146 return &HeaderPresentMatcher{key: key, present: present} 147 } 148 149 // Match returns whether the passed in HTTP Headers match according to the 150 // HeaderPresentMatcher. 151 func (hpm *HeaderPresentMatcher) Match(md metadata.MD) bool { 152 vs, ok := mdValuesFromOutgoingCtx(md, hpm.key) 153 present := ok && len(vs) > 0 // TODO: Are we sure we need this len(vs) > 0? 154 return present == hpm.present 155 } 156 157 func (hpm *HeaderPresentMatcher) String() string { 158 return fmt.Sprintf("headerPresent:%v:%v", hpm.key, hpm.present) 159 } 160 161 // HeaderPrefixMatcher matches on whether the prefix of the header value matches 162 // the prefix passed into this struct. 163 type HeaderPrefixMatcher struct { 164 key string 165 prefix string 166 invert bool 167 } 168 169 // NewHeaderPrefixMatcher returns a new HeaderPrefixMatcher. 170 func NewHeaderPrefixMatcher(key string, prefix string, invert bool) *HeaderPrefixMatcher { 171 return &HeaderPrefixMatcher{key: key, prefix: prefix, invert: invert} 172 } 173 174 // Match returns whether the passed in HTTP Headers match according to the 175 // HeaderPrefixMatcher. 176 func (hpm *HeaderPrefixMatcher) Match(md metadata.MD) bool { 177 v, ok := mdValuesFromOutgoingCtx(md, hpm.key) 178 if !ok { 179 return false 180 } 181 return strings.HasPrefix(v, hpm.prefix) != hpm.invert 182 } 183 184 func (hpm *HeaderPrefixMatcher) String() string { 185 return fmt.Sprintf("headerPrefix:%v:%v", hpm.key, hpm.prefix) 186 } 187 188 // HeaderSuffixMatcher matches on whether the suffix of the header value matches 189 // the suffix passed into this struct. 190 type HeaderSuffixMatcher struct { 191 key string 192 suffix string 193 invert bool 194 } 195 196 // NewHeaderSuffixMatcher returns a new HeaderSuffixMatcher. 197 func NewHeaderSuffixMatcher(key string, suffix string, invert bool) *HeaderSuffixMatcher { 198 return &HeaderSuffixMatcher{key: key, suffix: suffix, invert: invert} 199 } 200 201 // Match returns whether the passed in HTTP Headers match according to the 202 // HeaderSuffixMatcher. 203 func (hsm *HeaderSuffixMatcher) Match(md metadata.MD) bool { 204 v, ok := mdValuesFromOutgoingCtx(md, hsm.key) 205 if !ok { 206 return false 207 } 208 return strings.HasSuffix(v, hsm.suffix) != hsm.invert 209 } 210 211 func (hsm *HeaderSuffixMatcher) String() string { 212 return fmt.Sprintf("headerSuffix:%v:%v", hsm.key, hsm.suffix) 213 } 214 215 // HeaderContainsMatcher matches on whether the header value contains the 216 // value passed into this struct. 217 type HeaderContainsMatcher struct { 218 key string 219 contains string 220 invert bool 221 } 222 223 // NewHeaderContainsMatcher returns a new HeaderContainsMatcher. key is the HTTP 224 // Header key to match on, and contains is the value that the header should 225 // should contain for a successful match. An empty contains string does not 226 // work, use HeaderPresentMatcher in that case. 227 func NewHeaderContainsMatcher(key string, contains string, invert bool) *HeaderContainsMatcher { 228 return &HeaderContainsMatcher{key: key, contains: contains, invert: invert} 229 } 230 231 // Match returns whether the passed in HTTP Headers match according to the 232 // HeaderContainsMatcher. 233 func (hcm *HeaderContainsMatcher) Match(md metadata.MD) bool { 234 v, ok := mdValuesFromOutgoingCtx(md, hcm.key) 235 if !ok { 236 return false 237 } 238 return strings.Contains(v, hcm.contains) != hcm.invert 239 } 240 241 func (hcm *HeaderContainsMatcher) String() string { 242 return fmt.Sprintf("headerContains:%v%v", hcm.key, hcm.contains) 243 }