HTTP RequestWithContext in a server and error handling pattern

747 views
Skip to first unread message

Amit Saha

unread,
Jan 8, 2021, 5:51:43 PM1/8/21
to golang-nuts
Say I have created a RequestWithContext:

r, err := http.NewRequestWithContext(ctx, "GET", "http://127.0.0.1:8080/healthcheck", nil)

Now, I make the request in the context of *another* HTTP request handling function:

Func myHandler(w http.ResponseWriter, r *http.Request) {
...

resp, err := client.Do(r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}

}


When I cancel this context, the above http.Error() call triggers a superfluous write header message.
I dug down a bit and I can see that when the context is cancelled, the finishRequest() function (https://golang.org/src/net/http/server.go?h=finishRequest#L1613) is called which essentially writes a 200 status code. Hence, when the code above goes to write an error, it gets the superfluous writeHeader message.

I then updated my code above to be:

resp, err := client.Do(r)
if err != nil {
if err != context.Canceled {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}

Wondering if anyone has any thoughts on this pattern and if there is any reason why we complete the full lifecycle of finsihRequest() on context cancellation even if it is going to fail somewhere down the line since in this case, the client has already disconnected?

Amit Saha

unread,
Jan 8, 2021, 5:59:43 PM1/8/21
to golang-nuts
I missed the complete handler function which will make the query more sensible:

func getData(ctx context.Context, w http.ResponseWriter) {
done := make(chan bool)
client := http.Client{}

go func() {
r, err := http.NewRequestWithContext(ctx, "GET", "http://127.0.0.1:8080/healthcheck", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
resp, err := client.Do(r)
if err != nil && err != context.Canceled {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
defer resp.Body.Close()
io.Copy(w, resp.Body)
done <- true
}()

select {
case <-done:
return

case <-ctx.Done():
log.Printf("api: client disconnected.")
return # This return triggers the finishRequest() function call to be made
}
}




Reply all
Reply to author
Forward
0 new messages