ProxySharp
/
ProxyManager.cs
174 строки · 5.1 Кб
1using ProxySharp.Providers.Selectors;
2using System;
3using System.Net.Http;
4using System.Threading.Tasks;
5
6namespace ProxySharp
7{
8/// <summary>
9/// Provides methods to run requests through proxy by the current selector.
10/// </summary>
11public sealed class ProxyManager : IProxyManager<HttpClient, HttpResponseMessage>
12{
13private readonly IProxySelector _selector;
14private Func<HttpResponseMessage, Task<bool>> _validateAsync;
15private Action<HttpClient> _configure;
16private HttpClient _httpClient;
17private HttpClientHandler _httpClientHandler;
18
19/// <summary>
20/// Create new instance.
21/// </summary>
22/// <param name="selector">Proxy selector.</param>
23public ProxyManager(IProxySelector selector)
24{
25_selector = selector;
26
27_selector.CurrentChanged += SetWebProxy;
28
29_selector.SetCurrent();
30}
31
32/// <summary>
33/// Set next proxy by current selector.
34/// </summary>
35/// <returns>This instance.</returns>
36public IProxyManager<HttpClient, HttpResponseMessage> ChangeProxy()
37{
38_selector.SetCurrentToNext();
39
40return this;
41}
42
43/// <summary>
44/// Set validator to check query response.
45/// </summary>
46/// <param name="func">Validator.</param>
47/// <returns>This instance.</returns>
48public IProxyManager<HttpClient, HttpResponseMessage> UseValidator(Func<HttpResponseMessage, Task<bool>> func)
49{
50_validateAsync = func;
51
52return this;
53}
54
55/// <summary>
56/// Configure HttpClient used by the query.
57/// </summary>
58/// <param name="func">Setup function.</param>
59/// <returns>This instance.</returns>
60public IProxyManager<HttpClient, HttpResponseMessage> Configure(Action<HttpClient> func)
61{
62_configure = func;
63
64CreateAndConfigureClient();
65
66return this;
67}
68
69/// <summary>
70/// Run request to get the result.
71/// </summary>
72/// <remarks>
73/// If the request fails or the validator return <c>False</c>, will try to run the request through the next proxy.
74/// </remarks>
75/// <param name="func">Request.</param>
76/// <returns>Response.</returns>
77/// <exception cref="InvalidOperationException">Has no valid proxies.</exception>
78/// <exception cref="ArgumentNullException">Has no HTTP client.</exception>
79public async Task<HttpResponseMessage> RequestAsync(Func<HttpClient, Task<HttpResponseMessage>> func)
80{
81while (true)
82{
83++_selector.Current.UsedCount;
84
85HttpResponseMessage response;
86
87try
88{
89if (_httpClient == null)
90throw new ArgumentNullException(nameof(_httpClient));
91
92response = await func(_httpClient);
93}
94catch (Exception error)
95{
96error.LogError();
97
98++_selector.Current.FailsCount;
99_selector.Current.LastException = error;
100
101_selector.SetCurrentToNext();
102
103continue;
104}
105
106if (_validateAsync == null)
107return response;
108
109try
110{
111if (!await _validateAsync(response))
112{
113++_selector.Current.FailsCount;
114
115_selector.SetCurrentToNext();
116
117continue;
118}
119}
120catch (Exception error)
121{
122error.LogError();
123
124throw;
125}
126
127return response;
128}
129}
130
131/// <summary>
132/// Set current proxy.
133/// </summary>
134/// <param name="sender">Sender.</param>
135/// <param name="e">New proxy.</param>
136private void SetWebProxy(object sender, ProxyInfo e)
137{
138_httpClientHandler?.Dispose();
139
140_httpClientHandler = new HttpClientHandler
141{
142Proxy = e.ToWebProxy()
143};
144
145CreateAndConfigureClient();
146}
147
148/// <summary>
149/// Creates and configures an HTTP client.
150/// </summary>
151/// <exception cref="ArgumentNullException">Fires when the proxy is not set.</exception>
152private void CreateAndConfigureClient()
153{
154if (_httpClientHandler == null)
155throw new ArgumentNullException(nameof(_httpClientHandler));
156
157_httpClient?.Dispose();
158
159_httpClient = new HttpClient
160(
161handler: _httpClientHandler,
162disposeHandler: false
163);
164
165_configure?.Invoke(_httpClient);
166}
167
168public void Dispose()
169{
170_httpClient?.Dispose();
171_httpClientHandler?.Dispose();
172}
173}
174}