Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Publishing ASP.NET Core 3 Web API to Azure App Service with Http/2

In my previous post I was testing a new ASP.NET Core 3 Web API that I’d created that simply returns header and http information about the request. Having got everything working locally I decided that I should push it into an Azure App Service to make it accessible from anywhere (this seemed to be easier than attempting to connect to my locally running service from a Xamarin.Forms application). Here’s the process:

Right-click on the ASP.NET Core project and select Publish.

image

In this case we’re going to select App Service (ie a Windows host) and Create New, followed by the Publish button. Next we need to give the new App Service a name and specify both a Resource Group and an App Service Plan – in this case I’m going to create all of these as part of the publishing process

image

Hitting Create will firstly create the necessary Azure resources and then it will proceed with publishing the ASP.NET Core project into the App Service. Unfortunately, once this process has finished you’ll see that the launched url doesn’t load correctly:

image

And secondly, when you return to Visual Studio you’ll see a warning prompt indicating that ASP.NET Core 3 isn’t supported in Azure App Service right now.

image

Luckily Microsoft documentation has you covered. If you go to the main documentation on publishing to Azure App Service there is a link of to deploying preview versions of ASP.NET Core applications. This document covers two different ways to fix this issue – you can either install the preview site extensions for ASP.NET Core 3, or you can simply change your deployment to be a self-contained application. In this case we’re going to go with deploying a self-contained application, since this reduces any external dependencies which seems sensible to me.

After returning to Visual Studio and dismissing the above version warning, you’ll see the Publish properties page with the default publish configuration (you can get back to this page by right-clicking your ASP.NET Core project and selecting Publish in the future).

image

We’re going to click the pencil icon along side any of the summary properties to launch the Publish dialog and change the Deployment Mode to Self-Contained, and the Target Runtime to win-x86. You may be tempted to select win-x64 but only do this if the Platform setting on your App Service is set to 64 Bit, otherwise your service won’t start and you’ll see a 503 service error.

image

Click Save and then the Publish button to publish the application using the updated publishing properties. Note that if you’re on a network that has a slow uplink (eg ADSL) this might take a while, so you might consider jumping on a fast network (eg 4G mobile) to do the upload (and yes, this does make Australia sound like an under-developed nation when it comes to access to the internet – sigh!).

Without any further messing around, the ASP.NET Core application launches correctly:

image

Hmmm, but wait, shouldn’t it be reporting HTTP/2, after all that’s what the browser was reporting when I ran the same service on Kestrel. There’s a couple of pieces to this answer but before we do, I want to remove any element on confusion as to what’s going on here by switching across to using curl – this way we have both control over what protocol we’re requesting but also detailed logging on what protocol is being used (you’ll see why this is important in a minute). Executing the following:

curl https://headerhelper.azurewebsites.net/api/values -v

image

As you can see from the image, the response was indeed done over Http/1.1, which is consistent with the Http protocol listed by the service. Ok, so let’s try requesting Http/2

curl https://headerhelper.azurewebsites.net/api/values --http2 –v

image

This call is successful but again returns Http/1.1 – this is because curl is attempting to request an upgrade to http/2 but the service isn’t willing/able to upgrade the connection.

curl https://headerhelper.azurewebsites.net/api/values --http2-prior-knowledge -v

image

This call fails because curl is forcing the use of Http/2 when in fact the service isn’t able to communicate using Http/2. So, how do we fix this? Well the good news is that Azure App Service has a simple configuration setting that can be used to enable Http/2. Here I’m just setting the HTTP version in the Configuration page for the Azure App Service.

image

This can also be set via the resource explorer, as covered by a number of other people (eg this post). After making your change, don’t forget to Save changes and then give the service 30-60seconds for it to be restarted – if you attempt to request the service immediately you’ll still get Http/1.1 responses.

After the change has been applied, here’s what we see when we use the same curl commands as above:

curl https://headerhelper.azurewebsites.net/api/values --http2 –v

curl https://headerhelper.azurewebsites.net/api/values --http2-prior-knowledge –v

image

Note that it doesn’t matter whether we attempt to negotiate the http/2 upgrade (--http2 flag) or force the point (--http2-prior-knowledge), in both cases the connection reports HTTP/2. However, what’s not cool is that the Http protocol returned by the service is HTTP/1.1 – this is what is seen by the ASP.NET Core Web API.

What we’re seeing here is that Azure is terminating the Http/2 connection and then communicating to the underlying ASP.NET Core application using Http/1.1. This is consistent with the way that SSL support is done – Azure terminates the SSL connection, meaning that your ASP.NET Core application doesn’t need to worry about fronting a secure service. This is awesome for developers that want to add SSL or HTTP/2 to their existing services – you just enable the option in the configuration page of your App Service. However, the down side is that it makes leveraging some of the underlying capabilities of HTTP/2 impossible – for example, it’s currently impossible to host a GRPC service in an App Service as this relies on HTTP/2 to function.

The question still remains – when I request the service from the browser, what protocol is being used? The response returns HTTP/1.1 because that’s what our ASP.NET Core application sees. However, if we look at the browser debugging tools, we can see that the response is indeed being handled over a HTTP/2 connection. Note that this isn’t exposed in the UI of the debugging tools but if you save the request you can see the full details:

image

Opening the HAR file in VS Code:

image

And there you have it – deploying an ASP.NET Core 3 application to Azure App Service and exposing it using HTTP/2.

Testing ASP.NET Core Web API on Kestrel with Fiddler Composer Fails

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):

[HttpGet]
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));
     return headers;
}

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.

image

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&gt; 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. &lt; An existing connection was forcibly closed by the remote host

image

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).

image

