9

I'm using PushStreamContent in ASP.NET Web API to push events from server to client (using Server-Sent Events). After each sent event, I call Flush on the Stream to push the buffered data to the client. However, I noticed that the flushing does not (always) happen. Sometimes, part of the data is sent to the client, and the rest is sent when the next event is written (which could happen seconds later).

Here's a code sample:

public class MyController : ApiController
{
  private static readonly string[] LineSeparators 
    = new[] { Environment.NewLine };

  public HttpResponseMessage GetData(string id)
  {
    var response = Request.CreateResponse();
    response.Content = new PushStreamContent(
      new Func<Stream, HttpContent, TransportContext, Task>(StartStream),
      new MediaTypeHeaderValue("text/event-stream") { CharSet = "UTF-8" });
    return response;
  }

  private async Task StartStream(Stream outputStream, HttpContent content, TransportContext context)
  {
    using (outputStream)
    using (var writer = new StreamWriter(outputStream, new UTF8Encoding(false)))
    {
      writer.NewLine = "\n";
      while (true)
      {
        WriteEvent(writer, "ping", DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture));
        await Task.Delay(TimeSpan.FromSeconds(1));
      }
    }
  }

  private static void WriteEvent(TextWriter writer, string eventType, string data)
  {
    writer.WriteLine("event:" + eventType);
    writer.WriteLine("data:" + data);
    writer.WriteLine();
    writer.Flush(); // StreamWriter.Flush calls Flush on underlying Stream
  }
}

How can I disable the buffering of the data or force the flushing of the data?

Tommy Carlier
  • 7,951
  • 3
  • 26
  • 43

3 Answers3

3

I got it working.

In my case buffering was an isssue. I had to

1) disable gzip for my responses <urlCompression doStaticCompression="true" doDynamicCompression="false" />

2) Make sure that the proxy on Prod (Nginx) wasn't buffering either

Kugel
  • 19,354
  • 16
  • 71
  • 103
  • 1
    Setting the web.config values worked for me. – Lukie Jan 16 '17 at 20:56
  • I have the same issue, but I'm using `HttpSelfHostServer`(I use it in a windows service), so I can't control the compression there(I don't think it's even enabled by default). Still - the problem remains: `writer.Flush()` has no effect. If I use `writer.close()` it does send the message, but closes it and causes an error. Anyone had/solved this problem? – O. Aroesti Apr 15 '19 at 20:27
1

After spending an entire day trying to figure out where the problem is, and going as far as to (desperately) giving out a bounty, I found out that the problem lied in the fact that I was using HttpSelfHostServer, and needed to configure TransferMode = TransferMode.Streamed on the HttpSelfHostConfiguration. That's all.

O. Aroesti
  • 1,016
  • 11
  • 32
0

The source of the issue is the Stream being flushed.

At your code sample you warp the original stream with StreamWriter and then flush the StreamWriter .

You need to flush the original stream as well:

private async Task StartStream(Stream outputStream, HttpContent content, TransportContext context)
{
  using (outputStream)
  using (var writer = new StreamWriter(outputStream, new UTF8Encoding(false)))
  {
    writer.NewLine = "\n";
    while (true)
    {
      WriteEvent(writer, "ping", DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture));
outputStream.Flush();
      await Task.Delay(TimeSpan.FromSeconds(1));
    }
  }
}