fix: Handle interceptor errors as responses (#840) #842
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This PR forwards
Interceptor
errors/Status
es as valid HTTP responses, just like any other gRPC service generated bytonic
.Motivation
In the very specific case of missing Content-Type headers when requests are intercepted, this PR fixes the bugs reported in #840 and #759.
In the more general case, this class of bug arose because interceptors and their statuses are treated differently from the rest of the gRPC service stack: their errors/
Status
are bubbled up as errors to be handled at the tower service layer, but not as a gRPC response. That means that a basic HTTP response is returned, sans trailers or headers of any kind (including the missingContent-Type
header). See this repository and this comment to compare the responses generated by returning aStatus
from a method versus an interceptor.This behavior means that clients like
grpcurl
that encounter the responses from interceptors don't know how to handle them, and it's likely that this affects any consumer that depends on theContent-Type
header (among others).Solution
The solution in this PR is to handle
Status
errors the same way in both interceptors and regular ol' gRPC services: by converting those statuses to valid responses in the Interceptor service itself, matching the behavior in the existinggrpc
module.The downside to this approach is a few additional constraints on the response body type. These constraints seem reasonable given that they match up with the current uses of interceptors in
tonic
, but they do make wrapping non-tonic
services trickier.Next Steps
EDIT: looks like those constraints are too restrictive for
tonic-health
🤦. I'll take a look at either relaxing those constraints or updatingtonic-health
accordingly.EDIT 2: I've both relaxed the constraints and added a bit of restriction to clients in
tonic-build
. I'm not sure what the right mix between the two should be for a final implementation, but this does solve the issue for my use-case.