go net/http: HTTP CONNECT失败应返回更有益的错误

rjjhvcjd  于 4个月前  发布在  Go
关注(0)|答案(6)|浏览(42)

你正在使用哪个版本的Go( go version )?

$ go version
go version go1.14.1 darwin/amd64

这个问题在最新版本中是否重现?

是的

你正在使用什么操作系统和处理器架构( go env )?

go env 输出

$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN="..."
GOCACHE="/Users/.../Library/Caches/go-build"
GOENV="/Users/.../Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/.../gopath"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="..."
CXX="clang++"
CGO_ENABLED="1"
GOMOD="..."
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/2b/74fz3jhd4wz4vnbf4z7ywzww0000gp/T/go-build135874009=/tmp/go-build -gno-record-gcc-switches -fno-common"

你做了什么?

https://play.golang.org/p/CsZfrDwLeFb

你期望看到什么?

一个我可以检查的错误。

你看到了什么?

一个 *url.Error,其 Err 设置为 errors.New(...)
在某些环境中,我们使用HTTP CONNECT,偶尔一些目标服务器暂时无法访问。当这种情况发生在其他代理上时,我们可以检查HTTP响应(状态码和/或头信息)或错误(临时报告是否为真?)来确定我们是否应该退避并重试请求。
然而,net/http检查来自初始CONNECT请求的响应,如果它不是200,它会返回一个由状态码文本构造的通用错误:
go/src/net/http/transport.go
第1622行 in 564c76a
| | ifresp.StatusCode!=200 { |
这意味着我们只能做类似这样的事情:

var (
    httpStatusServiceUnavailable = http.StatusText(http.StatusServiceUnavailable)
    ...
)

resp, err := c.Get(...)
if err != nil {
    switch s := err.(*url.Error).Err.Error(); s {
    case httpStatusServiceUnavailable:
        /* backoff then retry */
    ...
    default:
        return err
    }
}
...

一个有用的错误示例可能是这样的:

type ConnectError struct {
    StatusCode int
    ...
}

func (c *ConnectError) Error() string {
    return StatusText(c.StatusCode)
}

var _ error = (*ConnectError)(nil)
km0tfn4u

km0tfn4u1#

感谢您提交这个bug @elagergren-spideroak,很高兴在这里遇到您!
我能够理解这个问题,这段代码几乎是在十年前(约9年前)编写的,当时我们只有os.ErrorString,而且这个包还很年轻。
感谢您建议添加ConnectError,但考虑到我已经遇到了这个问题,我认为可能还有其他地方可以使用StatusCode和message,也许我们可以将其改为CodedError,它看起来像这样:

type CodedError struct {
    Code int
    Message string
    Method string
}

type (ce *CodedError) Error() string { return ce.Message }

在使用时,您可以这样做:

resp, err := c.Get(...)
if err != nil {
     ce, ok := err.(*http.CodedError)
     if !ok {
             return err
     }

     // Otherwise check and deal with backoff + retries.
     ...
}

这种类型还可以泛化为其他包的使用,例如网络包,在其中我们可以提供更多关于拨号失败的原因,同时也可以指定拨号堆栈中失败的位置,通过指定Method。
我们需要意识到的一件事是,如果我们添加了这个错误:
a) 破坏几乎持续了几十年的代码的可能性有多大?有多少代码已经通过切换状态或期望err.Error()返回一个字符串作为唯一细节来处理错误?

xmjla07d

xmjla07d2#

就它的价值而言,我并不太担心实际的错误——ConnectError只是一个例子。这个票据是关于获取任何我可以检查的错误,而不是一个特定的错误:)
然而,正如你提到的,确保向后兼容性是很重要的,这就是为什么在我的例子中,Error方法只是返回了http.StatusText
与当前错误的比较总是评估为false,所以我无法想象有人会依赖这种行为。

polkgigr

polkgigr3#

@gopherbot remove WaitingForInfo

zdwk9cvp

zdwk9cvp4#

https://golang.org/cl/289929提到了这个问题:net/http: add new error type to be returned on unsuccessful CONNECT request

kmbjn2e3

kmbjn2e35#

你好,我几个月前提交了一个PR(合并请求),旨在解决这个问题及其重复项,但没有收到任何关于我的PR的反馈。请问你能帮忙吗?

6kkfgxo0

6kkfgxo06#

CC @neild@fraenkel@bradfitz

相关问题