unosquare / embedio

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

HttpContext.OnClose not firing #588

Open faljse opened 1 year ago

faljse commented 1 year 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 1 year 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.