diff --git a/internal/transport/http2_client.go b/internal/transport/http2_client.go index 0cd6da1e73f..ec6d4032e2d 100644 --- a/internal/transport/http2_client.go +++ b/internal/transport/http2_client.go @@ -1304,7 +1304,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { recvCompress string httpStatusCode *int httpStatusErr string - rawStatusCode = codes.Unknown + rawStatusCode *codes.Code // headerError is set if an error is encountered while parsing the headers headerError string ) @@ -1326,13 +1326,14 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { case "grpc-encoding": recvCompress = hf.Value case "grpc-status": - code, err := strconv.ParseInt(hf.Value, 10, 32) + code, err := strconv.ParseUint(hf.Value, 10, 32) if err != nil { se := status.New(codes.Internal, fmt.Sprintf("transport: malformed grpc-status: %v", err)) t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream) return } - rawStatusCode = codes.Code(uint32(code)) + rsc := codes.Code(uint32(code)) + rawStatusCode = &rsc case "grpc-message": grpcMessage = decodeGrpcMessage(hf.Value) case "grpc-status-details-bin": @@ -1380,7 +1381,10 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { if !isGRPC || httpStatusErr != "" { var code = codes.Internal // when header does not include HTTP status, return INTERNAL - if httpStatusCode != nil { + // grpc-status only takes precedence over http-status if everything else is ok. + if rawStatusCode != nil && contentTypeErr == "" && httpStatusErr == "" { + code = *rawStatusCode + } else if httpStatusCode != nil { var ok bool code, ok = HTTPStatusConvTab[*httpStatusCode] if !ok { @@ -1453,7 +1457,11 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { } if statusGen == nil { - statusGen = status.New(rawStatusCode, grpcMessage) + rsc := codes.Unknown + if rawStatusCode != nil { + rsc = *rawStatusCode + } + statusGen = status.New(rsc, grpcMessage) } // if client received END_STREAM from server while stream was still active, send RST_STREAM diff --git a/internal/transport/transport_test.go b/internal/transport/transport_test.go index 28cace0ba5d..bc1a7b2d72f 100644 --- a/internal/transport/transport_test.go +++ b/internal/transport/transport_test.go @@ -2046,7 +2046,7 @@ func (s) TestClientDecodeHeaderStatusErr(t *testing.T) { }, wantStatus: status.New( codes.Internal, - "transport: malformed grpc-status: strconv.ParseInt: parsing \"xxxx\": invalid syntax", + "transport: malformed grpc-status: strconv.ParseUint: parsing \"xxxx\": invalid syntax", ), }, { @@ -2090,7 +2090,7 @@ func (s) TestClientDecodeHeaderStatusErr(t *testing.T) { metaHeaderFrame: &http2.MetaHeadersFrame{ Fields: []hpack.HeaderField{ {Name: "content-type", Value: "application/grpc"}, - {Name: "grpc-status", Value: "0"}, + {Name: "grpc-status", Value: "14"}, {Name: ":status", Value: "504"}, }, }, diff --git a/test/end2end_test.go b/test/end2end_test.go index 5702f18bdb2..f81599273fb 100644 --- a/test/end2end_test.go +++ b/test/end2end_test.go @@ -34,6 +34,7 @@ import ( "os" "reflect" "runtime" + "strconv" "strings" "sync" "sync/atomic" @@ -7217,7 +7218,7 @@ func (s) TestHTTPHeaderFrameErrorHandlingHTTPMode(t *testing.T) { doHTTPHeaderTest(t, transport.HTTPStatusConvTab[int(httpCode)], []string{ ":status", fmt.Sprintf("%d", httpCode), "content-type", "text/html", // non-gRPC content type to switch to HTTP mode. - "grpc-status", "1", // Make up a gRPC status error + "grpc-status", strconv.Itoa(int(transport.HTTPStatusConvTab[httpCode])), "grpc-status-details-bin", "???", // Make up a gRPC field parsing error }) } @@ -7227,7 +7228,7 @@ func (s) TestHTTPHeaderFrameErrorHandlingHTTPMode(t *testing.T) { doHTTPHeaderTest(t, transport.HTTPStatusConvTab[int(httpCode)], []string{ ":status", fmt.Sprintf("%d", httpCode), // Omitting content type to switch to HTTP mode. - "grpc-status", "1", // Make up a gRPC status error + "grpc-status", strconv.Itoa(int(transport.HTTPStatusConvTab[httpCode])), "grpc-status-details-bin", "???", // Make up a gRPC field parsing error }) } @@ -7236,7 +7237,7 @@ func (s) TestHTTPHeaderFrameErrorHandlingHTTPMode(t *testing.T) { doHTTPHeaderTest(t, codes.Internal, []string{ ":status", "abc", // Omitting content type to switch to HTTP mode. - "grpc-status", "1", // Make up a gRPC status error + "grpc-status", "13", // Make up a gRPC status error "grpc-status-details-bin", "???", // Make up a gRPC field parsing error }) } @@ -7269,7 +7270,7 @@ func (s) TestHTTPHeaderFrameErrorHandlingInitialHeader(t *testing.T) { header: []string{ ":status", "502", "content-type", "application/grpc", - "grpc-status", "0", + "grpc-status", "14", "grpc-tags-bin", "???", }, errCode: codes.Unavailable, @@ -7279,7 +7280,7 @@ func (s) TestHTTPHeaderFrameErrorHandlingInitialHeader(t *testing.T) { header: []string{ ":status", "502", "content-type", "application/grpc", - "grpc-status", "3", + "grpc-status", "14", }, errCode: codes.Unavailable, },