【问题标题】:Detect gzip encoding to manually decompress response, but 'Content-Encoding' header missing检测 gzip 编码以手动解压缩响应,但缺少“Content-Encoding”标头
【发布时间】:2017-10-19 06:14:03
【问题描述】:

我在“Go”中使用net/http 库来发出HTTP GET 请求。在响应中,我得到 12 个标题。但是当我通过邮递员运行完全相同的查询时,我得到了 16 个标题。其中缺少的一个是“内容编码”。我知道这一定是CORS 问题。

但是由于我没有在我的请求中设置标题 Accept-Encoding: gzip,并且我仍然得到 gzip 编码作为响应,所以 Go 传输不是 automatically decompressing the response for me。所以,我需要能够手动检测编码然后解压缩它。但是,我无法检测到响应中是否缺少“内容编码”标头。

这是我尝试执行此操作的代码:

func calcDistanceAndDurationWithUberApi(originLat float64, originLon float64, destinationLat float64, destinationLon float64) (float64, float64, error) {

    endpoint := "https://api.uber.com/v1.2/estimates/price"
    parameters := fmt.Sprintf("?start_latitude=%v&start_longitude=%v&end_latitude=%v&end_longitude=%v", originLat, originLon, destinationLat, destinationLon)

    req, err := http.NewRequest("GET", endpoint + parameters, nil)
    if err != nil {
        return 0, 0, err
    }

    req.Header.Add("Authorization", "Token " + getUberApiKey())
    req.Header.Add("Accept-Language", "en_US")
    req.Header.Add("Content-Type", "application/json")

    httpClient := &http.Client{}
    resp, err := httpClient.Do(req)
    if err != nil {
        return 0, 0, err
    }
    if resp.StatusCode != 200 {
        return 0, 0, errors.NotFound("Response: %v", resp.StatusCode)
    }
    defer resp.Body.Close()

    pretty.Println("- REQUEST: ")
    pretty.Println(req)

    // Check if server sent gzipped response. Decompress if yes.
    var respReader io.ReadCloser
    switch resp.Header.Get("Content-Encoding") {
    case "gzip":
        fmt.Println("Content-Encoding is gzip")
        respReader, err = gzip.NewReader(resp.Body)
        defer respReader.Close()
    default:
        fmt.Println("Content-Encoding is Not gzip")
        respReader = resp.Body
    }

    pretty.Println("- RESPONSE HEADER: ")
    pretty.Println(resp.Header)

    pretty.Println("- RESPONSE BODY: ")
    pretty.Println(respReader)

    return 0, 0, nil
}

响应状态为“200 OK”。这是输出(响应):

- RESPONSE HEADER: 
http.Header{
    "Content-Language":          {"en"},
    "Cache-Control":             {"max-age=0"},
    "X-Uber-App":                {"uberex-nonsandbox", "optimus"},
    "Strict-Transport-Security": {"max-age=604800", "max-age=2592000"},
    "X-Content-Type-Options":    {"nosniff"},
    "Date":                      {"Fri, 19 May 2017 07:52:17 GMT"},
    "Content-Geo-System":        {"wgs-84"},
    "Connection":                {"keep-alive"},
    "X-Frame-Options":           {"SAMEORIGIN"},
    "X-Xss-Protection":          {"1; mode=block"},
    "Server":                    {"nginx"},
    "Content-Type":              {"application/json"},
}
- RESPONSE BODY: 
&http.gzipReader{
body: &http.bodyEOFSignal{
    body: &http.body{
        src: &internal.chunkedReader{
            r:  &bufio.Reader{
                buf: {0x48, 0x54, .......... }

【问题讨论】:

    标签: go http-headers httpresponse content-encoding


    【解决方案1】:

    我屈服于 uber api 的固执,并添加了另一个请求标头,req.Header.Add("Accept-Encoding", "gzip")

    现在我得到了响应头 "Content-Encoding": "gzip",虽然我仍然得到了一个无法辨认的响应体,但这超出了这个问题的范围。

    【讨论】:

    • 可能 Uber 的 API 足够聪明,仅在请求者接受 gzip 时才包含 Content-Encoding 标头,但实际上不够聪明当他们不接受时不 gzip 响应 '不接受 gzip。如果是这样的话,这绝对是优步的缺陷。
    • 但与此同时,无论我是否要求他们,他们都足够固执地给我 gzip-ped 响应
    【解决方案2】:

    如果您不禁用压缩 [1],并且您不使用 Accept-Encoding: gzip 手动请求压缩,则使用我所说的“自动模式”。在自动模式下,Go 自动添加 Accept-Encoding: gzip,然后如果服务器响应 Content-Encoding: gzip,Go 将响应正文包装在 Gzip 阅读器中,并删除 Content-Encoding 和 Content-Length 响应标头 [2 ]。我不同意这种做法,因为最终用户基本上被欺骗了真正的反应是什么。将此与 cURL 进行对比,无论您做什么,它都会为您提供纯粹的响应:

    PS C:\> curl -v --compressed https://github.com/manifest.json
    < content-encoding: gzip
    < content-length: 345
    

    为了解决这个问题,我为http.Transport写了一个包装器:

    package mech
    
    import (
       "compress/gzip"
       "io"
       "net/http"
       "strings"
    )
    
    type Transport struct { http.Transport }
    
    func (t Transport) RoundTrip(req *http.Request) (*http.Response, error) {
       if !t.DisableCompression {
          req.Header.Set("Accept-Encoding", "gzip")
       }
       res, err := t.Transport.RoundTrip(req)
       if err != nil {
          return nil, err
       }
       if strings.EqualFold(res.Header.Get("Content-Encoding"), "gzip") {
          gz, err := gzip.NewReader(res.Body)
          if err != nil {
             return nil, err
          }
          res.Body = readCloser{gz, res.Body}
       }
       return res, nil
    }
    
    type readCloser struct {
       io.Reader
       io.Closer
    }
    
    1. https://golang.org/pkg/net/http#Transport.DisableCompression
    2. https://github.com/golang/go/blob/go1.16.5/src/net/http/transport.go#L2186-L2192

    【讨论】:

      猜你喜欢
      • 2020-06-08
      • 2015-12-13
      • 1970-01-01
      • 2021-08-13
      • 2012-12-02
      • 2020-02-11
      • 1970-01-01
      • 2011-07-13
      • 2010-10-07
      相关资源
      最近更新 更多