当状态为 301 且没有位置 header 时,HTTP 客户端不会 return 响应

HTTP Client doesn't return response when status is 301 without a Location header

当我使用 Go 的 net/http/client 包发出请求时,服务器响应 301 或 302 状态码,但没有提供 Location header,我们得到一个错误 object,并且没有响应 object。示例 ReplIt session:https://replit.com/@sharat87/RedirectWithoutLocationGo#main.go。在此处包括源以防 session 将来消失:

package main

import (
  "log"
    "net/http"
)

func main() {
    client := &http.Client{}
    resp, err := client.Get("https://httpbun.com/status/301")
    if err != nil {
        log.Println("Error getting", err)
    } else {
        log.Println(resp.Status)
    }
}

这将打印错误消息:Error getting Get "https://httpbun.com/status/301": 301 response missing Location header

但是,301 或 302 状态响应中的位置 header 是可选的。服务器不需要提供它,客户端也没有义务重定向到它。事实上,就我而言,我需要查看其他 header 以及响应 body 以获得我需要的内容。但是当 Location header 丢失时,Go 只是 returns 一个错误并丢弃整个响应。例如,curl 在这种情况下不会抛出错误,实际上会响应其他 headers:

$ curl -v https://httpbun.com/status/301
[... TLS logs redacted]
> GET /status/301 HTTP/1.1
> Host: httpbun.com
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.18.0 (Ubuntu)
< Date: Sun, 25 Jul 2021 02:18:53 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 21
< Connection: keep-alive
< X-Powered-By: httpbun
<

我深入研究了 net/http/client 模块的源代码,试图看看可能发生了什么。错误消息来自 this line in the source. Here, for the redirected request, we first check if a Location header is available, and if not, we are returning an error. This is very limiting especially because the spec doesn't describe this to be an error condition and in fact, lists a legitimate reason for doing this (i.e., the server doesn't know where to redirect to) (reference, links to sources).

所以,我的问题是,这是故意的吗?我在这里错过了什么吗?会邀请 PR 来改变这个吗? (在这里问是因为看起来 GitHub 上的 Golang 回购问题是为 提案 保留的?)。

我很想在这里得到任何建议,因为我在这里基本上 被封锁

默认客户端遵循重定向。您可以使用 RoundTrip直接:

package main

import (
   "fmt"
   "net/http"
)

func main() {
   req, err := http.NewRequest("GET", "http://httpbun.com/status/301", nil)
   if err != nil {
      panic(err)
   }
   res, err := new(http.Transport).RoundTrip(req)
   if err != nil {
      panic(err)
   }
   defer res.Body.Close()
   fmt.Printf("%+v\n", res)
}

创建一个传输包装器来修复客户端的响应。以下示例在缺少 header 时设置位置 header。另一种选择是更改状态代码。

type locFix struct {
    http.RoundTripper
}

func (lf locFix) RoundTrip(req *http.Request) (*http.Response, error) {
    resp, err := lf.RoundTripper.RoundTrip(req)
    if err != nil {
        return resp, err
    }   
    if resp.StatusCode == 301 || resp.StatusCode == 302 {
        if resp.Header.Get("Location") == "" {      
            resp.Header.Set("Location", "http://example.com")
        }
    }
    return resp, err
}

将 http.Client 与打包传输一起使用。以下是为 net/http 默认客户端创建包装器的方法:

client := *http.DefaultClient
t := client.Transport
if t == nil {
    t = http.DefaultTransport
}
client.Transport = locFix{t}

resp, err = client.Get("https://httpbun.com/status/301")