@@ -6,12 +6,17 @@ namespace System.Net;
66public 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
79230public 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