Skip to content

Commit 6cb1c54

Browse files
committed
restore existing behaviour to return io.EOF on repeated client.RecvMsg() calls
1 parent 42a184d commit 6cb1c54

2 files changed

Lines changed: 71 additions & 12 deletions

File tree

stream.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,8 @@ type clientStream struct {
549549

550550
sentLast bool // sent an end stream
551551

552+
recvFirstMsg bool // set after the first message is received
553+
552554
methodConfig *MethodConfig
553555

554556
ctx context.Context // the application's context, wrapped by stats/tracing
@@ -1145,14 +1147,15 @@ func (a *csAttempt) recvMsg(m any, payInfo *payloadInfo) (err error) {
11451147
return statusErr
11461148
}
11471149
// Received no msg and status OK for non-server streaming rpcs.
1148-
if !cs.desc.ServerStreams {
1149-
return status.Error(codes.Internal, "cardinality violation: received no response message from non-streaming RPC")
1150+
if !cs.desc.ServerStreams && !cs.recvFirstMsg {
1151+
return status.Error(codes.Internal, "cardinality violation: received no response message from non-server-streaming RPC")
11501152
}
11511153
return io.EOF // indicates successful end of stream.
11521154
}
11531155

11541156
return toRPCErr(err)
11551157
}
1158+
cs.recvFirstMsg = true
11561159
if a.trInfo != nil {
11571160
a.mu.Lock()
11581161
if a.trInfo.tr != nil {
@@ -1363,6 +1366,7 @@ type addrConnStream struct {
13631366
transport transport.ClientTransport
13641367
ctx context.Context
13651368
sentLast bool
1369+
recvFirstMsg bool
13661370
desc *StreamDesc
13671371
codec baseCodec
13681372
sendCompressorV0 Compressor
@@ -1489,13 +1493,14 @@ func (as *addrConnStream) RecvMsg(m any) (err error) {
14891493
return statusErr
14901494
}
14911495
// Received no msg and status OK for non-server streaming rpcs.
1492-
if !as.desc.ServerStreams {
1493-
return status.Error(codes.Internal, "cardinality violation: received no response message from non-streaming RPC")
1496+
if !as.desc.ServerStreams && !as.recvFirstMsg {
1497+
return status.Error(codes.Internal, "cardinality violation: received no response message from non-server-streaming RPC")
14941498
}
14951499
return io.EOF // indicates successful end of stream.
14961500
}
14971501
return toRPCErr(err)
14981502
}
1503+
as.recvFirstMsg = true
14991504

15001505
if as.desc.ServerStreams {
15011506
// Subsequent messages should be received by subsequent RecvMsg calls.

test/end2end_test.go

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3738,7 +3738,7 @@ func (s) TestClientStreaming_ReturnErrorAfterSendAndClose(t *testing.T) {
37383738
}
37393739
}
37403740

3741-
// Tests that a client receives a cardinality violation error for unary
3741+
// Tests that client receives a cardinality violation error for unary
37423742
// RPCs if the server doesn't send a message before returning status OK.
37433743
func (s) TestUnaryRPC_ServerSendsOnlyTrailersWithOK(t *testing.T) {
37443744
lis, err := testutils.LocalTCPListener()
@@ -3769,8 +3769,8 @@ func (s) TestUnaryRPC_ServerSendsOnlyTrailersWithOK(t *testing.T) {
37693769
}
37703770
}
37713771

3772-
// Tests that client will receive cardinality violations when calling
3773-
// RecvMsg() multiple times for non-streaming response streams.
3772+
// Tests the behavior for unary RPC when client calls RecvMsg() twice.
3773+
// Second call to RecvMsg should fail with io.EOF.
37743774
func (s) TestUnaryRPC_ClientCallRecvMsgTwice(t *testing.T) {
37753775
e := tcpTLSEnv
37763776
te := newTest(t, e)
@@ -3801,13 +3801,67 @@ func (s) TestUnaryRPC_ClientCallRecvMsgTwice(t *testing.T) {
38013801
t.Fatalf("stream.RecvMsg() = %v , want <nil>", err)
38023802
}
38033803

3804-
if err = stream.RecvMsg(resp); status.Code(err) != codes.Internal {
3804+
if err = stream.RecvMsg(resp); err != io.EOF {
3805+
t.Errorf("stream.RecvMsg() = %v, want error %v", err, io.EOF)
3806+
}
3807+
}
3808+
3809+
// Tests the behavior for unary RPC when server calls SendMsg() twice.
3810+
// Client should fail with cardinality violation error.
3811+
func (s) TestUnaryRPC_ServerCallSendMsgTwice(t *testing.T) {
3812+
lis, err := testutils.LocalTCPListener()
3813+
if err != nil {
3814+
t.Fatal(err)
3815+
}
3816+
defer lis.Close()
3817+
3818+
s := grpc.NewServer()
3819+
serviceDesc := grpc.ServiceDesc{
3820+
ServiceName: "grpc.testing.TestService",
3821+
HandlerType: (*any)(nil),
3822+
Methods: []grpc.MethodDesc{},
3823+
Streams: []grpc.StreamDesc{
3824+
{
3825+
StreamName: "UnaryCall",
3826+
Handler: func(_ any, stream grpc.ServerStream) error {
3827+
if err := stream.RecvMsg(&testpb.Empty{}); err != nil {
3828+
t.Errorf("stream.RecvMsg() = %v, want <nil>", err)
3829+
}
3830+
3831+
if err = stream.SendMsg(&testpb.Empty{}); err != nil {
3832+
t.Errorf("stream.SendMsg() = %v, want <nil>", err)
3833+
}
3834+
3835+
if err = stream.SendMsg(&testpb.Empty{}); err != nil {
3836+
t.Errorf("stream.SendMsg() = %v, want <nil>", err)
3837+
}
3838+
return nil
3839+
},
3840+
ClientStreams: false,
3841+
ServerStreams: false,
3842+
},
3843+
},
3844+
}
3845+
s.RegisterService(&serviceDesc, &testServer{})
3846+
go s.Serve(lis)
3847+
defer s.Stop()
3848+
3849+
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
3850+
defer cancel()
3851+
cc, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
3852+
if err != nil {
3853+
t.Fatalf("grpc.NewClient(%q) failed unexpectedly: %v", lis.Addr(), err)
3854+
}
3855+
defer cc.Close()
3856+
3857+
client := testgrpc.NewTestServiceClient(cc)
3858+
if _, err = client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != codes.Internal {
38053859
t.Errorf("stream.RecvMsg() = %v, want error %v", status.Code(err), codes.Internal)
38063860
}
38073861
}
38083862

3809-
// Tests that client will receive cardinality violations when calling
3810-
// RecvMsg() multiple times for non-streaming response streams.
3863+
// Tests the behavior for client-streaming RPC when client calls RecvMsg() twice.
3864+
// Second call to RecvMsg should fail with io.EOF.
38113865
func (s) TestClientStreaming_ClientCallRecvMsgTwice(t *testing.T) {
38123866
ss := stubserver.StubServer{
38133867
StreamingInputCallF: func(stream testgrpc.TestService_StreamingInputCallServer) error {
@@ -3838,8 +3892,8 @@ func (s) TestClientStreaming_ClientCallRecvMsgTwice(t *testing.T) {
38383892
if err := stream.RecvMsg(resp); err != nil {
38393893
t.Fatalf("stream.RecvMsg() = %v , want <nil>", err)
38403894
}
3841-
if err = stream.RecvMsg(resp); status.Code(err) != codes.Internal {
3842-
t.Errorf("stream.RecvMsg() = %v, want error %v", status.Code(err), codes.Internal)
3895+
if err = stream.RecvMsg(resp); err != io.EOF {
3896+
t.Errorf("stream.RecvMsg() = %v, want error %v", err, io.EOF)
38433897
}
38443898
}
38453899

0 commit comments

Comments
 (0)