graphql-client icon indicating copy to clipboard operation
graphql-client copied to clipboard

Invalid Cast Exception when using Subscriptions

Open adlartuten opened this issue 2 years ago • 2 comments
trafficstars

I have been running into System.InvalidCastException when trying to set up a GraphQL subscription. I have tracked the error occuring to line 318 of GraphQLHttpWebSocket in the repo:

var certs = ((HttpClientHandler)Options.HttpMessageHandler).ClientCertificates;

I am using .NET Maui and am not entirely sure if I am doing something wrong or if it is a MAUI or repo issue.

adlartuten avatar Aug 03 '23 15:08 adlartuten

We've seen limitations at this point before (in earlier cases with WASM, see https://github.com/graphql-dotnet/graphql-client#blazor-webassembly-limitations).

Could you try to find out what type Options.HttpMessageHandler actually is in your app (because it obviously isn't of type HttpClientHandler or derived from it)?

rose-a avatar Aug 03 '23 18:08 rose-a

Ah, I definitely see my issue here now. My MessageHandler is a custom class in order to get around SSL when using the Android emulator for testing. It was the best solution I found for MAUI testing when connecting to localhost: https://github.com/dotnet/maui/discussions/8131 https://gist.github.com/Eilon/49e3c5216abfa3eba81e453d45cba2d4

The code I'm using looks like this:

public GraphQLHttpClient Client { get; set; }

var options = new GraphQLHttpClientOptions { HttpMessageHandler = GetInsecureHandler(), EndPoint = new Uri(endpoint) };
Client = new GraphQLHttpClient(options, new NewtonsoftJsonSerializer());

public HttpMessageHandler GetInsecureHandler()
{
    var handler = new CustomAndroidMessageHandler();
    handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
    {
        if (cert.Issuer.Equals("CN=localhost")) return true;
        return errors == System.Net.Security.SslPolicyErrors.None;
    };
    return handler;
}

internal sealed class CustomAndroidMessageHandler : Xamarin.Android.Net.AndroidMessageHandler
{
    protected override Javax.Net.Ssl.IHostnameVerifier GetSSLHostnameVerifier(Javax.Net.Ssl.HttpsURLConnection connection)
        => new CustomHostnameVerifier();

    private sealed class CustomHostnameVerifier : Java.Lang.Object, Javax.Net.Ssl.IHostnameVerifier
    {
        public bool Verify(string? hostname, Javax.Net.Ssl.ISSLSession? session)
        {
            return
                Javax.Net.Ssl.HttpsURLConnection.DefaultHostnameVerifier.Verify(hostname, session)
                || hostname == "10.0.2.2" && session.PeerPrincipal?.Name == "CN=localhost";
        }
    }
}

I am honestly not very familiar with SSL connections and backend development in general, could this solution be modified to be compatible? Or would the better option just be to somehow get a valid SSL certificate set up on the Android emulator?

Thank you so much for your patience and help!

adlartuten avatar Aug 07 '23 17:08 adlartuten