Skip to content

Latest commit

 

History

History
58 lines (41 loc) · 2.58 KB

File metadata and controls

58 lines (41 loc) · 2.58 KB
title HCR021 — DelegatingHandler.SendAsync should forward the cancellation token
description A handler that drops the incoming CancellationToken makes client disconnects and timeouts unable to cancel the outbound request.

HCR021

DelegatingHandler.SendAsync should forward the cancellation token to base.SendAsync.

Why

SendAsync overrides receive a CancellationToken that carries client disconnects, HttpClient.Timeout, and upstream cancellation. Passing CancellationToken.None, default, or a freshly created token to base.SendAsync severs that link: the outer caller cancels, but the actual socket request keeps running — leaking connections and exhausting the thread pool under load.

Bad

protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request,
    CancellationToken cancellationToken)
{
    return base.SendAsync(request, CancellationToken.None);
}

Better

Forward the token the override received:

protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request,
    CancellationToken cancellationToken)
{
    return base.SendAsync(request, cancellationToken);
}

When the handler needs its own timeout, link the tokens instead of discarding the caller's:

using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
timeout.CancelAfter(TimeSpan.FromSeconds(10));
return await base.SendAsync(request, timeout.Token);

Current Detection

The implementation reports base.SendAsync(...) invocations inside a SendAsync override on a DelegatingHandler-derived type when the token argument is CancellationToken.None, default, default(CancellationToken), or a newly constructed token — or when no token argument is passed — while the override declares a CancellationToken parameter it could forward. Overrides that forward their own parameter, a linked token, or a field token are skipped, as are overrides with no token parameter.

Suppression

Suppress only when the handler intentionally detaches cancellation — for example a fire-and-forget logging handler that must outlive the caller's request. Document the reason; the default should be forwarding.

References