Skip to content

Commit b1f5263

Browse files
committed
client: fix ForceCodec to set content-type header appropriately
1 parent 0439465 commit b1f5263

File tree

3 files changed

+62
-3
lines changed

3 files changed

+62
-3
lines changed

rpc_util.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -429,9 +429,10 @@ func (o ContentSubtypeCallOption) before(c *callInfo) error {
429429
}
430430
func (o ContentSubtypeCallOption) after(c *callInfo, attempt *csAttempt) {}
431431

432-
// ForceCodec returns a CallOption that will set codec to be
433-
// used for all request and response messages for a call. The result of calling
434-
// Name() will be used as the content-subtype in a case-insensitive manner.
432+
// ForceCodec returns a CallOption that will set codec to be used for all
433+
// request and response messages for a call. The result of calling Name() will
434+
// be used as the content-subtype after converting to lowercase, unless
435+
// CallContentSubtype is also used.
435436
//
436437
// See Content-Type on
437438
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for

stream.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"io"
2525
"math"
2626
"strconv"
27+
"strings"
2728
"sync"
2829
"time"
2930

@@ -246,6 +247,16 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
246247
return nil, err
247248
}
248249

250+
// Set the content subtype if it is already unset and ForceCodec is used.
251+
if c.codec != nil && c.contentSubtype == "" {
252+
// c.codec is a baseCodec to hide the difference between grpc.Codec and
253+
// encoding.Codec (Name vs. String method name). We only support
254+
// setting content subtype from encoding.Codec to avoid a behavior
255+
// change with the deprecated version.
256+
if ec, ok := c.codec.(encoding.Codec); ok {
257+
c.contentSubtype = strings.ToLower(ec.Name())
258+
}
259+
}
249260
callHdr := &transport.CallHdr{
250261
Host: cc.authority,
251262
Method: method,

test/end2end_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5284,6 +5284,53 @@ func (s) TestGRPCMethod(t *testing.T) {
52845284
}
52855285
}
52865286

5287+
// renameProtoCodec is an encoding.Codec wrapper that allows customizing the
5288+
// Name() of another codec.
5289+
type renameProtoCodec struct {
5290+
encoding.Codec
5291+
name string
5292+
}
5293+
5294+
func (r *renameProtoCodec) Name() string { return r.name }
5295+
5296+
func (s) TestForceCodecName(t *testing.T) {
5297+
wantContentTypeCh := make(chan string, 1)
5298+
defer close(wantContentTypeCh)
5299+
5300+
ss := &stubserver.StubServer{
5301+
EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
5302+
md, ok := metadata.FromIncomingContext(ctx)
5303+
if !ok {
5304+
return nil, status.Errorf(codes.Internal, "no metadata in context")
5305+
}
5306+
if got, want := md["content-type"], <-wantContentTypeCh; len(got) != 1 || got[0] != want {
5307+
return nil, status.Errorf(codes.Internal, "got content-type=%q; want [%q]", got, want)
5308+
}
5309+
return &testpb.Empty{}, nil
5310+
},
5311+
}
5312+
protoCodec := encoding.GetCodec("proto")
5313+
if err := ss.Start([]grpc.ServerOption{grpc.ForceServerCodec(encoding.GetCodec("proto"))}); err != nil {
5314+
t.Fatalf("Error starting endpoint server: %v", err)
5315+
}
5316+
defer ss.Stop()
5317+
5318+
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
5319+
defer cancel()
5320+
5321+
codec := &renameProtoCodec{Codec: protoCodec, name: "some-test-name"}
5322+
wantContentTypeCh <- "application/grpc+some-test-name"
5323+
if _, err := ss.Client.EmptyCall(ctx, &testpb.Empty{}, grpc.ForceCodec(codec)); err != nil {
5324+
t.Fatalf("ss.Client.EmptyCall(_, _) = _, %v; want _, nil", err)
5325+
}
5326+
5327+
codec.name = "aNoTHeRNaME"
5328+
wantContentTypeCh <- "application/grpc+anothername"
5329+
if _, err := ss.Client.EmptyCall(ctx, &testpb.Empty{}, grpc.ForceCodec(codec)); err != nil {
5330+
t.Fatalf("ss.Client.EmptyCall(_, _) = _, %v; want _, nil", err)
5331+
}
5332+
}
5333+
52875334
func (s) TestForceServerCodec(t *testing.T) {
52885335
ss := &stubserver.StubServer{
52895336
EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {

0 commit comments

Comments
 (0)