Skip to content

Conversation

mbissa
Copy link

@mbissa mbissa commented Sep 1, 2025

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.

Fixes grpc#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 http status for gRPC mode and return Internal error for
missing grpc status
@mbissa mbissa added this to the 1.76 Release milestone Sep 1, 2025
@mbissa mbissa requested a review from arjan-bal September 1, 2025 06:27
@mbissa mbissa self-assigned this Sep 1, 2025
Copy link

codecov bot commented Sep 1, 2025

Codecov Report

❌ Patch coverage is 93.75000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.02%. Comparing base (e60a04b) to head (734cd4d).
⚠️ Report is 9 commits behind head on master.

Files with missing lines Patch % Lines
internal/transport/http2_client.go 93.75% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8548      +/-   ##
==========================================
+ Coverage   81.64%   82.02%   +0.37%     
==========================================
  Files         413      415       +2     
  Lines       40621    40684      +63     
==========================================
+ Hits        33167    33370     +203     
+ Misses       5991     5923      -68     
+ Partials     1463     1391      -72     
Files with missing lines Coverage Δ
internal/transport/http2_client.go 92.20% <93.75%> (+8.84%) ⬆️

... and 37 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@arjan-bal
Copy link
Contributor

In the grpc-go repo, we use the assignee to indicate who the review is pending on (author or reviewer). We ignore the reviewer status (the yellow pending re-review dot). Assigning to myself.

@arjan-bal arjan-bal assigned arjan-bal and unassigned mbissa Sep 1, 2025
@mbissa mbissa requested a review from dfawley September 1, 2025 06:34
@arjan-bal
Copy link
Contributor

arjan-bal commented Sep 1, 2025

For the PR title, we add the name of the Go package (e.g. transport), with the prefix if it's ambiguous (e.g. xds/bootstrap), followed by the change description as a complete sentence, written as though it were an order (an imperative sentence), see go/cl-descriptions#first-line.

@arjan-bal arjan-bal assigned dfawley and unassigned arjan-bal Sep 1, 2025
@dfawley dfawley removed their assignment Sep 3, 2025
@mbissa mbissa force-pushed the ignore-status-for-grpc-content-type-8486 branch 2 times, most recently from e1f6dbc to f123124 Compare September 9, 2025 04:31
@mbissa mbissa force-pushed the ignore-status-for-grpc-content-type-8486 branch from f123124 to b4cce55 Compare September 9, 2025 04:34
@arjan-bal arjan-bal changed the title ignoring http status when content type is application/grpc grpc#8486 client : Ignore http status for gRPC mode and return Internal error for missing grpc status Sep 10, 2025
@arjan-bal arjan-bal assigned arjan-bal and dfawley and unassigned mbissa Sep 10, 2025
@mbissa mbissa changed the title client : Ignore http status for gRPC mode and return Internal error for missing grpc status client : Ignore http status for gRPC mode Sep 10, 2025
@mbissa mbissa changed the title client : Ignore http status for gRPC mode client transport : ignore http status for gRPC mode Sep 10, 2025
@mbissa mbissa removed the request for review from dfawley September 10, 2025 17:59
// headerError is set if an error is encountered while parsing the headers
headerError string
headerError string
receivedHTTPStatus string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We can omit the received prefix int the variable name, shortening it to httpStatus since it doesn't effect readability.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping on this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Comment on lines 1524 to 1525
case "200":
grpcErrorCode = codes.Unknown
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this explicit check required? Since 200 isn't present in the HTTPStatusConvTab map, I think it may be omitted.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is when no error information can be derived from http status since it is 200, we can simply return Unknown code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default case also adds a error message which seems useful. I think we should remove the special case unless it requires different handling in code. We don't need to worry too much about optimizing the error case, since they're expected to be infrequent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1; please remove special cases when possible most of the time. Less code = easier to understand code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@arjan-bal arjan-bal assigned mbissa and unassigned arjan-bal Sep 11, 2025
@arjan-bal arjan-bal changed the title client transport : ignore http status for gRPC mode client: ignore http status header for gRPC mode Sep 15, 2025
@arjan-bal arjan-bal changed the title client: ignore http status header for gRPC mode client: ignore http status header for gRPC streams Sep 15, 2025
Copy link
Contributor

@arjan-bal arjan-bal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM, left a few minor comments.

Comment on lines 1524 to 1525
case "200":
grpcErrorCode = codes.Unknown
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default case also adds a error message which seems useful. I think we should remove the special case unless it requires different handling in code. We don't need to worry too much about optimizing the error case, since they're expected to be infrequent.

// headerError is set if an error is encountered while parsing the headers
headerError string
headerError string
receivedHTTPStatus string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping on this.

"protocol error: informational header with status code %d must not have END_STREAM set", statusCode))
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
}
//In case of informational headers return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Need a space at the beginning of the comment here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@arjan-bal arjan-bal removed their assignment Sep 15, 2025
httpStatusCode *int
httpStatusErr string
rawStatusCode = codes.Unknown
rawStatusCode = codes.Internal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name isn't a good one, I think. Maybe call this grpcStatusCode and add a comment?

Suggested change
rawStatusCode = codes.Internal
grpcStatusCode = codes.Internal // the code from the grpc-status header, if present

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

// headerError is set if an error is encountered while parsing the headers
headerError string
headerError string
receivedHTTPStatus string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Comment on lines 1524 to 1525
case "200":
grpcErrorCode = codes.Unknown
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1; please remove special cases when possible most of the time. Less code = easier to understand code.

grpcMessage = decodeGrpcMessage(hf.Value)
case ":status":
c, err := strconv.ParseInt(hf.Value, 10, 32)
if !isGRPC {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is making the assumption that :status appears after content-type, which AFAIK is not required. That's why the code before does header processing by setting a bunch of locals and then looking at them later.

In this case it's not going to cause any problems, since we don't look at this variable if isGRPC is true, but I think it's better to remove it. And it's not even necessarily a performance win, since conditional branches are potentially worse for performance than setting a Go string (which is not a full copy of the string itself).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the explanation, thats a great catch! - done.

Comment on lines 1515 to 1516
// If a non gRPC response is received, then evaluate entire http status and process close stream / response.
// In case http status doesn't provide any error information (status : 200), evalute response code to be Unknown.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap comments to 80 columns.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

// In case http status doesn't provide any error information (status : 200), evalute response code to be Unknown.
if !isGRPC {
var grpcErrorCode = codes.Internal // when header does not include HTTP status, return INTERNAL
var errs []string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this back to where it was defined before; i.e. as late as possible.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

"protocol error: informational header with status code %d must not have END_STREAM set", statusCode))
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
}
//In case of informational headers return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@dfawley dfawley removed their assignment Sep 16, 2025
@Pranjali-2501 Pranjali-2501 modified the milestones: 1.76 Release, 1.77 Sep 17, 2025
@mbissa mbissa force-pushed the ignore-status-for-grpc-content-type-8486 branch from 9c920c8 to 734cd4d Compare September 17, 2025 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

client transport: ignore HTTP/2 status if content-type is application/grpc
4 participants