In Go, determining whether a request has been cancelled can be a challenge. Consider the following code:
package main import ( "context" "log" "net/http" ) func main() { r, _ := http.NewRequest("GET", "http://example.com", nil) ctx, cancel := context.WithCancel(context.Background()) r = r.WithContext(ctx) ch := make(chan bool) go func() { _, err := http.DefaultClient.Do(r) log.Println(err == context.Canceled) ch <- true }() cancel() <-ch }
Surprisingly, this code prints false in Go 1.9 even though the request should have been cancelled.
In more recent versions of Go, a better way to check for cancellation is to use the errors.Is function introduced in Go 1.13. Here's an updated version of the code:
import ( "context" "errors" "log" "net/http" ) func main() { // Create a context that is already canceled ctx, cancel := context.WithCancel(context.Background()) cancel() // Create the request with it r, _ := http.NewRequestWithContext(ctx, "GET", "http://example.com", nil) // Do it, it will immediately fail because the context is canceled. _, err := http.DefaultClient.Do(r) log.Println(err) // Get http://example.com: context canceled // This prints false, because the http client wraps the context.Canceled // error into another one with extra information. log.Println(err == context.Canceled) // This prints true, because errors.Is checks all the errors in the wrap chain, // and returns true if any of them matches. log.Println(errors.Is(err, context.Canceled)) }
By using errors.Is, we can reliably check if the underlying error is a context cancellation error, even if it has been wrapped by another error. The errors.Is function will traverse the entire chain of errors and return true if any of them matches the given error type.
The above is the detailed content of How do I reliably check for a request cancellation error in Go, even if it has been wrapped by another error?. For more information, please follow other related articles on the PHP Chinese website!