1// Copyright 2017 Michal Witkowski. All Rights Reserved. 2// See LICENSE for licensing terms. 3 4package grpc_opentracing 5 6import ( 7 "encoding/base64" 8 "fmt" 9 "strings" 10 11 "google.golang.org/grpc/metadata" 12) 13 14const ( 15 binHdrSuffix = "-bin" 16) 17 18// metadataTextMap extends a metadata.MD to be an opentracing textmap 19type metadataTextMap metadata.MD 20 21// Set is a opentracing.TextMapReader interface that extracts values. 22func (m metadataTextMap) Set(key, val string) { 23 // gRPC allows for complex binary values to be written. 24 encodedKey, encodedVal := encodeKeyValue(key, val) 25 // The metadata object is a multimap, and previous values may exist, but for opentracing headers, we do not append 26 // we just override. 27 m[encodedKey] = []string{encodedVal} 28} 29 30// ForeachKey is a opentracing.TextMapReader interface that extracts values. 31func (m metadataTextMap) ForeachKey(callback func(key, val string) error) error { 32 for k, vv := range m { 33 for _, v := range vv { 34 if decodedKey, decodedVal, err := metadata.DecodeKeyValue(k, v); err == nil { 35 if err = callback(decodedKey, decodedVal); err != nil { 36 return err 37 } 38 } else { 39 return fmt.Errorf("failed decoding opentracing from gRPC metadata: %v", err) 40 } 41 } 42 } 43 return nil 44} 45 46// encodeKeyValue encodes key and value qualified for transmission via gRPC. 47// note: copy pasted from private values of grpc.metadata 48func encodeKeyValue(k, v string) (string, string) { 49 k = strings.ToLower(k) 50 if strings.HasSuffix(k, binHdrSuffix) { 51 val := base64.StdEncoding.EncodeToString([]byte(v)) 52 v = string(val) 53 } 54 return k, v 55} 56