Web サーバーで独自の SPDY サポート実装を開発しようとしています。SPDY は、Web ページの読み込みの待ち時間を短縮するための 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 セッション (TCP 接続に対応) の各フレームのストリーム内の各フレームに同じコンプレッサー/デコンプレッサーを再利用するための SPDY 仕様のアドバイス。このように、すべてのヘッダー ブロックは、辞書を使用して内部状態が既に "ウォーム" されている z_stream によって脅かされます。 (SPDY仕様にも含まれます)。ディクショナリの設定と z_stream の初期化は成功しました。最初のブロックの解凍は正常に機能し、2 番目のフレーム ブロックの解凍は失敗しました。
rv = インフレート (デコンプ、Z_SYNC_FLUSH);
戻り値は -3 (Z_DATA_ERROR)、decomp->msg は「不正なヘッダー チェック」に設定されます。inflate が zlib ヘッダーを見つけられないように思えます。データの圧縮と解凍は、Google の仕様で提案されているように、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
2番目のブロック(私を怒らせている)は次のとおりです。
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
) を認識できますが、2 番目のフレームの先頭に適切なヘッダーがあるかどうかを確認できません。私の質問: 2 番目の圧縮コンテンツは実際にはヘッダーなしですか? そして、この場合、どの方法でこのストリームを解凍できますか?
以下のコメントで簡単に説明したように、エラーは 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 を参照してください。