-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Background and motivation
HttpClient and its middleware friends are based around passing the request message down through a nested HttpMessageHandler.SendAsync pipeline and returning the response message back.
The response being returned from SendAsync has historically been used to indicate that a request is complete. However, it only indicates the response header headers have been returned (apart from HttpClient and its option to read the response to completion before returning). This means that code that cares about the request being finished, such as counters, activities, and diagnostic source events, are reporting a HTTP request is finished even though the response body is still being read.
This becomes obvious in longer-running HTTP requests, such as gRPC streaming, where the response headers are returned almost immediately. Still, the response body periodically returns messages written by the server.
There should be an event that libraries and apps can subscribe to that indicates when the request is complete.
Potential users:
- Metrics built into HttpClient/HttpMessageInvoker can update counters based on when an HTTP request really ends. See [API Proposal]: HttpClient/HttpMessageInvoker metrics #84978
- Fix Activity lifetime. See HttpClient and Activity lifetime #37141
- Load balancing behavior based on the number of active requests has accurate information. See Support streaming calls in the
SubchannelCallTrackergrpc/grpc-dotnet#2133
API Proposal
An event on HttpResponseMessage that reports when the overall request is considered complete.
namespace System.Net.Http;
public class HttpResponseMessage
{
// Option 1: Cancellation token
public CancellationToken Completed { get; }
// Option 2: Register callback
public void OnCompleted(Action<HttpResponseMessage> callback);
}API options:
CancellationTokenmight not be the right thing. I can't think of a scenario where someone would use it with an async method call that takes a token. I suggest it because it is similar to ASP.NET Core's HttpContext.RequestAborted token.HttpResponseMessage.OnCompleted(Action callback)may be preferable. It's like ASP.NET Core's HttpResponse.OnCompleted.
When these APIs are used, the registered callback is invoked if the response is complete.
The interesting discussion point here is: when is the overall request considered complete?
I think a request should be considered complete when both of these events are true (or the HttpResponseMessage is explicitly disposed):
- The HttpResponseMessage.Content has been read to the end (or no content was returned at all),
- And the HTTP request middleware pipeline is finished. That means the response has been returned from
HttpClient.SendAsyncorHttpMessageInvoker.SendAsync.
Edit: Potential problem with the idea above. The HttpResponseMessage returned for a request doesn't have to exit pipeline. A handler could decide to create a new one or retry the HTTP request which returns a new response. I added explicitly calling HttpResponseMessage.Dispose as another way to indicate the response is complete, but that doesn't seem like a guaranteed fix because someone might not dispose the original HttpResponseMessage.
API Usage
public void MyDelegatingHandler : DelegatingHandler
{
public long CompletedRequests { get; private set; }
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
var response = await SendAsync(request);
response.Completed.Register(() => CompletedRequests++);
return response;
}
}Alternative Designs
No response
Risks
No response