Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: metadata supports key corresponding to multiple values #2772

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,61 @@ import (
// 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

// New creates an MD from a given key-values map.
func New(mds ...map[string]string) Metadata {
func New(mds ...map[string][]string) Metadata {
md := Metadata{}
for _, m := range mds {
for k, v := range m {
md.Set(k, v)
for k, vList := range m {
for _, v := range vList {
md.Add(k, v)
}
}
}
return md
}

// Add adds the key, value pair to the header.
func (m Metadata) Add(key, value string) {
if len(key) == 0 {
return
}

m[strings.ToLower(key)] = append(m[strings.ToLower(key)], value)
}

// Get returns the value associated with the passed key.
func (m Metadata) Get(key string) string {
return m[strings.ToLower(key)]
v := m[strings.ToLower(key)]
if len(v) == 0 {
return ""
}
return v[0]
}

// Set stores the key-value pair.
func (m Metadata) Set(key string, value string) {
if key == "" || value == "" {
return
}
m[strings.ToLower(key)] = value
m[strings.ToLower(key)] = []string{value}
}

// Range iterate over element in metadata.
func (m Metadata) Range(f func(k, v string) bool) {
func (m Metadata) Range(f func(k string, v []string) bool) {
for k, v := range m {
if !f(k, v) {
break
}
}
}

// Values returns a slice of values associated with the passed key.
func (m Metadata) Values(key string) []string {
return m[strings.ToLower(key)]
}

// Clone returns a deep copy of Metadata
func (m Metadata) Clone() Metadata {
md := make(Metadata, len(m))
Expand Down
134 changes: 103 additions & 31 deletions metadata/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

func TestNew(t *testing.T) {
type args struct {
mds []map[string]string
mds []map[string][]string
}
tests := []struct {
name string
Expand All @@ -17,13 +17,13 @@ func TestNew(t *testing.T) {
}{
{
name: "hello",
args: args{[]map[string]string{{"hello": "kratos"}, {"hello2": "go-kratos"}}},
want: Metadata{"hello": "kratos", "hello2": "go-kratos"},
args: args{[]map[string][]string{{"hello": {"kratos"}}, {"hello2": {"go-kratos"}}}},
want: Metadata{"hello": {"kratos"}, "hello2": {"go-kratos"}},
},
{
name: "hi",
args: args{[]map[string]string{{"hi": "kratos"}, {"hi2": "go-kratos"}}},
want: Metadata{"hi": "kratos", "hi2": "go-kratos"},
args: args{[]map[string][]string{{"hi": {"kratos"}}, {"hi2": {"go-kratos"}}}},
want: Metadata{"hi": {"kratos"}, "hi2": {"go-kratos"}},
},
}
for _, tt := range tests {
Expand All @@ -47,13 +47,13 @@ func TestMetadata_Get(t *testing.T) {
}{
{
name: "kratos",
m: Metadata{"kratos": "value", "env": "dev"},
m: Metadata{"kratos": {"value"}, "env": {"dev"}},
args: args{key: "kratos"},
want: "value",
},
{
name: "env",
m: Metadata{"kratos": "value", "env": "dev"},
m: Metadata{"kratos": {"value"}, "env": {"dev"}},
args: args{key: "env"},
want: "dev",
},
Expand All @@ -67,6 +67,38 @@ func TestMetadata_Get(t *testing.T) {
}
}

func TestMetadata_Values(t *testing.T) {
type args struct {
key string
}
tests := []struct {
name string
m Metadata
args args
want []string
}{
{
name: "kratos",
m: Metadata{"kratos": {"value", "value2"}, "env": {"dev"}},
args: args{key: "kratos"},
want: []string{"value", "value2"},
},
{
name: "env",
m: Metadata{"kratos": {"value", "value2"}, "env": {"dev"}},
args: args{key: "env"},
want: []string{"dev"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.m.Values(tt.args.key); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Get() = %v, want %v", got, tt.want)
}
})
}
}

func TestMetadata_Set(t *testing.T) {
type args struct {
key string
Expand All @@ -82,13 +114,13 @@ func TestMetadata_Set(t *testing.T) {
name: "kratos",
m: Metadata{},
args: args{key: "hello", value: "kratos"},
want: Metadata{"hello": "kratos"},
want: Metadata{"hello": {"kratos"}},
},
{
name: "env",
m: Metadata{"hello": "kratos"},
m: Metadata{"hello": {"kratos"}},
args: args{key: "env", value: "pro"},
want: Metadata{"hello": "kratos", "env": "pro"},
want: Metadata{"hello": {"kratos"}, "env": {"pro"}},
},
{
name: "empty",
Expand All @@ -107,6 +139,46 @@ func TestMetadata_Set(t *testing.T) {
}
}

func TestMetadata_Add(t *testing.T) {
type args struct {
key string
value string
}
tests := []struct {
name string
m Metadata
args args
want Metadata
}{
{
name: "kratos",
m: Metadata{},
args: args{key: "hello", value: "kratos"},
want: Metadata{"hello": {"kratos"}},
},
{
name: "env",
m: Metadata{"hello": {"kratos"}},
args: args{key: "hello", value: "again"},
want: Metadata{"hello": {"kratos", "again"}},
},
{
name: "empty",
m: Metadata{},
args: args{key: "", value: ""},
want: Metadata{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.m.Add(tt.args.key, tt.args.value)
if !reflect.DeepEqual(tt.m, tt.want) {
t.Errorf("Set() = %v, want %v", tt.m, tt.want)
}
})
}
}

func TestClientContext(t *testing.T) {
type args struct {
ctx context.Context
Expand All @@ -118,11 +190,11 @@ func TestClientContext(t *testing.T) {
}{
{
name: "kratos",
args: args{context.Background(), Metadata{"hello": "kratos", "kratos": "https://go-kratos.dev"}},
args: args{context.Background(), Metadata{"hello": {"kratos"}, "kratos": {"https://go-kratos.dev"}}},
},
{
name: "hello",
args: args{context.Background(), Metadata{"hello": "kratos", "hello2": "https://go-kratos.dev"}},
args: args{context.Background(), Metadata{"hello": {"kratos"}, "hello2": {"https://go-kratos.dev"}}},
},
}
for _, tt := range tests {
Expand Down Expand Up @@ -151,11 +223,11 @@ func TestServerContext(t *testing.T) {
}{
{
name: "kratos",
args: args{context.Background(), Metadata{"hello": "kratos", "kratos": "https://go-kratos.dev"}},
args: args{context.Background(), Metadata{"hello": {"kratos"}, "kratos": {"https://go-kratos.dev"}}},
},
{
name: "hello",
args: args{context.Background(), Metadata{"hello": "kratos", "hello2": "https://go-kratos.dev"}},
args: args{context.Background(), Metadata{"hello": {"kratos"}, "hello2": {"https://go-kratos.dev"}}},
},
}
for _, tt := range tests {
Expand Down Expand Up @@ -186,12 +258,12 @@ func TestAppendToClientContext(t *testing.T) {
{
name: "kratos",
args: args{Metadata{}, []string{"hello", "kratos", "env", "dev"}},
want: Metadata{"hello": "kratos", "env": "dev"},
want: Metadata{"hello": {"kratos"}, "env": {"dev"}},
},
{
name: "hello",
args: args{Metadata{"hi": "https://go-kratos.dev/"}, []string{"hello", "kratos", "env", "dev"}},
want: Metadata{"hello": "kratos", "env": "dev", "hi": "https://go-kratos.dev/"},
args: args{Metadata{"hi": {"https://go-kratos.dev/"}}, []string{"hello", "kratos", "env", "dev"}},
want: Metadata{"hello": {"kratos"}, "env": {"dev"}, "hi": {"https://go-kratos.dev/"}},
},
}
for _, tt := range tests {
Expand Down Expand Up @@ -240,13 +312,13 @@ func TestMergeToClientContext(t *testing.T) {
}{
{
name: "kratos",
args: args{Metadata{}, Metadata{"hello": "kratos", "env": "dev"}},
want: Metadata{"hello": "kratos", "env": "dev"},
args: args{Metadata{}, Metadata{"hello": {"kratos"}, "env": {"dev"}}},
want: Metadata{"hello": {"kratos"}, "env": {"dev"}},
},
{
name: "hello",
args: args{Metadata{"hi": "https://go-kratos.dev/"}, Metadata{"hello": "kratos", "env": "dev"}},
want: Metadata{"hello": "kratos", "env": "dev", "hi": "https://go-kratos.dev/"},
args: args{Metadata{"hi": {"https://go-kratos.dev/"}}, Metadata{"hello": {"kratos"}, "env": {"dev"}}},
want: Metadata{"hello": {"kratos"}, "env": {"dev"}, "hi": {"https://go-kratos.dev/"}},
},
}
for _, tt := range tests {
Expand All @@ -265,19 +337,19 @@ func TestMergeToClientContext(t *testing.T) {
}

func TestMetadata_Range(t *testing.T) {
md := Metadata{"kratos": "kratos", "https://go-kratos.dev/": "https://go-kratos.dev/", "go-kratos": "go-kratos"}
md := Metadata{"kratos": {"kratos"}, "https://go-kratos.dev/": {"https://go-kratos.dev/"}, "go-kratos": {"go-kratos"}}
tmp := Metadata{}
md.Range(func(k, v string) bool {
md.Range(func(k string, v []string) bool {
if k == "https://go-kratos.dev/" || k == "kratos" {
tmp[k] = v
}
return true
})
if !reflect.DeepEqual(tmp, Metadata{"https://go-kratos.dev/": "https://go-kratos.dev/", "kratos": "kratos"}) {
t.Errorf("metadata = %v, want %v", tmp, Metadata{"https://go-kratos.dev/": "https://go-kratos.dev/", "kratos": "kratos"})
if !reflect.DeepEqual(tmp, Metadata{"https://go-kratos.dev/": {"https://go-kratos.dev/"}, "kratos": {"kratos"}}) {
t.Errorf("metadata = %v, want %v", tmp, Metadata{"https://go-kratos.dev/": {"https://go-kratos.dev/"}, "kratos": {"kratos"}})
}
tmp = Metadata{}
md.Range(func(k, v string) bool {
md.Range(func(k string, v []string) bool {
return false
})
if !reflect.DeepEqual(tmp, Metadata{}) {
Expand All @@ -293,13 +365,13 @@ func TestMetadata_Clone(t *testing.T) {
}{
{
name: "kratos",
m: Metadata{"kratos": "kratos", "https://go-kratos.dev/": "https://go-kratos.dev/", "go-kratos": "go-kratos"},
want: Metadata{"kratos": "kratos", "https://go-kratos.dev/": "https://go-kratos.dev/", "go-kratos": "go-kratos"},
m: Metadata{"kratos": {"kratos"}, "https://go-kratos.dev/": {"https://go-kratos.dev/"}, "go-kratos": {"go-kratos"}},
want: Metadata{"kratos": {"kratos"}, "https://go-kratos.dev/": {"https://go-kratos.dev/"}, "go-kratos": {"go-kratos"}},
},
{
name: "go",
m: Metadata{"language": "golang"},
want: Metadata{"language": "golang"},
m: Metadata{"language": {"golang"}},
want: Metadata{"language": {"golang"}},
},
}
for _, tt := range tests {
Expand All @@ -308,7 +380,7 @@ func TestMetadata_Clone(t *testing.T) {
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Clone() = %v, want %v", got, tt.want)
}
got["kratos"] = "go"
got["kratos"] = []string{"go"}
if reflect.DeepEqual(got, tt.want) {
t.Errorf("want got != want got %v want %v", got, tt.want)
}
Expand Down
7 changes: 7 additions & 0 deletions middleware/auth/jwt/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ func (hc headerCarrier) Get(key string) string { return http.Header(hc).Get(key)

func (hc headerCarrier) Set(key string, value string) { http.Header(hc).Set(key, value) }

func (hc headerCarrier) Add(key string, value string) { http.Header(hc).Add(key, value) }

// Keys lists the keys stored in this carrier.
func (hc headerCarrier) Keys() []string {
keys := make([]string, 0, len(hc))
Expand All @@ -33,6 +35,11 @@ func (hc headerCarrier) Keys() []string {
return keys
}

// Values returns a slice value associated with the passed key.
func (hc headerCarrier) Values(key string) []string {
return http.Header(hc).Values(key)
}

func newTokenHeader(headerKey string, token string) *headerCarrier {
header := &headerCarrier{}
header.Set(headerKey, token)
Expand Down
22 changes: 15 additions & 7 deletions middleware/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ func Server(opts ...Option) middleware.Middleware {
header := tr.RequestHeader()
for _, k := range header.Keys() {
if options.hasPrefix(k) {
md.Set(k, header.Get(k))
for _, v := range header.Values(k) {
md.Add(k, v)
}
}
}
ctx = metadata.NewServerContext(ctx, md)
Expand All @@ -86,19 +88,25 @@ func Client(opts ...Option) middleware.Middleware {

header := tr.RequestHeader()
// x-md-local-
for k, v := range options.md {
header.Set(k, v)
for k, vList := range options.md {
for _, v := range vList {
header.Add(k, v)
}
}
if md, ok := metadata.FromClientContext(ctx); ok {
for k, v := range md {
header.Set(k, v)
for k, vList := range md {
for _, v := range vList {
header.Add(k, v)
}
}
}
// x-md-global-
if md, ok := metadata.FromServerContext(ctx); ok {
for k, v := range md {
for k, vList := range md {
if options.hasPrefix(k) {
header.Set(k, v)
for _, v := range vList {
header.Add(k, v)
}
}
}
}
Expand Down
Loading