Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go http context not able to capture cancellation signal when request has body (curl, postman)

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:

  1. Why and how the curl(postman) GET, POST (with or without request body) request make such a difference?
  2. How should i handle this case properly with go context package, i mean to capture the client is gone signal as soon as it can, and so further to cancel the server side unnecessary work to release the resources as early as it can.
like image 652
lnshi Avatar asked Mar 04 '23 16:03

lnshi


1 Answers

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).

like image 184
Bayta Darell Avatar answered May 09 '23 00:05

Bayta Darell