grpc / grpc-dotnet

gRPC for .NET
Apache License 2.0
4.17k stars 766 forks source link

GrpcWeb on AspNet.Core on Azure: Grpc.Core.RpcException #2367

Closed dgerding closed 7 months ago

dgerding commented 7 months ago

Thanks to JNK et al for very thorough documentation and examples in ASP NET Core Documentation repo. I think I’m close, but I still cannot get reliable , dev and ops dial-tone for my (hopefully) grpc-enable web service hosted on Azure.

Problem: Non-Functional Grpc on deployed

I'm returning to grpc on asp.net core having last "visited" in 2021. I put that project on hold because I couldn't ever find the right mix to create a devops-friendly aspnet core webservice that was hosted on a linux azure container. Linux docker appservice was newer to azure then.

The "gotcha" (I think) always seemed to be:

Current Bug

My best effort code (.net 8 linux container targeting, using grpc-web via aspnetcore grpc web) is currently deployed on azure and the asp net core page functionality, ssl (azure cert and keyvault secret store), authentication are all working well, connect to sql etc... But, attempts to call the grpc services hosted in the web service result in:

Grpc.Core.RpcException
HResult=0x80131500
Message=Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The HTTP/2 server sent invalid data on the connection. HTTP/2 error code 'PROTOCOL_ERROR' (0x1). (HttpProtocolError) HttpProtocolException: The HTTP/2 server sent invalid data on the connection. HTTP/2 error code 'PROTOCOL_ERROR' (0x1). (HttpProtocolError)", DebugException="System.Net.Http.HttpRequestException: The HTTP/2 server sent invalid data on the connection. HTTP/2 error code 'PROTOCOL_ERROR' (0x1). (HttpProtocolError)")

Related Azure App Service Runtime Settings

*Trying to set this to Grpc-only killed site page serving. Still need to serve pages.( What’s the correct setting for grpc web on asp net core ?) ** Added this Environment Setting toward support of grpc-web on azure (?) per https://learn.microsoft.com/en-us/azure/app-service/configure-grpc

Current appsettings.json

{

  "Logging": {
    "LogLevel": {
      "Default": "Trace",
      "System": "Trace",
      "Grpc": "Trace",
      "Microsoft": "Trace",
      "Microsoft.AspNetCore": "Trace"
    }
  },
  "AllowedHosts": "*",
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http2"
    }
  }
}

Related Program.cs and Startup.cs Fragments

From Program.cs ConfigureHttps

private static async void ConfigureHttps(HttpsConnectionAdapterOptions options)
 {
     try
     {
             // set the Kestrel HTTPS certificate
             options.ServerCertificate = await SetRuntimeCertificate(false);

             if (options.ServerCertificate == null) 
             {
                 throw new Exception("SSL Certificate could net be retrieved.");
             }

             //TODO: Resolve if we should spec 13 or 12
             options.SslProtocols = System.Security.Authentication.SslProtocols.Tls13;

     }
     catch (Exception ex)
     {
         Console.Error.WriteLine($"unable to load https cert: {ex}");
         throw;
     }
 }

From startup.cs Configure(IServiceCollection services…

services.AddGrpc(o => o.EnableDetailedErrors = true); //(options => { options.EnableDetailedErrors = true; })           

From startup.cs Configure(IApplicationBuilder…

    if (env.IsDevelopment())
    {

        app.UseDeveloperExceptionPage();
        app.UseMigrationsEndPoint();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();

    app.UseRouting();

    //PER 
    app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true });

    app.UseIdentityServer();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        // Communication with gRPC endpoints must be made through a gRPC client.
        // To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909
        endpoints.MapGrpcService<MembershipService>().EnableGrpcWeb();
        endpoints.MapRazorPages();

    });

Azure-Friendly Example?

Is there any chance someone has an example, or could modify an one of ASP NET Docs GRPC samples to demonstrate the correct way to set up an ASP NET Core Web Service

I need a sample solution that shows how I can reliably develop with and test locally while publishing regularly to Azure.

Any help appreciated. To reiterate: Are there examples or docs anywhere that can reliably deploy a GRPC-capable asp.net core app as azure web service with azure ?

Or anyone see fixes to my code initialization approaches documented above?

Thanks

JamesNK commented 7 months ago

I'm not experienced with using gRPC on Azure. I think you should reach out to them.

dgerding commented 7 months ago

@JamesNK Thanks. And kudos and thanks for all the code you've shared with world! Wow.

I have asked abundantly in Azure land, and worked with paid azure support. Too mixed a stack (asp.netcore, azure, ssl cert etc..) so everyone can "shrug and point at other guy" :). I really have been trying to get grpc working on Azure for 3 years. Seems like a reasonable assumption that someone on azure side of the world would tune in.

Looking at what I shared and the error I am getting do you have any clues? Honestly, I assume you would be most qualified in room to suggest what might be throwing the error or what I could be asking in Azure or AspNetCore communities to get grpc.net working on azure deploy.

I am going to repost this on AspNetCore repo since the grpc examples are part of AspNetCore Documentation.

My other option was to bounty on SO - which I've never tried, but I would happily "bounty" on SO an "azure friendly" pair of grpc.net on aspnetcore azure hosted web service example (figure one grpc example, one grpc-web) projects that:

if you think that will help me move to resolution.

My SetRuntimeCertificate method could easily be extended in an example to retrieve a cert from a store like azure keyvault or just load a local file cert so that the solution works with Azure even if the cert is not azure hosted, which is often the case. (In my case, it's a full MS Azure web service mix of keyvault hold cert etc...)

The maddening bit is I did, briefly, have it "all working" on azure (ssl, web service, grpc.net services grpc-web aspnet core libraries) but as the azure and grpc and asp.net core libraries churned, I lost the right mix and configuration settings.

Again, the version I have now, using the approaches demoed in code fragments, does everything it's supposed to (ssl, page serving, db-connecting, authenticating). It's only the calls to grpc I can't get to reliably host on azure.

(If I can call dotnet.grpc from my Maui 8 clients (cross-platform), I'm happy to leave the grpc-web variant in the rearview and do grpc only. I don't need to call my grpc from anything other than .net 8 client runtimes)

(as Princess Leia ): Can you point in the right direction, you're my best hope? :)