diff --git a/metadata/metadata.go b/metadata/metadata.go index 8b4ed9e1..e910f9a8 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -64,7 +64,7 @@ func encodeKeyValue(k, v string) (string, string) { if isASCII(v) { return k, v } - key := k + binHdrSuffix + key := strings.ToLower(k + binHdrSuffix) val := base64.StdEncoding.EncodeToString([]byte(v)) return key, string(val) } @@ -85,14 +85,14 @@ func DecodeKeyValue(k, v string) (string, string, error) { // MD is a mapping from metadata keys to values. Users should use the following // two convenience functions New and Pairs to generate MD. -type MD map[string]string +type MD map[string][]string // New creates a MD from given key-value map. func New(m map[string]string) MD { md := MD{} for k, v := range m { key, val := encodeKeyValue(k, v) - md[key] = val + md[key] = append(md[key],val) } return md } @@ -111,7 +111,7 @@ func Pairs(kv ...string) MD { continue } key, val := encodeKeyValue(k, s) - md[key] = val + md[key] = append(md[key],val) } return md } @@ -125,7 +125,9 @@ func (md MD) Len() int { func (md MD) Copy() MD { out := MD{} for k, v := range md { - out[k] = v + for _, i := range v { + out[k] = append(out[k],i) + } } return out } diff --git a/test/end2end_test.go b/test/end2end_test.go index fb83b452..c23d8262 100644 --- a/test/end2end_test.go +++ b/test/end2end_test.go @@ -59,8 +59,8 @@ import ( var ( testMetadata = metadata.MD{ - "key1": "value1", - "key2": "value2", + "key1": []string{"value1"}, + "key2": []string{"value2"}, } testAppUA = "myApp1/1.0 myApp2/0.9" ) @@ -75,7 +75,12 @@ func (s *testServer) EmptyCall(ctx context.Context, in *testpb.Empty) (*testpb.E if _, ok := md["user-agent"]; !ok { return nil, grpc.Errorf(codes.DataLoss, "got extra metadata") } - grpc.SendHeader(ctx, metadata.Pairs("ua", md["user-agent"])) + // kv := []string{"ua"} + // for _, entry := range md["user-agent"]{ + // kv = append(kv,entry) + // } + // grpc.SendHeader(ctx, metadata.Pairs(kv)) + grpc.SendHeader(ctx, metadata.Pairs("ua", md["user-agent"][0])) } return new(testpb.Empty), nil } @@ -499,7 +504,7 @@ func testEmptyUnaryWithUserAgent(t *testing.T, e env) { if err != nil || !proto.Equal(&testpb.Empty{}, reply) { t.Fatalf("TestService/EmptyCall(_, _) = %v, %v, want %v, ", reply, err, &testpb.Empty{}) } - if v, ok := header["ua"]; !ok || v != testAppUA { + if v, ok := header["ua"]; !ok || v[0] != testAppUA { t.Fatalf("header[\"ua\"] = %q, %t, want %q, true", v, ok, testAppUA) } tearDown(s, cc) diff --git a/transport/http2_client.go b/transport/http2_client.go index 0986ef01..02147e97 100644 --- a/transport/http2_client.go +++ b/transport/http2_client.go @@ -294,9 +294,12 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea if md, ok := metadata.FromContext(ctx); ok { hasMD = true for k, v := range md { - t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v}) + for _, entry := range v { + t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry}) } } + + } first := true // Sends the headers in a single batch even when they span multiple frames. for !endHeaders { diff --git a/transport/http2_server.go b/transport/http2_server.go index 2ec2bb13..a8e78cdb 100644 --- a/transport/http2_server.go +++ b/transport/http2_server.go @@ -440,8 +440,11 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error { t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"}) t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"}) for k, v := range md { - t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v}) + for _, entry := range v{ + t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry}) } +} + if err := t.writeHeaders(s, t.hBuf, false); err != nil { return err } @@ -473,8 +476,10 @@ func (t *http2Server) WriteStatus(s *Stream, statusCode codes.Code, statusDesc s t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-message", Value: statusDesc}) // Attach the trailer metadata. for k, v := range s.trailer { - t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v}) + for _, entry := range v { + t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry}) } +} if err := t.writeHeaders(s, t.hBuf, true); err != nil { t.Close() return err diff --git a/transport/http_util.go b/transport/http_util.go index af84fbf6..6cc8d335 100644 --- a/transport/http_util.go +++ b/transport/http_util.go @@ -100,7 +100,7 @@ type decodeState struct { timeout time.Duration method string // key-value metadata map from the peer. - mdata map[string]string + mdata map[string][]string } // An hpackDecoder decodes HTTP2 headers which may span multiple frames. @@ -173,14 +173,14 @@ func newHPACKDecoder() *hpackDecoder { f.Value = f.Value[:i] } if d.state.mdata == nil { - d.state.mdata = make(map[string]string) + d.state.mdata = make(map[string][]string) } k, v, err := metadata.DecodeKeyValue(f.Name, f.Value) if err != nil { grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err) return } - d.state.mdata[k] = v + d.state.mdata[k]= append(d.state.mdata[k],v) } } })