metadata: split context to incoming and outgoing

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
2021-02-09 01:08:45 +03:00
parent 1de9911b73
commit 0e51a79bb6
9 changed files with 84 additions and 136 deletions

View File

@@ -5,35 +5,64 @@ import (
"context"
)
type mdIncomingKey struct{}
type mdOutgoingKey struct{}
type mdKey struct{}
// FromIncomingContext returns metadata from incoming ctx
// returned metadata shoud not be modified or race condition happens
func FromIncomingContext(ctx context.Context) (Metadata, bool) {
if ctx == nil {
return nil, false
}
md, ok := ctx.Value(mdIncomingKey{}).(Metadata)
return md, ok
}
// FromOutgoingContext returns metadata from outgoing ctx
// returned metadata shoud not be modified or race condition happens
func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
if ctx == nil {
return nil, false
}
md, ok := ctx.Value(mdOutgoingKey{}).(Metadata)
return md, ok
}
// FromContext returns metadata from the given context
// returned metadata shoud not be modified or race condition happens
//
// Deprecated: use FromIncomingContext or FromOutgoingContext
func FromContext(ctx context.Context) (Metadata, bool) {
if ctx == nil {
return nil, false
}
md, ok := ctx.Value(metadataKey{}).(Metadata)
if !ok {
return nil, ok
}
nmd := Copy(md)
return nmd, ok
md, ok := ctx.Value(mdKey{}).(Metadata)
return md, ok
}
// NewContext creates a new context with the given metadata
//
// Deprecated: use NewIncomingContext or NewOutgoingContext
func NewContext(ctx context.Context, md Metadata) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, metadataKey{}, Copy(md))
return context.WithValue(ctx, mdKey{}, md)
}
// MergeContext merges metadata to existing metadata, overwriting if specified
func MergeContext(ctx context.Context, pmd Metadata, overwrite bool) context.Context {
// NewIncomingContext creates a new context with incoming metadata attached
func NewIncomingContext(ctx context.Context, md Metadata) context.Context {
if ctx == nil {
ctx = context.Background()
}
md, ok := FromContext(ctx)
if !ok {
return context.WithValue(ctx, metadataKey{}, Copy(pmd))
}
return context.WithValue(ctx, metadataKey{}, Merge(md, pmd, overwrite))
return context.WithValue(ctx, mdIncomingKey{}, md)
}
// NewOutgoingContext creates a new context with outcoming metadata attached
func NewOutgoingContext(ctx context.Context, md Metadata) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, mdOutgoingKey{}, md)
}

View File

@@ -2,7 +2,6 @@
package metadata
import (
"context"
"net/textproto"
"sort"
)
@@ -12,16 +11,14 @@ var (
HeaderPrefix = "Micro-"
)
type metadataKey struct{}
// Metadata is our way of representing request headers internally.
// They're used at the RPC level and translate back and forth
// from Transport headers.
type Metadata map[string]string
var (
// DefaultMetadataSize used when need to init new Metadata
DefaultMetadataSize = 6
// defaultMetadataSize used when need to init new Metadata
defaultMetadataSize = 2
)
type Iterator struct {
@@ -72,12 +69,9 @@ func (md Metadata) Set(key, val string) {
// Del is used to remove value from metadata
func (md Metadata) Del(key string) {
// fast path
if _, ok := md[key]; ok {
delete(md, key)
} else {
// slow path
delete(md, textproto.CanonicalMIMEHeaderKey(key))
}
delete(md, key)
// slow path
delete(md, textproto.CanonicalMIMEHeaderKey(key))
}
// Copy makes a copy of the metadata
@@ -89,39 +83,10 @@ func Copy(md Metadata) Metadata {
return nmd
}
// Del deletes key from metadata
func Del(ctx context.Context, key string) context.Context {
md, ok := FromContext(ctx)
if !ok {
md = New(0)
}
md.Del(key)
return context.WithValue(ctx, metadataKey{}, md)
}
// Set add key with val to metadata
func Set(ctx context.Context, key, val string) context.Context {
md, ok := FromContext(ctx)
if !ok {
md = New(0)
}
md.Set(key, val)
return context.WithValue(ctx, metadataKey{}, md)
}
// Get returns a single value from metadata in the context
func Get(ctx context.Context, key string) (string, bool) {
md, ok := FromContext(ctx)
if !ok {
return "", ok
}
return md.Get(key)
}
// New return new sized metadata
func New(size int) Metadata {
if size == 0 {
size = DefaultMetadataSize
size = defaultMetadataSize
}
return make(Metadata, size)
}

View File

@@ -2,8 +2,6 @@ package metadata
import (
"context"
"fmt"
"reflect"
"testing"
)
@@ -32,26 +30,27 @@ func TestIterator(t *testing.T) {
var k, v string
for iter.Next(&k, &v) {
fmt.Printf("k: %s, v: %s\n", k, v)
//fmt.Printf("k: %s, v: %s\n", k, v)
}
}
func TestMedataCanonicalKey(t *testing.T) {
ctx := Set(context.TODO(), "x-request-id", "12345")
v, ok := Get(ctx, "x-request-id")
md := New(1)
md.Set("x-request-id", "12345")
v, ok := md.Get("x-request-id")
if !ok {
t.Fatalf("failed to get x-request-id")
} else if v != "12345" {
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
}
v, ok = Get(ctx, "X-Request-Id")
v, ok = md.Get("X-Request-Id")
if !ok {
t.Fatalf("failed to get x-request-id")
} else if v != "12345" {
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
}
v, ok = Get(ctx, "X-Request-ID")
v, ok = md.Get("X-Request-ID")
if !ok {
t.Fatalf("failed to get x-request-id")
} else if v != "12345" {
@@ -61,9 +60,11 @@ func TestMedataCanonicalKey(t *testing.T) {
}
func TestMetadataSet(t *testing.T) {
ctx := Set(context.TODO(), "Key", "val")
md := New(1)
val, ok := Get(ctx, "Key")
md.Set("Key", "val")
val, ok := md.Get("Key")
if !ok {
t.Fatal("key Key not found")
}
@@ -78,15 +79,8 @@ func TestMetadataDelete(t *testing.T) {
"Baz": "empty",
}
ctx := NewContext(context.TODO(), md)
ctx = Del(ctx, "Baz")
emd, ok := FromContext(ctx)
if !ok {
t.Fatal("key Key not found")
}
_, ok = emd["Baz"]
md.Del("Baz")
_, ok := md.Get("Baz")
if ok {
t.Fatal("key Baz not deleted")
}
@@ -137,42 +131,3 @@ func TestMetadataContext(t *testing.T) {
t.Errorf("Expected metadata length 1 got %d", i)
}
}
func TestMergeContext(t *testing.T) {
type args struct {
existing Metadata
append Metadata
overwrite bool
}
tests := []struct {
name string
args args
want Metadata
}{
{
name: "matching key, overwrite false",
args: args{
existing: Metadata{"Foo": "bar", "Sumo": "demo"},
append: Metadata{"Sumo": "demo2"},
overwrite: false,
},
want: Metadata{"Foo": "bar", "Sumo": "demo"},
},
{
name: "matching key, overwrite true",
args: args{
existing: Metadata{"Foo": "bar", "Sumo": "demo"},
append: Metadata{"Sumo": "demo2"},
overwrite: true,
},
want: Metadata{"Foo": "bar", "Sumo": "demo2"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got, _ := FromContext(MergeContext(NewContext(context.TODO(), tt.args.existing), tt.args.append, tt.args.overwrite)); !reflect.DeepEqual(got, tt.want) {
t.Errorf("MergeContext() = %v, want %v", got, tt.want)
}
})
}
}