Aleksej_Shherbak
Aleksej_Shherbak

Reputation: 3088

Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2ConnectionErrorException

I have the following exception

Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2ConnectionErrorException: HTTP/2 connection error (PROTOCOL_ERROR): Invalid HTTP/2 connection preface.

I will describe my conditions. I have very simple grpc .Net Core project and I want make HTTP endpoint.

Here is Startup.cs

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(options => options.EnableEndpointRouting = false);
        services.AddGrpc();

        services.AddHttpClient<IAnalyticApiAsker, AnalyticApiAsker>();

        // db context
        services.AddSingleton<IApplicationMongoContext, ApplicationMongoContext>();

        // repos
        services.AddTransient<IWorkspacesRepo, WorkspaceRepo>();
        services.AddTransient<IApplicationRepo, ApplicationRepo>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        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<ApplicationsService>();
            endpoints.MapGrpcService<WorkspacesService>();
        });

        // I think I have to use this middleware for get http endpoints ability ?? Right? 
        app.UseMvc();
    }
}

And Program.cs

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

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

launchSettings.json

{
  "profiles": {
    "Dashboard": {
      "commandName": "Project",
      "launchBrowser": false,
      "applicationUrl": "http://*:50053",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Local"
      }
    }
  }
}

For the endpoint I have the following controller:

public class HealthController : Controller
{ 
    [Route("/healthcheck")]
    [HttpGet]
    public IActionResult Index()
    {
        return Ok(new
        {
            status = "ok",
            description = "works"
        });
    }
}

But when I request from a browser my application's URL http://localhost:50053/healthcheck I have this exception. it looks like Kestrel configured only for http2. If it's true, how to switch on HTTP handling?

Update

Ok, after some researching I have understood the following. It's impossible to switch on both protocol types handling: grpc (that works with HTTP 2) and ordinary http1 at the same time. Am I right?

I have modified my Startapp.cs to:

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureKestrel(options =>
            {
                options.ListenLocalhost(50053, listenOptions =>
                {
                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
                });
            })
            .UseStartup<Startup>();

HttpProtocols.Http1AndHttp2 this property has variant for each HTTP type (Http1, Http, Http1AndHttp2). But it does not work for GRPC. I think it's happening because of whether to use http1 to GRPC or http2. And I don't know how to set up it.

So, listenOptions.Protocols = HttpProtocols.Http1 - works for web, listenOptions.Protocols = HttpProtocols.Http2 - works for GRPC and listenOptions.Protocols = HttpProtocols.Http1AndHttp2 - works for web.

Update 2

I'm not sure if it answers or not, but I have solved the task with the help of the following Program.cs modification:

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

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureKestrel(options =>
            {
                options.ListenLocalhost(80, listenOptions =>
                {
                    listenOptions.Protocols = HttpProtocols.Http1;
                });

                options.ListenLocalhost(50053, listenOptions =>
                {
                    listenOptions.Protocols = HttpProtocols.Http2;
                });
            })
            .UseStartup<Startup>();
}

Now I can work with http1 and http2. But now I have a new problem, it works only on localhost.

Upvotes: 11

Views: 9101

Answers (2)

sunliusi
sunliusi

Reputation: 21

grpc need tls.

TLS is used for more than securing communication. The TLS Application-Layer Protocol Negotiation (ALPN) handshake is used to negotiate the connection protocol between the client and the server when an endpoint supports multiple protocols. This negotiation determines whether the connection uses HTTP/1.1 or HTTP/2.

https://learn.microsoft.com/en-us/aspnet/core/grpc/aspnetcore?view=aspnetcore-3.1&tabs=visual-studio

Upvotes: 1

Aleksej_Shherbak
Aleksej_Shherbak

Reputation: 3088

Ok, I have solved it with the help of the following Program.cs modification:

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

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureKestrel(options =>
            {
                options.ListenAnyIP(80, listenOptions =>
                {
                    listenOptions.Protocols = HttpProtocols.Http1;
                });

                options.ListenAnyIP(50053, listenOptions =>
                {
                    listenOptions.Protocols = HttpProtocols.Http2;
                });
            })
            .UseStartup<Startup>();
}

Now I can work with GRPC and http1 in the same time, and connection allowed for any IP.

I hope it will useful for somebody who will add http1 to GRPC configured project.

Upvotes: 8

Related Questions