Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using HttpClient's HttpCompletionOption.ResponseHeadersRead is slow with NativeHttpHandler #8848

Open
carmichaelalonso opened this issue Mar 29, 2024 · 0 comments
Assignees
Labels
Area: App Runtime Issues in `libmonodroid.so`. needs-triage Issues that need to be assigned.

Comments

@carmichaelalonso
Copy link

carmichaelalonso commented Mar 29, 2024

Android application type

.NET Android (net7.0-android, net8.0-android, etc.)

Affected platform version

VS 2022 17.9.4, .NET 8.0.200

Description

I use HttpClient's HttpCompletionOption.ResponseHeadersRead to be able to copy data to a buffer as it is streamed, and to use a Progress instance to track the progress of a download to display in the UI.

I noticed that this became orders of magnitude slower when we migrated to .NET Android from Xamarin. After investigating, whenever we call client.SendAsync(), it is slow whenever we are using ResponseHeadersRead as the completion option:
await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, uri), HttpCompletionOption.ResponseHeadersRead);

Sample project attached showing both cases and the printed times with a sample 100mb file from a speed test (feel free to try with different files). Here is the data from making an API call in that sample project:

Sample Project: HttpResponseHeadersRepro.zip

Method Time (Attempt 1) Time (Attempt 2)
await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, uri), HttpCompletionOption.ResponseHeadersRead); 1060292ms 707847ms
await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, uri)); 9718ms 9717ms

We are not experiencing this in net8-ios or net8-windows with the same code. This also only happens when using the Native HttpHandler (the default in net8-android and MAUI as far as I am aware).

Steps to Reproduce

  1. Create a new Android project
  2. Ensure that UseNativeHttpHandler is set to true in the csproj.
  3. Make a request with the sample code and take note of the returned time from the Stopwatch:
private async Task<long> TestHttpResponseHeadersApiCallAsync()
{
    var uri = new Uri("https://lon.download.datapacket.com/100mb.bin");
    var client = new HttpClient();
    var sw = Stopwatch.StartNew();
    var httpResponse = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, uri), HttpCompletionOption.ResponseHeadersRead);
    if (httpResponse != null && httpResponse.IsSuccessStatusCode)
    {
        long maxSize = httpResponse.Content.Headers.ContentLength.GetValueOrDefault(0);
        using (var download = await httpResponse.Content.ReadAsStreamAsync())
        {
            IProgress<long> progressWrapper = new Progress<long>(totalBytes => DownloadProgressChanged(totalBytes));
            var buffer = new byte[maxSize];
            long totalBytesRead = 0;
            int bytesRead;
            while ((bytesRead = await download.ReadAsync(buffer, 0, buffer.Length, default(CancellationToken)).ConfigureAwait(false)) != 0)
            {
                totalBytesRead += bytesRead;
                progressWrapper.Report(totalBytesRead);
            }
        }
    }
    return sw.ElapsedMilliseconds;
}
  1. Make the same call again, but remove HttpCompletionOption.ResponseHeadersRead (or set it to the default HttpCompletionOption.ResponseContentRead) and observe that the download time is significantly faster and en-par with other platforms.

Did you find any workaround?

Relevant log output

log.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: App Runtime Issues in `libmonodroid.so`. needs-triage Issues that need to be assigned.
Projects
None yet
Development

No branches or pull requests

2 participants