Home | History | Annotate | Download | only in metadata
      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.
     22 package metadata
     23 
     24 import (
     25 	"fmt"
     26 	"strings"
     27 
     28 	"golang.org/x/net/context"
     29 )
     30 
     31 // DecodeKeyValue returns k, v, nil.
     32 //
     33 // Deprecated: use k and v directly instead.
     34 func DecodeKeyValue(k, v string) (string, string, error) {
     35 	return k, v, nil
     36 }
     37 
     38 // MD is a mapping from metadata keys to values. Users should use the following
     39 // two convenience functions New and Pairs to generate MD.
     40 type MD map[string][]string
     41 
     42 // New creates an MD from a given key-value map.
     43 //
     44 // Only the following ASCII characters are allowed in keys:
     45 //  - digits: 0-9
     46 //  - uppercase letters: A-Z (normalized to lower)
     47 //  - lowercase letters: a-z
     48 //  - special characters: -_.
     49 // 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.
     53 func New(m map[string]string) MD {
     54 	md := MD{}
     55 	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:
     66 //  - digits: 0-9
     67 //  - uppercase letters: A-Z (normalized to lower)
     68 //  - lowercase letters: a-z
     69 //  - special characters: -_.
     70 // Uppercase letters are automatically converted to lowercase.
     71 //
     72 // Keys beginning with "grpc-" are reserved for grpc-internal use only and may
     73 // result in errors if set in metadata.
     74 func Pairs(kv ...string) MD {
     75 	if len(kv)%2 == 1 {
     76 		panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
     77 	}
     78 	md := MD{}
     79 	var key string
     80 	for i, s := range kv {
     81 		if i%2 == 0 {
     82 			key = strings.ToLower(s)
     83 			continue
     84 		}
     85 		md[key] = append(md[key], s)
     86 	}
     87 	return md
     88 }
     89 
     90 // Len returns the number of items in md.
     91 func (md MD) Len() int {
     92 	return len(md)
     93 }
     94 
     95 // Copy returns a copy of md.
     96 func (md MD) Copy() MD {
     97 	return Join(md)
     98 }
     99 
    100 // Get obtains the values for a given key.
    101 func (md MD) Get(k string) []string {
    102 	k = strings.ToLower(k)
    103 	return md[k]
    104 }
    105 
    106 // Set sets the value of a given key with a slice of values.
    107 func (md MD) Set(k string, vals ...string) {
    108 	if len(vals) == 0 {
    109 		return
    110 	}
    111 	k = strings.ToLower(k)
    112 	md[k] = vals
    113 }
    114 
    115 // Append adds the values to key k, not overwriting what was already stored at that key.
    116 func (md MD) Append(k string, vals ...string) {
    117 	if len(vals) == 0 {
    118 		return
    119 	}
    120 	k = strings.ToLower(k)
    121 	md[k] = append(md[k], vals...)
    122 }
    123 
    124 // Join joins any number of mds into a single MD.
    125 // The order of values for each key is determined by the order in which
    126 // the mds containing those values are presented to Join.
    127 func Join(mds ...MD) MD {
    128 	out := MD{}
    129 	for _, md := range mds {
    130 		for k, v := range md {
    131 			out[k] = append(out[k], v...)
    132 		}
    133 	}
    134 	return out
    135 }
    136 
    137 type mdIncomingKey struct{}
    138 type mdOutgoingKey struct{}
    139 
    140 // NewIncomingContext creates a new context with incoming md attached.
    141 func NewIncomingContext(ctx context.Context, md MD) context.Context {
    142 	return context.WithValue(ctx, mdIncomingKey{}, md)
    143 }
    144 
    145 // NewOutgoingContext creates a new context with outgoing md attached. If used
    146 // in conjunction with AppendToOutgoingContext, NewOutgoingContext will
    147 // overwrite any previously-appended metadata.
    148 func NewOutgoingContext(ctx context.Context, md MD) context.Context {
    149 	return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md})
    150 }
    151 
    152 // AppendToOutgoingContext returns a new context with the provided kv merged
    153 // with any existing metadata in the context. Please refer to the
    154 // documentation of Pairs for a description of kv.
    155 func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context {
    156 	if len(kv)%2 == 1 {
    157 		panic(fmt.Sprintf("metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d", len(kv)))
    158 	}
    159 	md, _ := ctx.Value(mdOutgoingKey{}).(rawMD)
    160 	added := make([][]string, len(md.added)+1)
    161 	copy(added, md.added)
    162 	added[len(added)-1] = make([]string, len(kv))
    163 	copy(added[len(added)-1], kv)
    164 	return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: added})
    165 }
    166 
    167 // FromIncomingContext returns the incoming metadata in ctx if it exists.  The
    168 // returned MD should not be modified. Writing to it may cause races.
    169 // Modification should be made to copies of the returned MD.
    170 func FromIncomingContext(ctx context.Context) (md MD, ok bool) {
    171 	md, ok = ctx.Value(mdIncomingKey{}).(MD)
    172 	return
    173 }
    174 
    175 // FromOutgoingContextRaw returns the un-merged, intermediary contents
    176 // of rawMD. Remember to perform strings.ToLower on the keys. The returned
    177 // MD should not be modified. Writing to it may cause races. Modification
    178 // should be made to copies of the returned MD.
    179 //
    180 // This is intended for gRPC-internal use ONLY.
    181 func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) {
    182 	raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
    183 	if !ok {
    184 		return nil, nil, false
    185 	}
    186 
    187 	return raw.md, raw.added, true
    188 }
    189 
    190 // FromOutgoingContext returns the outgoing metadata in ctx if it exists.  The
    191 // returned MD should not be modified. Writing to it may cause races.
    192 // Modification should be made to copies of the returned MD.
    193 func FromOutgoingContext(ctx context.Context) (MD, bool) {
    194 	raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
    195 	if !ok {
    196 		return nil, false
    197 	}
    198 
    199 	mds := make([]MD, 0, len(raw.added)+1)
    200 	mds = append(mds, raw.md)
    201 	for _, vv := range raw.added {
    202 		mds = append(mds, Pairs(vv...))
    203 	}
    204 	return Join(mds...), ok
    205 }
    206 
    207 type rawMD struct {
    208 	md    MD
    209 	added [][]string
    210 }
    211