Skip to content

Latest commit

 

History

History
63 lines (46 loc) · 2.44 KB

File metadata and controls

63 lines (46 loc) · 2.44 KB

HCR081

Dispose streams returned from HTTP content.

Why

Streams returned by HttpClient.GetStreamAsync(...) and HttpContent.ReadAsStreamAsync(...) hold response/content resources until they are disposed or ownership is clearly handed to another component. Leaving those streams undisposed can keep buffers, sockets, or file handles alive longer than intended.

Bad

public async Task CopyAsync(
    HttpResponseMessage response,
    Stream destination,
    CancellationToken cancellationToken)
{
    var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
    await stream.CopyToAsync(destination, cancellationToken);
}

Better

public async Task CopyAsync(
    HttpResponseMessage response,
    Stream destination,
    CancellationToken cancellationToken)
{
    using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
    await stream.CopyToAsync(destination, cancellationToken);
}

Returning the stream is also acceptable when the caller owns it:

public async Task<Stream> OpenAsync(
    HttpClient client,
    CancellationToken cancellationToken)
{
    var stream = await client.GetStreamAsync("https://example.com", cancellationToken);
    return stream;
}

Current Detection

The implementation reports local declarations and simple assignments where an awaited stream is materialized from HttpClient.GetStreamAsync(...) or HttpContent.ReadAsStreamAsync(...) and the local stream is not visibly disposed or transferred.

It recognizes using var, using (stream), direct Dispose() and DisposeAsync(), disposal in finally, returning the stream, and returning an object that is visibly constructed or initialized with the stream. Locals are reassignment-aware, and resolved custom HttpClient lookalikes are skipped.

Cancellation-token flow into streaming APIs is covered by HCR064; this rule focuses on stream ownership.

Suppression

Suppress only when stream ownership is transferred through a pattern the analyzer cannot currently see, such as storing it in a long-lived owner that is disposed elsewhere.

References