I would like to know how to properly implement/use context.Done() method when using it within an HTTP server and implementing middleware, my goal is to cancel subsequent events when a client disconnects across nested middleware.
For testing I created the following code, I don't know if is the correct way of doing it since I had to create a channel within the HandleFunc and a goroutine to handle the requests, putting all this together within a select wait statement.
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func hello(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log.Println("handler started")
defer log.Println("hander ended")
ch := make(chan struct{})
go func() {
time.Sleep(5 * time.Second)
fmt.Fprintln(w, "Hello")
ch <- struct{}{}
}()
select {
case <-ch:
case <-ctx.Done():
err := ctx.Err()
log.Println(err)
http.Error(w, err.Error(), http.StatusPartialContent)
}
}
func main() {
http.HandleFunc("/", hello)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Basically here the request simulates load by sleeping 5 seconds, and then prints Hello
, but if the client cancels the request, for example:
$ curl 0:8080
And then pressing <kbd>ctl</kbd> + <kbd> c</kbd>, this will be loged:
2017/07/07 22:22:40 handler started
2017/07/07 22:22:42 context canceled
2017/07/07 22:22:42 hander ended
This works but wondering if this pattern (the goroutine and select) should be used in every nested handler or if there is a better way of implementing this.:
ch := make(chan struct{})
go func() {
// some logic
ch <- struct{}{}
}()
select {
case <-ch:
case <-ctx.Done():
err := ctx.Err()
log.Println(err)
http.Error(w, err.Error(), http.StatusPartialContent)
}