Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom HttpClientHandler using FlurlClient doesn't use ClientCertificate

I need to add a client certificate to my web requests and tried to achieve it in this way: Stackoverflow

At the end of this answer the "FlurlClient way" is presented. Using and configuring a FlurlClient instead of a global FlurlHttp configuration. I've tried this, but it didn't work.

I've created a new .NET Core Console application to show you the problem:

static void Main(string[] args)
{
   /****** NOT WORKING *******/
   try
   {
      IFlurlClient fc1 = new FlurlClient(url)
         .ConfigureClient(c => c.HttpClientFactory = new X509HttpFactory(GetCert()));

      fc1.WithHeader("User-Agent", userAgent)
         .WithHeader("Accept-Language", locale);

      dynamic ret1 = fc1.Url.AppendPathSegments(pathSegments).GetJsonAsync()
         .GetAwaiter().GetResult();
   }
   catch
   {
      // --> Exception: 403 FORBIDDEN
   }


   /****** NOT WORKING *******/
   try
   {
      IFlurlClient fc2 = new FlurlClient(url);

      fc2.Settings.HttpClientFactory = new X509HttpFactory(GetCert());

      fc2.WithHeader("User-Agent", userAgent)
         .WithHeader("Accept-Language", locale);

      dynamic ret2 = fc2.Url.AppendPathSegments(pathSegments).GetJsonAsync()
         .GetAwaiter().GetResult();
   }
   catch
   {
      // --> Exception: 403 FORBIDDEN
   }


   /****** WORKING *******/
   FlurlHttp.Configure(c =>
   {
      c.HttpClientFactory = new X509HttpFactory(GetCert());
   });

   dynamic ret = url.AppendPathSegments(pathSegments).GetJsonAsync()
      .GetAwaiter().GetResult();
   // --> OK
}

The X509HttpFactory is copied from the linked StackOverflow answer (but using HttpClientHandler instead of WebRequestHandler):

public class X509HttpFactory : DefaultHttpClientFactory
{
   private readonly X509Certificate2 _cert;

   public X509HttpFactory(X509Certificate2 cert)
   {
      _cert = cert;
   }

   public override HttpMessageHandler CreateMessageHandler()
   {
      var handler = new HttpClientHandler();
      handler.ClientCertificates.Add(_cert);
      return handler;
   }
}

So using the global FlurlHttp configuration is working and configuring the FlurlClient is not working. Why?

like image 969
d03090 Avatar asked Jul 12 '17 14:07

d03090


1 Answers

This all comes down to the order you're calling things:

  • fc.Url returns a Url object, which is little more than a string-building thing. It doesn't hold a reference back to the FlurlClient. (This allows Flurl to exist as a URL building library independent of Flurl.Http.)
  • Url.AppendPathSegments returns "this" Url.
  • Url.GetJsonAsync is an extension method that first creates a FlurlClient, then uses it with the current Url to make the HTTP call.

So as you can see, you've lost your reference to fc in step 1 of that flow. 2 possible solutions:

1. Build the URL first, then fluently add in the HTTP bits:

url
    .AppendPathSegments(...)
    .ConfigureClient(...)
    .WithHeaders(...)
    .GetJsonAsync();

2. OR, if you want to reuse the FlurlClient, "attach" it to the URL using WithClient:

var fc = new FlurlClient()
    .ConfigureClient(...)
    .WithHeaders(...);

url
    .AppendPathSegments(...)
    .WithClient(fc)
    .GetJsonAsync();
like image 184
Todd Menier Avatar answered Nov 14 '22 15:11

Todd Menier