| // Copyright 2017 Michal Witkowski. All Rights Reserved. |
| // See LICENSE for licensing terms. |
| |
| package grpc_opentracing |
| |
| import ( |
| "encoding/base64" |
| "strings" |
| |
| "fmt" |
| |
| "google.golang.org/grpc/metadata" |
| ) |
| |
| const ( |
| binHdrSuffix = "-bin" |
| ) |
| |
| // metadataTextMap extends a metadata.MD to be an opentracing textmap |
| type metadataTextMap metadata.MD |
| |
| // Set is a opentracing.TextMapReader interface that extracts values. |
| func (m metadataTextMap) Set(key, val string) { |
| // gRPC allows for complex binary values to be written. |
| encodedKey, encodedVal := encodeKeyValue(key, val) |
| // The metadata object is a multimap, and previous values may exist, but for opentracing headers, we do not append |
| // we just override. |
| m[encodedKey] = []string{encodedVal} |
| } |
| |
| // ForeachKey is a opentracing.TextMapReader interface that extracts values. |
| func (m metadataTextMap) ForeachKey(callback func(key, val string) error) error { |
| for k, vv := range m { |
| for _, v := range vv { |
| if decodedKey, decodedVal, err := metadata.DecodeKeyValue(k, v); err == nil { |
| if err = callback(decodedKey, decodedVal); err != nil { |
| return err |
| } |
| } else { |
| return fmt.Errorf("failed decoding opentracing from gRPC metadata: %v", err) |
| } |
| } |
| } |
| return nil |
| } |
| |
| // encodeKeyValue encodes key and value qualified for transmission via gRPC. |
| // note: copy pasted from private values of grpc.metadata |
| func encodeKeyValue(k, v string) (string, string) { |
| k = strings.ToLower(k) |
| if strings.HasSuffix(k, binHdrSuffix) { |
| val := base64.StdEncoding.EncodeToString([]byte(v)) |
| v = string(val) |
| } |
| return k, v |
| } |