A stopwatch can stop waiting while the HTTP request continues. Supply cancellation to the operation itself when a proxy attempt has an intended budget.

For a modern .NET application with an existing proxy-configured HttpClient:

using var budget = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var response = await client.GetAsync(
    targetUrl,
    HttpCompletionOption.ResponseHeadersRead,
    budget.Token);
response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync(budget.Token);

The same budget starts before setup and remains active while the body is read. ResponseHeadersRead returns control after headers, so HttpClient's own completion timeout does not by itself define the later body-read budget.

Keep cancellation distinct from HTTP status and transport errors. An OperationCanceledException can come from the supplied token or another configured timeout; inspect the applicable cancellation source instead of reporting every failure as a dead proxy.

Reuse the client that owns this proxy route. Cancelling an attempt need not mean rebuilding an entire connection pool for every later request.

The example buffers a finite response body. A streaming download should pass the token through its stream-copy loop and release the response on every exit path.

Cancellation expresses an operation policy, not an exact wall-clock promise for every underlying platform action. Avoid replacing it with an endless polling loop over Task.IsCompleted.

Related articles