metadata as KeyValue type

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby
2019-06-13 18:05:06 +00:00
parent d134fe75a4
commit 694de9d955
7 changed files with 71 additions and 39 deletions

View File

@@ -112,7 +112,7 @@ func (c *Client) Call(ctx context.Context, service, method string, req, resp int
) )
if metadata, ok := GetMetadata(ctx); ok { if metadata, ok := GetMetadata(ctx); ok {
creq.Metadata = metadata metadata.setRequest(creq)
} }
if dl, ok := ctx.Deadline(); ok { if dl, ok := ctx.Deadline(); ok {

View File

@@ -109,7 +109,7 @@ func client() error {
} }
ctx := context.Background() ctx := context.Background()
md := ttrpc.Metadata{} md := ttrpc.MD{}
md.Set("name", "koye") md.Set("name", "koye")
ctx = ttrpc.WithMetadata(ctx, md) ctx = ttrpc.WithMetadata(ctx, md)

View File

@@ -16,53 +16,74 @@
package ttrpc package ttrpc
import "context" import (
"context"
"strings"
)
// Metadata represents the key-value pairs (similar to http.Header) to be passed to ttrpc server from a client. // MD is the user type for ttrpc metadata
type Metadata map[string]StringList type MD map[string][]string
// Get returns the metadata for a given key when they exist. // Get returns the metadata for a given key when they exist.
// If there is no metadata, a nil slice and false are returned. // If there is no metadata, a nil slice and false are returned.
func (m Metadata) Get(key string) ([]string, bool) { func (m MD) Get(key string) ([]string, bool) {
key = strings.ToLower(key)
list, ok := m[key] list, ok := m[key]
if !ok || len(list.List) == 0 { if !ok || len(list) == 0 {
return nil, false return nil, false
} }
return list.List, true return list, true
} }
// Set sets the provided values for a given key. // Set sets the provided values for a given key.
// The values will overwrite any existing values. // The values will overwrite any existing values.
// If no values provided, a key will be deleted. // If no values provided, a key will be deleted.
func (m Metadata) Set(key string, values ...string) { func (m MD) Set(key string, values ...string) {
key = strings.ToLower(key)
if len(values) == 0 { if len(values) == 0 {
delete(m, key) delete(m, key)
return return
} }
m[key] = values
m[key] = StringList{List: values}
} }
// Append appends additional values to the given key. // Append appends additional values to the given key.
func (m Metadata) Append(key string, values ...string) { func (m MD) Append(key string, values ...string) {
key = strings.ToLower(key)
if len(values) == 0 { if len(values) == 0 {
return return
} }
current, ok := m[key]
list, ok := m[key]
if ok { if ok {
m.Set(key, append(list.List, values...)...) m.Set(key, append(current, values...)...)
} else { } else {
m.Set(key, values...) m.Set(key, values...)
} }
} }
func (m MD) setRequest(r *Request) {
for k, values := range m {
for _, v := range values {
r.Metadata = append(r.Metadata, &KeyValue{
Key: k,
Value: v,
})
}
}
}
func (m MD) fromRequest(r *Request) {
for _, kv := range r.Metadata {
m[kv.Key] = append(m[kv.Key], kv.Value)
}
}
type metadataKey struct{} type metadataKey struct{}
// GetMetadata retrieves metadata from context.Context (previously attached with WithMetadata) // GetMetadata retrieves metadata from context.Context (previously attached with WithMetadata)
func GetMetadata(ctx context.Context) (Metadata, bool) { func GetMetadata(ctx context.Context) (MD, bool) {
metadata, ok := ctx.Value(metadataKey{}).(Metadata) metadata, ok := ctx.Value(metadataKey{}).(MD)
return metadata, ok return metadata, ok
} }
@@ -81,6 +102,6 @@ func GetMetadataValue(ctx context.Context, name string) (string, bool) {
} }
// WithMetadata attaches metadata map to a context.Context // WithMetadata attaches metadata map to a context.Context
func WithMetadata(ctx context.Context, headers Metadata) context.Context { func WithMetadata(ctx context.Context, md MD) context.Context {
return context.WithValue(ctx, metadataKey{}, headers) return context.WithValue(ctx, metadataKey{}, md)
} }

View File

