metadata: Fix bug where AppendToOutgoingContext could modify another context's metadata (#1930)

This commit is contained in:
dfawley
2018-03-19 16:23:41 -07:00
committed by GitHub
parent 738eb6b62f
commit b96718f8f0
2 changed files with 51 additions and 5 deletions

View File

@ -131,7 +131,11 @@ 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))) panic(fmt.Sprintf("metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d", len(kv)))
} }
md, _ := ctx.Value(mdOutgoingKey{}).(rawMD) md, _ := ctx.Value(mdOutgoingKey{}).(rawMD)
return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: append(md.added, kv)}) added := make([][]string, len(md.added)+1)
copy(added, md.added)
added[len(added)-1] = make([]string, len(kv))
copy(added[len(added)-1], kv)
return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: added})
} }
// FromIncomingContext returns the incoming metadata in ctx if it exists. The // FromIncomingContext returns the incoming metadata in ctx if it exists. The
@ -159,7 +163,7 @@ func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) {
// FromOutgoingContext returns the outgoing metadata in ctx if it exists. The // FromOutgoingContext returns the outgoing metadata in ctx if it exists. The
// returned MD should not be modified. Writing to it may cause races. // returned MD should not be modified. Writing to it may cause races.
// Modification should be made to the copies of the returned MD. // Modification should be made to copies of the returned MD.
func FromOutgoingContext(ctx context.Context) (MD, bool) { func FromOutgoingContext(ctx context.Context) (MD, bool) {
raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD) raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
if !ok { if !ok {

View File

@ -20,6 +20,7 @@ package metadata
import ( import (
"reflect" "reflect"
"strconv"
"testing" "testing"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -98,19 +99,60 @@ func TestAppendToOutgoingContext(t *testing.T) {
} }
} }
func TestAppendToOutgoingContext_Repeated(t *testing.T) {
ctx := context.Background()
for i := 0; i < 100; i = i + 2 {
ctx1 := AppendToOutgoingContext(ctx, "k", strconv.Itoa(i))
ctx2 := AppendToOutgoingContext(ctx, "k", strconv.Itoa(i+1))
md1, _ := FromOutgoingContext(ctx1)
md2, _ := FromOutgoingContext(ctx2)
if reflect.DeepEqual(md1, md2) {
t.Fatalf("md1, md2 = %v, %v; should not be equal", md1, md2)
}
ctx = ctx1
}
}
func TestAppendToOutgoingContext_FromKVSlice(t *testing.T) {
const k, v = "a", "b"
kv := []string{k, v}
ctx := AppendToOutgoingContext(context.Background(), kv...)
md, _ := FromOutgoingContext(ctx)
if md[k][0] != v {
t.Fatalf("md[%q] = %q; want %q", k, md[k], v)
}
kv[1] = "xxx"
md, _ = FromOutgoingContext(ctx)
if md[k][0] != v {
t.Fatalf("md[%q] = %q; want %q", k, md[k], v)
}
}
// Old/slow approach to adding metadata to context // Old/slow approach to adding metadata to context
func Benchmark_AddingMetadata_ContextManipulationApproach(b *testing.B) { func Benchmark_AddingMetadata_ContextManipulationApproach(b *testing.B) {
// TODO: Add in N=1-100 tests once Go1.6 support is removed.
const num = 10
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
ctx := context.Background() ctx := context.Background()
md, _ := FromOutgoingContext(ctx) for i := 0; i < num; i++ {
NewOutgoingContext(ctx, Join(Pairs("k1", "v1", "k2", "v2"), md)) md, _ := FromOutgoingContext(ctx)
NewOutgoingContext(ctx, Join(Pairs("k1", "v1", "k2", "v2"), md))
}
} }
} }
// Newer/faster approach to adding metadata to context // Newer/faster approach to adding metadata to context
func BenchmarkAppendToOutgoingContext(b *testing.B) { func BenchmarkAppendToOutgoingContext(b *testing.B) {
const num = 10
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
AppendToOutgoingContext(context.Background(), "k1", "v1", "k2", "v2") ctx := context.Background()
for i := 0; i < num; i++ {
ctx = AppendToOutgoingContext(ctx, "k1", "v1", "k2", "v2")
}
} }
} }