The problem can be reproduced with the below simple go code snippet:
Simple go http server:
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))
}
Start above http server, if i send one simple GET
request with curl(postman also) like:
curl -X GET http://localhost:8080/
then press Ctrl+C
to terminate the request, then i am able to see the printed message at server side:
message client connection has gone away, request got cancelled
above is the correct behaviour i expect: to simulate the situation that when client is gone the server can capture it and then cancel all the unnecessary work as early as it can.
But when i send one POST request with request body, this expected behaviour doesn't happen, the <-done
signal was captured until the request deadline meet.
curl -X POST http://localhost:8080/ -H 'Content-Type: application/json' -d '{}'
To summarize my questions:
GET
, POST
(with or without request body) request make such a difference?Read the request body to detect when the client closes the connection:
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:])
}
The net/http server checks for closed connections by reading the connection. No reads are started until the application starts reading the request body (if any).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With