go net/http: 在处理带有 Package 的http.ErrAbortHandler的恐慌时,程序崩溃,

bwleehnv  于 4个月前  发布在  Go
关注(0)|答案(4)|浏览(57)

你正在使用的Go版本是什么( go version )?

$ go version
go version go1.21.0 linux/amd64

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

是的

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

go env 输出

$ go env
GO111MODULE=''
GOARCH='amd64'
GOBIN='/home/eric/work/bin'
GOCACHE='/home/eric/.cache/go-build'
GOENV='/home/eric/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/eric/work/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/eric/work'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/eric/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/home/eric/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.21.0'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build978392017=/tmp/go-build -gno-record-gcc-switches'

你做了什么?

当调用下游 net/http.conn.serve 实现中的 panic 时, http.ErrAbortHandler 方法能够处理一个特殊情况,它会静默恢复并防止程序崩溃。
当链接 http.Handler 实现时,如果一个 http.Handler 使用了 golang.org/x/sync/singleflight.Group ,并且该 panic 下游的 http.Handler 使用 http.ErrAbortHandler 恐慌,那么 singleflight 包首先从恐慌中恢复,将恐慌值 Package 在其自己的私有错误类型中,并用其私有类型重新引发恐慌。当新的恐慌到达 net/http.conn.serve 时,进行直接比较以查看是否为 err != ErrAbortHandler。由于技术上 err 不是 ErrAbortHandler ,而只是另一个 Package ErrAbortHandler 的错误类型,因此 net/http.conn.serve 不处理这个特殊情况,允许程序崩溃。
处理恐慌且程序不崩溃的示例:

使用 golang.org/x/sync/singleflight.Group 但不处理恐慌导致程序崩溃的示例:

你期望看到什么?

在使用链式 http.Handler 实现中,当下游调用一个 panic(http.ErrAbortHandler 时, net/http.conn.serve 能够解包错误以确定错误是否实际上是 http.ErrAbortHandler 并处理恐慌,而不使程序崩溃。

你看到了什么?

程序以消息 panic: net/http: abort Handler 崩溃。

knpiaxh1

knpiaxh11#

https://go.dev/cl/526418提到了这个问题:net/http: unwrap errors when checking for ErrAbortHandler

nqwrtyyt

nqwrtyyt3#

https://go.dev/cl/526171提到了这个问题:singleflight: add panicError.Unwrap method

pftdvrlh

pftdvrlh4#

由于技术上 err 不是 ErrAbortHandler,而是另一种 Package ErrAbortHandler 的错误类型,net/http.conn.serve 没有处理这个特殊情况,允许程序崩溃。
net/http 会吞噬来自处理器的所有恐慌。ErrAbortHandler 只控制是否记录错误。如果程序崩溃,那么其他事情正在发生。
https://go.dev/cl/526418net/http 的变化对我来说似乎是合理的——我们检查 ErrAbortHandler ,我认为没有理由不检查 Package ErrAbortHandler 的错误。
singleflight 对恐慌的抽象是有漏洞的:如果传递给 singleflight.Group.Do 的函数发生恐慌,那么 Do 会传播恐慌,但它将原始恐慌值隐藏在一个添加堆栈跟踪的类型中。调用者无法恢复原始恐慌值。
singleflight 没有说明如何处理恐慌。
https://go.dev/cl/526171 建议将 singleflight 更改为 Package 原始恐慌值,当该值是一个错误时。这将允许恢复原始恐慌值,但仅限于该值是一个 error 的情况。
我对 error 值的特殊处理不确定;如果恐慌值不是错误,但调用者仍然需要它怎么办?另一方面,对于 singleflight 为原始恐慌源添加堆栈信息似乎很有用,而当原始值是一个使用错误 Package 来增强错误的错误时,这种做法相当合理。

相关问题