Skip to content

Commit

Permalink
log cancelation correctly and add test
Browse files Browse the repository at this point in the history
  • Loading branch information
dfawley committed Aug 17, 2023
1 parent 0e6d109 commit 11a7cd2
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 9 deletions.
38 changes: 38 additions & 0 deletions binarylog/binarylog_end2end_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ import (
"github.com/golang/protobuf/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/binarylog"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/grpclog"
iblog "google.golang.org/grpc/internal/binarylog"
"google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/stubserver"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"

Expand Down Expand Up @@ -1059,3 +1061,39 @@ func (s) TestServerBinaryLogFullDuplexError(t *testing.T) {
t.Fatal(err)
}
}

// TestCanceledStatus ensures a server that responds with a Canceled status has
// its trailers logged appropriately and is not treated as a canceled RPC.
func (s) TestCanceledStatus(t *testing.T) {
defer testSink.clear()

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

const statusMsgWant = "server returned Canceled"
ss := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
grpc.SetTrailer(ctx, metadata.Pairs("key", "value"))
return nil, status.Error(codes.Canceled, statusMsgWant)
},
}
if err := ss.Start(nil); err != nil {
t.Fatalf("Error starting endpoint server: %v", err)
}
defer ss.Stop()

if _, err := ss.Client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != codes.Canceled {
t.Fatalf("Received unexpected error from UnaryCall: %v; want Canceled", err)
}

got := testSink.logEntries(true)
last := got[len(got)-1]
if last.Type != binlogpb.GrpcLogEntry_EVENT_TYPE_SERVER_TRAILER ||
last.GetTrailer().GetStatusCode() != uint32(codes.Canceled) ||
last.GetTrailer().GetStatusMessage() != statusMsgWant ||
len(last.GetTrailer().GetMetadata().GetEntry()) != 1 ||
last.GetTrailer().GetMetadata().GetEntry()[0].GetKey() != "key" ||
string(last.GetTrailer().GetMetadata().GetEntry()[0].GetValue()) != "value" {
t.Fatalf("Got binary log: %+v; want last entry is server trailing with status Canceled", got)
}
}
3 changes: 3 additions & 0 deletions internal/transport/http2_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1505,6 +1505,9 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
return
}

// For headers, set them in s.header and close headerChan. For trailers or
// trailers-only, closeStream will set the trailers and close headerChan as
// needed.
if !endStream {
// If headerChan hasn't been closed yet (expected, given we checked it
// above, but something else could have potentially closed the whole
Expand Down
7 changes: 5 additions & 2 deletions rpc_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -867,15 +867,18 @@ func Errorf(c codes.Code, format string, a ...any) error {
return status.Errorf(c, format, a...)
}

var errContextCanceled = status.Error(codes.Canceled, context.Canceled.Error())
var errContextDeadline = status.Error(codes.DeadlineExceeded, context.DeadlineExceeded.Error())

// toRPCErr converts an error into an error from the status package.
func toRPCErr(err error) error {
switch err {
case nil, io.EOF:
return err
case context.DeadlineExceeded:
return status.Error(codes.DeadlineExceeded, err.Error())
return errContextDeadline
case context.Canceled:
return status.Error(codes.Canceled, err.Error())
return errContextCanceled
case io.ErrUnexpectedEOF:
return status.Error(codes.Internal, err.Error())
}
Expand Down
10 changes: 4 additions & 6 deletions stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -987,13 +987,11 @@ func (cs *clientStream) finish(err error) {
}

cs.mu.Unlock()
// For binary logging. only log cancel in finish (could be caused by RPC ctx
// canceled or ClientConn closed).
//
// Only one of cancel or trailer needs to be logged. In the cases where
// users don't call RecvMsg, users must have already canceled the RPC.
// Only one of cancel or trailer needs to be logged.
if len(cs.binlogs) != 0 {
if status.Code(err) == codes.Canceled {
if err == errContextCanceled ||
err == errContextDeadline ||
err == ErrClientConnClosing {
c := &binarylog.Cancel{
OnClientSide: true,
}
Expand Down
2 changes: 1 addition & 1 deletion test/end2end_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6343,7 +6343,7 @@ func (s) TestGlobalBinaryLoggingOptions(t *testing.T) {
if err == io.EOF {
return nil
}
return status.Errorf(codes.Unknown, "expected client to CloseSend")
return status.Errorf(codes.Unknown, "expected client to call CloseSend")
},
}

Expand Down

0 comments on commit 11a7cd2

Please sign in to comment.