Skip to content

Commit

Permalink
Add status details support to server HTTP handler (grpc#1438)
Browse files Browse the repository at this point in the history
  • Loading branch information
c4milo authored and dfawley committed Aug 15, 2017
1 parent 1308414 commit bfaf042
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 5 deletions.
13 changes: 11 additions & 2 deletions transport/handler_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"sync"
"time"

"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"golang.org/x/net/http2"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -197,7 +198,15 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) erro
h.Set("Grpc-Message", encodeGrpcMessage(m))
}

// TODO: Support Grpc-Status-Details-Bin
if p := st.Proto(); p != nil && len(p.Details) > 0 {
stBytes, err := proto.Marshal(p)
if err != nil {
// TODO: return error instead, when callers are able to handle it.
panic(err)
}

h.Set("Grpc-Status-Details-Bin", encodeBinHeader(stBytes))
}

if md := s.Trailer(); len(md) > 0 {
for k, vv := range md {
Expand Down Expand Up @@ -239,7 +248,7 @@ func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) {
// and https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
h.Add("Trailer", "Grpc-Status")
h.Add("Trailer", "Grpc-Message")
// TODO: Support Grpc-Status-Details-Bin
h.Add("Trailer", "Grpc-Status-Details-Bin")

if s.sendCompress != "" {
h.Set("Grpc-Encoding", s.sendCompress)
Expand Down
58 changes: 55 additions & 3 deletions transport/handler_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ import (
"testing"
"time"

"github.com/golang/protobuf/proto"
dpb "github.com/golang/protobuf/ptypes/duration"
"golang.org/x/net/context"
epb "google.golang.org/genproto/googleapis/rpc/errdetails"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -199,6 +202,7 @@ func TestHandlerTransport_NewServerHandlerTransport(t *testing.T) {
"user-agent": {"x/y a/b"},
"meta-foo": {"foo-val"},
}

if !reflect.DeepEqual(ht.headerMD, want) {
return fmt.Errorf("metdata = %#v; want %#v", ht.headerMD, want)
}
Expand Down Expand Up @@ -294,7 +298,7 @@ func TestHandlerTransport_HandleStreams(t *testing.T) {
wantHeader := http.Header{
"Date": nil,
"Content-Type": {"application/grpc"},
"Trailer": {"Grpc-Status", "Grpc-Message"},
"Trailer": {"Grpc-Status", "Grpc-Message", "Grpc-Status-Details-Bin"},
"Grpc-Status": {"0"},
}
if !reflect.DeepEqual(st.rw.HeaderMap, wantHeader) {
Expand All @@ -314,6 +318,7 @@ func TestHandlerTransport_HandleStreams_InvalidArgument(t *testing.T) {

func handleStreamCloseBodyTest(t *testing.T, statusCode codes.Code, msg string) {
st := newHandleStreamTest(t)

handleStream := func(s *Stream) {
st.ht.WriteStatus(s, status.New(statusCode, msg))
}
Expand All @@ -324,10 +329,11 @@ func handleStreamCloseBodyTest(t *testing.T, statusCode codes.Code, msg string)
wantHeader := http.Header{
"Date": nil,
"Content-Type": {"application/grpc"},
"Trailer": {"Grpc-Status", "Grpc-Message"},
"Trailer": {"Grpc-Status", "Grpc-Message", "Grpc-Status-Details-Bin"},
"Grpc-Status": {fmt.Sprint(uint32(statusCode))},
"Grpc-Message": {encodeGrpcMessage(msg)},
}

if !reflect.DeepEqual(st.rw.HeaderMap, wantHeader) {
t.Errorf("Header+Trailer mismatch.\n got: %#v\nwant: %#v", st.rw.HeaderMap, wantHeader)
}
Expand Down Expand Up @@ -375,11 +381,57 @@ func TestHandlerTransport_HandleStreams_Timeout(t *testing.T) {
wantHeader := http.Header{
"Date": nil,
"Content-Type": {"application/grpc"},
"Trailer": {"Grpc-Status", "Grpc-Message"},
"Trailer": {"Grpc-Status", "Grpc-Message", "Grpc-Status-Details-Bin"},
"Grpc-Status": {"4"},
"Grpc-Message": {encodeGrpcMessage("too slow")},
}
if !reflect.DeepEqual(rw.HeaderMap, wantHeader) {
t.Errorf("Header+Trailer Map mismatch.\n got: %#v\nwant: %#v", rw.HeaderMap, wantHeader)
}
}

func TestHandlerTransport_HandleStreams_ErrDetails(t *testing.T) {
errDetails := []proto.Message{
&epb.RetryInfo{
RetryDelay: &dpb.Duration{Seconds: 60},
},
&epb.ResourceInfo{
ResourceType: "foo bar",
ResourceName: "service.foo.bar",
Owner: "User",
},
}

statusCode := codes.ResourceExhausted
msg := "you are being throttled"
st, err := status.New(statusCode, msg).WithDetails(errDetails...)
if err != nil {
t.Fatal(err)
}

stBytes, err := proto.Marshal(st.Proto())
if err != nil {
t.Fatal(err)
}

hst := newHandleStreamTest(t)
handleStream := func(s *Stream) {
hst.ht.WriteStatus(s, st)
}
hst.ht.HandleStreams(
func(s *Stream) { go handleStream(s) },
func(ctx context.Context, method string) context.Context { return ctx },
)
wantHeader := http.Header{
"Date": nil,
"Content-Type": {"application/grpc"},
"Trailer": {"Grpc-Status", "Grpc-Message", "Grpc-Status-Details-Bin"},
"Grpc-Status": {fmt.Sprint(uint32(statusCode))},
"Grpc-Message": {encodeGrpcMessage(msg)},
"Grpc-Status-Details-Bin": {encodeBinHeader(stBytes)},
}

if !reflect.DeepEqual(hst.rw.HeaderMap, wantHeader) {
t.Errorf("Header+Trailer mismatch.\n got: %#v\nwant: %#v", hst.rw.HeaderMap, wantHeader)
}
}

0 comments on commit bfaf042

Please sign in to comment.