Skip to content

Commit 04a7af2

Browse files
authored
client: ignore http status header for gRPC streams (#8548)
Fixes #8486 When a gRPC response is received with content type application/grpc, we then do not expect any information in the http status and the status information needs to be conveyed by gRPC status only. In case of missing gRPC status, we will throw an Internal error instead of Unknown in accordance with https://grpc.io/docs/guides/status-codes/ Changes : - Ignore http status in case of content type application/grpc - Change the default rawStatusCode to return Internal for missing grpc status RELEASE NOTES: * client : Ignore the HTTP header status for gRPC streams and return Internal error for missing gRPC status.
1 parent 20c9f65 commit 04a7af2

File tree

3 files changed

+211
-122
lines changed

3 files changed

+211
-122
lines changed

internal/transport/http2_client.go

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,17 +1480,14 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
14801480
contentTypeErr = "malformed header: missing HTTP content-type"
14811481
grpcMessage string
14821482
recvCompress string
1483-
httpStatusCode *int
14841483
httpStatusErr string
1485-
rawStatusCode = codes.Unknown
1484+
// the code from the grpc-status header, if present
1485+
grpcStatusCode = codes.Internal
14861486
// headerError is set if an error is encountered while parsing the headers
14871487
headerError string
1488+
httpStatus string
14881489
)
14891490

1490-
if initialHeader {
1491-
httpStatusErr = "malformed header: missing HTTP status"
1492-
}
1493-
14941491
for _, hf := range frame.Fields {
14951492
switch hf.Name {
14961493
case "content-type":
@@ -1510,69 +1507,71 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
15101507
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
15111508
return
15121509
}
1513-
rawStatusCode = codes.Code(uint32(code))
1510+
grpcStatusCode = codes.Code(uint32(code))
15141511
case "grpc-message":
15151512
grpcMessage = decodeGrpcMessage(hf.Value)
15161513
case ":status":
1517-
c, err := strconv.ParseInt(hf.Value, 10, 32)
1514+
httpStatus = hf.Value
1515+
default:
1516+
if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) {
1517+
break
1518+
}
1519+
v, err := decodeMetadataHeader(hf.Name, hf.Value)
15181520
if err != nil {
1519-
se := status.New(codes.Internal, fmt.Sprintf("transport: malformed http-status: %v", err))
1521+
headerError = fmt.Sprintf("transport: malformed %s: %v", hf.Name, err)
1522+
logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
1523+
break
1524+
}
1525+
mdata[hf.Name] = append(mdata[hf.Name], v)
1526+
}
1527+
}
1528+
1529+
// If a non-gRPC response is received, then evaluate the HTTP status to
1530+
// process the response and close the stream.
1531+
// In case http status doesn't provide any error information (status : 200),
1532+
// then evalute response code to be Unknown.
1533+
if !isGRPC {
1534+
var grpcErrorCode = codes.Internal
1535+
if httpStatus == "" {
1536+
httpStatusErr = "malformed header: missing HTTP status"
1537+
} else {
1538+
// Parse the status codes (e.g. "200", 404").
1539+
statusCode, err := strconv.Atoi(httpStatus)
1540+
if err != nil {
1541+
se := status.New(grpcErrorCode, fmt.Sprintf("transport: malformed http-status: %v", err))
15201542
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
15211543
return
15221544
}
1523-
statusCode := int(c)
15241545
if statusCode >= 100 && statusCode < 200 {
15251546
if endStream {
15261547
se := status.New(codes.Internal, fmt.Sprintf(
15271548
"protocol error: informational header with status code %d must not have END_STREAM set", statusCode))
15281549
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
15291550
}
1551+
// In case of informational headers, return.
15301552
return
15311553
}
1532-
httpStatusCode = &statusCode
1533-
if statusCode == 200 {
1534-
httpStatusErr = ""
1535-
break
1536-
}
1537-
15381554
httpStatusErr = fmt.Sprintf(
15391555
"unexpected HTTP status code received from server: %d (%s)",
15401556
statusCode,
15411557
http.StatusText(statusCode),
15421558
)
1543-
default:
1544-
if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) {
1545-
break
1546-
}
1547-
v, err := decodeMetadataHeader(hf.Name, hf.Value)
1548-
if err != nil {
1549-
headerError = fmt.Sprintf("transport: malformed %s: %v", hf.Name, err)
1550-
logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
1551-
break
1552-
}
1553-
mdata[hf.Name] = append(mdata[hf.Name], v)
1554-
}
1555-
}
1556-
1557-
if !isGRPC || httpStatusErr != "" {
1558-
var code = codes.Internal // when header does not include HTTP status, return INTERNAL
1559-
1560-
if httpStatusCode != nil {
15611559
var ok bool
1562-
code, ok = HTTPStatusConvTab[*httpStatusCode]
1560+
grpcErrorCode, ok = HTTPStatusConvTab[statusCode]
15631561
if !ok {
1564-
code = codes.Unknown
1562+
grpcErrorCode = codes.Unknown
15651563
}
15661564
}
15671565
var errs []string
15681566
if httpStatusErr != "" {
15691567
errs = append(errs, httpStatusErr)
15701568
}
1569+
15711570
if contentTypeErr != "" {
15721571
errs = append(errs, contentTypeErr)
15731572
}
1574-
// Verify the HTTP response is a 200.
1575-
se := status.New(code, strings.Join(errs, "; "))
1573+
1574+
se := status.New(grpcErrorCode, strings.Join(errs, "; "))
15761575
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
15771576
return
15781577
}
@@ -1626,7 +1625,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
16261625
return
16271626
}
16281627

1629-
status := istatus.NewWithProto(rawStatusCode, grpcMessage, mdata[grpcStatusDetailsBinHeader])
1628+
status := istatus.NewWithProto(grpcStatusCode, grpcMessage, mdata[grpcStatusDetailsBinHeader])
16301629

16311630
// If client received END_STREAM from server while stream was still active,
16321631
// send RST_STREAM.

0 commit comments

Comments
 (0)