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 }