@@ -21,8 +21,8 @@ import (
"testing" "testing"
) )
func TestMetadata_Get(t *testing.T) { func TestMetadataGet(t *testing.T) {
metadata := make(Metadata) metadata := make(MD)
metadata.Set("foo", "1", "2") metadata.Set("foo", "1", "2")
if list, ok := metadata.Get("foo"); !ok { if list, ok := metadata.Get("foo"); !ok {
@@ -36,8 +36,8 @@ func TestMetadata_Get(t *testing.T) {
} }
} }
func TestMetadata_GetInvalidKey(t *testing.T) { func TestMetadataGetInvalidKey(t *testing.T) {
metadata := make(Metadata) metadata := make(MD)
metadata.Set("foo", "1", "2") metadata.Set("foo", "1", "2")
if _, ok := metadata.Get("invalid"); ok { if _, ok := metadata.Get("invalid"); ok {
@@ -45,8 +45,8 @@ func TestMetadata_GetInvalidKey(t *testing.T) {
} }
} }
func TestMetadata_Unset(t *testing.T) { func TestMetadataUnset(t *testing.T) {
metadata := make(Metadata) metadata := make(MD)
metadata.Set("foo", "1", "2") metadata.Set("foo", "1", "2")
metadata.Set("foo") metadata.Set("foo")
@@ -55,8 +55,8 @@ func TestMetadata_Unset(t *testing.T) {
} }
} }
func TestMetadata_Replace(t *testing.T) { func TestMetadataReplace(t *testing.T) {
metadata := make(Metadata) metadata := make(MD)
metadata.Set("foo", "1", "2") metadata.Set("foo", "1", "2")
metadata.Set("foo", "3", "4") metadata.Set("foo", "3", "4")
@@ -71,8 +71,8 @@ func TestMetadata_Replace(t *testing.T) {
} }
} }
func TestMetadata_Append(t *testing.T) { func TestMetadataAppend(t *testing.T) {
metadata := make(Metadata) metadata := make(MD)
metadata.Set("foo", "1") metadata.Set("foo", "1")
metadata.Append("foo", "2") metadata.Append("foo", "2")
metadata.Append("bar", "3") metadata.Append("bar", "3")
@@ -94,8 +94,8 @@ func TestMetadata_Append(t *testing.T) {
} }
} }
func TestMetadata_Context(t *testing.T) { func TestMetadataContext(t *testing.T) {
metadata := make(Metadata) metadata := make(MD)
metadata.Set("foo", "bar") metadata.Set("foo", "bar")
ctx := WithMetadata(context.Background(), metadata) ctx := WithMetadata(context.Background(), metadata)

View File

@@ -469,8 +469,10 @@ func (c *serverConn) run(sctx context.Context) {
var noopFunc = func() {} var noopFunc = func() {}
func getRequestContext(ctx context.Context, req *Request) (retCtx context.Context, cancel func()) { func getRequestContext(ctx context.Context, req *Request) (retCtx context.Context, cancel func()) {
if req.Metadata != nil { if len(req.Metadata) > 0 {
ctx = WithMetadata(ctx, req.Metadata) md := MD{}
md.fromRequest(req)
ctx = WithMetadata(ctx, md)
} }
cancel = noopFunc cancel = noopFunc

View File

@@ -546,7 +546,7 @@ func roundTrip(ctx context.Context, t *testing.T, client *testingClient, value s
} }
) )
ctx = WithMetadata(ctx, Metadata{"foo": makeStringList("bar")}) ctx = WithMetadata(ctx, MD{"foo": []string{"bar"}})
resp, err := client.Test(ctx, tp) resp, err := client.Test(ctx, tp)
if err != nil { if err != nil {

View File

@@ -23,11 +23,11 @@ import (
) )
type Request struct { type Request struct {
Service string `protobuf:"bytes,1,opt,name=service,proto3"` Service string `protobuf:"bytes,1,opt,name=service,proto3"`
Method string `protobuf:"bytes,2,opt,name=method,proto3"` Method string `protobuf:"bytes,2,opt,name=method,proto3"`
Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3"` Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3"`
TimeoutNano int64 `protobuf:"varint,4,opt,name=timeout_nano,proto3"` TimeoutNano int64 `protobuf:"varint,4,opt,name=timeout_nano,proto3"`
Metadata Metadata `protobuf:"bytes,5,opt,name=metadata,proto3" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Metadata []*KeyValue `protobuf:"bytes,5,rep,name=metadata,proto3"`
} }
func (r *Request) Reset() { *r = Request{} } func (r *Request) Reset() { *r = Request{} }
@@ -52,3 +52,12 @@ func (r *StringList) String() string { return fmt.Sprintf("%+#v", r) }
func (r *StringList) ProtoMessage() {} func (r *StringList) ProtoMessage() {}
func makeStringList(item ...string) StringList { return StringList{List: item} } func makeStringList(item ...string) StringList { return StringList{List: item} }
type KeyValue struct {
Key string `protobuf:"bytes,1,opt,name=key,proto3"`
Value string `protobuf:"bytes,2,opt,name=value,proto3"`
}
func (m *KeyValue) Reset() { *m = KeyValue{} }
func (*KeyValue) ProtoMessage() {}
func (m *KeyValue) String() string { return fmt.Sprintf("%+#v", m) }