Skip to content

Latest commit

 

History

History
52 lines (36 loc) · 3 KB

File metadata and controls

52 lines (36 loc) · 3 KB

HCR080

High-concurrency HTTP fan-out should use bounded concurrency or connection limits.

Why

Task.WhenAll(items.Select(item => client.GetAsync(...))) can start unbounded outbound HTTP work. Under load this can exhaust sockets, queues, rate limits, or upstream capacity.

Bad

await Task.WhenAll(urls.Select(url => client.GetAsync(url, cancellationToken)));

Better

await Parallel.ForEachAsync(urls, new ParallelOptions
{
    MaxDegreeOfParallelism = 8,
    CancellationToken = cancellationToken
}, async (url, ct) =>
{
    using var response = await client.GetAsync(url, ct);
});
using var client = new HttpClient(new SocketsHttpHandler
{
    MaxConnectionsPerServer = 8
});

await Task.WhenAll(urls.Select(url => client.GetAsync(url, cancellationToken)));

Current Detection

The implementation reports System.Threading.Tasks.Task.WhenAll(...) calls whose first argument contains a LINQ Select(...) lambda with an obvious HttpClient async call, including visible local task sequences such as var tasks = urls.Select(...); await Task.WhenAll(tasks); when the local has not been reassigned before WhenAll. The WhenAll target and Select(...) call are validated against BCL Task and LINQ symbols when available, and the HTTP call receiver must resolve to System.Net.Http.HttpClient or be visibly declared as HttpClient in unresolved source, so lookalike Task.WhenAll(...), custom Select(...) methods, resolved custom HttpClient types, and custom fan-out clients are skipped.

It does not report when the lambda visibly gates work with SemaphoreSlim.WaitAsync(...) and a matching Release() on the same symbol-equivalent or visibly declared SemaphoreSlim receiver, when code uses a bounded Parallel.ForEachAsync shape instead of Task.WhenAll, or when the called local, field, property, or this.-qualified member HttpClient is visibly constructed with an inline or member SocketsHttpHandler whose initializer sets MaxConnectionsPerServer. Local HttpClient and handler evidence must apply before those locals are reassigned; a later assigned client or handler without connection limits still reports. Lookalike custom gate types with WaitAsync(...) and Release(), and lookalike custom handler types with a MaxConnectionsPerServer property, do not suppress the diagnostic when Roslyn can resolve their symbols.

Suppression

Suppress when the input collection is already tightly bounded or concurrency is controlled outside the visible code.

References