Chunked NancyFX Compressed Self-Service Response

I have a system that needs to write strings in an HTTP response stream. Each line in this system represents an event, so you can see it as a stream of notifications. I am using .NET4 on Windows 7 using NancyFX and Nancy's own hosting (0.23). The following code is functional:

using System; using System.IO; using System.Threading; using Nancy; using Nancy.Hosting.Self; namespace TestNancy { public class ChunkedResponse : Response { public ChunkedResponse() { ContentType = "text/html; charset=utf-8"; Contents = stream => { using (var streamWriter = new StreamWriter(stream)) { while (true) { streamWriter.WriteLine("Hello"); streamWriter.Flush(); Thread.Sleep(1000); } } }; } } public class HomeModule : NancyModule { public HomeModule() { Get["/"] = args => new ChunkedResponse(); } } public class Program { public static void Main() { using (var host = new NancyHost(new Uri("http://localhost:1234"))) { host.Start(); Console.ReadLine(); } } } } 

Now I want to add compression to the stream to compress the amount of bandwidth. For some reason, when testing in the browser, I do not see any result. I tried many combinations to achieve the desired result, but this is what I have at the moment:

 using System; using System.IO; using System.IO.Compression; using System.Threading; using Nancy; using Nancy.Hosting.Self; namespace TestNancy { public class ChunkedResponse : Response { public ChunkedResponse() { Headers["Content-Encoding"] = "gzip"; ContentType = "text/html; charset=utf-8"; Contents = stream => { using (var gzip = new GZipStream(stream, CompressionMode.Compress)) using (var streamWriter = new StreamWriter(gzip)) { while (true) { streamWriter.WriteLine("Hello"); streamWriter.Flush(); Thread.Sleep(1000); } } }; } } public class HomeModule : NancyModule { public HomeModule() { Get["/"] = args => new ChunkedResponse(); } } public class Program { public static void Main() { using (var host = new NancyHost(new Uri("http://localhost:1234"))) { host.Start(); Console.ReadLine(); } } } } 

I am looking for help that either tells me what I am doing wrong with respect to the HTTP protocol (for example, I tried adding block lengths as described in HTTP1.1, which didn’t work), or helping Nancy where she does something not taken into account .

+5
source share
3 answers

The problem seems to be in the implementation of the Gzip structure, since it is never written to the output stream before closing,

I just used SharpZiplib and your code seems to work for me, here are my modifications

 public class ChunkedResponse : Response { public ChunkedResponse() { Headers["Transfer-Encoding"] = "chunked"; Headers["Content-Encoding"] = "gzip"; ContentType = "text/html; charset=utf-8"; Contents = stream => { var gzip = new ICSharpCode.SharpZipLib.GZip.GZipOutputStream(stream); using (var streamWriter = new StreamWriter(gzip)) { while (true) { streamWriter.WriteLine("Hello"); gzip.Flush(); streamWriter.Flush(); Thread.Sleep(1000); } } }; } } public class HomeModule : NancyModule { public HomeModule() { Get["/"] = args => new ChunkedResponse(); } } public class Program { public static void Main() { using (var host = new NancyHost(new HostConfiguration{AllowChunkedEncoding = true},new Uri("http://localhost:1234"))) { host.Start(); Console.ReadLine(); } } } 

Nuget Package for SharpZipLib: PM> Install-Package SharpZipLib

+5
source

It looks like any calls you make to the delegate since ChunkedReponse.Contents will never return due to while(true) . Is this intentional behavior? Not knowing what this structure is doing with this delegate, I could not guess.

At first glance, I wondered if the constructor would return, which I think would definitely cause a problem, but it didn’t take me a long time to notice that it was a lambda. Fortunately.

Edit # 1:

The documentation for GZipStream.Flush () says:

The current implementation of this method does not have functionality. (Overrides Stream.Flush ().)

This means that GZipStream does not write anything on the transport until it is closed. Do you experience different behavior if you do not execute the delegate mentioned forever and instead close the thread at some point?

+3
source

I tested it myself, and I think the problem is how the browser handles these compressed and compressed responses. This is what I tried and what basically worked:

 Contents = stream => { using (var gzip = new GZipStream(stream, CompressionMode.Compress)) using (var streamWriter = new StreamWriter(gzip)) { for (int i = 0; i < 5;i++ ) { string txt = "Hello"; streamWriter.WriteLine(txt); streamWriter.Flush(); Thread.Sleep(1000); } } }; 

The problem is that the browser expects the chunked response to be ready before it displays the result. He is probably waiting for decompression until all data has been sent, although gzip supports streaming video . Here is the first hint that supports my assumption.

+1
source

Source: https://habr.com/ru/post/1200532/


All Articles