Skip to content

Commit 8ef0951

Browse files
committed
🎨 WebProxy
1 parent 5c3cdbb commit 8ef0951

File tree

2 files changed

+223
-16
lines changed

2 files changed

+223
-16
lines changed

src/BD.Common8.Bcl/Net/DynamicHttpWindowsProxy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// https://www.cnblogs.com/lindexi/p/16706558.html
22
// https://blog.lindexi.com/post/dotnet-6-%E4%B8%BA%E4%BB%80%E4%B9%88%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82%E4%B8%8D%E8%B7%9F%E9%9A%8F%E7%B3%BB%E7%BB%9F%E7%BD%91%E7%BB%9C%E4%BB%A3%E7%90%86%E5%8F%98%E5%8C%96%E8%80%8C%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2%E4%BB%A3%E7%90%86.html
33

4-
#if WINDOWS
4+
#if WINDOWS && !NET9_0_OR_GREATER
55
using static System.String;
66
using ErrorEventArgs = System.IO.ErrorEventArgs;
77

src/BD.Common8.Bcl/Net/IWebProxySettings.cs

Lines changed: 222 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ namespace System.Net;
66
public interface IWebProxySettings
77
{
88
/// <summary>
9-
/// Web 代理地址
9+
/// Web 代理地址,与 <see cref="WebProxyHost"/> 和 <see cref="WebProxyPort"/> 互斥,优先级最高
1010
/// </summary>
1111
string? WebProxyAddress { get; }
1212

1313
/// <summary>
14-
/// Web 代理端口
14+
/// Web 代理 Host,当 <see cref="WebProxyAddress"/> 无效时且 <see cref="WebProxyPort"/> 有效时使用"/>
15+
/// </summary>
16+
string? WebProxyHost { get; }
17+
18+
/// <summary>
19+
/// Web 代理端口,当 <see cref="WebProxyAddress"/> 无效时且 <see cref="WebProxyHost"/> 有效时使用
1520
/// </summary>
1621
int? WebProxyPort { get; }
1722

@@ -24,6 +29,16 @@ public interface IWebProxySettings
2429
/// Web 代理的身份验证的密码
2530
/// </summary>
2631
string? WebProxyPassword { get; }
32+
33+
/// <summary>
34+
/// Web 代理的身份验证的域名
35+
/// </summary>
36+
string? WebProxyNetworkCredentialDomain { get; }
37+
38+
/// <summary>
39+
/// 是否跳过代理服务器而使用本地地址
40+
/// </summary>
41+
bool? WebProxyBypassProxyOnLocal { get; }
2742
}
2843

