unosquare / embedio

A tiny, cross-platform, module based web server for .NET
http://unosquare.github.io/embedio
Other
1.45k stars 175 forks source link

HttpContext.OnClose not firing #588

Open faljse opened 10 months ago

faljse commented 10 months ago

I am sending sse events to my clients; HTTPContext.onClose should fire if the http connection is closed; however it never does.

[Route(HttpVerbs.Get, "/alarms")]
public async Task<string> ReadAlarms() {
    Response.ContentType = "text/event-stream";
    CancellationTokenSource source = new CancellationTokenSource();
    HttpContext.OnClose((IHttpContext context) => {
        source.Cancel();
    });
    while(!source.Token.IsCancellationRequested) {
        await Task.Delay(1000);
        await Response.OutputStream.WriteAsync(new byte[] { 46 },0,1);
    }
}

Am i doing something wrong here? Edit: also tried setting server.Listener.IgnoreWriteExceptions = false with no success: no exception thrown ever (#457) It did work for #317; could using a controller/Route make a difference?

rdeago commented 10 months ago

Hello @faljse, thanks for using EmbedIO!

Your ReadAlarms method needs to return for the HTTP context to be closed. That's the very point of the HTTP context: it exists as long as the request is being served.

My advice is to use WebSockets for the type of communications you're aiming at here.

Alternatively, you can use HttpContext's own cancellation token to end your loop:

[Route(HttpVerbs.Get, "/alarms")]
public async Task<string> ReadAlarms() {
    Response.ContentType = "text/event-stream";
    for (; ;) {
        await Task.Delay(1000, HttpContext.CancellationToken);
        await Response.OutputStream.WriteAsync(new byte[] { 46 }, 0, 1, HttpContext.CancellationToken);
    }
}

Note that you don't need to check IsCancellationRequested: just pass the cancellation token along to any async method you call and let EmbedIO take care of any OperationCanceledException caused by the token.