github.com/bytedance/gopkg@v0.0.0-20240514070511-01b2cbcf35e1/cloud/metainfo/http.go (about) 1 // Copyright 2021 ByteDance Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package metainfo 16 17 import ( 18 "context" 19 "strings" 20 21 "golang.org/x/net/http/httpguts" 22 ) 23 24 // HTTP header prefixes. 25 const ( 26 HTTPPrefixTransient = "rpc-transit-" 27 HTTPPrefixPersistent = "rpc-persist-" 28 HTTPPrefixBackward = "rpc-backward-" 29 30 lenHPT = len(HTTPPrefixTransient) 31 lenHPP = len(HTTPPrefixPersistent) 32 lenHPB = len(HTTPPrefixBackward) 33 ) 34 35 // HTTPHeaderToCGIVariable performs an CGI variable conversion. 36 // For example, an HTTP header key `abc-def` will result in `ABC_DEF`. 37 func HTTPHeaderToCGIVariable(key string) string { 38 return strings.ToUpper(strings.ReplaceAll(key, "-", "_")) 39 } 40 41 // CGIVariableToHTTPHeader converts a CGI variable into an HTTP header key. 42 // For example, `ABC_DEF` will be converted to `abc-def`. 43 func CGIVariableToHTTPHeader(key string) string { 44 return strings.ToLower(strings.ReplaceAll(key, "_", "-")) 45 } 46 47 // HTTPHeaderSetter sets a key with a value into a HTTP header. 48 type HTTPHeaderSetter interface { 49 Set(key, value string) 50 } 51 52 // HTTPHeaderCarrier accepts a visitor to access all key value pairs in an HTTP header. 53 type HTTPHeaderCarrier interface { 54 Visit(func(k, v string)) 55 } 56 57 // HTTPHeader is provided to wrap an http.Header into an HTTPHeaderCarrier. 58 type HTTPHeader map[string][]string 59 60 // Visit implements the HTTPHeaderCarrier interface. 61 func (h HTTPHeader) Visit(v func(k, v string)) { 62 for k, vs := range h { 63 v(k, vs[0]) 64 } 65 } 66 67 // Set sets the header entries associated with key to the single element value. 68 // The key will converted into lowercase as the HTTP/2 protocol requires. 69 func (h HTTPHeader) Set(key, value string) { 70 h[strings.ToLower(key)] = []string{value} 71 } 72 73 // FromHTTPHeader reads metainfo from a given HTTP header and sets them into the context. 74 // Note that this function does not call TransferForward inside. 75 func FromHTTPHeader(ctx context.Context, h HTTPHeaderCarrier) context.Context { 76 if ctx == nil || h == nil { 77 return ctx 78 } 79 nd := getNode(ctx) 80 if nd == nil || nd.size() == 0 { 81 return newCtxFromHTTPHeader(ctx, h) 82 } 83 84 // inherit from exist ctx node 85 persistent := newKVStore() 86 transient := newKVStore() 87 sliceToMap(nd.persistent, persistent) 88 sliceToMap(nd.transient, transient) 89 90 // insert new kvs from http header 91 h.Visit(func(k, v string) { 92 if len(v) == 0 { 93 return 94 } 95 kk := strings.ToLower(k) 96 ln := len(kk) 97 if ln > lenHPT && strings.HasPrefix(kk, HTTPPrefixTransient) { 98 kk = HTTPHeaderToCGIVariable(kk[lenHPT:]) 99 transient[kk] = v 100 } else if ln > lenHPP && strings.HasPrefix(kk, HTTPPrefixPersistent) { 101 kk = HTTPHeaderToCGIVariable(kk[lenHPP:]) 102 persistent[kk] = v 103 } 104 }) 105 106 // return original ctx if no invalid key in http header 107 if (persistent.size() + transient.size()) == 0 { 108 return ctx 109 } 110 111 // make new kvs 112 nd = newNodeFromMaps(persistent, transient, nil) 113 persistent.recycle() 114 transient.recycle() 115 ctx = withNode(ctx, nd) 116 return ctx 117 } 118 119 func newCtxFromHTTPHeader(ctx context.Context, h HTTPHeaderCarrier) context.Context { 120 nd := &node{ 121 persistent: make([]kv, 0, 16), // 32B * 16 = 512B 122 transient: make([]kv, 0, 16), 123 stale: []kv{}, 124 } 125 // insert new kvs from http header to node 126 h.Visit(func(k, v string) { 127 if len(v) == 0 { 128 return 129 } 130 kk := strings.ToLower(k) 131 ln := len(kk) 132 if ln > lenHPT && strings.HasPrefix(kk, HTTPPrefixTransient) { 133 kk = HTTPHeaderToCGIVariable(kk[lenHPT:]) 134 nd.transient = append(nd.transient, kv{key: kk, val: v}) 135 } else if ln > lenHPP && strings.HasPrefix(kk, HTTPPrefixPersistent) { 136 kk = HTTPHeaderToCGIVariable(kk[lenHPP:]) 137 nd.persistent = append(nd.persistent, kv{key: kk, val: v}) 138 } 139 }) 140 141 // return original ctx if no invalid key in http header 142 if nd.size() == 0 { 143 return ctx 144 } 145 return withNode(ctx, nd) 146 } 147 148 // ToHTTPHeader writes all metainfo into the given HTTP header. 149 // Note that this function does not call TransferForward inside. 150 // Any key or value that does not follow the HTTP specification 151 // will be discarded. 152 func ToHTTPHeader(ctx context.Context, h HTTPHeaderSetter) { 153 if ctx == nil || h == nil { 154 return 155 } 156 157 for k, v := range GetAllValues(ctx) { 158 if httpguts.ValidHeaderFieldName(k) && httpguts.ValidHeaderFieldValue(v) { 159 k := HTTPPrefixTransient + CGIVariableToHTTPHeader(k) 160 h.Set(k, v) 161 } 162 } 163 164 for k, v := range GetAllPersistentValues(ctx) { 165 if httpguts.ValidHeaderFieldName(k) && httpguts.ValidHeaderFieldValue(v) { 166 k := HTTPPrefixPersistent + CGIVariableToHTTPHeader(k) 167 h.Set(k, v) 168 } 169 } 170 }