1

I am trying to send a request from android to a gRPC service. With an application of WPF net core running in the same computer it works, but from android it doesn't work. Also I have tried this WPF application in another computer (in a virtual machine) and it works fine too.

Code of my xamarion application:

private void SetVariablesServicio()
{
    _serviceAddress = "http://1.2.3.4:5001";

    using (StreamReader miCertificado = new StreamReader(Assets.Open("CA.crt")))
    {
        _caCertificate = miCertificado.ReadToEnd();
    }

    using (StreamReader miCertificado = new StreamReader(Assets.Open("Client.crt")))
    {
        _clientCertificate = miCertificado.ReadToEnd();
    }

    using (StreamReader miCertificado = new StreamReader(Assets.Open("Client.key")))
    {
        _clientPrivateKey = miCertificado.ReadToEnd();
    }
}



private async void btnTestServicio_Click()
{
    try
    {
        GrpcClient.GestorAplicaciones miGrpcClient = new GrpcClient.GestorAplicaciones(_serviceAddress, _caCertificate, _clientCertificate, _clientPrivateKey);

        string miSaludo = await miGrpcClient.PingAsync("Álvaro");

        ShowAlert("Error", miSaludo);
    }
    catch (Exception ex)
    {
        ShowAlert("Error", ex.Message);
    }
}

Common library for the gRPC clients:

public class GestorAplicaciones
    {
        #region constructor
        public GestorAplicaciones(string paramAddressService, string paramStrCertificadoCa, string paramStrCertificadoCliente, string paramStrClaveCertificadoCliente)
        {
            _serviceAddress = paramAddressService;
            _caCertificate = paramStrCertificadoCa;
            _clientCertificate = paramStrCertificadoCliente;
            _clientPrivateKey = paramStrClaveCertificadoCliente;
        }
        #endregion constructor


        #region variables de clase
        private string _serviceAddress;
        private string _caCertificate;
        private string _clientPrivateKey;
        private string _clientCertificate;
        #endregion variables de clase



        #region método de llamada al servicio
        public async Task<string> PingAsync(string paramStrNombreLlamante)
        {

            Gestor.GestorClient miClienteGrpc = GetClient();




            PingRequest input = new PingRequest { NomreLlamante = paramStrNombreLlamante };
            var reply = await miClienteGrpc.PingAsync(input);




            return reply.MensajeRespuesta;
        }



        private Gestor.GestorClient GetClient()
        {
            var httpClientHandler = new System.Net.Http.HttpClientHandler();
            httpClientHandler.ServerCertificateCustomValidationCallback =
                System.Net.Http.HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
            var httpClient = new System.Net.Http.HttpClient(httpClientHandler);
            var channel = GrpcChannel.ForAddress(_serviceAddress,
                new GrpcChannelOptions { HttpClient = httpClient });
            var client = new Gestor.GestorClient(channel);





            return client;
        }

The WPF client:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();


        _serviceAddress = "https://1.2.3.4:5001";
        _caCertificate = System.IO.File.ReadAllText("certificados/ca.crt");
        _clientPrivateKey = System.IO.File.ReadAllText("certificados/client.key");
        _clientCertificate = System.IO.File.ReadAllText("certificados/client.crt");
    }



    #region variables de clase
    private readonly string _serviceAddress;

    private readonly string _caCertificate;
    private readonly string _clientPrivateKey;
    private readonly string _clientCertificate;
    #endregion variables de clase



    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            GrpcClient.GestorAplicaciones miGrpcClient = new GrpcClient.GestorAplicaciones(_serviceAddress, _caCertificate, _clientCertificate, _clientPrivateKey);

            string miSaludo = await miGrpcClient.PingAsync("Álvaro");

            MessageBox.Show(miSaludo);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

The service:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    // Additional configuration is required to successfully run gRPC on macOS.
    // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.ConfigureKestrel(options =>
                {
                    //NOTA: se tiene que poner la IP porque si no, se dice que el equipo remoto ha denegado la conexión.
                    options.Listen(miAddress, 5001, o =>
                    {
                        o.Protocols = HttpProtocols.Http2;
                        o.UseHttps(@"certificados\service.pfx", "123456");
                    });
                })


                .ConfigureLogging(logging =>
                 {
                     logging.AddFilter("Grpc", LogLevel.Debug);
                 });

                webBuilder.UseStartup<Startup>();
            });
}

My idea is to have a common library for the client side, that could be use by the xamarin and wpf clients.

I am debugging the xamarion application deploying it in an android phone.

Why the request works from the WPF application and not from the xamarin application?

Thanks.

Álvaro García
  • 18,114
  • 30
  • 102
  • 193
  • are you getting exceptions or errors? What are they? Have you verified that your server is reachable from your Android device? Have you configured your app to allow non-https requests? – Jason Feb 16 '20 at 17:58
  • I have installed a ping application in the android device and I get response. I have tried to insecure credentials, so the service is http instead of https, but the problem is the same. Anyway, I would like to learn how to use credentials and SSL certificates. – Álvaro García Feb 16 '20 at 18:12
  • About https with credentials in Xamarin.forms ,maybe you could read [HTTPS Service With Self-Signed Certificate](https://www.c-sharpcorner.com/article/consume-https-service-with-self-signed-certificate-in-xamarin-forms/) – Leo Zhu Feb 17 '20 at 03:01
  • There is an issue in xamarin that the SocketsHttpHandler used internally seems to be an old version that doesn't support Http2. You can see this using wireshark and also by investigating the HttpClient used by the grpc channel on android. – Kavinda Gayashan Feb 22 '20 at 13:59
  • https://stackoverflow.com/questions/60100916/xamarin-forms-grpc-error-starting-grpc-call-unexpected-end-of-stream-on-connect I'm fighting with this issue that several people have reported recently on Stackoverflow. Above answer seems incomplete. I tried to debug this to the xamarin-android code. Difficult to get to the internals of it because it is a large repo with complex dependencies. Will post an answer if I managed to solve. – Kavinda Gayashan Feb 22 '20 at 13:59

1 Answers1

2

Grpc.Net fails on Xamarin because there seems to be an issue in sending http2 traffic through System.Net.Http.HttpClientHandler on monodroid.

What you can do is, add a dependency to Grpc.Core nuget and create the grpc client through it. Rest of the API is pretty much shared (The Grpc.Core.Api nuget is shared by both libraries).

// For secured grpc service
var channel = new Channel("1.2.3.4:5001", new SslCredentials());
// cleartext
var channel = new Channel("1.2.3.4:5001", ChannelCredentials.Insecure);

Please do your own research on how to create the ChannelCredentials instance. Above code works for me, but I'm not sure if it works for all scenarios.

You can remove the Grpc.Net.Client nuget dependency. Make sure to add the Grpc.Core dependency to the Xamarin.Android project as well.

The reason that Grpc.Core works is because it is a managed wrapper around the core grpc client lib written in c++. Therefore it doesn't rely on Http support from mono/.net.

For me, not only Grpc but any Http2 request fails on xamarin android because of this issue. Why does Xamarin Android fails to send GRPC/Http2 requests?

Kavinda Gayashan
  • 387
  • 4
  • 17