How does a done signal from a context help in cancelling a request?

Viewed 609

This is a code snippet from the golang context page https://blog.golang.org/context

func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error {
    // Run the HTTP request in a goroutine and pass the response to f.
    c := make(chan error, 1)
    req = req.WithContext(ctx)
    go func() { c <- f(http.DefaultClient.Do(req)) }()
    select {
        case <-ctx.Done():
            <-c // Wait for f to return.
            return ctx.Err()
        case err := <-c:
            return err
    }
}

The description for this method says

The httpDo function runs the HTTP request and processes its response in a new goroutine. It cancels the request if ctx.Done is closed before the goroutine exits:

How is the request cancelled here? the way i see it, even if the context is done, we are still waiting for the result from the request? how does this help?

2 Answers

The trick is that context is passed to http.DefaultClient.Do(req) using req = req.WithContext(ctx), so req argument keeps context inside. Functionhttp.DefaultClient.Do() uses it and will exit as soon as original context (ctx) is cancelled.

In fact I wouldn't say that "httpDo function cancels the request", it is cancelled through ctx by caller of httpDo. Using goroutine here is arguable, we can just return f(http.DefaultClient.Do(req))

Do(req) will exit with an error as soon as context is cancelled. You're right that we're still waiting for a value in c but we should get it almost immediately after context cancellation. Also, it's not necessary to wait for completion of Do(req). It's possible to just early return from the function based on your use case.

Of course, random func Foo(ctx context.Context) function will not necessary listen to a context so it is not safe to assume that all the functions that take the context will handle the cancellation correctly. You should act based on documentation and/or code.

Related