1

In the code snippet below I am trying to implement a DelegatingHandler. You will notice that in the SendAsync method I am deviating from the conventional practice of passing the request to the InnerHandler...

base.SendAsync(request, cancellationToken);

...and instead am creating a new instance of HttpClient, to which I am passing the request:

new HttpClient().SendAsync(request, cancellationToken);

This may seem strange in a bare bone snippet like this one, but the idea here is to pass the request to different HttpClientHandlers depending on certain conditions, so I will need to be able to instantiate the HttpClient dynamically.

However, when I run this code I receive the following exception:

The request message was already sent. Cannot send the same request message multiple times.

Can someone please help me understand what causes it and what would be a better way to implement my requirement (short of cloning the request in the handler rather than passing it on, which works, but doesn't appear elegant)?

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace DelegatingHandlerTest
{
    internal static class Program
    {
        private static void Main()
        {
            var httpClient = new HttpClient(new MyTestHandler());

            var response = httpClient.GetAsync("http://icanhazip.com/").Result;

            Console.WriteLine(response.Content.ReadAsStringAsync().Result);
        }

    private class MyTestHandler : DelegatingHandler
    {

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return await new HttpClient().SendAsync(request, cancellationToken);
        }

    }
}
July.Tech
  • 1,336
  • 16
  • 20
  • Looking at the source code of HttpClient, I now see where the exception comes from: https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/System/Net/Http/HttpClient.cs#L599 Looks like my code causes the request to pass through two instances of HttpClient: as soon as the first one sees it, it marks it as sent, and by the time it gets to the second one it's already damaged goods. Still need help finding a workaround though -- simply cloning the request seems like a dumb way to deal with the issue. – July.Tech Apr 24 '17 at 20:22

2 Answers2

2

The DelagatingHandler needs an inner handler to function, so it seems like you just need to make sure you pass a request handler instance in there.

private static void Main()
{
      var httpClient = new HttpClient(new MyTestHandler(new HttpClientHandler()));

      var response = httpClient.GetAsync("http://icanhazip.com/").Result;

      Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}


private class MyTestHandler : DelegatingHandler
{

      public MyTestHandler( HttpClientHandler handler) {
            base.InnerHandler = handler;
      }

      protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
      {  
            return await base.SendAsync(request, cancellationToken);
      }

}
andym1978
  • 61
  • 4
  • Thank you @andym1978, but the response you posted is an example of calling the SendAsync method of the base class (which is why you need to define an inner handler in the constructor). As I mentioned in the question, I need the flexibility of creating a new instance of HttpClient in my SendAsync override, and in that case the inner handler would never be invoked. Thanks again for trying to help. – July.Tech Apr 24 '17 at 20:47
0

I may misunderstand your requirements, but if you only need to decide between various handlers, can you not assign the appropriate HttpClientHandler to the InnerHandler before calling base.SendAsync()? Like this:

private class MyTestHandler : DelegatingHandler
{

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

        if (someCondition)
        {
            base.InnerHandler = new MyHttpClientHandler(...);
        }

        else
        {
            base.InnerHandler = new MyOtherHttpClientHandler(...);
        }

        return await base.SendAsync(request, cancellationToken);

    }

}
Matt
  • 78
  • 7