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.

Missing Stack trace when eliding the await keyword

You may have heard that when you elide the await keyword in a method that returns a Task or Task<T>, you lose the stack trace. Buy why does that happen? Let's find out!

ASP.NET Core - Why async await is useful

Did you ever wonder why you "should" use async and await in your ASP.NET Core applications? Most probably, you heard something about performance. And there is some truth to it, but not in the way you might think.

So let's discuss this with smaller examples.

Async Await Pitfalls / Guidelines - .NET User Group Zurich Presentation

On 6th of July I had the honor to present some topics about async/await. Mainly:

  • What is asynchronous programming
  • Deadlocks and ConfigureAwait
  • How does the state machine work
  • Pitfalls and general Guidelines
  • ValueTask

You'll find all the slides and the whole talk in the blog.

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