如果可以取消网络请求 [...]
您可以取消传递给Director
函数的请求,但有一些细节需要考虑:
- 取消请求的正确方法是取消其上下文
- 您不能取消您自己没有设置 (deadline|timeout|cancelfunc) 的上下文 → 即您必须有权访问该
cancel
功能 → 即您不能取消其他人创建的父上下文。
*http.Request
传递给Director
函数是原始请求的克隆
基于以上几点,您可以将 中的请求替换为Director
另一个具有可取消上下文的请求。它可能如下所示:
proxy := &httputil.ReverseProxy{
Director: func(req *http.Request) {
// create a cancellable context, and re-set the request
ctx, cancel := context.WithCancel(req.Context())
*req = *req.WithContext(ctx)
err := somethingThatThrows()
if err != nil {
cancel()
return
}
},
}
然后上面的代码本身不会做任何其他事情。应该发生的是,在实际向上游服务发送任何内容之前httputil.ReverseProxy.Transport
,实现检查请求上下文是否被取消的函数。http.RoundTripper
国家的文件Director
:
Director 必须是将请求修改为要使用 Transport 发送的新请求的函数。
如果Transport
未提供 ,它将回退到 ,这会http.DefaultTransport
在取消上下文时中止请求。当前代码(Go 1.17.5)如下所示:
select {
case <-ctx.Done():
req.closeBody()
return nil, ctx.Err()
default:
}
如果您提供自己的实现,http.RoundTripper
您可能希望自己实现该行为。还要记住,上下文完成通道是nil
如果它不可取消,所以你必须设置一个取消函数并调用cancel()
,以便select
让它运行“完成”情况。
或向 ReverseProxy.Director 内的客户端发送内部响应
基于上面文档的相同引用,您不应该在函数内写入http.ResponseWriter
from Director
——假设您甚至关闭它。如您所见,Director
它本身并没有http.ResponseWriter
作为参数,这应该已经是一个不言自明的细节。
如果您想在无法转发请求的情况下指定一些其他行为,并假设在取消 req 上下文时http.RoundTripper
返回的任何实现,您可以提供您的函数:error
ReverseProxy.ErrorHandler
proxy.ErrorHandler = func(writer http.ResponseWriter, request *http.Request, err error) {
// inspect err
// write to writer
}
将在返回错误时ErrorHandler
调用Transport
,包括当错误来自取消的请求时,并且它确实有http.ResponseWriter
作为参数。