blob: a2cdcaf12a87ff7353f22ffb3f7c3237283f0781 [file] [log] [blame]
khenaidoo5fc5cea2021-08-11 17:39:16 -04001/*
2 *
3 * Copyright 2014 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 metadata define the structure of the metadata supported by gRPC library.
20// Please refer to https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
21// for more information about custom-metadata.
22package metadata // import "google.golang.org/grpc/metadata"
23
24import (
25 "context"
26 "fmt"
27 "strings"
28)
29
30// DecodeKeyValue returns k, v, nil.
31//
32// Deprecated: use k and v directly instead.
33func DecodeKeyValue(k, v string) (string, string, error) {
34 return k, v, nil
35}
36
37// MD is a mapping from metadata keys to values. Users should use the following
38// two convenience functions New and Pairs to generate MD.
39type MD map[string][]string
40
41// New creates an MD from a given key-value map.
42//
43// Only the following ASCII characters are allowed in keys:
Joey Armstrongba3d9d12024-01-15 14:22:11 -050044// - digits: 0-9
45// - uppercase letters: A-Z (normalized to lower)
46// - lowercase letters: a-z
47// - special characters: -_.
48//
khenaidoo5fc5cea2021-08-11 17:39:16 -040049// Uppercase letters are automatically converted to lowercase.
50//
51// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
52// result in errors if set in metadata.
53func New(m map[string]string) MD {
Akash Kankanala761955c2024-02-21 19:32:20 +053054 md := make(MD, len(m))
khenaidoo5fc5cea2021-08-11 17:39:16 -040055 for k, val := range m {
56 key := strings.ToLower(k)
57 md[key] = append(md[key], val)
58 }
59 return md
60}
61
62// Pairs returns an MD formed by the mapping of key, value ...
63// Pairs panics if len(kv) is odd.
64//
65// Only the following ASCII characters are allowed in keys:
Joey Armstrongba3d9d12024-01-15 14:22:11 -050066// - digits: 0-9
67// - uppercase letters: A-Z (normalized to lower)
68// - lowercase letters: a-z
69// - special characters: -_.
70//
khenaidoo5fc5cea2021-08-11 17:39:16 -040071// Uppercase letters are automatically converted to lowercase.
72//
73// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
74// result in errors if set in metadata.
75func Pairs(kv ...string) MD {
76 if len(kv)%2 == 1 {
77 panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
78 }
Akash Kankanala761955c2024-02-21 19:32:20 +053079 md := make(MD, len(kv)/2)
khenaidoo5fc5cea2021-08-11 17:39:16 -040080 for i := 0; i < len(kv); i += 2 {
81 key := strings.ToLower(kv[i])
82 md[key] = append(md[key], kv[i+1])
83 }
84 return md
85}
86
87// Len returns the number of items in md.
88func (md MD) Len() int {
89 return len(md)
90}
91
92// Copy returns a copy of md.
93func (md MD) Copy() MD {
Akash Kankanala761955c2024-02-21 19:32:20 +053094 out := make(MD, len(md))
95 for k, v := range md {
96 out[k] = copyOf(v)
97 }
98 return out
khenaidoo5fc5cea2021-08-11 17:39:16 -040099}
100
101// Get obtains the values for a given key.
102//
103// k is converted to lowercase before searching in md.
104func (md MD) Get(k string) []string {
105 k = strings.ToLower(k)
106 return md[k]
107}
108
109// Set sets the value of a given key with a slice of values.
110//
111// k is converted to lowercase before storing in md.
112func (md MD) Set(k string, vals ...string) {
113 if len(vals) == 0 {
114 return
115 }
116 k = strings.ToLower(k)
117 md[k] = vals
118}
119
120// Append adds the values to key k, not overwriting what was already stored at
121// that key.
122//
123// k is converted to lowercase before storing in md.
124func (md MD) Append(k string, vals ...string) {
125 if len(vals) == 0 {
126 return
127 }
128 k = strings.ToLower(k)
129 md[k] = append(md[k], vals...)
130}
131
132// Delete removes the values for a given key k which is converted to lowercase
133// before removing it from md.
134func (md MD) Delete(k string) {
135 k = strings.ToLower(k)
136 delete(md, k)
137}
138
139// Join joins any number of mds into a single MD.
140//
141// The order of values for each key is determined by the order in which the mds
142// containing those values are presented to Join.
143func Join(mds ...MD) MD {
144 out := MD{}
145 for _, md := range mds {
146 for k, v := range md {
147 out[k] = append(out[k], v...)
148 }
149 }
150 return out
151}
152
153type mdIncomingKey struct{}
154type mdOutgoingKey struct{}
155
156// NewIncomingContext creates a new context with incoming md attached.
157func NewIncomingContext(ctx context.Context, md MD) context.Context {
158 return context.WithValue(ctx, mdIncomingKey{}, md)
159}
160
161// NewOutgoingContext creates a new context with outgoing md attached. If used
162// in conjunction with AppendToOutgoingContext, NewOutgoingContext will
163// overwrite any previously-appended metadata.
164func NewOutgoingContext(ctx context.Context, md MD) context.Context {
165 return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md})
166}
167
168// AppendToOutgoingContext returns a new context with the provided kv merged
169// with any existing metadata in the context. Please refer to the documentation
170// of Pairs for a description of kv.
171func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context {
172 if len(kv)%2 == 1 {
173 panic(fmt.Sprintf("metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d", len(kv)))
174 }
175 md, _ := ctx.Value(mdOutgoingKey{}).(rawMD)
176 added := make([][]string, len(md.added)+1)
177 copy(added, md.added)
Akash Kankanala761955c2024-02-21 19:32:20 +0530178 kvCopy := make([]string, 0, len(kv))
179 for i := 0; i < len(kv); i += 2 {
180 kvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1])
181 }
182 added[len(added)-1] = kvCopy
khenaidoo5fc5cea2021-08-11 17:39:16 -0400183 return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: added})
184}
185
186// FromIncomingContext returns the incoming metadata in ctx if it exists.
187//
188// All keys in the returned MD are lowercase.
189func FromIncomingContext(ctx context.Context) (MD, bool) {
190 md, ok := ctx.Value(mdIncomingKey{}).(MD)
191 if !ok {
192 return nil, false
193 }
Akash Kankanala761955c2024-02-21 19:32:20 +0530194 out := make(MD, len(md))
khenaidoo5fc5cea2021-08-11 17:39:16 -0400195 for k, v := range md {
196 // We need to manually convert all keys to lower case, because MD is a
197 // map, and there's no guarantee that the MD attached to the context is
198 // created using our helper functions.
199 key := strings.ToLower(k)
Akash Kankanala761955c2024-02-21 19:32:20 +0530200 out[key] = copyOf(v)
khenaidoo5fc5cea2021-08-11 17:39:16 -0400201 }
202 return out, true
203}
204
Akash Kankanala761955c2024-02-21 19:32:20 +0530205// ValueFromIncomingContext returns the metadata value corresponding to the metadata
206// key from the incoming metadata if it exists. Key must be lower-case.
207//
208// # Experimental
209//
210// Notice: This API is EXPERIMENTAL and may be changed or removed in a
211// later release.
212func ValueFromIncomingContext(ctx context.Context, key string) []string {
213 md, ok := ctx.Value(mdIncomingKey{}).(MD)
214 if !ok {
215 return nil
216 }
217
218 if v, ok := md[key]; ok {
219 return copyOf(v)
220 }
221 for k, v := range md {
222 // We need to manually convert all keys to lower case, because MD is a
223 // map, and there's no guarantee that the MD attached to the context is
224 // created using our helper functions.
225 if strings.ToLower(k) == key {
226 return copyOf(v)
227 }
228 }
229 return nil
230}
231
232// the returned slice must not be modified in place
233func copyOf(v []string) []string {
234 vals := make([]string, len(v))
235 copy(vals, v)
236 return vals
237}
238
khenaidoo5fc5cea2021-08-11 17:39:16 -0400239// FromOutgoingContextRaw returns the un-merged, intermediary contents of rawMD.
240//
241// Remember to perform strings.ToLower on the keys, for both the returned MD (MD
242// is a map, there's no guarantee it's created using our helper functions) and
243// the extra kv pairs (AppendToOutgoingContext doesn't turn them into
244// lowercase).
245//
246// This is intended for gRPC-internal use ONLY. Users should use
247// FromOutgoingContext instead.
248func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) {
249 raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
250 if !ok {
251 return nil, nil, false
252 }
253
254 return raw.md, raw.added, true
255}
256
257// FromOutgoingContext returns the outgoing metadata in ctx if it exists.
258//
259// All keys in the returned MD are lowercase.
260func FromOutgoingContext(ctx context.Context) (MD, bool) {
261 raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
262 if !ok {
263 return nil, false
264 }
265
Akash Kankanala761955c2024-02-21 19:32:20 +0530266 mdSize := len(raw.md)
267 for i := range raw.added {
268 mdSize += len(raw.added[i]) / 2
269 }
270
271 out := make(MD, mdSize)
khenaidoo5fc5cea2021-08-11 17:39:16 -0400272 for k, v := range raw.md {
273 // We need to manually convert all keys to lower case, because MD is a
274 // map, and there's no guarantee that the MD attached to the context is
275 // created using our helper functions.
276 key := strings.ToLower(k)
Akash Kankanala761955c2024-02-21 19:32:20 +0530277 out[key] = copyOf(v)
khenaidoo5fc5cea2021-08-11 17:39:16 -0400278 }
279 for _, added := range raw.added {
280 if len(added)%2 == 1 {
281 panic(fmt.Sprintf("metadata: FromOutgoingContext got an odd number of input pairs for metadata: %d", len(added)))
282 }
283
284 for i := 0; i < len(added); i += 2 {
285 key := strings.ToLower(added[i])
286 out[key] = append(out[key], added[i+1])
287 }
288 }
289 return out, ok
290}
291
292type rawMD struct {
293 md MD
294 added [][]string
295}