headers are not making their way back to the Grpc library
mbruce opened this issue · 5 comments
When doing a simple unary call, I can see the server sending the response correctly. I can also debug this library and see that the grpc-status is in the trailing header.
That is coming in to GRPCBestHttpHandler, and the bestRequest.OnHeadersReceived gets the trailing headers and these are set with grpcResponseMessage.TrailingHeaders.
However, Grpc library is getting:
Exception: Grpc.Core.RpcException: Status(StatusCode="Cancelled", Detail="No grpc-status found on response.")
Full stack from GRPC is:
Exception: Grpc.Core.RpcException: Status(StatusCode="Cancelled", Detail="No grpc-status found on response.")
at Grpc.Net.Client.Internal.HttpClientCallInvoker.BlockingUnaryCall[TRequest,TResponse] (Grpc.Core.Method`2[TRequest,TResponse] method, System.String host, Grpc.Core.CallOptions options, TRequest request) [0x00016] in <d97e013995eb4d27a7e6fafbdd95d13e>:0
at Grpc.Core.Interceptors.InterceptingCallInvoker.<BlockingUnaryCall>b__3_0[TRequest,TResponse] (TRequest req, Grpc.Core.Interceptors.ClientInterceptorContext`2[TRequest,TResponse] ctx) [0x0001b] in <18da7575174844b1b40d2cd0a1e5ca02>:0
at Grpc.Core.ClientBase+ClientBaseConfiguration+ClientBaseConfigurationInterceptor.BlockingUnaryCall[TRequest,TResponse] (TRequest request, Grpc.Core.Interceptors.ClientInterceptorContext`2[TRequest,TResponse] context, Grpc.Core.Interceptors.Interceptor+BlockingUnaryCallContinuation`2[TRequest,TResponse] continuation) [0x00009] in <18da7575174844b1b40d2cd0a1e5ca02>:0
at Grpc.Core.Interceptors.InterceptingCallInvoker.BlockingUnaryCall[TRequest,TResponse] (Grpc.Core.Method`2[TRequest,TResponse] method, System.String host, Grpc.Core.CallOptions options, TRequest request) [0x00010] in <18da7575174844b1b40d2cd0a1e5ca02>:0
Hi @mbruce , what grpc server implementation are you using (and which version)?
(I assume I only verified unary calls where the status is already in the answer given that the answer is small and buffered.)
server is in java, and I'm using NettyServerBuilder (version: io.grpc:grpc-netty:1.49.2). the call is very small. pretty much a hello world.
service TestService {
rpc JoinGame(JoinGameRequest) returns (JoinGameResponse) {}
}
message JoinGameRequest {
string game_id = 1;
string player_id = 2;
string skin_name = 3;
float spawn_x = 4;
float spawn_y = 5;
}
message JoinGameResponse {
bool success = 1;
// if failed, the reason for failure
optional string error_reason = 2;
}
Digging through GRPC source I found that it is expecting to find trailing headers in grpcResponseMessage.RequestMessage.Properties["__ResponseTrailers"]
so I made this private class in GRPCBestHttpHandler:
private class ResponseTrailers : HttpHeaders {}
and then I modified GRPCBestHttpHandler like this:
// Write incoming headers OR trailing headers
bestRequest.OnHeadersReceived += (HTTPRequest _, HTTPResponse response, Dictionary<string, List<string>> headers) =>
{
// Only leading header contains :status
bool isLeadingHeader = headers.Keys.Contains(":status");
ResponseTrailers trailers = null;
if (!isLeadingHeader) trailers = new ResponseTrailers(); // <------- Added line
foreach (KeyValuePair<string, List<string>> kvp in headers)
{
// Either leading or trailing header
if (isLeadingHeader)
{
grpcResponseMessage.Content.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value);
}
else
{
trailers.Add(kvp.Key, kvp.Value); // <------- Added line
grpcResponseMessage.TrailingHeaders.TryAddWithoutValidation(kvp.Key, kvp.Value);
}
}
if (isLeadingHeader)
{
// Copy HTTP status fields
grpcResponseMessage.ReasonPhrase = response.Message;
grpcResponseMessage.StatusCode = (HttpStatusCode) response.StatusCode;
grpcResponseMessage.Version = new Version(response.VersionMajor, response.VersionMinor);
}
// If it is a trailing header and contains grpc-status we reached EOF
else if (headers.Keys.Contains("grpc-status"))
{
incomingDataStream.Close();
grpcResponseMessage.RequestMessage.Properties["__ResponseTrailers"] = trailers; // <------- Added line
}
};
The GRPC code that handles this can be seen here: https://github.com/grpc/grpc-dotnet/blob/master/src/Shared/TrailingHeadersHelpers.cs. It seems the problem is actually that we installed .netstandard2 dlls and we could have installed .nestandard2.1 since latest LTS Unity 2021.3 support it.
Thanks @igor84 for figuring this out. I also verified that the reported issue happens only when you use netstandard2.0
of https://www.nuget.org/packages/Grpc.Net.Client/2.50.0. So updating to netstandard2.1
should fix your issue @mbruce.
In general, the versions of all dependencies this code was tested on are mentioned here: https://github.com/doctorseus/grpc-dotnet-unity#version-100