It’s been one of those days when you set out to do something so simple and yet you get distracted by having to fix something that should just work. I’ll set the scene – I wanted to generate a simple ASP.NET Core Web API that would return the HTTP protocol and headers of a particular request. All up I think the code for this took me about 30seconds to write as follows (this was in a new ASP.NET Core 3 project created using the Api template in Visual Studio 2019):
public ActionResult<IEnumerable<string>> Get()
var headers = (from h in Request.Headers
select h.Key + ” – ” + h.Value).ToList();
headers.Add(“Http – ” + string.Join(“,”, Request.Protocol));
When I ran this in Visual Studio it launched the browser and did indeed return the headers and HTTP protocol version. At this point I was a bit surprised as it return Http/2 even though I had done nothing either in the browser or the service to indicate that I wanted Http/2.
Realising that this was something that the browser was negotiating I thought I’d see what result I got when I called the service from Fiddler where I could control what Http version was being requested. As you can see from the image what I got back was a 502 response:
[Fiddler] The connection to ‘localhost’ failed. <br />System.Security.SecurityException Failed to negotiate HTTPS connection with server.fiddler.network.https> HTTPS handshake to localhost (for #1) failed. System.IO.IOException Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. < An existing connection was forcibly closed by the remote host
This was frustrating because this should have just worked. Furthermore there was no exception raised within my ASP.NET Core project. I was running my project on Kestrel which also was exposing a non-https endpoint, http://localhost:5000/api/values. However, the Api template adheres to best practice and comes with the line “app.UseHttpsRedirection” in Startup.cs which caused the request from Fiddler to be redirected to https, which of course then fails as before. If I remove the redirection, the request again fails with the 502 exception.
Luckily I’ve been in this situation and realised that whilst there’s no exception being raised in my code, there was most likely an exception being thrown internally as part of the ASP.ENT Core middleware. To investigate this further I firstly made sure that all “Common Language Runtime Exceptions would trigger a break in Visual Studio (you need to run the application in order to see this window by default, or you can open it from the Debug / Windows / Exception Settings menu item).
By itself this isn’t sufficient, you also need to uncheck the “Enable Just My Code” checkbox in Tools / Options menu item.
Invoking the service from Fiddler now generates the following exception:
System.Security.Authentication.AuthenticationException: ‘Authentication failed, see inner exception.’
InnerException: Win32Exception: The client and server cannot communicate, because they do not possess a common algorithm.
After a bit of investigation I realised that the combination of this exception and the 502 response returned to Fiddler was pointing to a miss-match between the protocols being requested and those supported. Out of the box Fiddler requests only support a very limited set of protocols for secure connections, shown on the Https tab in the Options dialog.
Clicking on the list of protocols allows you to edit them, in this case to include tls 1.1 and 1.2.
After applying this change I was able to execute the requests from Fiddler (in this case I’ve left the Https redirection off) and see that the Http protocol matches the 1.1 of the request.
The truly annoying thing after all this effort, it would appear the Fiddler doesn’t appear to actually support Http/2, despite there being a dropdown on the Compose tab for it. Using the Http/2.0 option causes exceptions to be raised within the ASP.Core application. Furthermore this seems to be consistent with what happens if you attempt to intercept requests coming from Chrome (they get reverted to HTTP/1.1) and this post.
What does work is using Curl from the command line. However you’ll find that the version you have installed may not support http/2.0 requests. If this is the case, you should download the latest version from https://curl.haxx.se/download.html
At this point it’s also worth having a read through the ASP.NET Core 3 information on Kestrel hosting, specifically the part that talks about http/2 support. In your appsettings.json you can adjust whether you want Http1, Http1AndHttp2, or just Http2 support.
Depending on what protocols you choose to support, you’ll find that different CURL commands will work. Here are some examples:
Protocols = Http2 in the appsettings.json file
curl http://localhost:5000/api/values –http2 -v -k
This attempts to connect with Http1.1 with header Connection: Upgrade, HTTP2-Settings but this fails as connection is Http (not supported scenario on Kestrel)
curl https://localhost:5001/api/values –http2 -v -k
As part of Https negotiation this also upgrades from http1.1 connection to http2. Request succeeds over Http/2
curl http://localhost:5000/api/values –http2-prior-knowledge –v -k
This forces a http/2 connection but still unsecured
Note that the –v option for curl shows verbose information, whilst –k is required when connecting to Kestrel on local machine since the default developer certificate isn’t trusted.