【问题标题】:Reading gzipped HTTP response in Go在 Go 中读取 gzip 后的 HTTP 响应
【发布时间】:2018-12-16 10:08:57
【问题描述】:

我正在尝试使用 Go! 读取压缩后的 HTTP 响应!但我总是收到以下错误消息:

panic: gzip: invalid header
[...] stack trace [...]

如果我运行 "curl -H "Accept-Encoding: gzip" http://foo.com/ | gunzip -" 我会正确压缩响应。我还使用 ngrep 进行了仔细检查,并且 Accept-Encoding/Content-Encoding 对已正确发送/返回。

如果我创建一个包含一些虚拟内容的文件并 gzip 压缩它,我可以从我的 Go!程序。

我用来测试的程序:

package main

import (
    "io"
    //"os"
    "fmt"
    "compress/gzip"
    "net/http"
)

func main() {
    /* This works fine
    f, _ := os.Open("/tmp/test.gz")
    defer f.Close()
    reader, err := gzip.NewReader(f)
    */

    // This does not :/
    resp, _ := http.Get("http://foo.com/")
    defer resp.Body.Close()
    reader, err := gzip.NewReader(resp.Body)

    if err != nil { panic(err) }

    buff := make([]byte, 1024)
    for {
        n, err := reader.Read(buff)

        if err != nil && err != io.EOF {
            panic(err)
        }

        if n == 0 {
            break
        }
    }

    s := fmt.Sprintf("%s", buff)
    fmt.Println(s)
}

我是否忽略了什么?

【问题讨论】:

    标签: go


    【解决方案1】:

    net/http#Transport 处理 gzip 压缩响应。你不必做任何特别的事情。

    看看DisableCompression选项here

    【讨论】:

    • 我在文档中没有发现任何相关内容。你有什么指示吗?谢谢:)
    • 嗯...奇怪...net/http.Transport 有一个名为DisableCompression 的字段。我不得不在不久前关闭它以防止它解压缩由 sourceforge 发回的 gzip 文件。
    • 我赞成。标准客户端 Transport 为您处理 gzip。这包括在您从 response.Body 读取数据时自动解压缩数据。 OP 的问题是他第二次尝试解压。
    • 如果要手动解压数据,需要手动要求gzip压缩的数据。例如,请参阅我的答案。
    • 它似乎无法处理deflate,不过
    【解决方案2】:

    编辑:以下是手动处理压缩的示例。如果你不设置header,默认Transport会帮你做,然后在你读取response.Body的时候解压。

    client := new(http.Client)
    
    request, err := http.NewRequest("GET", "http://stackoverflow.com", nil)
    request.Header.Add("Accept-Encoding", "gzip")
    
    response, err := client.Do(request)
    defer response.Body.Close()
    
    // Check that the server actually sent compressed data
    var reader io.ReadCloser
    switch response.Header.Get("Content-Encoding") {
    case "gzip":
        reader, err = gzip.NewReader(response.Body)
        defer reader.Close()
    default:
        reader = response.Body
    }
    
    io.Copy(os.Stdout, reader) // print html to standard out
    

    为简洁起见,删除了错误处理。我保留了延期。

    【讨论】:

    • http.Get 似乎会自动发送 Content-Encoding (除非我弄错了)。我尝试使用以下代码:play.golang.org/p/haoPEZV6_H 并运行“sudo ngrep -W byline -d en1 port 80”返回:pastebin.com/PqhCX8gQ。我们清楚地看到了 Accept-Encoding 标头。但是,如果您运行play.golang.org/p/haoPEZV6_H,您会收到运行时错误吗?也许我毕竟滥用了 net/http API。
    • 很好的例子,谢谢!我玩了一下 - 这是完整的脚本play.golang.org/p/Dmf06rhhcs
    • http.NewRequest 的第一个参数应完全大写以实现最广泛的兼容性。此处的示例不适用于 NginX。
    • 需要注意的是,有时,即使没有通过设置“Accept-Encoding: gzip”的要求,某些 API 也可能会发送压缩响应。在这种情况下,Go Transport 不会自动解压缩它,因为它没有明确要求它。更多信息:github.com/golang/go/issues/13298
    【解决方案3】:

    根据net/http docs (line 110),如果你手动设置Accept-Encoding请求头,那么gzipped响应不会被http.Transport自动解压。否则,行为由 Transport 的 DisableCompression 布尔值

    控制

    【讨论】:

    【解决方案4】:

    以下是手动处理压缩的示例。

    
    import "compress/gzip"
    
    func ReadAll(r io.Reader) ([]byte, error) {
        reader, err := gzip.NewReader(r)
        if err != nil {
            return nil, err
        }
        defer reader.Close()
        buff, err := ioutil.ReadAll(reader)
        return buff, err
    }
    

    【讨论】:

      猜你喜欢
      • 2013-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-10
      • 2019-12-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多