dotnet / WatsonWebserver

Watson is the fastest, easiest way to build scalable RESTful web servers and services in C#.
MIT License
403 stars 83 forks source link

`Send()` should include `close` as parameter #153

Open Infiziert90 opened 2 weeks ago

Infiziert90 commented 2 weeks ago

Working with Server-Sent Events, i noticed that the current way to achieve the required functionality is to use chunk based sending. It would be a lot nicer to be able to just use Send() with a string, or any other of the overloads, while not forcefully closing the connection.

Other libraries give the option to Flush() content back to the client while keeping the connection alive.

Example for the current implementation:

try
{
    ctx.Response.Headers.Add("Content-Type", "text/event-stream");
    ctx.Response.Headers.Add("Cache-Control", "no-cache");
    ctx.Response.Headers.Add("Connection", "keep-alive");

    ctx.Response.ChunkedTransfer = true;
    while (!Token.IsCancellationRequested && !Stopping)
    {
        await Task.Delay(10, Token);
        if (Token.IsCancellationRequested)
            return;

        if (!OutboundStack.TryPop(out var message))
            continue;

        var data = JsonConvert.SerializeObject(message);
        await ctx.Response.SendChunk(Encoding.UTF8.GetBytes($"id: {Index}\ndata: {data}\n\n"), Token);
        Index++;
    }
}
catch (TaskCanceledException)
{
    // Ignore
}
catch (Exception ex)
{
    Log.Error(ex, "SSE handler failed.");
}
finally
{
    // "No Content" (204) didn't work for Firefox, so manually closing the connection on client side
    await ctx.Response.SendFinalChunk("data: 0\nevent: close\n\n"u8.ToArray());

    Done = true;
}        

SSE reads all data as a UTF8 text, so using Send() with a string would be the cleanest method, as the library could handle all the internal working