我正在探索如何在C#中实现HTTP服务器。(在你问之前,我知道有Kestrel(没有其他不过时的东西),我想要一个小得多的应用程序。)因此,响应可能是一个无法查找且长度未知的流。对于这种情况,可以使用分块编码,而不是发送内容长度报头。
响应也可以按照客户端的指示用gzip或br压缩。这可以通过GZipStream类来实现。我几乎说了“很容易”,因为事实并非如此。每次使用GZipStream API时,我总是觉得它很混乱。我通常会遇到每个异常,直到我最终把它弄对。
看起来我只能写入(推送)到GZipStream,压缩数据将从另一端慢慢流出到指定的“基”流中。但这并不理想,因为我不能让压缩数据流到客户端。它需要被分块。也就是说,压缩数据的每一位都需要以其分块大小作为前缀。当然,GZipStream不能产生这种格式。
相反,我想从压缩的GZipStream中读取(拉),但这似乎不可能。文档说如果我尝试这样做,它会抛出一个异常。但必须有一些示例将压缩的字节转化为分块格式。
那么我怎样才能得到预期的结果呢?用这个API能实现吗?为什么我不能从压缩流中拉出来,只能推出来呢?
我并不想编写(非功能性的)示例代码,因为那样只会让人感到困惑。
PS:好吧,也许这个:
Stream responseBody = ...;
if (canCompress)
{
responseBody = new GZipStream(responseBody, CompressionMode.Compress); // <-- probably wrong
}
// not shown: add appropriate headers
while (true)
{
int chunkLength = responseBody.Read(buffer); // <-- not possible
if (chunkLength == 0)
break;
response.Write($"{chunkLength:X}\r\n");
response.Write(buffer.AsMemory()[..chunkLength]);
response.Write("\r\n");
}
response.Write("0\r\n\r\n");
1条答案
按热度按时间yzuktlbb1#
您对
GZipStream
的使用不完整。虽然您输入的responseBuffer
是正确的目标缓冲区,但您必须实际将字节写入GZipStream
本身。此外,一旦完成写入,必须关闭
GZipStream
示例以将所有压缩字节写入目标缓冲区。这是关键的一步,因为在GZip中不存在输入流的“部分压缩”。为了正确地压缩它,必须分析整个输入。因此,这是关键的缺失环节,必须发生之前,你可以继续写回应。最后,您需要重置输出流的位置,以便可以将其读入中间响应缓冲区。
在我的测试示例中,我的
bytes
输入是241字节,而写入缓冲区的压缩字节总计为82字节。