Search code examples
c#flurl

Multiple factory methods chained on FlurlClient


I'm trying to make some request via the FlurlClient to some websites. But I need to chain some methods that I wrote in a factory.

I use this approach to create the Proxy and AllowAutoRedirect factory.

AllowAutoRedirect Extension:

public static IFlurlClient AllowAutoRedirect(this IFlurlClient fc, bool allowAutoRedirect)
{
    fc.Settings.HttpClientFactory = new CustomFlurlHttpClientFactory(allowAutoRedirect);
     return fc;
}

Proxy Extension :

public static IFlurlClient Proxy(this IFlurlClient fc, DML.Proxy proxy)
{
      fc.Settings.HttpClientFactory = new CustomFlurlHttpClientFactory(proxy);
      return fc;
}

And finally this are my factory methods

private Proxy _proxy;
private bool? _allowAutoRedirect;

public CustomFlurlHttpClientFactory(Proxy proxy)
{
     _proxy = proxy;
}


public CustomFlurlHttpClientFactory (bool? allowAutoRedirect)
{
    _allowAutoRedirect = allowAutoRedirect;
}


public override HttpClient CreateHttpClient(HttpMessageHandler handler)
{
    return base.CreateHttpClient(handler);
}


public override HttpMessageHandler CreateMessageHandler()
{
    if(_proxy != null)
        return ProxyClientHandlerConfiguration();


    if (_allowAutoRedirect != null)
        return AutoRedirectClientHandlerConfiguration();


    return base.CreateMessageHandler();
}

private HttpClientHandler AutoRedirectClientHandlerConfiguration() => new HttpClientHandler { AllowAutoRedirect = _allowAutoRedirect ?? true };


private HttpClientHandler ProxyClientHandlerConfiguration() => 
    new HttpClientHandler {
        Proxy = new WebProxy {
            Address = _proxy.GetFullUri(),
            BypassProxyOnLocal = true,
            UseDefaultCredentials = _proxy.UseDefaultCredentials()
        },
        UseProxy = true
    };

But when the client is created, only the second method executes correctly (Proxy).

I understand that when I call AllowAutoRedirect, it returns a new HttpClientHandler, and when Proxy gets called, it overrides the HttClientHandler returned by AllowAutoRedirect

var cli = new FlurlClient(url)
             .WithHeaders(headers)
             .WithCookies(cookies)
             .AllowAutoRedirect(false) /*Custom Factory Method*/
             .Proxy(proxy) /*Custom Factory Method*/
             .EnableCookies(); 

So, how can I get only one HttpClientHandler using both methods, AllowAutoRedirect and Proxy ?


Solution

  • The problem here is that your extension methods are overwriting the existing factory with a new one every time, so the last one will always "win". You need to edit the custom factory (if it exists), not replace it. Start making the factory editable. Something like this:

    public class CustomHttpClientFactory : DefaultHttpClientFactory
    {
        public bool? AutoRedirect { get; set; }
        public Proxy Proxy { get; set; }
    
        public override HttpMessageHandler CreateMessageHandler()
        {
            var handler = new HttpClientHandler();
    
            if (AutoRedirect != null)
                handler.AllowAutoRedirect = AutoRedirect;
    
            if (Proxy != null) {
                handler.Proxy = new WebProxy { ... };
                handler.UseProxy = true;
            }
    
            return handler;
        }
    }
    

    Then your extension methods should look something like this:

    public static IFlurlClient AllowAutoRedirect(this IFlurlClient fc, bool allowAutoRedirect)
    {
        var fac = fc.Settings.HttpClientFactory as CustomHttpClientFactory ??
            new CustomHttpClientFactory();
        fac.AutoRedirect = allowAutoRedirect;
        fc.Settings.HttpClientFactory = fac;
        return fc;
    }
    
    public static IFlurlClient Proxy(this IFlurlClient fc, Proxy proxy)
    {
        var fac = fc.Settings.HttpClientFactory as CustomHttpClientFactory ??
            new CustomHttpClientFactory();
        fac.Proxy = proxy;
        fc.Settings.HttpClientFactory = fac;
        return fc;
    }
    

    Note that you could end up with race conditions if you hit those extension methods for the same FlurlClient from different threads. But since they're about configuring a client and not a request, hopefully you're just doing that once (per client) and it's not an issue.