Skip to content

Commit 1b5a905

Browse files
committed
Add status details when aborting early
revive errs
1 parent f9d2bdb commit 1b5a905

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

internal/transport/controlbuf.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ import (
3232
"golang.org/x/net/http2/hpack"
3333
"google.golang.org/grpc/internal/grpclog"
3434
"google.golang.org/grpc/internal/grpcutil"
35+
istatus "google.golang.org/grpc/internal/status"
3536
"google.golang.org/grpc/mem"
3637
"google.golang.org/grpc/status"
38+
"google.golang.org/protobuf/proto"
3739
)
3840

3941
var updateHeaderTblSize = func(e *hpack.Encoder, v uint32) {
@@ -854,6 +856,13 @@ func (l *loopyWriter) earlyAbortStreamHandler(eas *earlyAbortStream) error {
854856
{Name: "grpc-message", Value: encodeGrpcMessage(eas.status.Message())},
855857
}
856858

859+
if p := istatus.RawStatusProto(eas.status); len(p.GetDetails()) > 0 {
860+
stBytes, err := proto.Marshal(p)
861+
if err == nil {
862+
headerFields = append(headerFields, hpack.HeaderField{Name: grpcStatusDetailsBinHeader, Value: encodeBinHeader(stBytes)})
863+
}
864+
}
865+
857866
if err := l.writeHeader(eas.streamID, true, headerFields, nil); err != nil {
858867
return err
859868
}

test/end2end_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,6 +2154,56 @@ func testTap(t *testing.T, e env) {
21542154
}
21552155
}
21562156

2157+
func (s) TestTapStatusDetails(t *testing.T) {
2158+
wantDetails := &testpb.Empty{}
2159+
st := status.New(codes.ResourceExhausted, "rate limit exceeded")
2160+
st, err := st.WithDetails(wantDetails)
2161+
if err != nil {
2162+
t.Fatalf("status.WithDetails() failed: %v", err)
2163+
}
2164+
2165+
tapHandler := func(_ context.Context, _ *tap.Info) (context.Context, error) {
2166+
// Return error with details for all RPCs.
2167+
return nil, st.Err()
2168+
}
2169+
2170+
ss := &stubserver.StubServer{
2171+
EmptyCallF: func(_ context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
2172+
// This should never be called since TAP handler rejects the RPC.
2173+
return &testpb.Empty{}, nil
2174+
},
2175+
}
2176+
sopts := []grpc.ServerOption{grpc.InTapHandle(tapHandler)}
2177+
if err := ss.Start(sopts); err != nil {
2178+
t.Fatalf("Error starting server: %v", err)
2179+
}
2180+
defer ss.Stop()
2181+
2182+
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
2183+
defer cancel()
2184+
2185+
_, err = ss.Client.EmptyCall(ctx, &testpb.Empty{})
2186+
if err == nil {
2187+
t.Fatal("EmptyCall() succeeded; want error")
2188+
}
2189+
2190+
gotStatus := status.Convert(err)
2191+
if gotStatus.Code() != codes.ResourceExhausted {
2192+
t.Fatalf("EmptyCall() returned code %v; want %v", gotStatus.Code(), codes.ResourceExhausted)
2193+
}
2194+
if gotStatus.Message() != "rate limit exceeded" {
2195+
t.Fatalf("EmptyCall() returned message %q; want %q", gotStatus.Message(), "rate limit exceeded")
2196+
}
2197+
2198+
details := gotStatus.Details()
2199+
if len(details) != 1 {
2200+
t.Fatalf("EmptyCall() returned %d details; want 1", len(details))
2201+
}
2202+
if _, ok := details[0].(*testpb.Empty); !ok {
2203+
t.Fatalf("EmptyCall() returned detail type %T; want *testpb.Empty", details[0])
2204+
}
2205+
}
2206+
21572207
func (s) TestEmptyUnaryWithUserAgent(t *testing.T) {
21582208
for _, e := range listTestEnv() {
21592209
testEmptyUnaryWithUserAgent(t, e)

0 commit comments

Comments
 (0)