Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gRPC improve error status for non-200 h2 response status #2221

Merged
merged 2 commits into from
May 27, 2022

Conversation

Scottmitch
Copy link
Member

Motivation:
ServiceTalk currently assumes the response status is 200
when decoding into gRPC. This means we will fail during
checking the content-type or during deserialization if
an error is return from a middle proxy with a different
encoding type. We can provide more informative gRPC
error codes if we validate the h2 response status.

statusCode == GATEWAY_TIMEOUT.code() || statusCode == TOO_MANY_REQUESTS.code()) {
grpcStatusCode = UNAVAILABLE;
} else if (statusCode == UNAUTHORIZED.code()) {
grpcStatusCode = UNAUTHENTICATED;
Copy link
Contributor

Choose a reason for hiding this comment

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

I would map this to PERMISSION_DENIED which implies external action is required before it is wort attempting again. UNAUTHENTICATED that with authentication the result might succeed.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think UNAUTHENTICATED make sense in this case.

https://datatracker.ietf.org/doc/html/rfc7235#section-3.1

The 401 (Unauthorized) status code indicates that the request has not
been applied because it lacks valid authentication credentials for
the target resource.

Where 403 Forbidden means we authenticated the client, but permission was denied.

The unit test also confirms we use a consistent strategy to grpc-java.

@Scottmitch
Copy link
Member Author

test failure due to #2117

@Scottmitch Scottmitch force-pushed the grpc_200 branch 2 times, most recently from da15823 to 6c81cea Compare May 26, 2022 14:36
@@ -49,7 +49,7 @@ final class HttpResponseUponGrpcRequestTest {
ServerContext serverContext = HttpServers.forAddress(localAddress(0))
.protocols(h2Default())
.listenAndAwait((ctx, request, responseFactory) ->
succeeded(responseFactory.badRequest().payloadBody(responsePayload, textSerializerUtf8())));
succeeded(responseFactory.ok().payloadBody(responsePayload, textSerializerUtf8())));
Copy link
Member Author

@Scottmitch Scottmitch May 26, 2022

Choose a reason for hiding this comment

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

protocol compatibility now tests all http response codes, this test validates content-type (which is what is was asserting before this change too bcz we weren't validating http status codes)

} else if (statusCode == NOT_FOUND.code()) {
grpcStatusCode = UNIMPLEMENTED;
} else if (statusCode == BAD_REQUEST.code() || statusCode == REQUEST_HEADER_FIELDS_TOO_LARGE.code()) {
grpcStatusCode = INTERNAL;
Copy link
Member

@idelpivnitskiy idelpivnitskiy May 26, 2022

Choose a reason for hiding this comment

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

I'm surprised grpc-java maps these cases to INTERNAL.
In HTTP, bad request usually means that client sent something incorrectly rather than a server-side error occurred. I would expect INVALID_ARGUMENT in both of these cases.

Also surprised that 412 Precondition Failed is not mapped to FAILED_PRECONDITION and many other 4xx/5xx mappings could have a better grpc-status code.

Copy link
Member Author

Choose a reason for hiding this comment

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

I took a stab at providing better mappings. I would suggest we can refine the http response code mapping in followup as well.

// grpc-java maps 1xx responses to error code INTERNAL, we currently map to UNKNOWN. The test server
// isn't following the http protocol by returning only a 1xx response and each framework catches
// this exception differently internally.
assertThat("mismatch for h2 response code: " + httpCode, stCode, equalTo(UNKNOWN.value()));
Copy link
Member

Choose a reason for hiding this comment

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

  1. Consider adding assertion for grpc status code too:
assertThat("mismatch for h2 response code: " + httpCode, grpcJavaCode, equalTo(INTERNAL.value()));
  1. Can you please clarify what prevents us from mapping it to INTERNAL too?

Copy link
Member Author

Choose a reason for hiding this comment

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

(1) -> done

(2) -> when there is a 1xx response w/out an expect request our client ignore the initial response headers, splicer sees a NettyBuffer and tries to cast it to HttpResponseMetaData (fails with ClassCastException) which gets mapped to unknown via DefaultGrpcClientBuilder$CatchAllHttpClientFilter.

Motivation:
ServiceTalk currently assumes the response status is 200
when decoding into gRPC. This means we will fail during
checking the content-type or during deserialization if
an error is return from a middle proxy with a different
encoding type. We can provide more informative gRPC
error codes if we validate the h2 response status.
@Scottmitch Scottmitch merged commit 05a015c into apple:main May 27, 2022
@Scottmitch Scottmitch deleted the grpc_200 branch May 27, 2022 04:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants