// Copyright 2016 Michal Witkowski. All Rights Reserved. // See LICENSE for licensing terms. package metautils import ( "context" "strings" "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.EqualFold(allowedKey, 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 }