diff --git a/metadata/context.go b/metadata/context.go index 475e1b49..471b144a 100644 --- a/metadata/context.go +++ b/metadata/context.go @@ -1,3 +1,5 @@ +//go:build !exclude + // Package metadata is a way of defining message headers package metadata diff --git a/metadata/metadata.go b/metadata/metadata.go index 347c4ec5..f2729e73 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -1,3 +1,5 @@ +//go:build !exclude + // Package metadata is a way of defining message headers package metadata diff --git a/metadata/metadata_grpc.go b/metadata/metadata_grpc.go index 4c5102d3..f5f2bbf1 100644 --- a/metadata/metadata_grpc.go +++ b/metadata/metadata_grpc.go @@ -1,7 +1,7 @@ //go:build exclude -// TODO need compare with micro metadata -package metadata +// Package metadata TODO need compare with micro metadata +package metadata // import "google.golang.org/grpc/metadata" import ( "context" @@ -9,16 +9,11 @@ import ( "strings" ) -// DecodeKeyValue returns k, v, nil. -// -// Deprecated: use k and v directly instead. -func DecodeKeyValue(k, v string) (string, string, error) { - return k, v, nil -} - // MD is a mapping from metadata keys to values. Users should use the following // two convenience functions New and Pairs to generate MD. -type MD map[string]string +type MD map[string][]string + +type Metadata map[string]string // New creates an MD from a given key-value map. // @@ -36,7 +31,7 @@ func New(m map[string]string) MD { md := make(MD, len(m)) for k, val := range m { key := strings.ToLower(k) - md[key] = val + md[key] = append(md[key], val) } return md } @@ -61,7 +56,7 @@ func Pairs(kv ...string) MD { md := make(MD, len(kv)/2) for i := 0; i < len(kv); i += 2 { key := strings.ToLower(kv[i]) - md[key] = kv[i+1] + md[key] = append(md[key], kv[i+1]) } return md } @@ -75,7 +70,7 @@ func (md MD) Len() int { func (md MD) Copy() MD { out := make(MD, len(md)) for k, v := range md { - out[k] = v + out[k] = copyOf(v) } return out } @@ -83,7 +78,7 @@ func (md MD) Copy() MD { // Get obtains the values for a given key. // // k is converted to lowercase before searching in md. -func (md MD) Get(k string) string { +func (md MD) Get(k string) []string { k = strings.ToLower(k) return md[k] } @@ -91,7 +86,7 @@ func (md MD) Get(k string) string { // Set sets the value of a given key with a slice of values. // // k is converted to lowercase before storing in md. -func (md MD) Set(k string, vals string) { +func (md MD) Set(k string, vals ...string) { if len(vals) == 0 { return } @@ -103,12 +98,12 @@ func (md MD) Set(k string, vals string) { // that key. // // k is converted to lowercase before storing in md. -func (md MD) Append(k string, vals string) { +func (md MD) Append(k string, vals ...string) { if len(vals) == 0 { return } k = strings.ToLower(k) - md[k] = vals + md[k] = append(md[k], vals...) } // Delete removes the values for a given key k which is converted to lowercase @@ -126,7 +121,7 @@ func Join(mds ...MD) MD { out := MD{} for _, md := range mds { for k, v := range md { - out[k] = v + out[k] = append(out[k], v...) } } return out @@ -137,16 +132,26 @@ type mdOutgoingKey struct{} // NewIncomingContext creates a new context with incoming md attached. md must // not be modified after calling this function. -func NewIncomingContext(ctx context.Context, md MD) context.Context { - return context.WithValue(ctx, mdIncomingKey{}, md) +func NewIncomingContext(ctx context.Context, md Metadata) context.Context { + in := make(MD, len(md)) + for k, v := range md { + in[k] = []string{v} + } + + return context.WithValue(ctx, mdIncomingKey{}, in) } // NewOutgoingContext creates a new context with outgoing md attached. If used // in conjunction with AppendToOutgoingContext, NewOutgoingContext will // overwrite any previously-appended metadata. md must not be modified after // calling this function. -func NewOutgoingContext(ctx context.Context, md MD) context.Context { - return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md}) +func NewOutgoingContext(ctx context.Context, md Metadata) context.Context { + out := make(MD, len(md)) + for k, v := range md { + out[k] = []string{v} + } + + return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: out}) } // AppendToOutgoingContext returns a new context with the provided kv merged @@ -157,70 +162,121 @@ func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context panic(fmt.Sprintf("metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d", len(kv))) } md, _ := ctx.Value(mdOutgoingKey{}).(rawMD) - + added := make([][]string, len(md.added)+1) + copy(added, md.added) kvCopy := make([]string, 0, len(kv)) for i := 0; i < len(kv); i += 2 { kvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1]) } - - return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md}) + added[len(added)-1] = kvCopy + return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: added}) } // FromIncomingContext returns the incoming metadata in ctx if it exists. // // All keys in the returned MD are lowercase. -func FromIncomingContext(ctx context.Context) (MD, bool) { +func FromIncomingContext(ctx context.Context) (Metadata, bool) { md, ok := ctx.Value(mdIncomingKey{}).(MD) if !ok { return nil, false } - out := make(MD, len(md)) + out := make(Metadata, len(md)) for k, v := range md { // We need to manually convert all keys to lower case, because MD is a // map, and there's no guarantee that the MD attached to the context is // created using our helper functions. - key := strings.ToLower(k) - out[key] = v + + if len(v) > 0 { + key := strings.ToLower(k) + out[key] = v[0] + } + } return out, true } +// ValueFromIncomingContext returns the metadata value corresponding to the metadata +// key from the incoming metadata if it exists. Keys are matched in a case insensitive +// manner. +func ValueFromIncomingContext(ctx context.Context, key string) []string { + md, ok := ctx.Value(mdIncomingKey{}).(MD) + if !ok { + return nil + } + + if v, ok := md[key]; ok { + return copyOf(v) + } + for k, v := range md { + // Case insensitive comparison: MD is a map, and there's no guarantee + // that the MD attached to the context is created using our helper + // functions. + if strings.EqualFold(k, key) { + return copyOf(v) + } + } + return nil +} + +func copyOf(v []string) []string { + vals := make([]string, len(v)) + copy(vals, v) + return vals +} + // fromOutgoingContextRaw returns the un-merged, intermediary contents of rawMD. // // Remember to perform strings.ToLower on the keys, for both the returned MD (MD // is a map, there's no guarantee it's created using our helper functions) and // the extra kv pairs (AppendToOutgoingContext doesn't turn them into // lowercase). -func fromOutgoingContextRaw(ctx context.Context) (MD, bool) { +func fromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) { raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD) if !ok { - return nil, false + return nil, nil, false } - return raw.md, true + return raw.md, raw.added, true } // FromOutgoingContext returns the outgoing metadata in ctx if it exists. // // All keys in the returned MD are lowercase. -func FromOutgoingContext(ctx context.Context) (MD, bool) { +func FromOutgoingContext(ctx context.Context) (Metadata, bool) { raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD) if !ok { return nil, false } - out := make(MD, len(raw.md)) + mdSize := len(raw.md) + for i := range raw.added { + mdSize += len(raw.added[i]) / 2 + } + + out := make(Metadata, mdSize) for k, v := range raw.md { // We need to manually convert all keys to lower case, because MD is a // map, and there's no guarantee that the MD attached to the context is // created using our helper functions. - key := strings.ToLower(k) - out[key] = v + if len(v) > 0 { + key := strings.ToLower(k) + out[key] = v[0] + } } + for _, added := range raw.added { + if len(added)%2 == 1 { + panic(fmt.Sprintf("metadata: FromOutgoingContext got an odd number of input pairs for metadata: %d", len(added))) + } + for i := 0; i < len(added); i += 2 { + key := strings.ToLower(added[i]) + out[key] = added[i+1] + } + } return out, ok } type rawMD struct { - md MD + md MD + added [][]string }