I am having a golang server that is sending GRPC stream and I am trying to connect to it via windows .NET client but I am only able to do it if I connect to the grpc-web server directly and the connection doesn't work if I use proxy_pass or grpc_pass over nginx to pass through this connection.
This is my nginx configuration :
server {
listen 8081;
server_name localhost;
location / {
root /usr/share/nginx/html/app-mobile;
index index.html;
add_header Pragma public;
add_header Cache-Control "public";
expires $expires;
etag off;
# First attempt to serve request as file, then as directory, then fall back to index.html.
try_files $uri $uri/ /index.html;
}
location /api/printerconnection/v1/ {
proxy_pass http://0.0.0.0:8107/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
This is how I connect to it from .NET client :
private static async Task waitForOrderGRPCstream()
{
var httpHandler = new HttpClientHandler() { UseProxy = true, AllowAutoRedirect = true };
using var channel = GrpcChannel.ForAddress("http://localhost:3031/api/printerconnection/v1", new GrpcChannelOptions
{
HttpHandler = new GrpcWebHandler(httpHandler)
});
var client = new PrinterConnectionClient(channel);
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5000));
try
{
var myRequest = new GetPrinterConnectionRequest {};
Console.WriteLine($"Making GRPC Call for streaming");
using var streamingCall = client.GetPrinterConnectionStream(myRequest, cancellationToken: cts.Token);
Console.WriteLine($"Will now wait GRPC streaming...");
while (await streamingCall.ResponseStream.MoveNext(cancellationToken: cts.Token))
{
var response = streamingCall.ResponseStream.Current;
// Handle the incoming response from the server.
Console.WriteLine($"Received GRPC message: {response.Content}");
}
}
catch (Exception ex)
{
Console.WriteLine("Stream cancelled because of : "+ex);
}
}
The above connection gives error Stream cancelled because of : Grpc.Core.RpcException: Status(StatusCode="Unknown", Detail="Bad gRPC response. HTTP status code: 405")
I tried changing to different nginx headers and tried adding different options to the HttpClienthandler on .NET client side but the error persists.
But when I replace http://localhost:8081/api/printerconnection/v1
with http://localhost:8107
to connect to my GRPC web server directly, it works without any issues and I am able to see the content that I send from my server.
The problem turned out to be because services hosted in subdirectories were not supported.
For more information : https://learn.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-7.0#calling-grpc-services-hosted-in-a-sub-directory
So I had to add a sub directory handler like this :
/// <summary>
/// A delegating handler that adds a subdirectory to the URI of gRPC requests.
/// </summary>
public class SubdirectoryHandler : DelegatingHandler
{
private readonly string _subdirectory;
public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
: base(innerHandler)
{
_subdirectory = subdirectory;
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var old = request.RequestUri;
var url = $"{old.Scheme}://{old.Host}:{old.Port}";
url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
request.RequestUri = new Uri(url, UriKind.Absolute);
return base.SendAsync(request, cancellationToken);
}
}
And use this from the code like this :
var handler = new SubdirectoryHandler(new HttpClientHandler(), "/api/printerconnection/v1");