Golang:"compress/flate" 模块无法解压缩有效的 deflate 压缩 HTTP 正文

Golang: "compress/flate" module can't decompress valid deflate compressed HTTP body

这个问题继续讨论开始。我发现 HTTP 响应正文无法解组为 JSON 对象,因为后者的 deflate 压缩。现在我想知道如何使用 Golang 进行解压。如果有人能指出我的代码中的错误,我将不胜感激。

输入数据

我已将 HTTP 响应正文转储到“test”文件中。在这里:

$ cat test
x��PAN�0�
;��NtJ�FӮdU�|"oVR�C%�f�����Z.�^Hs�dW뮑�'��DH�S�SFVC����r)G,�����<���z}�x_g�+�2��sl�r/�Oy>��J3\�G�9���N���#[5M�^v/�2Ҕ��|�h��[�~7�_崛<D*���/��i

让我们确保这个文件可以解压,甚至包含有效的JSON:

$ zlib-flate -uncompress < test
{"timestamp":{"tv_sec":1428488670,"tv_usec":197041},"string_timestamp":"2015-04-08 10:24:30.197041","monitor_status":"enabled","commands":{"REVERSE_LOOKUP":{"cache":{"outside":{"successes":0,"failures":0,"size":0,"time":0},"internal":{"successes":0,"failures":0,"size":0,"time":0}},"disk":{"outside":{"successes":0,"failures":0,"size":0,"time":0},"internal":{"successes":13366,"failures":0,"size":0,"time":501808}},"total":{"storage":{"successes":0,"failures":0},"proxy":{"successes":13366,"failures":0}}},"clients":{}}}
$ zlib-flate -uncompress < test | python -m json.tool
{
    "commands": {
        "REVERSE_LOOKUP": {
            "cache": {
               ....

源代码

package main

import (
    "bytes"
    "compress/flate"
    "fmt"
    "io/ioutil"
)

func main() {
    fname := "./test"
    content, err := ioutil.ReadFile(fname)
    if err != nil {
        panic(err)
    }
    fmt.Println("File content:\n", content)

    enflated, err := ioutil.ReadAll(flate.NewReader(bytes.NewReader(content)))
    if err != nil {
        panic(err)
    }
    fmt.Println("Enflated:\n", enflated)
}

错误

$ go run uncompress.go 
File content:
 [120 156 181 80 65 78 195 48 16 252 10 242 57 69 118 226 166 38 247 156 64 42 42 130 107 100 156 165 88 196 118 149 93 35 160 234 223 89 183 61 112 42 226 192 109 118 118 102 103 180 123 65 62 0 146 13 59 209 237 5 189 15 8 78 116 74 215 70 27 211 174 100 85 184 124 34 111 86 82 171 67 37 144 102 31 183 195 15 167 168 165 90 46 164 94 72 115 165 100 87 235 174 145 215 39 189 168 68 72 209 83 154 7 22 83 70 86 67 180 207 19 140 188 114 41 4 27 71 44 225 155 254 169 223 60 244 195 221 122 125 251 120 95 24 103 221 43 20 144 50 161 31 143 16 179 115 128 8 108 225 114 47 214 79 121 62 15 232 191 224 8 74 51 6 92 213 71 130 57 218 233 175 78 182 142 30 223 254 35 91 53 77 219 94 118 47 165 50 210 148 18 148 232 124 128 31 104 183 151 91 176 126 55 167 143 207 95 3 15 229 180 155 60 68 42 159 231 241 27 47 165 167 25]
panic: flate: corrupt input before offset 5

goroutine 1 [running]:
runtime.panic(0x4a7180, 0x5)
    /usr/lib/go/src/pkg/runtime/panic.c:266 +0xb6
main.main()
    /home/isaev/side-projects/elliptics-manager/uncompress.go:20 +0x2a3
exit status 2

PS Ubuntu 14.10,Go 1.2.1

您的输入不是简单的压缩块,而是 zlib 流

根据ZLIB Compressed Data Format Specification 3.3前2个字节是:

-------------
| CMF | FLG |
-------------

压缩方法和标志。您的输入以 [120, 156] 开头,在六进制中是 78 9C。这是默认压缩。后面也没有字典,所以后面的数据就是压缩后的数据。

Bits 0 到 3 是 CM Compression Method,bits 4 到 7 是 CINFO Compression Info。在这种情况下,CINFO=7 表示 32K window 大小,CM=8 表示“deflate”压缩方法。 FLG 位 5 指示是否预设了字典,在本例中就是这样。 FLG 的详细信息也在链接 RFC 1950.

所以您的输入基本上告诉其余数据是使用默认压缩构建的,但是 go flate 包不会对此进行解码。

像这样更改您的解压缩以省略前 2 个字节,它将起作用:

enflated, err := ioutil.ReadAll(flate.NewReader(bytes.NewReader(content[2:])))

Go Playground 上试一试。但是...

使用正确的ZLib解压!

这次我们很幸运,因为压缩级别是默认的并且字典是预设的。否则,您将无法使用 flate 包对其进行解码。由于输入是一个 zlib 流,你应该使用 compress/zlib 包来正确解码它而不是依靠运气:

r, err := zlib.NewReader(bytes.NewReader(content))
if err != nil {
    panic(err)
}
enflated, err := ioutil.ReadAll(r)
if err != nil {
    panic(err)
}
fmt.Println(string(enflated))

Go Playground 上尝试 zlib 变体。