Search code examples
c#asp.net-coreiisazure-web-app-servicecancellation-token

CancellationToken not Triggering when using IIS


I know questions like this have been asked a dozen of times here, but none of the provided answers solved my particular problem. So I'm giving it another try by asking a question myself.

I'm working on a ASP.NET Core web application running on regular net461 (also tried net48), where I defined an HTTP endpoint like this:

[HttpGet]
[Route("query/{queryName}")]
public async Task<IActionResult> Call(string queryName, CancellationToken cancellationToken)
{
  await Task.Delay(5_000, cancellationToken);
  ...
}

As the title already states, I can't make the cancellationToken trigger a TaskCanceledException upon initiating a refresh on browser side, while the initial request is still being processed (Chrome devtools displays these events as cancelled requests). The only way I can make it work, is by selecting "Project" in Visual Studio's Debug settings (see screenshot below), which I guess, makes the whole application run in Kestrel only - without any reverse proxy in front.

enter image description here

I came across various post that claimed this issue would be solved with ASP.NET Core 2.2, where in-process hosting should work in combination with CancellationToken. So I updated all my nuget packages, but unfortunately with no effect, the error still persists. Yes, locally I'm debugging using IIS Express and I'm not sure how exactly this differs from a regular IIS setup, but I'm also running this app on Azure, where the whole process looks like it was running in-process, because there is only a single process running (see screenshot below).

enter image description here

This is an extract from my csproj with the AspNetCore packages I installed:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net461</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.13.1" />
    <PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
    <PackageReference Include="Microsoft.AspNetCore.Server.IIS" Version="2.2.6" />
    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.2.0" />
    <PackageReference Include="System.Net.Http" Version="4.3.4" />
  </ItemGroup>
</Project>

I also played around with the Program.cs, because I suspect this might be another potential source of error. I Tried to use the UseIIS() extension method only to enforce in-process hosting, but also no visible effect with regard to CancellationToken:

public class Program
{
    public static void Main(string[] args)
    {
        var host = WebHost.CreateDefaultBuilder(args)
            //.UseKestrel()
            .UseIIS()
            .UseContentRoot(Directory.GetCurrentDirectory())
            //.UseIISIntegration()
            .UseStartup<Startup>()
            .UseApplicationInsights()
            .Build();

        host.Run();
    }
}

My Questions:

  1. Does CancellationToken only work with netcoreapp2.x (or higher) as target framework?
  2. Is this a configuration issue (Program.cs, Startup.cs, csproj, etc)?
  3. Can I run this app on an Azure Web App Service on Windows while using Kestrel only? If yes, how do I configure this?
  4. Is there anything else that I might have missed to make this work?

Solution

  • Long story short: It turned out that the Azure App Service is using a level 7 load balancer, which does NOT forward connection-close events to the web app. So it does not matter whether you use a Kestrel or in-process hosted web app on IIS, your app will never get notified.

    The obvious solution is to handle such events yourself on application level. Either by setting a KeepAliveInterval (WebSockets) or by directly subscribing to the connection-closed event using SignalR.