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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With