Halve the stack depth for both the new single step tests
[mono.git] / mcs / class / referencesource / System / net / System / Net / webproxy.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="webproxy.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8     using System.Net.NetworkInformation;
9     using System.Globalization;
10     using System.Security.Permissions;
11     using System.Text;
12     using System.Text.RegularExpressions;
13     using System.Collections;
14     using System.Runtime.Serialization;
15     using System.Collections.Generic;
16     using System.Diagnostics.CodeAnalysis;
17
18     class WebProxyData {
19         internal bool bypassOnLocal;
20         internal bool automaticallyDetectSettings;
21         internal Uri proxyAddress;
22         internal Hashtable proxyHostAddresses;
23         internal Uri scriptLocation;
24 #if USE_WINIET_AUTODETECT_CACHE
25         internal Uri lkgScriptLocation;
26 #endif
27         internal ArrayList bypassList;
28     }
29
30     // Handles default proxy setting implementation for the Http proxy.
31     //
32     // The following order is used when determinig proxy settings:
33     // 1. web.config/app.config: if available, use settings specified in <system.net><defaultProxy>
34     // 2. If the config file doesn't contain proxy settings, read the IE proxy settings
35     //
36     // If the IE proxy settings contain invalid settings (e.g. "invalid;host" - note the semicolon), then
37     // a FormatException will be thrown.
38     [Serializable]
39     public class WebProxy : IAutoWebProxy, ISerializable {
40         // these are settable by the user
41         private bool _UseRegistry;    // This is just around for serialization. Can we get rid of it?
42         private bool _BypassOnLocal;
43         private bool m_EnableAutoproxy;
44         private Uri _ProxyAddress;
45         private ArrayList _BypassList;
46         private ICredentials _Credentials;
47
48         // these are computed on the fly
49         private Regex[] _RegExBypassList;
50         private Hashtable _ProxyHostAddresses;
51
52         /// <devdoc>
53         ///    <para>[To be supplied.]</para>
54         /// </devdoc>
55         public WebProxy()
56             : this((Uri) null, false, null, null) {
57         }
58
59         /// <devdoc>
60         ///    <para>[To be supplied.]</para>
61         /// </devdoc>
62         public WebProxy(Uri Address)
63             : this(Address, false, null, null) {
64         }
65
66         /// <devdoc>
67         ///    <para>[To be supplied.]</para>
68         /// </devdoc>
69         public WebProxy(Uri Address, bool BypassOnLocal)
70             : this(Address, BypassOnLocal, null, null) {
71         }
72
73         /// <devdoc>
74         ///    <para>[To be supplied.]</para>
75         /// </devdoc>
76         public WebProxy(Uri Address, bool BypassOnLocal, string[] BypassList)
77             : this(Address, BypassOnLocal, BypassList, null) {
78         }
79
80         /// <devdoc>
81         ///    <para>[To be supplied.]</para>
82         /// </devdoc>
83         public WebProxy(Uri Address, bool BypassOnLocal, string[] BypassList, ICredentials Credentials) {
84             _ProxyAddress = Address;
85             _BypassOnLocal = BypassOnLocal;
86             if (BypassList != null) {
87                 _BypassList = new ArrayList(BypassList);
88                 UpdateRegExList(true);
89             }
90             _Credentials = Credentials;
91             m_EnableAutoproxy = true;
92         }
93
94         /// <devdoc>
95         ///    <para>[To be supplied.]</para>
96         /// </devdoc>
97         public WebProxy(string Host, int Port)
98             : this(new Uri("http://" + Host + ":" + Port.ToString(CultureInfo.InvariantCulture)), false, null, null) {
99         }
100
101         /// <devdoc>
102         ///    <para>[To be supplied.]</para>
103         /// </devdoc>
104         public WebProxy(string Address)
105             : this(CreateProxyUri(Address), false, null, null) {
106         }
107
108         /// <devdoc>
109         ///    <para>[To be supplied.]</para>
110         /// </devdoc>
111         public WebProxy(string Address, bool BypassOnLocal)
112             : this(CreateProxyUri(Address), BypassOnLocal, null, null) {
113         }
114
115         /// <devdoc>
116         ///    <para>[To be supplied.]</para>
117         /// </devdoc>
118         public WebProxy(string Address, bool BypassOnLocal, string[] BypassList)
119             : this(CreateProxyUri(Address), BypassOnLocal, BypassList, null) {
120         }
121
122         /// <devdoc>
123         ///    <para>[To be supplied.]</para>
124         /// </devdoc>
125         public WebProxy(string Address, bool BypassOnLocal, string[] BypassList, ICredentials Credentials)
126             : this(CreateProxyUri(Address), BypassOnLocal, BypassList, Credentials) {
127         }
128
129         /// <devdoc>
130         ///    <para>[To be supplied.]</para>
131         /// </devdoc>
132         public Uri Address {
133             get {
134 #if !FEATURE_PAL
135                 CheckForChanges();
136 #endif // !FEATURE_PAL
137                return _ProxyAddress;
138             }
139             set {
140                 _UseRegistry = false;
141                 DeleteScriptEngine();
142                _ProxyHostAddresses = null;  // hash list of proxies
143                _ProxyAddress = value;
144             }
145         }
146
147         /// <devdoc>
148         ///    <para>[To be supplied.]</para>
149         /// </devdoc>
150         internal bool AutoDetect
151         {
152             set {
153                 GlobalLog.Assert(_UseRegistry == false, "Cannot set AutoDetect if we are using registry for proxy settings");
154                 GlobalLog.Assert(m_EnableAutoproxy, "WebProxy#{0}::.ctor()|Cannot set AutoDetect if usesystemdefault is set.", ValidationHelper.HashString(this));
155
156                 if (ScriptEngine == null)
157                 {
158                     ScriptEngine = new AutoWebProxyScriptEngine(this, false);
159                 }
160                 ScriptEngine.AutomaticallyDetectSettings = value;
161             }
162         }
163
164         /// <devdoc>
165         ///    <para>[To be supplied.]</para>
166         /// </devdoc>
167         internal Uri ScriptLocation {
168             set {
169                 GlobalLog.Assert(value != null, "Cannot set ScriptLocation to null");
170                 GlobalLog.Assert(_UseRegistry == false, "Cannot set AutoDetect if we are using registry for proxy settings");
171                 GlobalLog.Assert(m_EnableAutoproxy, "WebProxy#{0}::.ctor()|Cannot set ScriptLocation if usesystemdefault is set.", ValidationHelper.HashString(this));
172
173                 if (ScriptEngine == null)
174                 {
175                     ScriptEngine = new AutoWebProxyScriptEngine(this, false);
176                 }
177                 ScriptEngine.AutomaticConfigurationScript = value;
178             }
179         }
180
181         /// <devdoc>
182         ///    <para>[To be supplied.]</para>
183         /// </devdoc>
184         public bool BypassProxyOnLocal {
185             get {
186 #if !FEATURE_PAL
187                 CheckForChanges();
188 #endif // !FEATURE_PAL
189                 return _BypassOnLocal;
190             }
191             set {
192                 _UseRegistry = false;
193                 DeleteScriptEngine();
194                 _BypassOnLocal = value;
195             }
196         }
197
198         /// <devdoc>
199         ///    <para>[To be supplied.]</para>
200         /// </devdoc>
201         public string[] BypassList {
202             get {
203 #if !FEATURE_PAL
204                 CheckForChanges();
205 #endif // !FEATURE_PAL
206                 if (_BypassList == null) {
207                     _BypassList = new ArrayList();
208                 }
209                 return (string[])_BypassList.ToArray(typeof(string));
210             }
211             set {
212                 _UseRegistry = false;
213                 DeleteScriptEngine();
214                 _BypassList = new ArrayList(value);
215                 UpdateRegExList(true);
216             }
217         }
218
219         /// <devdoc>
220         ///    <para>[To be supplied.]</para>
221         /// </devdoc>
222         public ICredentials Credentials {
223             get {
224                 return _Credentials;
225             }
226             set {
227                 _Credentials = value;
228             }
229         }
230
231         /// <devdoc>
232         ///    <para>Sets Credentials to CredentialCache.DefaultCredentials</para>
233         /// </devdoc>
234         public bool UseDefaultCredentials  {
235             get {
236                 return (Credentials is SystemNetworkCredential) ? true : false;
237             }
238             set {
239                 _Credentials = value ? CredentialCache.DefaultCredentials : null;
240             }
241         }
242
243         /// <devdoc>
244         ///    <para>[To be supplied.]</para>
245         /// </devdoc>
246         public ArrayList BypassArrayList {
247             get {
248 #if !FEATURE_PAL
249                 CheckForChanges();
250 #endif // !FEATURE_PAL
251                 if ( _BypassList == null ) {
252                     _BypassList = new ArrayList();
253                 }
254                 return _BypassList;
255             }
256         }
257
258         internal void CheckForChanges() {
259             if (ScriptEngine != null)
260             {
261                 ScriptEngine.CheckForChanges();
262             }
263         }
264
265         /// <devdoc>
266         ///    <para>[To be supplied.]</para>
267         /// </devdoc>
268         public Uri GetProxy(Uri destination) {
269             GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::GetProxy() destination:" + ValidationHelper.ToString(destination));
270             if (destination == null)
271             {
272                 throw new ArgumentNullException("destination");
273             }
274
275             Uri result;
276             if (GetProxyAuto(destination, out result)) {
277                 return result;
278             }
279             if (IsBypassedManual(destination)) {
280                 return destination;
281             }
282             Hashtable proxyHostAddresses = _ProxyHostAddresses;
283             Uri proxy = proxyHostAddresses!=null ? proxyHostAddresses[destination.Scheme] as Uri : _ProxyAddress;
284             return proxy!=null? proxy : destination;
285         }
286
287         //
288         // CreateProxyUri - maps string to Uri
289         //
290
291         private static Uri CreateProxyUri(string address) {
292             if (address == null) {
293                 return null;
294             }
295             if (address.IndexOf("://") == -1) {
296                 address = "http://" + address;
297             }
298             return new Uri(address);
299         }
300
301         //
302         // UpdateRegExList - Update internal _RegExBypassList
303         //  warning - can throw if the RegEx doesn't parse??
304         //
305         private void UpdateRegExList(bool canThrow) {
306             Regex[] regExBypassList = null;
307             ArrayList bypassList = _BypassList;
308             try {
309                 if ( bypassList != null && bypassList.Count > 0 ) {
310                     regExBypassList = new Regex[bypassList.Count];
311                     for (int i = 0; i < bypassList.Count; i++ ) {
312                         regExBypassList[i] = new Regex((string)bypassList[i], RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
313                     }
314                 }
315             }
316             catch {
317                 if (!canThrow) {
318                     _RegExBypassList = null;
319                     return;
320                 }
321                 throw;
322             }
323             // only update here, cause it could throw earlier in the loop
324             _RegExBypassList = regExBypassList;
325         }
326
327         //
328         // IsMatchInBypassList - match input against _RegExBypassList
329         //
330         private bool IsMatchInBypassList(Uri input) {
331             UpdateRegExList(false);
332             if ( _RegExBypassList == null ) {
333                 return false;
334             }
335             string matchUriString = input.Scheme + "://" + input.Host + (!input.IsDefaultPort ? (":"+input.Port) : "" );
336             for (int i = 0; i < _BypassList.Count; i++ ) {
337                 if (_RegExBypassList[i].IsMatch(matchUriString)) {
338                     return true;
339                 }
340             }
341             return false;
342         }
343
344         /// <devdoc>
345         /// Determines if the host Uri should be routed locally or go through the proxy.
346         /// </devdoc>
347         private bool IsLocal(Uri host) {
348             string hostString = host.Host;
349
350             IPAddress hostAddress;
351             if (IPAddress.TryParse(hostString, out hostAddress))
352             {
353                 return (IPAddress.IsLoopback(hostAddress) || NclUtilities.IsAddressLocal(hostAddress));
354             }
355             
356             int dot = hostString.IndexOf('.');
357
358             // No dot?  Local.
359             if (dot == -1)
360             {
361                 return true;
362             }
363             
364             // If it matches the primary domain, it's local.  (Whether or not the hostname matches.)
365             string local = "." + IPGlobalProperties.InternalGetIPGlobalProperties().DomainName;
366             if (local !=  null && local.Length == (hostString.Length - dot) &&
367                 string.Compare(local, 0, hostString, dot, local.Length, StringComparison.OrdinalIgnoreCase ) == 0) {
368                 return true;
369             }
370             return false;
371         }
372
373         /// <devdoc>
374         /// Determines if the host Uri should be routed locally or go through a proxy.
375         /// </devdoc>
376         private bool IsLocalInProxyHash(Uri host) {
377             Hashtable proxyHostAddresses = _ProxyHostAddresses;
378             if (proxyHostAddresses != null) {
379                 Uri proxy = (Uri) proxyHostAddresses[host.Scheme];
380                 if (proxy == null) {
381                     return true; // no proxy entry for this scheme, then bypass
382                 }
383             }
384             return false;
385         }
386
387
388         /// <devdoc>
389         ///    <para>[To be supplied.]</para>
390         /// </devdoc>
391         public bool IsBypassed(Uri host) {
392             GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::IsBypassed() destination:" + ValidationHelper.ToString(host));
393             if (host == null)
394             {
395                 throw new ArgumentNullException("host");
396             }
397
398             bool result; 
399             if (IsBypassedAuto(host, out result)) {
400                 return result;
401             }
402             return IsBypassedManual(host);
403         }
404
405         private bool IsBypassedManual(Uri host) {
406             if (host.IsLoopback) {
407                 return true; // bypass localhost from using a proxy.
408             }
409             return (_ProxyAddress==null && _ProxyHostAddresses==null) || (_BypassOnLocal && IsLocal(host)) || IsMatchInBypassList(host) || IsLocalInProxyHash(host);
410         }
411
412         /// <devdoc>
413         ///    <para>[To be supplied.]</para>
414         /// </devdoc>
415         [Obsolete("This method has been deprecated. Please use the proxy selected for you by default. http://go.microsoft.com/fwlink/?linkid=14202")]
416         public static WebProxy GetDefaultProxy() {
417             ExceptionHelper.WebPermissionUnrestricted.Demand();
418             return new WebProxy(true);
419         }
420
421         //
422         // ISerializable constructor
423         //
424         /// <devdoc>
425         ///    <para>[To be supplied.]</para>
426         /// </devdoc>
427         protected WebProxy(SerializationInfo serializationInfo, StreamingContext streamingContext) {
428             // first check for useRegistry on the serialized proxy
429             bool useRegistry = false;
430             try {
431                 useRegistry = serializationInfo.GetBoolean("_UseRegistry");
432             }
433             catch {
434             }
435             if (useRegistry) {
436                 // just make the proxy advanced, don't populate with any settings
437                 // note - this will happen in the context of the user performing the deserialization (their proxy settings get read)
438                 ExceptionHelper.WebPermissionUnrestricted.Demand();
439                 UnsafeUpdateFromRegistry();
440                 return;
441             }
442             // normal proxy
443             _ProxyAddress   = (Uri)serializationInfo.GetValue("_ProxyAddress", typeof(Uri));
444             _BypassOnLocal  = serializationInfo.GetBoolean("_BypassOnLocal");
445             _BypassList     = (ArrayList)serializationInfo.GetValue("_BypassList", typeof(ArrayList));
446             try {
447                 UseDefaultCredentials = serializationInfo.GetBoolean("_UseDefaultCredentials");
448             }
449             catch {
450             }
451         }
452
453         //
454         // ISerializable method
455         //
456         /// <internalonly/>
457         /// <devdoc>
458         ///    <para>[To be supplied.]</para>
459         /// </devdoc>
460         [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")]
461         [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter, SerializationFormatter=true)]
462         void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
463         {
464             GetObjectData(serializationInfo, streamingContext);
465         }
466
467         //
468         // FxCop: provide a way for derived classes to access this method even if they reimplement ISerializable.
469         //
470         [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter=true)]
471         protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
472         {
473             serializationInfo.AddValue("_BypassOnLocal", _BypassOnLocal);
474             serializationInfo.AddValue("_ProxyAddress", _ProxyAddress);
475             serializationInfo.AddValue("_BypassList", _BypassList);
476             serializationInfo.AddValue("_UseDefaultCredentials", UseDefaultCredentials);
477             if (_UseRegistry) {
478                 serializationInfo.AddValue("_UseRegistry", true);
479             }
480         }
481
482
483         /// <summary>
484         ///     Handles proxy settings by using Internet Explorer based settings,
485         ///     keep in mind the security implications when downloading and running
486         ///     script from any network source configured in Internet Explorer.
487         /// </summary>
488
489         private AutoWebProxyScriptEngine m_ScriptEngine;
490
491         internal AutoWebProxyScriptEngine ScriptEngine {
492             get {
493                 return m_ScriptEngine;
494             }
495             set {
496                 m_ScriptEngine = value;
497             }
498         }
499
500         // This constructor is used internally to make WebProxies that read their state from the registry.
501         // 
502         internal WebProxy(bool enableAutoproxy)
503         {
504             m_EnableAutoproxy = enableAutoproxy;
505             UnsafeUpdateFromRegistry();
506         }
507
508         internal void DeleteScriptEngine() {
509             if (ScriptEngine != null) {
510                 ScriptEngine.Close();
511                 ScriptEngine = null;
512             }
513         }
514
515         internal void UnsafeUpdateFromRegistry() {
516             GlobalLog.Assert(!_UseRegistry, "WebProxy#{0}::UnsafeUpdateFromRegistry()|_UseRegistry ScriptEngine#{1}", ValidationHelper.HashString(this), ValidationHelper.HashString(m_ScriptEngine));
517             _UseRegistry = true;
518 #if !FEATURE_PAL
519             ScriptEngine = new AutoWebProxyScriptEngine(this, true);
520             WebProxyData webProxyData = ScriptEngine.GetWebProxyData();
521
522             Update(webProxyData);
523 #endif
524         }
525
526         internal void Update(WebProxyData webProxyData) {
527 #if TRAVE
528             GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::Update() Before " + DumpIWebProxy(this));
529 #endif
530             // update needs to happen atomically
531             lock (this) {
532                 _BypassOnLocal = webProxyData.bypassOnLocal;
533                 _ProxyAddress = webProxyData.proxyAddress;
534                 _ProxyHostAddresses = webProxyData.proxyHostAddresses;
535                 _BypassList = webProxyData.bypassList;
536
537                 ScriptEngine.AutomaticallyDetectSettings = m_EnableAutoproxy && webProxyData.automaticallyDetectSettings;
538                 ScriptEngine.AutomaticConfigurationScript = m_EnableAutoproxy ? webProxyData.scriptLocation : null;
539             }
540 #if TRAVE
541             GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::Update() After " + DumpIWebProxy(this));
542 #endif
543         }
544
545         /// <devdoc>
546         /// <para>
547         /// We really didn't want to expose this. IWebProxy is kind of broken so we needed
548         /// a different way of calling into IsBypassed/GetProxy with a single method call.
549         /// We need to make it public though, so it is. This API will return null if
550         /// the proxy is to be bypassed, otherwise it returns an array of Uri to proxise
551         /// that may be used to access the destination. If an entry in the array is null
552         /// we want to try a direct access. Today we only attempt using the first entry.
553         /// </para>
554         /// </devdoc>
555         ProxyChain IAutoWebProxy.GetProxies(Uri destination) {
556             GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::GetProxies() destination:" + ValidationHelper.ToString(destination));
557             if (destination == null)
558             {
559                 throw new ArgumentNullException("destination");
560             }
561             return new ProxyScriptChain(this, destination);
562         }
563
564 #if TRAVE
565         internal static string DumpIWebProxy(IWebProxy proxy) {
566             StringBuilder stringBuilder = new StringBuilder();
567             stringBuilder.Append(" Type: " + ValidationHelper.ToString(proxy.GetType()) + "\r\n");
568             WebProxy webProxy = proxy as WebProxy;
569             if (webProxy!=null) {
570                 stringBuilder.Append(" - Address: " + ValidationHelper.ToString(webProxy._ProxyAddress) + "\r\n");
571                 stringBuilder.Append(" - BypassProxyOnLocal: " + ValidationHelper.ToString(webProxy._BypassOnLocal) + "\r\n");
572             }
573             stringBuilder.Append(" - -------------------------------------------------");
574             return stringBuilder.ToString();
575         }
576 #endif
577
578         //
579         // IWebProxy implementation
580         //
581
582         // Get proxies can never return null in the case of ExecutionSuccess.
583         private bool GetProxyAuto(Uri destination, out Uri proxyUri) {
584             GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::GetProxyAuto() destination:" + ValidationHelper.ToString(destination));
585             
586             proxyUri = null;
587             if (ScriptEngine == null) {
588                 return false;
589             }
590             IList<string> proxies = null;
591             if (!ScriptEngine.GetProxies(destination, out proxies)) {
592                 return false;
593             }
594
595             // Returning null in case 'proxies.Count == 0' means, no proxy available (incl. DIRECT), the request is prohibited.
596             if (proxies.Count > 0) {
597                 if (AreAllBypassed(proxies, true)) {
598                     // this is the broken behaviour of IWebProxy. Returning the same destination means bypass
599                     proxyUri = destination;
600                 }
601                 else {
602                     proxyUri = ProxyUri(proxies[0]);
603                 }
604             }
605             return true;
606         }
607
608         private bool IsBypassedAuto(Uri destination, out bool isBypassed) {
609             GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::IsBypassedAuto() destination:" + ValidationHelper.ToString(destination));
610
611             isBypassed = true;
612
613             if (ScriptEngine == null) {
614                 return false;
615             }
616             IList<string> proxyList; 
617             if (!ScriptEngine.GetProxies(destination, out proxyList)) {
618                 return false;
619             }
620             if (proxyList.Count == 0) {
621                 isBypassed = false;
622             }
623             else {
624                 isBypassed = AreAllBypassed(proxyList, true);
625             }
626             return true;
627         }
628
629         internal Uri[] GetProxiesAuto(Uri destination, ref int syncStatus)
630         {
631             GlobalLog.Print("WebProxy#" + ValidationHelper.HashString(this) + "::GetProxiesAuto() destination:" + ValidationHelper.ToString(destination));
632
633             if (ScriptEngine == null) {
634                 return null;
635             }
636
637             IList<string> proxyList = null;           
638             if (!ScriptEngine.GetProxies(destination, out proxyList, ref syncStatus)) {
639                 return null;
640             }
641
642             Uri[] proxyUris = null;
643             if (proxyList.Count == 0) {
644                 proxyUris = new Uri[] { };
645             }
646             else if (AreAllBypassed(proxyList, false)) {
647                 proxyUris = new Uri[] { null };
648             }
649             else {
650                 proxyUris = new Uri[proxyList.Count];
651                 for (int i = 0; i < proxyList.Count; i++) {
652                     proxyUris[i] = ProxyUri(proxyList[i]);
653                 }
654             }
655             return proxyUris;
656         }
657
658         internal void AbortGetProxiesAuto(ref int syncStatus)
659         {
660             if (ScriptEngine != null)
661             {
662                 ScriptEngine.Abort(ref syncStatus);
663             }
664         }
665
666         internal Uri GetProxyAutoFailover(Uri destination)
667         {
668             if (IsBypassedManual(destination))
669             {
670                 return null;
671             }
672
673             Uri proxy = _ProxyAddress;
674             Hashtable proxyHostAddresses = _ProxyHostAddresses;
675             if (proxyHostAddresses != null)
676             {
677                 proxy = proxyHostAddresses[destination.Scheme] as Uri;
678             }
679             return proxy;
680         }
681
682         private static bool AreAllBypassed(IEnumerable<string> proxies, bool checkFirstOnly) {
683             bool isBypassed = true;
684
685             foreach (string proxy in proxies) {
686                 isBypassed = string.IsNullOrEmpty(proxy);
687                 
688                 if (checkFirstOnly || !isBypassed) {
689                     break;
690                 }
691             }
692
693             return isBypassed;
694         }
695
696         private static Uri ProxyUri(string proxyName) {
697             return proxyName==null || proxyName.Length==0 ? null : new Uri("http://" + proxyName);
698         }
699     }
700 }