Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
2023-02-11 01:04:43 +03:00
parent f4d0237785
commit 88c7439c01
4 changed files with 83 additions and 65 deletions

View File

@@ -124,7 +124,7 @@ func AppendOutgoingContext(ctx context.Context, kv ...string) context.Context {
return NewOutgoingContext(ctx, md)
}
for k, v := range md {
omd.Set(k, v)
omd[k] = v
}
return NewOutgoingContext(ctx, omd)
}
@@ -140,7 +140,7 @@ func AppendIncomingContext(ctx context.Context, kv ...string) context.Context {
return NewIncomingContext(ctx, md)
}
for k, v := range md {
omd.Set(k, v)
omd[k] = v
}
return NewIncomingContext(ctx, omd)
}

View File

@@ -24,7 +24,7 @@ var (
// 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
type Metadata map[string][]string
type rawMetadata struct {
md Metadata
@@ -42,7 +42,7 @@ type Iterator struct {
}
// Next advance iterator to next element
func (iter *Iterator) Next(k, v *string) bool {
func (iter *Iterator) Next(k *string, v *[]string) bool {
if iter.cur+1 > iter.cnt {
return false
}
@@ -64,8 +64,11 @@ func (md Metadata) Iterator() *Iterator {
return iter
}
// Get returns value from metadata by key
func (md Metadata) Get(key string) (string, bool) {
// Values returns values from metadata by key
func (md Metadata) Values(key string) ([]string, bool) {
if md == nil {
return nil, false
}
// fast path
val, ok := md[key]
if !ok {
@@ -75,16 +78,42 @@ func (md Metadata) Get(key string) (string, bool) {
return val, ok
}
// Get returns value from metadata by key
func (md Metadata) Get(key string) (string, bool) {
if md == nil {
return "", false
}
// fast path
val, ok := md[key]
if !ok {
// slow path
val, ok = md[textproto.CanonicalMIMEHeaderKey(key)]
}
if len(val) == 0 {
return "", false
}
return val[0], ok
}
// Set is used to store value in metadata
func (md Metadata) Set(kv ...string) {
if md == nil {
return
}
if len(kv)%2 == 1 {
kv = kv[:len(kv)-1]
}
for idx := 0; idx < len(kv); idx += 2 {
md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = kv[idx+1]
md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = []string{kv[idx+1]}
}
}
// Add is used to append value in metadata
func (md Metadata) Add(k string, v string) {
kn := textproto.CanonicalMIMEHeaderKey(k)
md[kn] = append(md[kn], v)
}
// Del is used to remove value from metadata
func (md Metadata) Del(keys ...string) {
for _, key := range keys {
@@ -99,7 +128,7 @@ func (md Metadata) Del(keys ...string) {
func Copy(md Metadata) Metadata {
nmd := New(len(md))
for key, val := range md {
nmd.Set(key, val)
nmd[key] = val
}
return nmd
}
@@ -114,16 +143,24 @@ func New(size int) Metadata {
// Merge merges metadata to existing metadata, overwriting if specified
func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
var ok bool
nmd := Copy(omd)
for key, val := range mmd {
_, ok = nmd[key]
for key, nval := range mmd {
oval, ok := nmd[key]
switch {
case ok && !overwrite:
continue
case val != "":
nmd.Set(key, val)
case ok && val == "":
case len(nval) == 1 && nval[0] != "":
nmd[key] = nval
case ok && len(nval) > 1:
sort.Strings(nval)
sort.Strings(oval)
for idx, v := range nval {
if oval[idx] != v {
oval[idx] = v
}
}
nmd[key] = oval
case ok && nval[0] == "":
nmd.Del(key)
}
}

View File

@@ -68,10 +68,10 @@ func TestPassing(t *testing.T) {
func TestMerge(t *testing.T) {
omd := Metadata{
"key1": "val1",
"key1": []string{"val1"},
}
mmd := Metadata{
"key2": "val2",
"key2": []string{"val2"},
}
nmd := Merge(omd, mmd, true)
@@ -82,13 +82,14 @@ func TestMerge(t *testing.T) {
func TestIterator(t *testing.T) {
md := Metadata{
"1Last": "last",
"2First": "first",
"3Second": "second",
"1Last": []string{"last"},
"2First": []string{"first"},
"3Second": []string{"second"},
}
iter := md.Iterator()
var k, v string
var k string
var v []string
for iter.Next(&k, &v) {
// fmt.Printf("k: %s, v: %s\n", k, v)
@@ -101,20 +102,20 @@ func TestMedataCanonicalKey(t *testing.T) {
v, ok := md.Get("x-request-id")
if !ok {
t.Fatalf("failed to get x-request-id")
} else if v != "12345" {
} else if len(v) != 1 && v != "12345" {
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
}
v, ok = md.Get("X-Request-Id")
if !ok {
t.Fatalf("failed to get x-request-id")
} else if v != "12345" {
} else if len(v) != 1 && v != "12345" {
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
}
v, ok = md.Get("X-Request-ID")
if !ok {
t.Fatalf("failed to get x-request-id")
} else if v != "12345" {
} else if len(v) != 1 && v != "12345" {
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
}
}
@@ -135,8 +136,8 @@ func TestMetadataSet(t *testing.T) {
func TestMetadataDelete(t *testing.T) {
md := Metadata{
"Foo": "bar",
"Baz": "empty",
"Foo": []string{"bar"},
"Baz": []string{"empty"},
}
md.Del("Baz")
@@ -157,14 +158,14 @@ func TestNilContext(t *testing.T) {
func TestMetadataCopy(t *testing.T) {
md := Metadata{
"Foo": "bar",
"Bar": "baz",
"Foo": []string{"bar"},
"Bar": []string{"baz"},
}
cp := Copy(md)
for k, v := range md {
if cv := cp[k]; cv != v {
if cv := cp[k]; len(cv) != len(v) || (cv[0] != v[0] && cv[1] != v[1]) {
t.Fatalf("Got %s:%s for %s:%s", k, cv, k, v)
}
}
@@ -172,7 +173,7 @@ func TestMetadataCopy(t *testing.T) {
func TestMetadataContext(t *testing.T) {
md := Metadata{
"Foo": "bar",
"Foo": []string{"bar"},
}
ctx := NewContext(context.TODO(), md)
@@ -182,7 +183,7 @@ func TestMetadataContext(t *testing.T) {
t.Errorf("Unexpected error retrieving metadata, got %t", ok)
}
if emd["Foo"] != md["Foo"] {
if len(emd["Foo"]) != len(md["Foo"]) || (emd["Foo"][0] != md["Foo"][0]) {
t.Errorf("Expected key: %s val: %s, got key: %s val: %s", "Foo", md["Foo"], "Foo", emd["Foo"])
}