使用 NancyFX 自托管分块压缩响应
Posted
技术标签:
【中文标题】使用 NancyFX 自托管分块压缩响应【英文标题】:Chunked compressed response with NancyFX self hosting 【发布时间】:2014-10-10 13:41:45 【问题描述】:我有一个系统应该将行写入 HTTP 响应流。该系统中的每一行都代表某种事件,因此您可以将其视为通知流。我在使用 NancyFX 和 Nancy 自托管 (0.23) 的 Windows 7 上使用 .NET4。以下代码是有效的:
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();
现在我想对流添加压缩以压缩带宽量。出于某种原因,在浏览器中进行测试时,我看不到任何结果。我尝试了很多组合来达到预期的效果,但这就是我目前所拥有的:
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();
我正在寻求帮助,要么告诉我我在 HTTP 协议方面做错了什么(例如,我尝试按照 HTTP1.1 中的描述添加块长度,但没有奏效),或者关于 Nancy 的帮助没有考虑。
【问题讨论】:
使用 NancyFx 自托管,您会使用 HttpListener 对吗?通常,如果您在写入内容之前省略内容长度,则内容将作为分块发送而无需进一步干预。我不知道南希在写作之前做了什么魔法,所以这可能是相关的,也可能是不相关的。 【参考方案1】:问题似乎出在框架的 Gzip 实现中,因为它在关闭之前从不写入输出流,
我只是使用了 SharpZiplib,你的代码似乎对我有用,这是我的修改
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 HostConfigurationAllowChunkedEncoding = true,new Uri("http://localhost:1234")))
host.Start();
Console.ReadLine();
SharpZipLib 的 Nuget 包:PM> Install-Package SharpZipLib
【讨论】:
【参考方案2】:看起来,由于while(true)
,您提供为ChunkedReponse.Contents
的任何调用代理都将永远不会返回。这是预期的行为吗?不知道这个框架对那个委托做了什么,我猜不出来。
乍一看,我确实想知道构造函数是否永远不会返回——我想这肯定会导致问题——但没过多久我就注意到它是一个 lambda。幸运。
编辑#1:
GZipStream.Flush() 的文档说:
此方法的当前实现没有功能。 (覆盖 Stream.Flush()。)
这暗示 GZipStream 在关闭之前不会向传输中写入任何内容。如果您不永远运行提到的委托,而是在某个时候关闭流,您是否会遇到不同的行为?
【讨论】:
意在永不停止。 Nancy 调用委托来写入流 - 我这样做是分块的,因此您每秒将获得大约一行。请注意,第一个示例按预期执行此操作。【参考方案3】:我自己测试过,我认为问题在于浏览器如何处理这些分块和压缩的响应。这是我尝试过的,基本上是有效的:
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);
;
问题是浏览器在显示结果之前等待分块响应准备好。尽管gzip supports streaming,它可能会等待解压缩,直到所有数据都发送完毕。 Here 是支持我假设的第一个提示。
【讨论】:
以上是关于使用 NancyFX 自托管分块压缩响应的主要内容,如果未能解决你的问题,请参考以下文章