Go: Определить кодировку gzip для ручной распаковки ответа, но заголовок "Content-Encoding" отсутствует

Я использую net/http библиотека в "Go", чтобы сделать HTTP GET запрос. В ответ я получаю 12 заголовков. Но когда я выполняю тот же самый запрос через почтальона, я получаю 16 заголовков. Одним из тех, кто отсутствует, является "Content-Encoding". Я понимаю, что это должно быть проблемой CORS.

Но так как я не установил заголовок Accept-Encoding: gzip в моем запросе, и я все еще получаю кодировку gzip в ответ, транспорт Go автоматически не распаковывает ответ для меня. Итак, мне нужно иметь возможность вручную определить кодировку и затем распаковать ее. Но я не могу определить, отсутствует ли в ответе заголовок "Content-Encoding".

Вот мой код, где я пытаюсь сделать это:

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, .......... }

2 ответа

Решение

Я поддался упрямству Uber API и добавил еще один заголовок запроса, req.Header.Add("Accept-Encoding", "gzip"),

Теперь я получаю заголовок ответа "Content-Encoding": "gzip"Хотя я все еще получаю неразборчивое тело ответа, но это выходит за рамки этого вопроса.

Если вы не отключаете сжатие [1] и не запрашиваете сжатие вручную, то используется то, что я называю «автоматическим режимом». В автоматическом режиме 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

Чтобы разобраться с этим, я написал своему собственному клиенту:

      package http

import (
   "compress/gzip"
   "io"
   "net/http"
   "strings"
)

type Client struct { http.Transport }

func (c Client) Get(addr string) (*http.Response, error) {
   req, err := http.NewRequest("GET", addr, nil)
   if err != nil { return nil, err }
   if !c.DisableCompression {
      req.Header.Set("Accept-Encoding", "gzip")
   }
   res, err := c.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
Другие вопросы по тегам