Example: HTTP Client Cancel a Request
An HTTP request may outlive the code that started it.
A user may leave a page. A job may be canceled. An upstream operation may already have failed. Continuing to wait for the HTTP operation wastes time and resources.
In Go, use the request's context.Context to control its lifetime.
For an outgoing request, the context applies to the request exchange, including connection establishment, sending the request, waiting for response headers, and reading the response body.
Quick Example
The core pattern is:
Calling cancel() signals that the operation should stop.
panic is used only to simplify the minimal demo. Production code must propagate and handle errors instead.
Cancel When the Operation Is No Longer Needed
Explicit cancellation is useful when an external event decides that the request should stop.
Later, another part of the program can call:
This differs from a fixed time limit:
Use WithCancel when cancellation comes from the lifecycle of the operation:
- a user aborts an operation
- a parent job is canceled
- a worker shuts down
- a result is no longer needed
Cancel a Request After It Starts
Cancellation does not have to happen before Do.
The request may already be waiting for:
- connection establishment
- request transmission
- response headers
- response body data
The request context remains associated with the HTTP operation after Do returns.
A simple example with an external cancellation event:
The cancellation signal belongs to the request's context, not to a custom channel watched by the HTTP client.
The channel above only communicates the result back to the caller.
Do not build a custom HTTP cancellation mechanism when context.Context already provides the cancellation boundary.
Cancellation Also Covers Response-Body Reads
This is one of the most important details.
Cancellation does not stop being relevant when Do returns.
If the context is canceled while the body read is blocked, the pending HTTP I/O can be interrupted.
This matters especially for streaming responses:
You do not need a separate cancellation mechanism for resp.Body.
There is an important boundary, however:
Cancellation does not undo I/O that has already completed.
If data has already been read from the network and is buffered in memory, canceling the context does not erase those bytes. Cancellation affects the operation's pending I/O; it is not a rollback mechanism.
Cancel the Parent Operation
In production code, an HTTP request usually should not create an unrelated lifetime.
Instead, accept a context from the caller:
Now the caller controls the lifetime:
In a larger application, the context usually already exists:
The HTTP request becomes part of the larger operation instead of having an independent lifetime.
This is especially important when the HTTP call is only one step in a larger workflow.
Use WithTimeout for a Time Limit
If the requirement is:
Stop waiting after five seconds.
Use a timeout context:
WithTimeout is cancellation with an automatic deadline.
When the deadline expires, the context is canceled automatically.
Always call the returned cancel function and defer it immediately:
Even when the operation finishes normally, calling cancel releases resources associated with the derived context.
The distinction is:
Context Timeout vs Client.Timeout
Both can terminate HTTP requests, but they represent different ownership boundaries.
A request-scoped timeout applies to one operation:
http.Client.Timeout applies to requests made through that client instance:
Client.Timeout is a hard wall-clock limit covering the entire request lifecycle, including:
- connection setup
- redirects
- sending the request
- waiting for response headers
- reading the response body
It is not merely a connection timeout.
A useful taxonomy is:
In practice, a request-specific context is usually the right place for an operation's deadline because it follows the operation through the call chain.
A client-level timeout can still provide a useful safety guardrail.
Don't Use Transport.CancelRequest
Older Go code may contain:
Do not use this for new code.
Transport.CancelRequest was deprecated in Go 1.5. Request context cancellation is the modern mechanism and works across the HTTP transport's supported protocols.
Prefer:
The cancellation policy is then attached directly to the request lifecycle.
Cancellation Is an Error Condition
After cancellation, Do normally returns an error rather than a successful HTTP response.
Do not swallow cancellation:
Cancellation is still an error condition for the operation.
Whether the caller treats that error as expected or ignores it is an application-level decision. The HTTP helper should not silently convert cancellation into success.
These cases are also useful to distinguish in logging and metrics:
Do not treat context cancellation as equivalent to a server-side failure such as HTTP 500.
Don't Forget to Close the Response Body
Cancellation does not replace normal response-body ownership.
When Do succeeds:
You still need to close resp.Body.
These solve different problems:
Use both where applicable.
If you are reading a response body and cancellation occurs, the body should still be closed by the code that owns the response.
Production Example
A production HTTP helper should accept the caller's context instead of creating an isolated context.Background() internally.
The key design choice is:
The caller owns the operation's lifetime.
Cancellation then propagates naturally:
The helper does not need its own cancellation channel, timeout goroutine, or global cancellation state.
Common Mistakes
1. Creating context.Background inside the HTTP helper
The caller can no longer cancel the request through its own context.
Prefer:
2. Creating a timeout and discarding cancel
Prefer:
3. Assuming cancellation only affects Client.Do
The request context also governs pending response-body reads.
4. Assuming cancellation can undo completed I/O
It cannot.
Already-read or already-buffered data is not rolled back when the context is canceled.
5. Using Transport.CancelRequest
It is deprecated. Use request context cancellation for new code.
6. Treating cancellation as a server failure
An explicitly canceled operation is different from an HTTP 5xx response or a network failure.
Keep those cases distinguishable in application metrics and logging.
Rule of Thumb
The core pattern is:
The rule is simple:
The caller owns the operation's lifetime. The request carries that lifetime through its context. Cancel the context when the operation is no longer needed.
That is the standard cancellation boundary for Go HTTP clients.