代理凭据在第一次请求后被缓存。 IWebProxy 的实现

问题描述 投票:0回答:1

我已经实现了 IWebProxy

public class DynamicProxy : IWebProxy
{

    public Uri? Address { get; private set; }
    public ProxyData? Data
    {
        get => _data;
        set => SetData(value);
    }
    private ProxyData? _data;
    public HashSet<string> BypassHosts { get; init; } = new();

    /// <summary>
    /// Property is readonly
    /// </summary>
    public ICredentials? Credentials
    {
        get => _credentials;
        set => throw new MemberAccessException("Credentials property of DynamicProxy is readonly");
    }

    private ICredentials? _credentials;

    public DynamicProxy(ProxyData? data)
    {
        SetData(data);
    }

    public void SetData(ProxyData? data)
    {
        _data = data;
        if (_data == null)
        {
            Address = null;
            return;
        };

        var address = _data.ToString();
        Address = new Uri(address);
        if (_data.AuthEnabled)
        {
            _credentials = new NetworkCredential(_data.Username, _data.Password);
        }
    }

    public Uri? GetProxy(Uri destination)
    {
        return IsBypassed(destination) ? destination : Address;
    }

    public bool IsBypassed(Uri host)
    {
        return BypassHosts.Contains(host.Host);
    }

    public void AddToBypass(Uri host)
    {
        BypassHosts.Add(host.Host);
    }
}

此类的要点是根据需要使用

SetProxy(ProxyData? data)
方法动态更改代理地址和凭据,因为在第一次请求后不再可能更改
HttpClientHandler
属性。但是,在使用新凭证更改代理后,我发现了
HttpRequestException
the proxy tunnel request to proxy '...' failed with status code '407'

经过实际测试,我意识到只有更改凭据时才会发生这种情况。事实证明这些数据以某种方式被缓存了。
我想澄清一下,这个类的全部目的是避免重新创建
HttpClient
HttpClientHandler
。如果我可以强制 HttpClient(或处理程序)不缓存/刷新缓存,那就太理想了。

class  Example
{
    DynamicProxy Proxy;
    HttpClient Client;
    public Example()
    {
        Proxy = new DynamicProxy(null);
        var handler = new HttpClientHandler();
        handler.Proxy = Proxy;
        Client = new HttpClient(handler);
    }

    //ProxyData here have different Addresses and Credentials
    public async Task Test(ProxyData first, ProxyData second)
    {
        Proxy.SetData(first);
        var req1 = await Client.GetStringAsync("https://www.gstatic.com/generate_204"); // Ok
        Proxy.SetData(second);
        var req2 = await Client.GetStringAsync("https://www.gstatic.com/generate_204"); //Exception with code 407

    }
}
c# proxy httpclient
1个回答
0
投票

HttpConnectionPoolManager
用于在
HttpClientHandler
中发送请求,具有名为
_proxyCredentials
的私有只读字段。因此,在第一次请求后,它会缓存代理的凭据。可能的解决方案如下:

internal class ProxyCredentials : ICredentials
{
    private string? Username { get; set; }
    private string? Password { get; set; }

    private NetworkCredential Credential => _credential ?? new NetworkCredential(Username, Password);
    private NetworkCredential? _credential;
    private bool _isSet;

    public ProxyCredentials(){}

    public ProxyCredentials(string? username, string? password)
    {
        if (username != null && password != null)
        {
            _isSet = true;
            Username = username;
            Password = password;
        }
    }

    public void Set(string username, string password)
    {
        Username = username;
        Password = password;
        if (username != Username || password != Password)
        {
            _credential = null;
        }
        _isSet = true;
    }

    public void Reset()
    {
        Username = null;
        Password = null;
        _isSet = false;
    }

    public NetworkCredential? GetCredential(Uri uri, string authType)
    {
        return _isSet ? Credential : null;
    }
}

然后放入

DynamicProxy

public class DynamicProxy : IWebProxy
{

    public Uri? Address { get; private set; }
    public ProxyData? Data
    {
        get => _data;
        set => SetData(value);
    }
    private ProxyData? _data;
    public HashSet<string> BypassHosts { get; init; } = new();

    /// <summary>
    /// Property is readonly
    /// </summary>
    public ICredentials? Credentials
    {
        get => _credentials;
        set => throw new MemberAccessException("Credentials property of DynamicProxy is readonly");
    }

    private ProxyCredentials _credentials = new();

//Other code
}

现在

DynamicProxy
将引用凭据包装器而不是直接
NetworkCredentials
实例,并且该包装器类将被缓存。

© www.soinside.com 2019 - 2024. All rights reserved.