So I have a gRPC server written in Rust using the Tonic crate. I have all the proto files written and I can make requests using a Tonic based client and grpcurl so there are no issues on the server side.
I have a Unity 3D based game which I want to use as a client. Unity uses C#/.NET/Mono so in theory it should be easy to get a gRPC client working. It turns out it isn't so easy.
According to the internet (in particular https://grpc.io/blog/grpc-csharp-future/) we should be using the Grpc.Net.Client package. So I dropped that in to Unity using the very useful NuGetForUnity tool.
I got the following error which at least was very informative
PlatformNotSupportedException: gRPC requires extra configuration on .NET implementations that don't support gRPC over HTTP/2. An HTTP provider must be specified using GrpcChannelOptions.HttpHandler.The configured HTTP provider must either support HTTP/2 or be configured to use gRPC-Web. See https://aka.ms/aspnet/grpc/netstandard for details.
After some Googling I came across the following links which make it clear that Unity/Xamarin and others don't support Grpc.Net.Client.
https://github.com/grpc/grpc-dotnet/issues/1309#issuecomment-850303082
https://learn.microsoft.com/en-us/aspnet/core/grpc/netstandard?view=aspnetcore-6.0
In summary:
.NET implementations that don't support HTTP/2, such as UWP, Xamarin, and Unity, can use gRPC-Web as an alternative.
That sounded straight forward so I imported Grpc.Net.Client.Web and hooked it up as suggested in the article.
At least the request went through this time, but the gRPC server started causing a “Connection reset by peer” error.
Breaking out tcpdump, I realised that gRPC web makes HTTP 1.x requests which Tonic does not like.
I found the 'accept_http1' config option in Tonic which has the following description and set to to "true".
Accepting http1 requests is only useful when developing grpc-web enabled services. If this setting is set to true but services are not correctly configured to handle grpc-web requests, your server may return confusing (but correct) protocol errors.
After doing that, the server would at least accept the connection, but I started getting an error:
(StatusCode="Cancelled", Detail="No grpc-status found on response.")
I saw this issue https://github.com/grpc/grpc-dotnet/issues/1164 but it didn't help at all.
Seems like Tonic doesn't like the Grpc.Net.Client.Web implementation of gRPC-web or just doesn't support it properly.
I then spent ages trying to force gRPC-Web to use HTTP/2 by using TLS (which I’m not sure even made sense) but I couldn’t get the TLS handshake to work with the Tonic server. I kept getting the vague message:
TlsException: Handshake failed - error code: UNITYTLS_INTERNAL_ERROR, verify result: (some large number)
I’m pretty sure it is because Tonic was expecting TLS 1.3 and the version of Mono that Unity uses has a maximum TLS version of 1.2 (and even that was only supported recently). I couldn’t work out how to configure Tonic/Rustls to accept a lower version of TLS.
I also realised after reading the documentation more thoroughly that I would lose a lot of good gRPC features such as streaming if I used gRPC-web so I decided to avoid that method.
Does anyone know of a way to plug in a custom HTTP client into the gRPC library that does all the stuff that Unity doesn't (e.g. HTTP/2 and TLS 1.3)? If not does anyone know of a workaround that will let me at least get the RPC working correctly?
Thanks!
tonic_web
andaccept_http1
with version 2.40. Would love to get real HTTP2 support, of course, but it's "years away" from shipping with Unity. – Superheterodyne