1/*
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 "google.golang.org/grpc/internal"
30)
31
32func init() {
33 internal.FromOutgoingContextRaw = fromOutgoingContextRaw
34}
35
36// DecodeKeyValue returns k, v, nil.
37//
38// Deprecated: use k and v directly instead.
39func DecodeKeyValue(k, v string) (string, string, error) {
40 return k, v, nil
41}
42
43// MD is a mapping from metadata keys to values. Users should use the following
44// two convenience functions New and Pairs to generate MD.
45type MD map[string][]string
46
47// New creates an MD from a given key-value map.
48//
49// Only the following ASCII characters are allowed in keys:
50// - digits: 0-9
51// - uppercase letters: A-Z (normalized to lower)
52// - lowercase letters: a-z
53// - special characters: -_.
54//
55// Uppercase letters are automatically converted to lowercase.
56//
57// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
58// result in errors if set in metadata.
59func New(m map[string]string) MD {
60 md := make(MD, len(m))
61 for k, val := range m {
62 key := strings.ToLower(k)
63 md[key] = append(md[key], val)
64 }
65 return md
66}
67
68// Pairs returns an MD formed by the mapping of key, value ...
69// Pairs panics if len(kv) is odd.
70//
71// Only the following ASCII characters are allowed in keys:
72// - digits: 0-9
73// - uppercase letters: A-Z (normalized to lower)
74// - lowercase letters: a-z
75// - special characters: -_.
76//
77// Uppercase letters are automatically converted to lowercase.
78//
79// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
80// result in errors if set in metadata.
81func Pairs(kv ...string) MD {
82 if len(kv)%2 == 1 {
83 panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
84 }
85 md := make(MD, len(kv)/2)
86 for i := 0; i < len(kv); i += 2 {
87 key := strings.ToLower(kv[i])
88 md[key] = append(md[key], kv[i+1])
89 }
90 return md
91}
92
93// Len returns the number of items in md.
94func (md MD) Len() int {
95 return len(md)
96}
97
98// Copy returns a copy of md.
99func (md MD) Copy() MD {
100 out := make(MD, len(md))
101 for k, v := range md {
102 out[k] = copyOf(v)
103 }
104 return out
105}
106
107// Get obtains the values for a given key.
108//
109// k is converted to lowercase before searching in md.
110func (md MD) Get(k string) []string {
111 k = strings.ToLower(k)
112 return md[k]
113}
114
115// Set sets the value of a given key with a slice of values.
116//
117// k is converted to lowercase before storing in md.
118func (md MD) Set(k string, vals ...string) {
119 if len(vals) == 0 {
120 return
121 }
122 k = strings.ToLower(k)
123 md[k] = vals
124}
125
126// Append adds the values to key k, not overwriting what was already stored at
127// that key.
128//
129// k is converted to lowercase before storing in md.
130func (md MD) Append(k string, vals ...string) {
131 if len(vals) == 0 {
132 return
133 }
134 k = strings.ToLower(k)
135 md[k] = append(md[k], vals...)
136}
137
138// Delete removes the values for a given key k which is converted to lowercase
139// before removing it from md.
140func (md MD) Delete(k string) {
141 k = strings.ToLower(k)
142 delete(md, k)
143}
144
145// Join joins any number of mds into a single MD.
146//
147// The order of values for each key is determined by the order in which the mds
148// containing those values are presented to Join.
149func Join(mds ...MD) MD {
150 out := MD{}
151 for _, md := range mds {
152 for k, v := range md {
153 out[k] = append(out[k], v...)
154 }
155 }
156 return out
157}
158
159type mdIncomingKey struct{}
160type mdOutgoingKey struct{}
161
162// NewIncomingContext creates a new context with incoming md attached. md must
163// not be modified after calling this function.
164func NewIncomingContext(ctx context.Context, md MD) context.Context {
165 return context.WithValue(ctx, mdIncomingKey{}, md)
166}
167
168// NewOutgoingContext creates a new context with outgoing md attached. If used
169// in conjunction with AppendToOutgoingContext, NewOutgoingContext will
170// overwrite any previously-appended metadata. md must not be modified after
171// calling this function.
172func NewOutgoingContext(ctx context.Context, md MD) context.Context {
173 return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md})
174}
175
176// AppendToOutgoingContext returns a new context with the provided kv merged
177// with any existing metadata in the context. Please refer to the documentation
178// of Pairs for a description of kv.
179func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context {
180 if len(kv)%2 == 1 {
181 panic(fmt.Sprintf("metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d", len(kv)))
182 }
183 md, _ := ctx.Value(mdOutgoingKey{}).(rawMD)
184 added := make([][]string, len(md.added)+1)
185 copy(added, md.added)
186 kvCopy := make([]string, 0, len(kv))
187 for i := 0; i < len(kv); i += 2 {
188 kvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1])
189 }
190 added[len(added)-1] = kvCopy
191 return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: added})
192}
193
194// FromIncomingContext returns the incoming metadata in ctx if it exists.
195//
196// All keys in the returned MD are lowercase.
197func FromIncomingContext(ctx context.Context) (MD, bool) {
198 md, ok := ctx.Value(mdIncomingKey{}).(MD)
199 if !ok {
200 return nil, false
201 }
202 out := make(MD, len(md))
203 for k, v := range md {
204 // We need to manually convert all keys to lower case, because MD is a
205 // map, and there's no guarantee that the MD attached to the context is
206 // created using our helper functions.
207 key := strings.ToLower(k)
208 out[key] = copyOf(v)
209 }
210 return out, true
211}
212
213// ValueFromIncomingContext returns the metadata value corresponding to the metadata
214// key from the incoming metadata if it exists. Keys are matched in a case insensitive
215// manner.
216func ValueFromIncomingContext(ctx context.Context, key string) []string {
217 md, ok := ctx.Value(mdIncomingKey{}).(MD)
218 if !ok {
219 return nil
220 }
221
222 if v, ok := md[key]; ok {
223 return copyOf(v)
224 }
225 for k, v := range md {
226 // Case insensitive comparison: MD is a map, and there's no guarantee
227 // that the MD attached to the context is created using our helper
228 // functions.
229 if strings.EqualFold(k, key) {
230 return copyOf(v)
231 }
232 }
233 return nil
234}
235
236func copyOf(v []string) []string {
237 vals := make([]string, len(v))
238 copy(vals, v)
239 return vals
240}
241
242// fromOutgoingContextRaw returns the un-merged, intermediary contents of rawMD.
243//
244// Remember to perform strings.ToLower on the keys, for both the returned MD (MD
245// is a map, there's no guarantee it's created using our helper functions) and
246// the extra kv pairs (AppendToOutgoingContext doesn't turn them into
247// lowercase).
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
266 mdSize := len(raw.md)
267 for i := range raw.added {
268 mdSize += len(raw.added[i]) / 2
269 }
270
271 out := make(MD, mdSize)
272 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)
277 out[key] = copyOf(v)
278 }
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}