请求正文时,转到http上下文无法捕获取消信号(curl,postman)

时间:2019-07-29 03:24:31

标签: http go curl postman

可以通过下面的简单go代码段重现该问题:

简单的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服务器上方开始,如果我发送一个简单的GET请求,并且带有curl(邮递员也这样):

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(postman)GETPOST(带有或不带有请求正文)请求为何以及如何产生这种区别?
  2. 我应该如何使用go context包正确处理这种情况,我的意思是捕获客户端尽快消失的信号,并进一步取消服务器端不必要的工作以尽早释放资源。

1 个答案:

答案 0 :(得分:2)

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

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服务器通过读取连接来检查关闭的连接。在应用程序开始读取请求正文(如果有)之前,不会开始读取。