Does an HttpClient await the Header and the body?

10/06/2024

If you call HttpClient.GetAsync or HttpClient.PostAsync and then await the result, does the await wait only for the headers to be received, or does it wait for the body to be received? Let's find out!

Our simple server

We will create a simple server that sends a response with a header and a body. The body will be sent in chunks so we can see when it is received.

app.MapGet("/", async context =>
    {
        await context.Response.WriteAsync("1");
        await Task.Delay(500);
        await context.Response.WriteAsync("2");
        await Task.Delay(500);
        await context.Response.WriteAsync("3");
        await context.Response.CompleteAsync();
    });

So, the whole request takes at least 1 second to complete. So, let's write a very simple call to that endpoint

The client

using var client = new HttpClient();

// Let's measure the time it takes to get the response
var sw = Stopwatch.StartNew();
await client.GetAsync("https://localhost:5001");
Console.WriteLine($"Response-Time: {sw.ElapsedMilliseconds}ms");

If we execute this code, we will see that the response time is around 1 second. So the await waits for the whole response to be received:

Response-Time: 1088ms

Now that we answered the question, let's see how we can actually not await the body! We can use the HttpCompletionOption.ResponseHeadersRead option:

var sw = Stopwatch.StartNew();
await client.GetAsync("https://localhost:5001", HttpCompletionOption.ResponseHeadersRead);
Console.WriteLine($"Response-Time: {sw.ElapsedMilliseconds}ms");

So we added the HttpCompletionOption.ResponseHeadersRead flag to the GetAsync call. Now the response time is much lower:

Response-Time: 78ms

So the await only waits for the headers to be received. The body is still being received in the background. If you want to wait for the body to be received, you can use the ReadAsStringAsync method:

using var response = await client.GetAsync("https://localhost:5001", HttpCompletionOption.ResponseHeadersRead);
var body = await response.Content.ReadAsStringAsync();

Now very important is that we are using using var response - the object is maybe still holding on to resources, so we should dispose it as soon as possible. If you omit the HttpCompletionOption.ResponseHeadersRead flag, the response object will be disposed when the await is done, so you don't have to worry about it.

All in all, there is a performance and memory benefit when using HttpCompletionOption.ResponseHeadersRead, and you can still read the body if you want to. But you have to be careful with the response object, as it might still hold on to resources and your code becomes more complex. So this shouldn't be your default choice, but only if you really need it.

9
An error has occurred. This application may no longer respond until reloaded. Reload x