HttpRequest not aborted (cancelled) on browser abort in ASP.NET Core MVC
Asked Answered
E

1

16

I wrote the following MVC Controller to test cancellation functionality:

class MyController : Controller
{
    [HttpGet("api/CancelTest")]
    async Task<IActionResult> Get()
    {
        await Task.Delay(1000);
        CancellationToken token = HttpContext.RequestAborted;
        bool cancelled = token.IsCancellationRequested;
        logger.LogDebug(cancelled.ToString());
        return Ok();
    }
}

Say, I want to cancel the request, so the value 'true' is logged in the controller action above. This is possible server-side if the server implements the IHttpRequestLifetimeFeature. Luckily Kestrel does, and this can be accomplished the following way:

var feature = (IHttpRequestLifetimeFeature) HttpContext.Features[typeof(IHttpRequestLifetimeFeature)];
feature.Abort();

The problem however is that I want to cancel the request on the client side. For example, in the browser. In pre-core versions of ASP.NET MVC/WebApi the cancellation token would automatically be cancelled if the browser aborted a request. Example: refresh the page a couple of times in Chrome. In the Network tab of the chrome dev tools you can now see the previous (unfinished) request be cancelled.

The thing is: in ASP.NET Core running on Kestrel, I can only see the following entry in the log:

Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -4081 ECANCELED operation canceled

So the abort request from the browser DOES arrive and is handled by the Kestrel webserver. It does however not affect the RequestAborted property of the HttpContext in the controller, because the value 'false' is still logged by the method.

Question: Is there a way to abort/cancel my controller's method, so that the HttpContext.RequestAborted property will be marked as cancelled?

Perhaps I can make something that would subscribe to Kestrel's operation cancelled trigger and call the IHttpRequestLifetimeFeature.Abort() method?

Update: I did some further testing and it seems the HttpRequest IS in fact aborted, but there seems to be some kind of delay before the cancellation actually takes place. The delay is not time-factored, and seems to come straight from libuv (the library where the Kestrel webserver is build on top of). I posted more info on https://github.com/aspnet/KestrelHttpServer/issues/1103

More updates: Issue has been moved to another one, because the previous one contained multiple problems. https://github.com/aspnet/KestrelHttpServer/issues/1139

Episcopalism answered 19/9, 2016 at 13:10 Comment(4)
I'm voting to close this question as off-topic because issue seems to be fixed nowBasifixed
@Set: That's fine. It should indeed be fixed in 2.0. I can't close the question myself, can I?Episcopalism
you may remove question if you think it is not helpful anymore for other users, or you can post an own answer (and accept it later) and say that it was a bug in Kestrel Server implementation is fixed now.Basifixed
There is additional issue with this, not being fixed yet (as of version 2.1) , when using IIS as reverse proxy to Kestrel github.com/aspnet/AspNetCoreModule/issues/38. So even though original issue was fixed, this is still very informative question.Concrete
E
4

Turns out that that simply using HttpContext.RequestAborted is indeed the right way, but due to a bug in Kestrel (the order in which FIN/RST packages were handled), the request was not aborted on a browser abort.

The bug should finally be fixed in Kestrel 2.0.

See the updates in my question for more information.

Episcopalism answered 20/6, 2017 at 10:6 Comment(2)
Thanks. Yes you are right. May be there is a bug in Kestrel. I tried using HttpContext.RequestAborted but still it didn't work so I tried without using IIS Express (refer #47154025) and it worked.Dorise
I don't think that's a bug, instead it is due to browser reusing TCP connections. Standard HTTP does not support cancellation, there is no message saying something like previous message was aborted, thus server can only detect TCP disconnect and treat that as an abort signal. Even worse, TCP disconnect are not guarantee to be reliable, you can have broken pipe that remains undetected.Unpracticed

© 2022 - 2024 — McMap. All rights reserved.