By itself this isn’t sufficient, you also need to uncheck the “Enable Just My Code” checkbox in Tools / Options menu item.

image

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.

image

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.

image

Clicking on the list of protocols allows you to edit them, in this case to include tls 1.1 and 1.2.

image

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.

image

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.

image 

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.

Deploying ASP.NET Core 3 to Linux Azure App Service with Docker

In my earlier post I covered creating and debugging an ASP.NET Core service using Docker Desktop. I’m going to build on that and look at how you then push the service into an Azure App Service. Normally I’d simply use the publish option that would allow me to push the service directly into an Azure App Service – this would run the service in much the same way as it runs locally if I was debugging on IISExpress. However, since I’m debugging via a docker container I figured it’d be great to push to Azure in a way that it continues to run in a container. Actually the reason I explored this in the first place was that I’ve been experimenting with GRPC and currently this doesn’t seem to be able to be supported on a regular Azure App Service. I figured if I could run my code in a container there would be less restrictions so I would be able to get GRPC to work (this is work in progress still).

What I did notice in Visual Studio is that when I right-clicked on my ASP.NET Core project and selected Publish, one of the options I saw was to Create new App Service for Containers.

image

Clicking Publish started the wizard to allow me to create the appropriate resources in Azure.

image

Clicking Create will trigger the creation of the selected Azure resources and then proceed to publish the application.

Note: My initial attempt to published failed with an error

“The system cannot find the file specified. In the default daemon configuration on Windows, the docker client must be run elevated to connect. This error may also indicate that the docker daemon is not running.”

Turns out I didn’t have Docker Desktop running (I have so many apps like Slack, Teams, Skype etc that run in background that I force quit most of them each day to try to retain some semblance of a reasonable battery life on my laptop).

Once I realised that I needed to start Docker Desktop, the publish process kicked off and I saw the Docker deployment console appear with quite a detailed breakdown of the upload status – seriously why can’t Visual Studio’s build progress window be this useful. I really need to hand it to the Docker team as their uploading was super resilient. I started off uploading off our standard wifi connection which is based on an ADSL connection, so minimal upload bandwidth. I got impatient so switched mid-upload across to my mobile hotspot – after a second or two delay, the upload retried and continued without missing a beat.

image

Once publishing has completed, the Azure App Service should be all setup and ready to go with your code already published. You can use the Site URL in the publish information pane to launch the service for testing. Since my application was a web api, I’ve appended the /api/values so that I get a valid response from the Get request.

image

One of the thing that continue to amaze me about Visual Studio is the ability to create and publish new projects to Azure. Of course, for production apps, you wouldn’t follow this process at all but it does make spinning up end to end prototype applications a walk in the park.

Debugging ASP.NET Core with Visual Studio and Docker Desktop

With Visual Studio 2019 hot off the press I’ve been experimenting with a few of the new project templates and the improvements that have been made in Visual Studio. In this post I’m going to cover how to solve a particularly annoying problem I encountered when attempting to run and debug an ASP.NET Core 3 application from within Visual Studio, hosted within a Docker image. I’ll walk through the whole process of creating the new project and the issue I ran into when first attempting to debug the application.

When you launch Visual Studio 2019, or go to create a new project, you’ll see the Create a new project dialog. We’re going to select the ASP.NET Core Web Application template.

image

Next we need to provide the standard project information such as name and location.

image

The next stage is to provide more information about how the template should be configured. Here we’re selecting the API template from the left of the screen, and checking both the https and the Enable Docker Support.

Note: At this point if you haven’t already downloaded and installed Docker Desktop, do it now. It’s a half Gb or so download, so not small, and may take a while based on your network bandwidth.

image

After creating the template, you’ll see that there are a number of options available to us in order to run the application. We’re going to proceed with the Docker option.

image

If you haven’t already, make sure you have launched Docker Desktop, otherwise you’ll see the following warning in Visual Studio when you attempt to run the application.

image

Unless you’ve previously setup Docker Desktop you’ll most likely see the following error. Essentially you need to award Docker Desktop access to a drive in order to create images etc.

image

Right-click the Docker icon in the tray and select Settings

image

Under Shared Drives tab, check the local drives you want to make available to Docker Desktop.

image

When you click Apply you’ll be prompt to authenticate. It will detect the credentials of the current user, which for me is an Azure Active Directory user.

image

Important: Unfortunately after providing my password and clicking OK, Docker Desktop decides that it will uncheck the drive that I had selected. This seems to be a common issue, raised by a couple of different people online. Anyhow, the following steps demonstrate how to setup a different account and using it to allow Docker Desktop to access the drive. Whilst a bit hacky, this does seem to be the only work around for this issue.

To setup a new account launch Settings, click Other users and then click the + button under Other users.

image

When prompted to enter email or phone number, instead click the “I don’t have this persons’ sign-in information” option.

image

Next, click the “Add a user without a Microsoft account” option

image

When prompted, enter username, password and some security questions. Next you need to change this user to be an administrator, so expand out the account under Other users and click Change account type.

image

Change Account type to Administrator

image

Return now to Docker Desktop and enter the new account as part of setting up the shared drive. You shouldn’t see any further issues within the Docker Desktop application.

image

Attempting to run the application from within Visual Studio again reveals an error, this time complaining it doesn’t have authority to crLeate or adjust folders (including creating files). 

image

Locate the folder indicated in the error message, right-click on the folder and select Properties. From the Security tab, click Edit.

image

Add the local account you just created and make sure it’s assigned all permissions.

image

You may need to repeat this process for 2 or 3 folders that Docker Desktop requires access to, and in some case assigning permissions can take a minute or two. Once done, your application will be launched from within a Docker image, with the Visual Studio debugger attached.