取消 Web 请求并处理 ReverseProxy Director 函数中的错误

cancel a web request and handle errors inside the ReverseProxy Director function

我想知道是否可以在 ReverseProxy.Director 函数内取消 Web 请求或向客户端发送内部响应。

假设我们做某事抛出错误,或者我们有其他原因不转发请求。

proxy := &httputil.ReverseProxy{
    Director: func(r *http.Request) {
        err := somethingThatThrows()
    },
}

http.Handle("/", proxy)

下面可能是解决这个问题的方法,但它不像上面使用代理的方法那么简洁。我也不确定请求应该以这种方式修改到什么程度。 director 好像是做那个的地方

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    err := somethingThatThrows()
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    proxy.ServeHTTP(w, r)
})

if it would be possible to cancel a web request [...]

您可以取消传递给 Director 函数的请求,但是 有一些细节需要考虑:

  • 取消请求的正确方法是取消其上下文
  • 你不能取消你自己没有设置 (deadline|timeout|cancelfunc) 的上下文 → 即你必须有权访问 cancel 函数 → 即你不能取消 parent 上下文由其他人创建。
  • 传递给 Director 函数的 *http.Request 是原始请求的克隆

基于以上几点,您可以将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
        }
    },
}

那么上面的代码本身并没有做任何事情。应该发生的是实现 http.RoundTripperhttputil.ReverseProxy.Transport 函数在实际向上游服务发送任何内容之前检查请求上下文是否被取消。

Director 的文档指出:

Director must be a function which modifies the request into a new request to be sent using Transport.

当未提供 Transport 时,它将退回到 http.DefaultTransport,这会在上下文被取消时中止请求。当前代码(Go 1.17.5)看起来像:

        select {
        case <-ctx.Done():
            req.closeBody()
            return nil, ctx.Err()
        default:
        }

如果您提供自己的 http.RoundTripper 实现,您可能希望自己实现该行为。还要记住上下文完成通道是 nil 如果它不可取消,所以你 必须 设置取消函数 并调用 cancel() 以便 select 运行 “完成”案例。


or send an internal response to the client inside the ReverseProxy.Director

根据文档上面的相同引用,您应该 而不是 Director 函数中写入 http.ResponseWriter — 假设您甚至关闭它。如您所见,Director 本身没有将 http.ResponseWriter 作为参数,这应该已经是一个 self-explanatory 细节。

如果您想指定一些其他行为以防无法转发请求,并假设取消请求上下文时 http.RoundTripper 的任何实现都返回 error,您可以提供您的 ReverseProxy.ErrorHandler 功能:

proxy.ErrorHandler = func(writer http.ResponseWriter, request *http.Request, err error) {
    // inspect err
    // write to writer
}

ErrorHandler 将在 Transport returns 错误时调用,包括当错误来自已取消的请求时,并且它确实有 http.ResponseWriter 作为参数。