2944
/// <inheritdoc cref="IWebProxySettings"/>
@@ -71,6 +86,142 @@ public partial record WebProxySettings : IWebProxySettings
7186
[MP2Key(3)]
7287
#endif
7388
public string? WebProxyPassword { get; set; }
89+
90+
/// <inheritdoc/>
91+
#if !(NETFRAMEWORK && !NET462_OR_GREATER) && !(NETSTANDARD && !NETSTANDARD2_0_OR_GREATER)
92+
[MPKey(4)]
93+
#endif
94+
#if !NETFRAMEWORK && !(NETSTANDARD && !NETSTANDARD2_1_OR_GREATER)
95+
[MP2Key(4)]
96+
#endif
97+
public string? WebProxyHost { get; set; }
98+
99+
/// <inheritdoc/>
100+
#if !(NETFRAMEWORK && !NET462_OR_GREATER) && !(NETSTANDARD && !NETSTANDARD2_0_OR_GREATER)
101+
[MPKey(5)]
102+
#endif
103+
#if !NETFRAMEWORK && !(NETSTANDARD && !NETSTANDARD2_1_OR_GREATER)
104+
[MP2Key(5)]
105+
#endif
106+
public string? WebProxyNetworkCredentialDomain { get; }
107+
108+
/// <inheritdoc/>
109+
#if !(NETFRAMEWORK && !NET462_OR_GREATER) && !(NETSTANDARD && !NETSTANDARD2_0_OR_GREATER)
110+
[MPKey(6)]
111+
#endif
112+
#if !NETFRAMEWORK && !(NETSTANDARD && !NETSTANDARD2_1_OR_GREATER)
113+
[MP2Key(6)]
114+
#endif
115+
public bool? WebProxyBypassProxyOnLocal { get; }
116+
}
117+
118+
/// <summary>
119+
/// 自定义代理构造的包装类,用于比较代理是否相等
120+
/// </summary>
121+
file sealed class WebProxyIdWrap : IWebProxy
122+
{
123+
readonly WebProxy innerProxy;
124+
readonly string id;
125+
126+
/// <summary>
127+
/// 自定义代理参数的唯一标识,用作比较值是否相等
128+
/// </summary>
129+
internal string Id => id;
130+
131+
static void SetCredentials(WebProxy webProxy, string? userName, string? password, string? domain)
132+
{
133+
NetworkCredential? credential = null;
134+
if (!string.IsNullOrEmpty(userName))
135+
{
136+
if (string.IsNullOrWhiteSpace(domain))
137+
{
138+
credential = new(userName, password);
139+
}
140+
else
141+
{
142+
credential = new(userName, password, domain);
143+
}
144+
}
145+
if (credential != null)
146+
{
147+
webProxy.Credentials = credential;
148+
}
149+
}
150+
151+
static string GetId(WebProxy webProxy, string? userName, string? password, string? domain)
152+
{
153+
var id =
154+
$"""
155+
Address: {webProxy.Address}
156+
BypassProxyOnLocal: {webProxy.BypassProxyOnLocal}
157+
UserName: {ToSecure(userName)}
158+
Password: {ToSecure(password)}
159+
Domain: {domain}
160+
WebProxyIdWrap
161+
""";
162+
return id;
163+
164+
static string? ToSecure(string? s)
165+
{
166+
var r = Hashs.String.SHA384((s ?? "") + "WebProxyIdWrap.ToSecure");
167+
#if DEBUG
168+
return
169+
$"""
170+
{s}
171+
{r}
172+
""";
173+
#else
174+
return r;
175+
#endif
176+
}
177+
}
178+
179+
internal WebProxyIdWrap(string? userName, string? password, string? domain, string host, int port)
180+
{
181+
innerProxy = new(host, port);
182+
SetCredentials(innerProxy, userName, password, domain);
183+
id = GetId(innerProxy, userName, password, domain);
184+
}
185+
186+
internal WebProxyIdWrap(string? userName, string? password, string? domain, string host, int port, bool bypassOnLocal)
187+
{
188+
innerProxy = new(host, port)
189+
{
190+
BypassProxyOnLocal = bypassOnLocal,
191+
};
192+
SetCredentials(innerProxy, userName, password, domain);
193+
id = GetId(innerProxy, userName, password, domain);
194+
}
195+
196+
internal WebProxyIdWrap(string? userName, string? password, string? domain, string? address)
197+
{
198+
innerProxy = new(address);
199+
SetCredentials(innerProxy, userName, password, domain);
200+
id = GetId(innerProxy, userName, password, domain);
201+
}
202+
203+
internal WebProxyIdWrap(string? userName, string? password, string? domain, string? address, bool bypassOnLocal)
204+
{
205+
innerProxy = new(address, bypassOnLocal);
206+
SetCredentials(innerProxy, userName, password, domain);
207+
id = GetId(innerProxy, userName, password, domain);
208+
}
209+
210+
/// <inheritdoc/>
211+
ICredentials? IWebProxy.Credentials
212+
{
213+
get => innerProxy.Credentials;
214+
set => innerProxy.Credentials = value;
215+
}
216+
217+
/// <inheritdoc/>
218+
Uri? IWebProxy.GetProxy(Uri destination) => innerProxy.GetProxy(destination);
219+
220+
/// <inheritdoc/>
221+
bool IWebProxy.IsBypassed(Uri host) => innerProxy.IsBypassed(host);
222+
223+
/// <inheritdoc/>
224+
public override string? ToString() => id;
74225
}
75226

