Go http 上下文无法在请求具有正文(curl、邮递员)时捕获取消信号



这个问题可以用下面的简单go代码片段重现:

简单的去http服务器:

package main
import (
"fmt"
"log"
"net/http"
"time"
)
func handler(w http.ResponseWriter, r *http.Request) {
go func(done <-chan struct{}) {
<-done
fmt.Println("message", "client connection has gone away, request got cancelled")
}(r.Context().Done())
time.Sleep(30 * time.Second)
fmt.Fprintf(w, "Hi there, I love %s!n", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}

从 http 服务器开始,如果我使用 curl(邮递员也)发送一个简单的GET请求,例如:

curl -X GET http://localhost:8080/

然后按Ctrl+C终止请求,然后我可以在服务器端看到打印的消息:

message client connection has gone away, request got cancelled

以上是我期望的正确行为:模拟客户端消失时服务器可以捕获它然后尽早取消所有不必要的工作的情况。

但是当我发送一个带有请求正文的 POST 请求时,这种预期的行为不会发生,<-done信号被捕获,直到请求截止日期满足。

curl -X POST http://localhost:8080/ -H 'Content-Type: application/json' -d '{}'

总结一下我的问题:

  1. 为什么以及如何 curl(邮递员)GETPOST(有或没有请求正文)请求会产生如此大的差异?
  2. 应该如何使用go上下文包正确处理这种情况,我的意思是尽快捕获客户端消失的信号,因此进一步取消服务器端不必要的工作以尽早释放资源。

读取请求正文以检测客户端何时关闭连接:

func handler(w http.ResponseWriter, r *http.Request) {
go func(done <-chan struct{}) {
<-done
fmt.Println("message", "client connection has gone away, request got cancelled")
}(r.Context().Done())
io.Copy(ioutil.Discard, r.Body) // <-- read the body
time.Sleep(30 * time.Second)
fmt.Fprintf(w, "Hi there, I love %s!n", r.URL.Path[1:])
}

net/http 服务器通过读取连接来检查已关闭的连接。 在应用程序开始读取请求正文(如果有)之前,不会启动读取。

最新更新