0

Background (ignorable)

I am trying to re-create a bug situation on my development machine, described in this github issue: https://github.com/dotnet/aspnetcore/issues/28568. The actual bug I am seeing is when my application runs in Kubernetes. The details of that bug are documented here: Handling Errors in Health Checks

Problem

I am trying to get an HttpClient to abort a connection, to hopefully cause a System.Threading.Tasks.TaskCanceledException in my ASP.Net Core WebAPI Service. The text I am working off (found in the GitHub issue linked above) says that my issue happens:

when the client disconnect[s] before the request has been completely processed and is caused by the HttpContext.RequestAborted cancellation token.

I need to recreate this issue on my development machine. To do that I have created a console application with an HttpClient that calls my service endpoint. I have tried two different ways to recreate this so far. First I call:

var sendTask = httpClient.SendAsync(message, cancellationTokenSource.Token);

Then, in the first attempt I called:

cancellationTokenSource.Cancel();

In the second attempt, I took out the call to Cancel and put in this:

httpClient.Dispose();

I put the logic in a loop and ran it a bunch of times, but I was unable to reproduce the exception I am looking for (TaskCanceledException) in the service (confusingly it is thrown in the client when Cancel is called).

Is there a way to case the HttpClient to disconnect before the call is done?

Vaccano
  • 78,325
  • 149
  • 468
  • 850
  • 1
    Did you actually `await` the `sendTask` though? – DavidG Sep 20 '22 at 19:18
  • @DavidG - No, because I want to disconnect the call before it finishes. If I `await` it then it will finish the call before it resumes execution. Am I wrong in this thinking? – Vaccano Sep 20 '22 at 19:23
  • If an exception is caught in a library and does not throw the exception, then the application will never see the exception. HTTP uses TCP for transport layer and the only way for HTTP to see exception is if he connection closes (an exception will occur when writing to a closed object) or if an ACK is not received. You ca check the c source code to see what happens with the exception. You can also just pull the ethernet cable out of the socket to duplicate issue. – jdweng Sep 20 '22 at 19:25
  • The exception occurring in this example is not opaque to the main thread. If you want to really catch the exception you should be awaiting the HTTP call. You can call `cancellationTokenSource.Cancel();` in another Task to cancel the request before its get completed. – Eldar Sep 20 '22 at 19:28
  • Worth to mention that as I remember `HttpClient`s Linux version of implementation was based on curl. That might be the reason not to replicate the issue. – Eldar Sep 20 '22 at 19:31
  • If you don't await it, where do you think the exception is going to be raised though? If you want a fire-and-forget task, then you could wrap the entire thing in a new task and handle the exception in there. – DavidG Sep 20 '22 at 19:33
  • 1
    And please disregard everything @jdweng said above, it's nonsense I'm afraid. – DavidG Sep 20 '22 at 19:33
  • Thank you for the helpful comments everyone. I figured out that I had not been resetting the `CancellationTokenSource`, so when I called cancel, it had not reset for later calls in my loop. – Vaccano Sep 20 '22 at 19:41

1 Answers1

0

I realized that I had not been resetting my CancellationTokenSource after I called Cancel. Once I did that, about 1 in 20ish calls started throwing the TaskCanceledException in my service.

Just incase others want it (and for later reference), here is the code that I used to generate the error:

var url = "https://localhost:7249/health/liveness";
var httpClient = new HttpClient();
for (int loopIndex = 0; loopIndex < 1000; loopIndex++)
{
    try
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        var message = new HttpRequestMessage(HttpMethod.Get, url);
        // Important: don't await this or it will cause the call to finish before continuing.
        httpClient.SendAsync(message, cancellationTokenSource.Token);
        cancellationTokenSource.Cancel();
    }
    catch (Exception e) { }

    Console.WriteLine($"Next {loopIndex}");
}

Console.WriteLine("All Done");
Vaccano
  • 78,325
  • 149
  • 468
  • 850