gRPC status code header destroyed on error
See original GitHub issueBeen using this grpc-web client on a project for a while, but recently started handling specific grpc errors on the front-end. Our requests hit an Envoy proxy, which does its own httpToGrpc status conversion, but in the grpc-web-client code specifically we are also losing the grpc-status header.
Through some local testing, it looks like the problem comes from https://github.com/improbable-eng/grpc-web/blob/master/ts/src/client.ts#L99
const code = httpStatusToCode(status);
this.props.debug && debug("onHeaders.code", code);
const gRPCMessage = headers.get("grpc-message") || [];
this.props.debug && debug("onHeaders.gRPCMessage", gRPCMessage);
if (code !== Code.OK) {
const statusMessage = this.decodeGRPCStatus(gRPCMessage[0]);
this.rawOnError(code, statusMessage);
return;
}
My problem could be solved if instead of passing the newly created code
to rawOnError, it checked for a grpc-status header first and used that value before defaulting to the new code
. Something like:
const code = parseInt(headers.get("grpc-status"), 10) || httpStatusToCode(status);
This seems to be the simplest way to fix it, although there may be reasons why this is unwanted.
Option 2: If we made sure the headers were being passed correctly, we could just pull them off the response. Right now when it throws an error it makes a new Metadata()
and hands that off, but it that isn’t very helpful.
rawOnError(code: Code, msg: string) {
this.props.debug && debug("rawOnError", code, msg);
if (this.completed) return;
this.completed = true;
this.onEndCallbacks.forEach(callback => {
detach(() => {
callback(code, msg, new Metadata());
});
});
}
If instead, rawOnError took an optional third parameter of the headers
rawOnError(code: Code, msg: string, headers?: Metadata) {
and passed those (if they exist) to the callback, we could accomplish essentially the same thing.
callback(code, msg, headers || new Metadata());
I could submit a PR for this anytime, just wanted to know if there were any extra thoughts about potential hiccups or problems with either of these. Personally, I would opt for the first option for simplicity.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:2
- Comments:5 (5 by maintainers)
@easyCZ After digging through this more, it appears the tests that are testing “error” cases are not actually ever hitting the error case here:
(This is in client.ts)
The test server returns 200 responses even when “testing” the grpc error statuses, and with the current way the responses are handled that gets converted into a grpc status of
0
by thehttpStatusToCode
method.So in the tests, while the test server returns a grpc error status, the http status is 200, thus bypassing the if statement. Many tests test for “headers / trailers” which are never available inside the error case. So it looks like to me that the tests are giving false positives.
I still think the appropriate change is to NOT do the http code conversion if there is a valid
grpc-status
on the headers. However this would mean that for users who are currently returning http 200 but with a grpc-error code, they would now be hitting the error case and I can see this being a breaking change. That said, I still think its an appropriate change.Are you guys willing to address this issue? What are your thoughts?
Here is an example of a test that is exhibiting this behavior: (https://github.com/improbable-eng/grpc-web/blob/master/test/ts/src/invoke.spec.ts#L283)
This test is checking for headers/trailers with a grpc error code. It works currently because the server while it does add the grpc-status
12
to the response, the http status is 200. It never hits therawOnError
inside theif (code !== Code.OK) {
block, and instead always hits thethis.rawOnHeaders(headers);
just after that check. The primary difference being that the headers are provided torawOnHeaders
but not torawOnError
.When I make the proposed change it causes many of the tests to fail because they are no longer hitting the
rawOnHeaders
and instead hitting therawOnError
.I believe this was fixed by #226.