76227
/// <summary>
@@ -79,27 +230,83 @@ public partial record WebProxySettings : IWebProxySettings
79230
public static partial class WebProxySettingsExtensions
80231
{
81232
/// <summary>
82-
/// 根据配置参数创建 <see cref="IWebProxy"/> 实例
233+
/// 比较两个 <see cref="IWebProxy"/> 实例是否相等
234+
/// </summary>
235+
/// <param name="l"></param>
236+
/// <param name="r"></param>
237+
/// <returns></returns>
238+
public static bool IdEquals(
239+
this IWebProxy? l,
240+
IWebProxy? r)
241+
{
242+
if (l == null && r == null)
243+
{
244+
return true;
245+
}
246+
else if (l == null || r == null)
247+
{
248+
return false;
249+
}
250+
else if (l is WebProxyIdWrap l2 && r is WebProxyIdWrap r2)
251+
{
252+
return l2.Id == r2.Id;
253+
}
254+
else
255+
{
256+
return l.Equals(r);
257+
}
258+
}
259+
260+
/// <summary>
261+
/// 根据配置参数创建 <see cref="IWebProxy"/> 实例,返回类型可通过 <see cref="IdEquals"/> 进行比较,当 <see cref="IWebProxySettings"/> 代理设置相同时,返回的不同实例比较将都相等
83262
/// </summary>
84263
/// <param name="settings"></param>
85-
/// <param name="bypassOnLocal"></param>
86-
/// <param name="bypassList"></param>
87264
/// <returns></returns>
88-
public static IWebProxy? GetWebProxy(this IWebProxySettings? settings,
89-
bool bypassOnLocal = true,
90-
[StringSyntax("Regex", new[] { RegexOptions.IgnoreCase | RegexOptions.CultureInvariant })] string[]? bypassList = null)
265+
public static IWebProxy? GetWebProxy(
266+
this IWebProxySettings? settings)
91267
{
268+
WebProxyIdWrap? webProxy = null;
92269
if (settings != null)
93270
{
94-
if (!string.IsNullOrWhiteSpace(settings?.WebProxyAddress))
271+
if (!string.IsNullOrWhiteSpace(settings.WebProxyAddress))
272+
{
273+
if (settings.WebProxyBypassProxyOnLocal.HasValue)
274+
{
275+
webProxy = new(settings.WebProxyUserName,
276+
settings.WebProxyPassword,
277+
settings.WebProxyNetworkCredentialDomain,
278+
settings.WebProxyAddress,
279+
settings.WebProxyBypassProxyOnLocal.Value);
280+
}
281+
else
282+
{
283+
webProxy = new(settings.WebProxyUserName,
284+
settings.WebProxyPassword,
285+
settings.WebProxyNetworkCredentialDomain,
286+
settings.WebProxyAddress);
287+
}
288+
}
289+
else if (!string.IsNullOrWhiteSpace(settings.WebProxyHost) && settings.WebProxyPort.HasValue)
95290
{
96-
ICredentials? credentials = null;
97-
if (!string.IsNullOrEmpty(settings.WebProxyUserName) && !string.IsNullOrEmpty(settings.WebProxyPassword))
98-
credentials = new NetworkCredential(settings.WebProxyUserName, settings.WebProxyPassword);
99-
var url = $"{settings.WebProxyAddress}:{settings.WebProxyPort}";
100-
return new WebProxy(url, bypassOnLocal, bypassList, credentials);
291+
if (settings.WebProxyBypassProxyOnLocal.HasValue)
292+
{
293+
webProxy = new(settings.WebProxyUserName,
294+
settings.WebProxyPassword,
295+
settings.WebProxyNetworkCredentialDomain,
296+
settings.WebProxyHost,
297+
settings.WebProxyPort.Value,
298+
settings.WebProxyBypassProxyOnLocal.Value);
299+
}
300+
else
301+
{
302+
webProxy = new(settings.WebProxyUserName,
303+
settings.WebProxyPassword,
304+
settings.WebProxyNetworkCredentialDomain,
305+
settings.WebProxyHost,
306+
settings.WebProxyPort.Value);
307+
}
101308
}
102309
}
103-
return default;
310+
return webProxy;
104311
}
105312
}

0 commit comments

Comments
 (0)