Search code examples
c#httpclientdelegatinghandler

Using DelegatingHandler to implement HTTP request retry logic


I'm trying to implement retry using DelegatingHandler but I also want to set HttpCompletionOption.

My code looks like following :

public class RetryHandler : DelegatingHandler
{
    private readonly int MaxRetries;

    public RetryHandler(HttpMessageHandler innerHandler, int retryCount = 3)
        : base(innerHandler)
    {
        MaxRetries = retryCount;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        HttpResponseMessage response = null;
        for (var i = 0; i < MaxRetries; i++)
        {
            response = await base.SendAsync(request, cancellationToken);
            if (response.IsSuccessStatusCode)
            {
                return response;
            }
        }

        return response;
    }
}

    static void Main(string[] args)
    {
        using (var client = new HttpClient(new RetryHandler(new HttpClientHandler())))
        {
            var request = new HttpRequestMessage(HttpMethod.Post, "https://ptsv2.com/t/smz9v-1564979884/post");
            var myResult = client.SendAsync(request).Result;
            Console.WriteLine(myResult.ToString());
        }

        Console.ReadLine();
    }

As you can see there is no option to set HttpCompletionOption by inheriting DelegatingHandler, I tried creating CustomDelegatinghandler but in that case I'm unable to use new RetryHandler(new HttpClientHandler()) as I have used in the main method.

Is there any way to implement DelegatingHandler(or custom) that has/ supports following signature?

protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, HttpCompletionOption option,
        CancellationToken cancellationToken)

Any help would be appreciated. Thanks,


Solution

  • You don't need to include HttpCompletionOption in the message handler signature, because handling HttpCompletionOption is responsibility of HttpClient, not of HttpMessageHandler.

    An HttpMessageHandler must initiate a task of sending a request and return a Task<HttpResponseMessage> as early as possible. In general, it should not perform any buffering of the response content (well, unless it's a middleware created for the purpose of buffering), and that's why it doesn't receive HttpCompletionOption. It should always act as if it was passed HttpCompletionOption.ResponseHeadersRead.

    The intended way to specify HttpCompletionOption is include it in a call to an overload of HttpClient method such as GetAsync or SendAsync:

    client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
    

    Roughly speaking, HttpClient handles HttpCompletionOption as follows:

    1. Invoke message handler and get a Task<HttpResponseMessage> (see code)
    2. If HttpCompletionOption.ResponseHeadersRead is specified, then done: return the task to the caller (see code)
    3. If HttpCompletionOption.ResponseContentRead is specified, then instead, return the task of HttpContent.LoadIntoBufferAsync, as a continuation of the original Task<HttpResponseMessage> (see code)