Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I write streamed output in NancyFX?

Tags:

nancy

I'm writing a simple web application using Nancy. At least one request results in a stream of unknown length, so I can't provide Content-Length. I'd like to use Transfer-Encoding: chunked, or (equally acceptable in this case, Connection: close).

I've had a quick hack on the Nancy source code, and I've add Response.BufferOutput, and code to set HttpContext.Response.BufferOutput to false. You can see that here:

public class HomeModule : NancyModule
{
    public HomeModule()
    {
        Get["/slow"] = _ => new SlowStreamResponse();
    }

    private class SlowStreamResponse : Response
    {
        public SlowStreamResponse()
        {
            ContentType = "text/plain";
            BufferOutput = false;
            Contents = s => {
                byte[] bytes = Encoding.UTF8.GetBytes("Hello World\n");
                for (int i = 0; i < 10; ++i)
                {
                    s.Write(bytes, 0, bytes.Length);
                    Thread.Sleep(500);
                }
            };
        }
    }

It doesn't seem to have any effect. The response turns up all at once, after 5 seconds. I've tested this a simple WebRequest-based client.

How do I get chunked output to work in Nancy? I'm using the ASP.NET hosting, but I'd be interested in answers for the other hosting options.

If I write a simple server using HttpListener, I can set SendChunked to true, and it sends chunked output, which my simple client correctly receives in chunks.

like image 658
Roger Lipscombe Avatar asked Jul 14 '12 15:07

Roger Lipscombe


2 Answers

During my experimentation, I discovered that I needed the following configuration. First, set up your web.config file as documented in the Nancy Wiki. It is worth noting that in order to set the disableoutputbuffer value (which is what we want), it appears that you currently need to also specify a bootstrapper. Creating a class in your assembly that inherits from Nancy.Hosting.Aspnet.DefaultNancyAspNetBootstrapper and specifying it in the configuration file appears to work.

<configSections>
  <section name="nancyFx" type="Nancy.Hosting.Aspnet.NancyFxSection" />
</configSections>
<nancyFx>
  <bootstrapper assembly="YourAssembly" type="YourBootstrapper"/>
  <disableoutputbuffer value="true" />
</nancyFx>

Afterwards, you should not set the Transfer-Encoding header. Rather, the following route definition appears to correctly stream the results from my IIS Express development server to Chrome:

Get["/chunked"] = _ =>
{
  var response = new Response();
  response.ContentType = "text/plain";
  response.Contents = s =>
  {
    byte[] bytes = System.Text.Encoding.UTF8.GetBytes("Hello World ");
    for (int i = 0; i < 10; ++i)
    {
      for (var j = 0; j < 86; j++)
      {
        s.Write(bytes, 0, bytes.Length);
      }
      s.WriteByte(10);
      s.Flush();
      System.Threading.Thread.Sleep(500);
    }
  };

  return response;
};

I specified more content per chunk than the previous example due to the minimum sizes prior to first render documented in other StackOverflow questions

like image 114
erdomke Avatar answered Nov 04 '22 15:11

erdomke


You have to call Flush() after each Write(), otherwise the response is buffered anyway. Moreover, Google Chrome doesn't render the output until it's all received.

I discovered this by writing a simple client application that logged what it was reading from the response stream as it arrived.

like image 4
Roger Lipscombe Avatar answered Nov 04 '22 16:11

Roger Lipscombe