| // Copyright 2016 Michal Witkowski. All Rights Reserved. |
| // See LICENSE for licensing terms. |
| |
| package metautils |
| |
| import ( |
| "strings" |
| |
| "golang.org/x/net/context" |
| "google.golang.org/grpc/metadata" |
| ) |
| |
| // NiceMD is a convenience wrapper definiting extra functions on the metadata. |
| type NiceMD metadata.MD |
| |
| // ExtractIncoming extracts an inbound metadata from the server-side context. |
| // |
| // This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns |
| // a new empty NiceMD. |
| func ExtractIncoming(ctx context.Context) NiceMD { |
| md, ok := metadata.FromIncomingContext(ctx) |
| if !ok { |
| return NiceMD(metadata.Pairs()) |
| } |
| return NiceMD(md) |
| } |
| |
| // ExtractOutgoing extracts an outbound metadata from the client-side context. |
| // |
| // This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns |
| // a new empty NiceMD. |
| func ExtractOutgoing(ctx context.Context) NiceMD { |
| md, ok := metadata.FromOutgoingContext(ctx) |
| if !ok { |
| return NiceMD(metadata.Pairs()) |
| } |
| return NiceMD(md) |
| } |
| |
| // Clone performs a *deep* copy of the metadata.MD. |
| // |
| // You can specify the lower-case copiedKeys to only copy certain whitelisted keys. If no keys are explicitly whitelisted |
| // all keys get copied. |
| func (m NiceMD) Clone(copiedKeys ...string) NiceMD { |
| newMd := NiceMD(metadata.Pairs()) |
| for k, vv := range m { |
| found := false |
| if len(copiedKeys) == 0 { |
| found = true |
| } else { |
| for _, allowedKey := range copiedKeys { |
| if strings.ToLower(allowedKey) == strings.ToLower(k) { |
| found = true |
| break |
| } |
| } |
| } |
| if !found { |
| continue |
| } |
| newMd[k] = make([]string, len(vv)) |
| copy(newMd[k], vv) |
| } |
| return NiceMD(newMd) |
| } |
| |
| // ToOutgoing sets the given NiceMD as a client-side context for dispatching. |
| func (m NiceMD) ToOutgoing(ctx context.Context) context.Context { |
| return metadata.NewOutgoingContext(ctx, metadata.MD(m)) |
| } |
| |
| // ToIncoming sets the given NiceMD as a server-side context for dispatching. |
| // |
| // This is mostly useful in ServerInterceptors.. |
| func (m NiceMD) ToIncoming(ctx context.Context) context.Context { |
| return metadata.NewIncomingContext(ctx, metadata.MD(m)) |
| } |
| |
| // Get retrieves a single value from the metadata. |
| // |
| // It works analogously to http.Header.Get, returning the first value if there are many set. If the value is not set, |
| // an empty string is returned. |
| // |
| // The function is binary-key safe. |
| func (m NiceMD) Get(key string) string { |
| k, _ := encodeKeyValue(key, "") |
| vv, ok := m[k] |
| if !ok { |
| return "" |
| } |
| return vv[0] |
| } |
| |
| // Del retrieves a single value from the metadata. |
| // |
| // It works analogously to http.Header.Del, deleting all values if they exist. |
| // |
| // The function is binary-key safe. |
| |
| func (m NiceMD) Del(key string) NiceMD { |
| k, _ := encodeKeyValue(key, "") |
| delete(m, k) |
| return m |
| } |
| |
| // Set sets the given value in a metadata. |
| // |
| // It works analogously to http.Header.Set, overwriting all previous metadata values. |
| // |
| // The function is binary-key safe. |
| func (m NiceMD) Set(key string, value string) NiceMD { |
| k, v := encodeKeyValue(key, value) |
| m[k] = []string{v} |
| return m |
| } |
| |
| // Add retrieves a single value from the metadata. |
| // |
| // It works analogously to http.Header.Add, as it appends to any existing values associated with key. |
| // |
| // The function is binary-key safe. |
| func (m NiceMD) Add(key string, value string) NiceMD { |
| k, v := encodeKeyValue(key, value) |
| m[k] = append(m[k], v) |
| return m |
| } |