Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting async client disconnect in ASP.NET MVC

Given an async controller:

public class MyController : AsyncController 
{
    [NoAsyncTimeout]
    public void MyActionAsync() { ... }

    public void MyActionCompleted() { ... }
}

Assume MyActionAsync kicks off a process that takes several minutes. If the user now goes to the MyAction action, the browser will wait with the connection open. If the user closes his browser, the connection is closed. Is it possible to detect when that happens on the server (preferably inside the controller)? If so, how? I've tried overriding OnException but that never fires in this scenario.

Note: I do appreciate the helpful answers below, but the key aspect of this question is that I'm using an AsyncController. This means that the HTTP requests are still open (they are long-lived like COMET or BOSH) which means it's a live socket connection. Why can't the server be notified when this live connection is terminated (i.e. "connection reset by peer", the TCP RST packet)?

like image 636
Kirk Woll Avatar asked Jan 23 '11 06:01

Kirk Woll


1 Answers

I realise this question is old, but it turned up frequently in my search for the same answer. The details below only apply to .Net 4.5

HttpContext.Response.ClientDisconnectedToken is what you want. That will give you a CancellationToken you can pass to your async/await calls.

public async Task<ActionResult> Index()
{
    //The Connected Client 'manages' this token. 
    //HttpContext.Response.ClientDisconnectedToken.IsCancellationRequested will be set to true if the client disconnects
    try
    {
        using (var client = new System.Net.Http.HttpClient())
        {
            var url = "http://google.com";
            var html = await client.GetAsync(url,  HttpContext.Response.ClientDisconnectedToken);
        }
    }
    catch (TaskCanceledException e)
    {
        //The Client has gone
        //you can handle this and the request will keep on being processed, but no one is there to see the resonse
    }
    return View();
}

You can test the snippet above by putting a breakpoint at the start of the function then closing your browser window.


And another snippet, not directly related to your question but useful all the same...

You can also put a hard limit on the amount of time an action can execute for by using the AsyncTimeout attribute. To use this use add an additional parameter of type CancellationToken. This token will allow ASP.Net to time-out the request if execution takes too long.

[AsyncTimeout(500)] //500ms
public async Task<ActionResult> Index(CancellationToken cancel)
{
    //ASP.Net manages the cancel token.
    //cancel.IsCancellationRequested will be set to true after 500ms
    try
    {
        using (var client = new System.Net.Http.HttpClient())
        {
            var url = "http://google.com";
            var html = await client.GetAsync(url, cancel);
        }
    }
    catch (TaskCanceledException e)
    {
        //ASP.Net has killed the request
        //Yellow Screen Of Death with System.TimeoutException
        //the return View() below wont render
    }
    return View();
}

You can test this one by putting a breakpoint at the start of the function (thus making the request take more than 500ms when the breakpoint is hit) then letting it run out.

like image 87
Will D Avatar answered Oct 04 '22 22:10

Will D