metadata.go raw

   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 // import "google.golang.org/grpc/metadata"
  23  
  24  import (
  25  	"context"
  26  	"fmt"
  27  	"strings"
  28  
  29  	"google.golang.org/grpc/internal"
  30  )
  31  
  32  func init() {
  33  	internal.FromOutgoingContextRaw = fromOutgoingContextRaw
  34  }
  35  
  36  // DecodeKeyValue returns k, v, nil.
  37  //
  38  // Deprecated: use k and v directly instead.
  39  func 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.
  45  type 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.
  59  func 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.
  81  func 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.
  94  func (md MD) Len() int {
  95  	return len(md)
  96  }
  97  
  98  // Copy returns a copy of md.
  99  func (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.
 110  func (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.
 118  func (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.
 130  func (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.
 140  func (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.
 149  func 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  
 159  type mdIncomingKey struct{}
 160  type mdOutgoingKey struct{}
 161  
 162  // NewIncomingContext creates a new context with incoming md attached. md must
 163  // not be modified after calling this function.
 164  func 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.
 172  func 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.
 179  func 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.
 197  func 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.
 216  func 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  
 236  func 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).
 248  func 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.
 260  func 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  
 292  type rawMD struct {
 293  	md    MD
 294  	added [][]string
 295  }
 296