1

gzip エンコーディングでチャンク化されたデータをどのように管理しますか? 次の方法でデータを送信するサーバーがあります。

HTTP/1.1 200 OK\r\n
...
Transfer-Encoding: chunked\r\n
Content-Encoding: gzip\r\n
\r\n
1f50\r\n\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xec}\xebr\xdb\xb8\xd2\xe0\xef\xb8\xea\xbc\x03\xa2\xcc\x17\xd9\xc7\xba\xfa\x1e\xc9r*\x93\xcbL\xf6\xcc\x9c\xcc7\xf1\x9c\xf9\xb6r\xb2.H ... L\x9aFs\xe7d\xe3\xff\x01\x00\x00\xff\xff\x03\x00H\x9c\xf6\xe93\x00\x01\x00\r\n0\r\n\r\n

これにはいくつかの異なるアプローチがありましたが、ここで忘れていることがあります。

data = b''
depleted = False
while not depleted:
    depleted = True
    for fd, event in poller.poll(2.0):
        depleted = False
        if event == select.EPOLLIN:
            tmp = sock.recv(8192)
            data += zlib.decompress(tmp, 15 + 32)

与えます(また、\r\n\r\nobvの後にデータのみをデコードしようとしました):
zlib.error: Error -3 while decompressing data: incorrect header check

したがって、データが完全な形式で受信されたら、データを解凍する必要があると考えました..

        ...
        if event == select.EPOLLIN:
            data += sock.recv(8192)
data = zlib.decompress(data.split(b'\r\n\r\n',1)[1], 15 + 32)

同じエラー。data[:-7]また、データの最後にあるチャンクIDとdata[2:-7]、その他のさまざまな組み合わせのために解凍を試みましたが、同じエラーが発生しました。

gzipまた、次の方法でモジュールを試しました。

with gzip.GzipFile(fileobj=Bytes(data), 'rb') as fh:
    fh.read()

しかし、それは「gzipされたファイルではありません」となります。

サーバーによって受信されたデータ (ヘッダー + データ) をファイルに記録し、ポート 80 でサーバー ソケットを作成してデータを (再びそのまま) ブラウザに提供した後でも、完全にレンダリングされるため、データは無傷。私はこのデータを取得し、ヘッダーを取り除き (他には何もありません)、ファイルで gzip を試しました。 ここに画像の説明を入力

@mark-adler のおかげで、チャンク化されたデータをアンチャンクする次のコードを作成しました。

unchunked = b''
pos = 0
while pos <= len(data):
    chunkLen = int(binascii.hexlify(data[pos:pos+2]), 16)
    unchunked += data[pos+2:pos+2+chunkLen]
    pos += 2+len('\r\n')+chunkLen

with gzip.GzipFile(fileobj=BytesIO(data[:-7])) as fh:
    data = fh.read()

これによりOSError: CRC check failed 0x70a18ee9 != 0x5666e236、一歩近づいたものが生成されます。つまり、次の 4 つの部分に従ってデータをクリップします。

  • <chunk length o' X bytes> \r\n <chunk> \r\n

私はおそらくそこに着いていますが、十分に近づいていません。

脚注: はい、ソケットは最適とはほど遠いですが、ソケットからすべてのデータを取得していないと思ったため、このように見えますdepleted

4

2 に答える 2

3

\r\n圧縮されたデータにはそのシーケンスが含まれている可能性があり、十分に長い場合は確実にそのシーケンスが含まれているため、分割することはできません。提供された長さ (最初の length1f50など) を使用して最初にチャンクを解除し、結果のチャンクをフィードして解凍する必要があります。圧縮データは\x1f\x8b.

チャンクは、16 進数、crlf、そのバイト数のチャンク、crlf、16 進数、crlf、チャンク、crlf、...、最後のチャンク (長さゼロ)、[おそらくいくつかのヘッダー]、crlf です。

于 2014-03-05T14:57:48.770 に答える
1

@mark-adler は、HTML プロトコルのチャンクモードがどのように機能するかについていくつかの良い指針を与えてくれました。これに加えて、データを解凍するさまざまな方法をいじりました。

  1. チャンクを1つの大きなヒープにステッチすることになっています
  2. gzipnotを使用することになっていますzlib
  3. ステッチされたチャンク全体のみを解凍できます。部分的に行うと機能しません

上記の問題の 3 つすべてに対する解決策は次のとおりです。

unchunked = b''
pos = 0
while pos <= len(data):
    chunkNumLen = data.find(b'\r\n', pos)-pos
#   print('Chunk length found between:',(pos, pos+chunkNumLen))
    chunkLen=int(data[pos:pos+chunkNumLen], 16)
#   print('This is the chunk length:', chunkLen)
    if chunkLen == 0:
#       print('The length was 0, we have reached the end of all chunks')
        break
    chunk = data[pos+chunkNumLen+len('\r\n'):pos+chunkNumLen+len('\r\n')+chunkLen]
#   print('This is the chunk (Skipping',pos+chunkNumLen+len('\r\n'),', grabing',len(chunk),'bytes):', [data[pos+chunkNumLen+len('\r\n'):pos+chunkNumLen+len('\r\n')+chunkLen]],'...',[data[pos+chunkNumLen+len('\r\n')+chunkLen:pos+chunkNumLen+len('\r\n')+chunkLen+4]])
    unchunked += chunk
    pos += chunkNumLen+len('\r\n')+chunkLen+len('\r\n')

with gzip.GzipFile(fileobj=BytesIO(unchunked)) as fh:
    unzipped = fh.read()

return unzipped

そこにデバッグ出力を残しましたが、理由によりコメントを外しました。
あなた/私が実際に解凍しようとしていたデータと、どの部分がどこで取得され、各計算が4番目にもたらす値を取得するのは混乱しているように見えますが、非常に役立ちました。

このコードは、次の形式でチャンク データを処理します。
<chunk length o' X bytes> \r\n <chunk> \r\n

に入れる前に最初に使用しなければならなかった を最初X bytesに抽出するときは注意が必要でした。しかし、それは私が実際に他のデータを与えていなくても、論理的ではなかった、本当に大きな数を突然与えました.. とにかく。その後は、数値が正しいことを確認してから、すべてのチャンクを 1 つの巨大なデータの山に結合し、それを にフィードするだけでした。1f50binascii.hexlify(data[0:4])int()gzip.GzipFile(...)

3年後に編集:

これは最初はクライアント側の問題であったことは承知していますが、これはサーバー側の関数で、機能テストを送信するものです。

def http_gzip(data):
    compressed = gzip.compress(data)

    # format(49, 'x') returns `31` which is `\x31` but without the `\x` notation.
    # basically the same as `hex(49)` but ment for these kind of things.
    return bytes(format(len(compressed), 'x')),'UTF-8') + b'\r\n' + compressed + b'\r\n0\r\n\r\n'
于 2014-03-05T18:06:55.007 に答える