dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.22k stars 9.95k forks source link

Requests with MapGet or Api Controllers fail with unsecure URLs (http://) and HTTP/2 #23872

Closed ChiefInnovator closed 4 years ago

ChiefInnovator commented 4 years ago

Requests fail with unsecure URLs (http://) and HTTP/2

We are trying to host both gRPC services and MapGet [or WebApi] endpoints. Interestingly, the gRPC template does exactly this. When you use the sample out-of-the-box, it works. But when we change to use an unsecure applicationUrl in launchSettings MapGet [or WebApi] stops working. The MapGet("/") fails with an unsecure URL (ex. http://localhost:5000). This does not make sense. It worked fine over https://localhost:5001 just fine. The only difference is TLS.

NOTE: We are using unsecure services due to these services being hosted in a private vnet and us doing TLS offloading. This is also happening in Kubernetes inside of containers.

Scenario #1 - Secure URL (https://localhost:5001), Http2 Protocol MapGet Succeeds, gRPC Succeeds

Scenario #2 - Unsecure URL (http://localhost:5000), Http2 Protocol MapGet Fails, gRPC Succeeds

What operating system (Linux, Windows,...) and version?

Windows 10

What did you do?

Start with ASP.NET Core gRPC template. dotnet new grpc -o HelloGrpc

Change launchSettings to unsecure URL breaks MapGet

  "profiles": {
    "DeviceQuery.Host": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }

Kestrel is using HTTP/2 "Kestrel": {

"EndpointDefaults": {
"Protocols": "Http2"
}

What did you expect to see? MapGet should just work when using an insecure URL and protocol Http2.

What did you see instead? MapGet fails with with ERR_INVALID_HTTP_RESPONSE. Changing the URL back to https://localhost:5001 fixes the issue. Unfortunately we definitely need unsecure URLs.

To Reproduce

I create a sample project that shows the issue ... https://github.com/ChiefInnovator/repro_http2_http_issue

It is currently setup to fail by using the launchSettings: applicationUrl of "http://localhost:5000" to demonstrate the issue. If you change the URL to "https://localhost:5001", it works fine.

Further technical details


NET Core SDK (reflecting any global.json):
 Version:   3.1.301
 Commit:    7feb845744

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.20161
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\3.1.301\

Host (useful for support):
  Version: 3.1.5
  Commit:  65cd789777

.NET Core SDKs installed:
  2.1.806 [C:\Program Files\dotnet\sdk]
  2.2.207 [C:\Program Files\dotnet\sdk]
  2.2.402 [C:\Program Files\dotnet\sdk]
  3.0.103 [C:\Program Files\dotnet\sdk]
  3.1.301 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.0-preview9.19424.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.0-preview3.19555.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.0-preview9-19423-09 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.0-preview3.19553.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.0.0-preview9-19423-09 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.0-preview3.19553.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Tratcher commented 4 years ago

You're getting that error in a browser? That's a client limitation, not a server issue.

Most clients require https (TLS+ALPN) to negotiate Http/2, that's right in the spec. When you tell the server to do Http/2 without Https that's an alternative mode called "prior knowledge", which most clients (and servers) don't support. You would have to set a similar setting on the client for it to work because there's no way to negotiate the version without ALPN.

ChiefInnovator commented 4 years ago

Your suggestion is that this is a browser issue which I understand why you would say that. So I coded up a sample using the new HttpClient with Http/2 support (see below). So using the new HttpClient and setting the version to Http/2 still fails with an unsecure url. I am using the latest .NET Core 3.x. Unfortunately it still fails.

var httpClient = new HttpClient();

var req = new HttpRequestMessage(HttpMethod.Get, url)
{
    Version = HttpVersion.Version20,
};

var resp = await httpClient.SendAsync(req);
Console.WriteLine($"Version: {resp.Version}");
ChiefInnovator commented 4 years ago

I just tested my client code against a known endpoint that supports HTTP/2 with unsecure URLs. It works fine. So this does not seem to be a client issue, but a server side issue with ASP.NET Core.

var client = new HttpClient();

var req = new HttpRequestMessage(HttpMethod.Get, "http://http2.akamai.com/demo"){ Version = new Version(2, 0) };

var x = await client.SendAsync(req); var version = x.Version;

Console.WriteLine(version);

ChiefInnovator commented 4 years ago

I figured out a workaround.

Tratcher commented 4 years ago

Note HttpClient treats HttpRequestMessage.Version as a maximum version, not an exact version. See https://github.com/dotnet/runtime/issues/987

HttpClient currently requires an AppContext switch in order to support HTTP/2 without https. https://github.com/dotnet/aspnetcore/blob/fae3dd12aeba7c9995f69bfaa1c9b74d82307ef1/src/Servers/Kestrel/test/Interop.FunctionalTests/HttpClientHttp2InteropTests.cs#L32-L33

http://http2.akamai.com/demo redirects to https://http2.akamai.com/demo in order to support HTTP/2.

ChiefInnovator commented 4 years ago

Tratcher, Thanks for all the help. I figured out that Akamai was redirecting. Ugh! My bad. 👍

Also, thanks for the update for the client support.