我正在尝试在 Web 服务器上开发自己的 SPDY 支持实现,SPDY 是 Google 实验性协议,用于减少加载网页的延迟。客户端/服务器交互通过“流”发生,即客户端和服务器之间名为“帧”的 SPDY 信息单元的双向交换。其中一些帧中有一个标题块,并且这个块总是被压缩的。我正在使用以下函数(Web服务器是用C编写的)来解压“头块”,压缩/解压算法是deflate,所以zlib是这样的:
int spdy_decompress_header_block( Byte* data, size_t data_len, unsigned char* processed, size_t* processed_len, z_stream* decomp) {
if (!decomp) {
// I should return error...
spdy_log_error("Error occurred getting decompressor!!!");
return -1;
}
char decomp_buffer[HEADER_CHUNK_MAX_SIZE];
size_t decompressed_len = 0;
int rv = -1;
inflateReset(decomp);
decomp->next_in = data;
decomp->avail_in = data_len;
while (decomp->avail_in > 0) {
decomp->next_out = (Byte*)decomp_buffer;
decomp->avail_out = HEADER_CHUNK_MAX_SIZE;
rv = inflate(decomp,Z_SYNC_FLUSH);
if (rv == Z_NEED_DICT) {
uLong dictionary_id = calculate_dictionary_id((Byte*)SPDY_dictionary, sizeof(SPDY_dictionary));
if (decomp->adler == dictionary_id){
rv = inflateSetDictionary(decomp, (Byte*)SPDY_dictionary, sizeof(SPDY_dictionary));
rv = inflate(decomp, Z_SYNC_FLUSH);
}
}
if ((rv == Z_OK) || ((rv == Z_BUF_ERROR) && (decomp->avail_in == 0))) {
decompressed_len = HEADER_CHUNK_MAX_SIZE - decomp->avail_out;
if (decompressed_len > 0) {
memcpy(processed + *processed_len, decomp_buffer, decompressed_len);
*processed_len += decompressed_len;
}
} else {
if(rv == Z_DATA_ERROR){
spdy_log_error("Error inflating headers: Z_DATA_ERROR encountered" );
}else{
spdy_log_error("Error inflating headers!!!");
}
return -1;
}
}
return *processed_len;
}
SPDY 规范建议为 SPDY 会话中的每个帧(对应于 TCP 连接)中的每个帧重用相同的压缩器/解压缩器,这样每个标头块都受到 z_stream 的威胁,内部状态已经通过使用字典“预热” (也包括在 SPDY 规范中)。字典设置和z_stream初始化成功,第一块解压正常,第二帧块解压失败
rv = 膨胀(decomp,Z_SYNC_FLUSH);
返回值为 -3 (Z_DATA_ERROR),decomp->msg 设置为“Incorrect header check”。在我看来,inflate 找不到 zlib 标头。压缩和解压缩数据是通过按照谷歌规范的建议使用 Z_SYNC_FLUSH 调用 deflate() 和 inflate() 来完成的,我在 chromium(我实际用于测试的浏览器)源代码中检查了这一点以确保。第一个块(成功解压)是:
00111000 11101010 11011111 10100010 01010001 10110010 01100010 11100000 01100110 01100000
10000011 10100100 00010111 00000110 01111011 10111000 00001011 01110101 00110000 00101100
...
00001000 01001110 01001100 01001011 00101100 11001010 10000100 01101010 01100010 01100000
10000111 01000110 00001001 00000011 00000111 00101100 10100110 00000000 00000000 00000000
00000000 11111111 11111111
而第二个街区(这让我发疯)是:
00011010 01110110 00010001 00000011 00010000 01000000 10111001 10001001 00010101 10111010 10001001 11101001 10101001 10110110 00000110 00000000 00000001 01000100 01101110 11000100 00000000 00000100 10010000 00111110 01000000 00000000 00001101 10001110 10001000 00000001 00000000 00000000 00000000 11111111 11111111
在他们两个中,我都可以识别Z_SYNC_FLUSH
预告片(00FF
),但我无法确定第二帧是否在其开头有正确的标题。我的问题:第二个压缩内容实际上是无标题的吗?并且,在这种情况下,我可以通过哪种方式解压这个流,也许使用原始的 deflate 解压(使用 inflateBack())?
就像我在下面的评论中简要解释的那样,错误出现在 inflateReset() 调用中,因为它破坏了使用 00FF 预告片获得的压缩器和解压缩器之间的同步。最后,代码的正确版本是:
int spdy_decompress_header_block( Byte* data, size_t data_len, unsigned char* processed, size_t* processed_len, z_stream* decomp) {
if (!decomp) {
// I should return error...
spdy_log_error("Error occurred getting decompressor!!!");
return -1;
}
char decomp_buffer[HEADER_CHUNK_MAX_SIZE];
size_t decompressed_len = 0;
int rv = -1;
decomp->next_in = data;
decomp->avail_in = data_len;
while (decomp->avail_in > 0) {
decomp->next_out = (Byte*)decomp_buffer;
decomp->avail_out = HEADER_CHUNK_MAX_SIZE;
rv = inflate(decomp,Z_SYNC_FLUSH);
if (rv == Z_NEED_DICT) {
uLong dictionary_id = calculate_dictionary_id((Byte*)SPDY_dictionary, sizeof(SPDY_dictionary));
if (decomp->adler == dictionary_id){
rv = inflateSetDictionary(decomp, (Byte*)SPDY_dictionary, sizeof(SPDY_dictionary));
rv = inflate(decomp, Z_SYNC_FLUSH);
}
}
if ((rv == Z_OK) || ((rv == Z_BUF_ERROR) && (decomp->avail_in == 0))) {
decompressed_len = HEADER_CHUNK_MAX_SIZE - decomp->avail_out;
if (decompressed_len > 0) {
memcpy(processed + *processed_len, decomp_buffer, decompressed_len);
*processed_len += decompressed_len;
}
} else {
if(rv == Z_DATA_ERROR){
spdy_log_error("Error inflating headers: Z_DATA_ERROR encountered" );
}else{
spdy_log_error("Error inflating headers!!!");
}
return -1;
}
}
return *processed_len;
}
有关更多信息,请参阅有趣的:http: //www.bolet.org/~pornin/deflate-flush.htm