Skip to content

[API Proposal]: Accept HttpCompletionOptions to more places #115106

Closed as not planned
@huoyaoyuan

Description

@huoyaoyuan

Background and motivation

Currently, HttpClient uses HttpCompletionOption.ResponseContentRead as the default option:

private const HttpCompletionOption DefaultCompletionOption = HttpCompletionOption.ResponseContentRead;

This can simplify some logic of consumers, for example cancellation and duplex buffering can be handled at once.

When ResponseHeadersRead is desired, for example passing the content stream to some parser or image control that supports incremental loading, there are only a limited members available. Importantly, the most-convenient GetStreamAsync method doesn't accept HttpCompletionOption. The streaming json helpers are explicitly specifying ResponseHeadersRead:

private static readonly Func<HttpClient, Uri?, CancellationToken, Task<HttpResponseMessage>> s_getAsync =
static (client, uri, cancellation) => client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, cancellation);

API Proposal

namespace System.Net.Http;

public class HttpClient
{
    public Task<Stream> GetStreamAsync([StringSyntax("Uri")] string? requestUri);
    public Task<Stream> GetStreamAsync([StringSyntax("Uri")] string? requestUri, CancellationToken cancellationToken);
+    public Task<Stream> GetStreamAsync([StringSyntax("Uri")] string? requestUri, HttpCompletionOption completionOption);
+    public Task<Stream> GetStreamAsync([StringSyntax("Uri")] string? requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken);
    public Task<Stream> GetStreamAsync(Uri? requestUri);
    public Task<Stream> GetStreamAsync(Uri? requestUri, CancellationToken cancellationToken);
+    public Task<Stream> GetStreamAsync(Uri? requestUri, HttpCompletionOption completionOption);
+    public Task<Stream> GetStreamAsync(Uri? requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken);

+    public HttpCompletionOptions DefaultCompletionOptions { get; set; }
}

API Usage

using var httpClient = httpClientFactory.GetClient();
var imaTeapot = httpClient.GetStreamAsync("https://http.cat/images/418.jpg", HttpCompletionOption.ResponseHeadersRead);
imageControl.SetSourceStream(imaTeapot);

Alternative Designs

Are the overloads redundant? Can they be combined with CancellationToken cancellationToken = default?

Risks

Allow specifying DefaultCompletionOptions may have unintended effects for callers having assumptions for ResponseContentRead. We can choose to only expose the completion options on individual methods. The CanSeek property for content stream is changed.

This also increases the risk of using ResponseHeadersRead inappropriately. Per testing, if the HttpClient is disposed before the stream is read, the response stream will be effectively "detached" and become empty. I didn't find explicit mentions about this at https://learn.microsoft.com/dotnet/fundamentals/runtime-libraries/system-net-http-httpclient#buffering-and-request-lifetime.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions