Merge pull request #1079 from esdrubal/webclient_cancel
[mono.git] / mcs / class / System / System.Net / MacProxy.cs
1 // 
2 // MacProxy.cs
3 //  
4 // Author: Jeffrey Stedfast <jeff@xamarin.com>
5 // 
6 // Copyright (c) 2012-2014 Xamarin Inc.
7 // 
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 // 
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
17 // 
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 // THE SOFTWARE.
25 // 
26
27 using System;
28 using System.Collections.Generic;
29 using System.Runtime.InteropServices;
30 using System.Threading;
31
32 namespace System.Net
33 {
34         internal class CFObject : IDisposable
35         {
36                 public const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
37                 const string SystemLibrary = "/usr/lib/libSystem.dylib";
38
39                 [DllImport (SystemLibrary)]
40                 public static extern IntPtr dlopen (string path, int mode);
41
42                 [DllImport (SystemLibrary)]
43                 public static extern IntPtr dlsym (IntPtr handle, string symbol);
44
45                 [DllImport (SystemLibrary)]
46                 public static extern void dlclose (IntPtr handle);
47
48                 public static IntPtr GetIndirect (IntPtr handle, string symbol)
49                 {
50                         return dlsym (handle, symbol);
51                 }
52
53                 public static IntPtr GetCFObjectHandle (IntPtr handle, string symbol)
54                 {
55                         var indirect = dlsym (handle, symbol);
56                         if (indirect == IntPtr.Zero)
57                                 return IntPtr.Zero;
58
59                         return Marshal.ReadIntPtr (indirect);
60                 }
61
62                 public CFObject (IntPtr handle, bool own)
63                 {
64                         Handle = handle;
65
66                         if (!own)
67                                 Retain ();
68                 }
69
70                 ~CFObject ()
71                 {
72                         Dispose (false);
73                 }
74
75                 public IntPtr Handle { get; private set; }
76
77                 [DllImport (CoreFoundationLibrary)]
78                 extern static IntPtr CFRetain (IntPtr handle);
79
80                 void Retain ()
81                 {
82                         CFRetain (Handle);
83                 }
84
85                 [DllImport (CoreFoundationLibrary)]
86                 extern static void CFRelease (IntPtr handle);
87
88                 void Release ()
89                 {
90                         CFRelease (Handle);
91                 }
92
93                 protected virtual void Dispose (bool disposing)
94                 {
95                         if (Handle != IntPtr.Zero) {
96                                 Release ();
97                                 Handle = IntPtr.Zero;
98                         }
99                 }
100
101                 public void Dispose ()
102                 {
103                         Dispose (true);
104                         GC.SuppressFinalize (this);
105                 }
106         }
107
108         internal class CFArray : CFObject
109         {
110                 public CFArray (IntPtr handle, bool own) : base (handle, own) { }
111
112                 [DllImport (CoreFoundationLibrary)]
113                 extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr values, /* CFIndex */ IntPtr numValues, IntPtr callbacks);
114                 static readonly IntPtr kCFTypeArrayCallbacks;
115
116                 static CFArray ()
117                 {
118                         var handle = dlopen (CoreFoundationLibrary, 0);
119                         if (handle == IntPtr.Zero)
120                                 return;
121
122                         try {
123                                 kCFTypeArrayCallbacks = GetIndirect (handle, "kCFTypeArrayCallBacks");
124                         } finally {
125                                 dlclose (handle);
126                         }
127                 }
128
129                 static unsafe CFArray Create (params IntPtr[] values)
130                 {
131                         if (values == null)
132                                 throw new ArgumentNullException ("values");
133
134                         fixed (IntPtr *pv = values) {
135                                 IntPtr handle = CFArrayCreate (IntPtr.Zero, (IntPtr) pv, (IntPtr) values.Length, kCFTypeArrayCallbacks);
136
137                                 return new CFArray (handle, false);
138                         }
139                 }
140
141                 public static CFArray Create (params CFObject[] values)
142                 {
143                         if (values == null)
144                                 throw new ArgumentNullException ("values");
145
146                         IntPtr[] _values = new IntPtr [values.Length];
147                         for (int i = 0; i < _values.Length; i++)
148                                 _values[i] = values[i].Handle;
149
150                         return Create (_values);
151                 }
152
153                 [DllImport (CoreFoundationLibrary)]
154                 extern static /* CFIndex */ IntPtr CFArrayGetCount (IntPtr handle);
155
156                 public int Count {
157                         get { return (int) CFArrayGetCount (Handle); }
158                 }
159
160                 [DllImport (CoreFoundationLibrary)]
161                 extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, /* CFIndex */ IntPtr index);
162
163                 public IntPtr this[int index] {
164                         get {
165                                 return CFArrayGetValueAtIndex (Handle, (IntPtr) index);
166                         }
167                 }
168         }
169
170         internal class CFNumber : CFObject
171         {
172                 public CFNumber (IntPtr handle, bool own) : base (handle, own) { }
173
174                 [DllImport (CoreFoundationLibrary)]
175                 [return: MarshalAs (UnmanagedType.I1)]
176                 extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, [MarshalAs (UnmanagedType.I1)] out bool value);
177
178                 public static bool AsBool (IntPtr handle)
179                 {
180                         bool value;
181
182                         if (handle == IntPtr.Zero)
183                                 return false;
184
185                         CFNumberGetValue (handle, (IntPtr) 1, out value);
186
187                         return value;
188                 }
189
190                 public static implicit operator bool (CFNumber number)
191                 {
192                         return AsBool (number.Handle);
193                 }
194
195                 [DllImport (CoreFoundationLibrary)]
196                 [return: MarshalAs (UnmanagedType.I1)]
197                 extern static bool CFNumberGetValue (IntPtr handle, /* CFNumberType */ IntPtr type, out int value);
198
199                 public static int AsInt32 (IntPtr handle)
200                 {
201                         int value;
202
203                         if (handle == IntPtr.Zero)
204                                 return 0;
205
206                         // 9 == kCFNumberIntType == C int
207                         CFNumberGetValue (handle, (IntPtr) 9, out value);
208
209                         return value;
210                 }
211
212                 public static implicit operator int (CFNumber number)
213                 {
214                         return AsInt32 (number.Handle);
215                 }
216         }
217
218         internal struct CFRange {
219                 public IntPtr Location, Length;
220                 
221                 public CFRange (int loc, int len)
222                 {
223                         Location = (IntPtr) loc;
224                         Length = (IntPtr) len;
225                 }
226         }
227
228         internal class CFString : CFObject
229         {
230                 string str;
231
232                 public CFString (IntPtr handle, bool own) : base (handle, own) { }
233
234                 [DllImport (CoreFoundationLibrary)]
235                 extern static IntPtr CFStringCreateWithCharacters (IntPtr alloc, IntPtr chars, /* CFIndex */ IntPtr length);
236
237                 public static CFString Create (string value)
238                 {
239                         IntPtr handle;
240
241                         unsafe {
242                                 fixed (char *ptr = value) {
243                                         handle = CFStringCreateWithCharacters (IntPtr.Zero, (IntPtr) ptr, (IntPtr) value.Length);
244                                 }
245                         }
246
247                         if (handle == IntPtr.Zero)
248                                 return null;
249
250                         return new CFString (handle, true);
251                 }
252
253                 [DllImport (CoreFoundationLibrary)]
254                 extern static /* CFIndex */ IntPtr CFStringGetLength (IntPtr handle);
255
256                 public int Length {
257                         get {
258                                 if (str != null)
259                                         return str.Length;
260
261                                 return (int) CFStringGetLength (Handle);
262                         }
263                 }
264
265                 [DllImport (CoreFoundationLibrary)]
266                 extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
267
268                 [DllImport (CoreFoundationLibrary)]
269                 extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
270
271                 public static string AsString (IntPtr handle)
272                 {
273                         if (handle == IntPtr.Zero)
274                                 return null;
275                         
276                         int len = (int) CFStringGetLength (handle);
277                         
278                         if (len == 0)
279                                 return string.Empty;
280                         
281                         IntPtr chars = CFStringGetCharactersPtr (handle);
282                         IntPtr buffer = IntPtr.Zero;
283                         
284                         if (chars == IntPtr.Zero) {
285                                 CFRange range = new CFRange (0, len);
286                                 buffer = Marshal.AllocHGlobal (len * 2);
287                                 CFStringGetCharacters (handle, range, buffer);
288                                 chars = buffer;
289                         }
290
291                         string str;
292
293                         unsafe {
294                                 str = new string ((char *) chars, 0, len);
295                         }
296                         
297                         if (buffer != IntPtr.Zero)
298                                 Marshal.FreeHGlobal (buffer);
299
300                         return str;
301                 }
302
303                 public override string ToString ()
304                 {
305                         if (str == null)
306                                 str = AsString (Handle);
307
308                         return str;
309                 }
310
311                 public static implicit operator string (CFString str)
312                 {
313                         return str.ToString ();
314                 }
315
316                 public static implicit operator CFString (string str)
317                 {
318                         return Create (str);
319                 }
320         }
321
322         internal class CFDictionary : CFObject
323         {
324                 public CFDictionary (IntPtr handle, bool own) : base (handle, own) { }
325
326                 [DllImport (CoreFoundationLibrary)]
327                 extern static IntPtr CFDictionaryGetValue (IntPtr handle, IntPtr key);
328
329                 public IntPtr GetValue (IntPtr key)
330                 {
331                         return CFDictionaryGetValue (Handle, key);
332                 }
333
334                 public IntPtr this[IntPtr key] {
335                         get {
336                                 return GetValue (key);
337                         }
338                 }
339         }
340
341         internal class CFUrl : CFObject
342         {
343                 public CFUrl (IntPtr handle, bool own) : base (handle, own) { }
344
345                 [DllImport (CoreFoundationLibrary)]
346                 extern static IntPtr CFURLCreateWithString (IntPtr allocator, IntPtr str, IntPtr baseURL);
347
348                 public static CFUrl Create (string absolute)
349                 {
350                         if (string.IsNullOrEmpty (absolute))
351                                 return null;
352
353                         CFString str = CFString.Create (absolute);
354                         IntPtr handle = CFURLCreateWithString (IntPtr.Zero, str.Handle, IntPtr.Zero);
355                         str.Dispose ();
356
357                         if (handle == IntPtr.Zero)
358                                 return null;
359
360                         return new CFUrl (handle, true);
361                 }
362         }
363
364         internal enum CFProxyType {
365                 None,
366                 AutoConfigurationUrl,
367                 AutoConfigurationJavaScript,
368                 FTP,
369                 HTTP,
370                 HTTPS,
371                 SOCKS
372         }
373         
374         internal class CFProxy {
375                 //static IntPtr kCFProxyAutoConfigurationHTTPResponseKey;
376                 static IntPtr kCFProxyAutoConfigurationJavaScriptKey;
377                 static IntPtr kCFProxyAutoConfigurationURLKey;
378                 static IntPtr kCFProxyHostNameKey;
379                 static IntPtr kCFProxyPasswordKey;
380                 static IntPtr kCFProxyPortNumberKey;
381                 static IntPtr kCFProxyTypeKey;
382                 static IntPtr kCFProxyUsernameKey;
383
384                 //static IntPtr kCFProxyTypeNone;
385                 static IntPtr kCFProxyTypeAutoConfigurationURL;
386                 static IntPtr kCFProxyTypeAutoConfigurationJavaScript;
387                 static IntPtr kCFProxyTypeFTP;
388                 static IntPtr kCFProxyTypeHTTP;
389                 static IntPtr kCFProxyTypeHTTPS;
390                 static IntPtr kCFProxyTypeSOCKS;
391
392                 static CFProxy ()
393                 {
394                         IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
395
396                         //kCFProxyAutoConfigurationHTTPResponseKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationHTTPResponseKey");
397                         kCFProxyAutoConfigurationJavaScriptKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationJavaScriptKey");
398                         kCFProxyAutoConfigurationURLKey = CFObject.GetCFObjectHandle (handle, "kCFProxyAutoConfigurationURLKey");
399                         kCFProxyHostNameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyHostNameKey");
400                         kCFProxyPasswordKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPasswordKey");
401                         kCFProxyPortNumberKey = CFObject.GetCFObjectHandle (handle, "kCFProxyPortNumberKey");
402                         kCFProxyTypeKey = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeKey");
403                         kCFProxyUsernameKey = CFObject.GetCFObjectHandle (handle, "kCFProxyUsernameKey");
404
405                         //kCFProxyTypeNone = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeNone");
406                         kCFProxyTypeAutoConfigurationURL = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationURL");
407                         kCFProxyTypeAutoConfigurationJavaScript = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeAutoConfigurationJavaScript");
408                         kCFProxyTypeFTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeFTP");
409                         kCFProxyTypeHTTP = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTP");
410                         kCFProxyTypeHTTPS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeHTTPS");
411                         kCFProxyTypeSOCKS = CFObject.GetCFObjectHandle (handle, "kCFProxyTypeSOCKS");
412
413                         CFObject.dlclose (handle);
414                 }
415
416                 CFDictionary settings;
417                 
418                 internal CFProxy (CFDictionary settings)
419                 {
420                         this.settings = settings;
421                 }
422                 
423                 static CFProxyType CFProxyTypeToEnum (IntPtr type)
424                 {
425                         if (type == kCFProxyTypeAutoConfigurationJavaScript)
426                                 return CFProxyType.AutoConfigurationJavaScript;
427
428                         if (type == kCFProxyTypeAutoConfigurationURL)
429                                 return CFProxyType.AutoConfigurationUrl;
430
431                         if (type == kCFProxyTypeFTP)
432                                 return CFProxyType.FTP;
433
434                         if (type == kCFProxyTypeHTTP)
435                                 return CFProxyType.HTTP;
436
437                         if (type == kCFProxyTypeHTTPS)
438                                 return CFProxyType.HTTPS;
439
440                         if (type == kCFProxyTypeSOCKS)
441                                 return CFProxyType.SOCKS;
442                         
443                         return CFProxyType.None;
444                 }
445                 
446 #if false
447                 // AFAICT these get used with CFNetworkExecuteProxyAutoConfiguration*()
448                 
449                 // TODO: bind CFHTTPMessage so we can return the proper type here.
450                 public IntPtr AutoConfigurationHTTPResponse {
451                         get { return settings[kCFProxyAutoConfigurationHTTPResponseKey]; }
452                 }
453 #endif
454
455                 public IntPtr AutoConfigurationJavaScript {
456                         get {
457                                 if (kCFProxyAutoConfigurationJavaScriptKey == IntPtr.Zero)
458                                         return IntPtr.Zero;
459                                 
460                                 return settings[kCFProxyAutoConfigurationJavaScriptKey];
461                         }
462                 }
463                 
464                 public IntPtr AutoConfigurationUrl {
465                         get {
466                                 if (kCFProxyAutoConfigurationURLKey == IntPtr.Zero)
467                                         return IntPtr.Zero;
468                                 
469                                 return settings[kCFProxyAutoConfigurationURLKey];
470                         }
471                 }
472                 
473                 public string HostName {
474                         get {
475                                 if (kCFProxyHostNameKey == IntPtr.Zero)
476                                         return null;
477                                 
478                                 return CFString.AsString (settings[kCFProxyHostNameKey]);
479                         }
480                 }
481                 
482                 public string Password {
483                         get {
484                                 if (kCFProxyPasswordKey == IntPtr.Zero)
485                                         return null;
486
487                                 return CFString.AsString (settings[kCFProxyPasswordKey]);
488                         }
489                 }
490                 
491                 public int Port {
492                         get {
493                                 if (kCFProxyPortNumberKey == IntPtr.Zero)
494                                         return 0;
495                                 
496                                 return CFNumber.AsInt32 (settings[kCFProxyPortNumberKey]);
497                         }
498                 }
499                 
500                 public CFProxyType ProxyType {
501                         get {
502                                 if (kCFProxyTypeKey == IntPtr.Zero)
503                                         return CFProxyType.None;
504                                 
505                                 return CFProxyTypeToEnum (settings[kCFProxyTypeKey]);
506                         }
507                 }
508                 
509                 public string Username {
510                         get {
511                                 if (kCFProxyUsernameKey == IntPtr.Zero)
512                                         return null;
513
514                                 return CFString.AsString (settings[kCFProxyUsernameKey]);
515                         }
516                 }
517         }
518         
519         internal class CFProxySettings {
520                 static IntPtr kCFNetworkProxiesHTTPEnable;
521                 static IntPtr kCFNetworkProxiesHTTPPort;
522                 static IntPtr kCFNetworkProxiesHTTPProxy;
523                 static IntPtr kCFNetworkProxiesProxyAutoConfigEnable;
524                 static IntPtr kCFNetworkProxiesProxyAutoConfigJavaScript;
525                 static IntPtr kCFNetworkProxiesProxyAutoConfigURLString;
526
527                 static CFProxySettings ()
528                 {
529                         IntPtr handle = CFObject.dlopen (CFNetwork.CFNetworkLibrary, 0);
530
531                         kCFNetworkProxiesHTTPEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPEnable");
532                         kCFNetworkProxiesHTTPPort = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPPort");
533                         kCFNetworkProxiesHTTPProxy = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesHTTPProxy");
534                         kCFNetworkProxiesProxyAutoConfigEnable = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigEnable");
535                         kCFNetworkProxiesProxyAutoConfigJavaScript = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigJavaScript");
536                         kCFNetworkProxiesProxyAutoConfigURLString = CFObject.GetCFObjectHandle (handle, "kCFNetworkProxiesProxyAutoConfigURLString");
537
538                         CFObject.dlclose (handle);
539                 }
540
541                 CFDictionary settings;
542                 
543                 public CFProxySettings (CFDictionary settings)
544                 {
545                         this.settings = settings;
546                 }
547                 
548                 public CFDictionary Dictionary {
549                         get { return settings; }
550                 }
551                 
552                 public bool HTTPEnable {
553                         get {
554                                 if (kCFNetworkProxiesHTTPEnable == IntPtr.Zero)
555                                         return false;
556
557                                 return CFNumber.AsBool (settings[kCFNetworkProxiesHTTPEnable]);
558                         }
559                 }
560                 
561                 public int HTTPPort {
562                         get {
563                                 if (kCFNetworkProxiesHTTPPort == IntPtr.Zero)
564                                         return 0;
565                                 
566                                 return CFNumber.AsInt32 (settings[kCFNetworkProxiesHTTPPort]);
567                         }
568                 }
569                 
570                 public string HTTPProxy {
571                         get {
572                                 if (kCFNetworkProxiesHTTPProxy == IntPtr.Zero)
573                                         return null;
574                                 
575                                 return CFString.AsString (settings[kCFNetworkProxiesHTTPProxy]);
576                         }
577                 }
578                 
579                 public bool ProxyAutoConfigEnable {
580                         get {
581                                 if (kCFNetworkProxiesProxyAutoConfigEnable == IntPtr.Zero)
582                                         return false;
583                                 
584                                 return CFNumber.AsBool (settings[kCFNetworkProxiesProxyAutoConfigEnable]);
585                         }
586                 }
587                 
588                 public string ProxyAutoConfigJavaScript {
589                         get {
590                                 if (kCFNetworkProxiesProxyAutoConfigJavaScript == IntPtr.Zero)
591                                         return null;
592                                 
593                                 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigJavaScript]);
594                         }
595                 }
596                 
597                 public string ProxyAutoConfigURLString {
598                         get {
599                                 if (kCFNetworkProxiesProxyAutoConfigURLString == IntPtr.Zero)
600                                         return null;
601                                 
602                                 return CFString.AsString (settings[kCFNetworkProxiesProxyAutoConfigURLString]);
603                         }
604                 }
605         }
606         
607         internal static class CFNetwork {
608 #if !MONOTOUCH
609                 public const string CFNetworkLibrary = "/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork";
610 #else
611                 public const string CFNetworkLibrary = "/System/Library/Frameworks/CFNetwork.framework/CFNetwork";
612 #endif
613                 
614                 [DllImport (CFNetworkLibrary, EntryPoint = "CFNetworkCopyProxiesForAutoConfigurationScript")]
615                 // CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL, CFErrorRef* error);
616                 extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScriptSequential (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error);
617
618                 class GetProxyData : IDisposable {
619                         public IntPtr script;
620                         public IntPtr targetUri;
621                         public IntPtr error;
622                         public IntPtr result;
623                         public ManualResetEvent evt = new ManualResetEvent (false);
624
625                         public void Dispose ()
626                         {
627                                 evt.Close ();
628                         }
629                 }
630
631                 static object lock_obj = new object ();
632                 static Queue<GetProxyData> get_proxy_queue;
633                 static AutoResetEvent proxy_event;
634
635                 static void CFNetworkCopyProxiesForAutoConfigurationScriptThread ()
636                 {
637                         GetProxyData data;
638                         var data_left = true;
639
640                         while (true) {
641                                 proxy_event.WaitOne ();
642
643                                 do {
644                                         lock (lock_obj) {
645                                                 if (get_proxy_queue.Count == 0)
646                                                         break;
647                                                 data = get_proxy_queue.Dequeue ();
648                                                 data_left = get_proxy_queue.Count > 0;
649                                         }
650
651                                         data.result = CFNetworkCopyProxiesForAutoConfigurationScriptSequential (data.script, data.targetUri, out data.error);
652                                         data.evt.Set ();
653                                 } while (data_left);
654                         }
655                 }
656
657                 static IntPtr CFNetworkCopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error)
658                 {
659                         // This method must only be called on only one thread during an application's life time.
660                         // Note that it's not enough to use a lock to make calls sequential across different threads,
661                         // it has to be one thread. Also note that that thread can't be the main thread, because the
662                         // main thread might be blocking waiting for this network request to finish.
663                         // Another possibility would be to use JavaScriptCore to execute this piece of
664                         // javascript ourselves, but unfortunately it's not available before iOS7.
665                         // See bug #7923 comment #21+.
666
667                         using (var data = new GetProxyData ()) {
668                                 data.script = proxyAutoConfigurationScript;
669                                 data.targetUri = targetURL;
670
671                                 lock (lock_obj) {
672                                         if (get_proxy_queue == null) {
673                                                 get_proxy_queue = new Queue<GetProxyData> ();
674                                                 proxy_event = new AutoResetEvent (false);
675                                                 new Thread (CFNetworkCopyProxiesForAutoConfigurationScriptThread) {
676                                                         IsBackground = true,
677                                                 }.Start ();
678                                         }
679                                         get_proxy_queue.Enqueue (data);
680                                         proxy_event.Set ();
681                                 }
682
683                                 data.evt.WaitOne ();
684
685                                 error = data.error;
686
687                                 return data.result;
688                         }
689                 }
690
691                 static CFArray CopyProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
692                 {
693                         IntPtr err = IntPtr.Zero;
694                         IntPtr native = CFNetworkCopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL.Handle, out err);
695                         
696                         if (native == IntPtr.Zero)
697                                 return null;
698                         
699                         return new CFArray (native, true);
700                 }
701                 
702                 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, CFUrl targetURL)
703                 {
704                         if (proxyAutoConfigurationScript == IntPtr.Zero)
705                                 throw new ArgumentNullException ("proxyAutoConfigurationScript");
706                         
707                         if (targetURL == null)
708                                 throw new ArgumentNullException ("targetURL");
709                         
710                         CFArray array = CopyProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
711                         
712                         if (array == null)
713                                 return null;
714                         
715                         CFProxy[] proxies = new CFProxy [array.Count];
716                         for (int i = 0; i < proxies.Length; i++) {
717                                 CFDictionary dict = new CFDictionary (array[i], false);
718                                 proxies[i] = new CFProxy (dict);
719                         }
720
721                         array.Dispose ();
722                         
723                         return proxies;
724                 }
725                 
726                 public static CFProxy[] GetProxiesForAutoConfigurationScript (IntPtr proxyAutoConfigurationScript, Uri targetUri)
727                 {
728                         if (proxyAutoConfigurationScript == IntPtr.Zero)
729                                 throw new ArgumentNullException ("proxyAutoConfigurationScript");
730                         
731                         if (targetUri == null)
732                                 throw new ArgumentNullException ("targetUri");
733                         
734                         CFUrl targetURL = CFUrl.Create (targetUri.AbsoluteUri);
735                         CFProxy[] proxies = GetProxiesForAutoConfigurationScript (proxyAutoConfigurationScript, targetURL);
736                         targetURL.Dispose ();
737                         
738                         return proxies;
739                 }
740                 
741                 [DllImport (CFNetworkLibrary)]
742                 // CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
743                 extern static IntPtr CFNetworkCopyProxiesForURL (IntPtr url, IntPtr proxySettings);
744                 
745                 static CFArray CopyProxiesForURL (CFUrl url, CFDictionary proxySettings)
746                 {
747                         IntPtr native = CFNetworkCopyProxiesForURL (url.Handle, proxySettings != null ? proxySettings.Handle : IntPtr.Zero);
748                         
749                         if (native == IntPtr.Zero)
750                                 return null;
751                         
752                         return new CFArray (native, true);
753                 }
754                 
755                 public static CFProxy[] GetProxiesForURL (CFUrl url, CFProxySettings proxySettings)
756                 {
757                         if (url == null || url.Handle == IntPtr.Zero)
758                                 throw new ArgumentNullException ("url");
759                         
760                         if (proxySettings == null)
761                                 proxySettings = GetSystemProxySettings ();
762                         
763                         CFArray array = CopyProxiesForURL (url, proxySettings.Dictionary);
764                         
765                         if (array == null)
766                                 return null;
767
768                         CFProxy[] proxies = new CFProxy [array.Count];
769                         for (int i = 0; i < proxies.Length; i++) {
770                                 CFDictionary dict = new CFDictionary (array[i], false);
771                                 proxies[i] = new CFProxy (dict);
772                         }
773
774                         array.Dispose ();
775                         
776                         return proxies;
777                 }
778                 
779                 public static CFProxy[] GetProxiesForUri (Uri uri, CFProxySettings proxySettings)
780                 {
781                         if (uri == null)
782                                 throw new ArgumentNullException ("uri");
783                         
784                         CFUrl url = CFUrl.Create (uri.AbsoluteUri);
785                         if (url == null)
786                                 return null;
787                         
788                         CFProxy[] proxies = GetProxiesForURL (url, proxySettings);
789                         url.Dispose ();
790                         
791                         return proxies;
792                 }
793                 
794                 [DllImport (CFNetworkLibrary)]
795                 // CFDictionaryRef CFNetworkCopySystemProxySettings (void);
796                 extern static IntPtr CFNetworkCopySystemProxySettings ();
797                 
798                 public static CFProxySettings GetSystemProxySettings ()
799                 {
800                         IntPtr native = CFNetworkCopySystemProxySettings ();
801                         
802                         if (native == IntPtr.Zero)
803                                 return null;
804                         
805                         var dict = new CFDictionary (native, true);
806
807                         return new CFProxySettings (dict);
808                 }
809                 
810                 class CFWebProxy : IWebProxy {
811                         ICredentials credentials;
812                         bool userSpecified;
813                         
814                         public CFWebProxy ()
815                         {
816                         }
817                         
818                         public ICredentials Credentials {
819                                 get { return credentials; }
820                                 set {
821                                         userSpecified = true;
822                                         credentials = value;
823                                 }
824                         }
825                         
826                         static Uri GetProxyUri (CFProxy proxy, out NetworkCredential credentials)
827                         {
828                                 string protocol;
829                                 
830                                 switch (proxy.ProxyType) {
831                                 case CFProxyType.FTP:
832                                         protocol = "ftp://";
833                                         break;
834                                 case CFProxyType.HTTP:
835                                 case CFProxyType.HTTPS:
836                                         protocol = "http://";
837                                         break;
838                                 default:
839                                         credentials = null;
840                                         return null;
841                                 }
842                                 
843                                 string username = proxy.Username;
844                                 string password = proxy.Password;
845                                 string hostname = proxy.HostName;
846                                 int port = proxy.Port;
847                                 string uri;
848                                 
849                                 if (username != null)
850                                         credentials = new NetworkCredential (username, password);
851                                 else
852                                         credentials = null;
853                                 
854                                 uri = protocol + hostname + (port != 0 ? ':' + port.ToString () : string.Empty);
855                                 
856                                 return new Uri (uri, UriKind.Absolute);
857                         }
858                         
859                         static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
860                         {
861                                 CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
862                                 
863                                 if (proxies == null) {
864                                         credentials = null;
865                                         return targetUri;
866                                 }
867                                 
868                                 for (int i = 0; i < proxies.Length; i++) {
869                                         switch (proxies[i].ProxyType) {
870                                         case CFProxyType.HTTPS:
871                                         case CFProxyType.HTTP:
872                                         case CFProxyType.FTP:
873                                                 // create a Uri based on the hostname/port/etc info
874                                                 return GetProxyUri (proxies[i], out credentials);
875                                         case CFProxyType.SOCKS:
876                                         default:
877                                                 // unsupported proxy type, try the next one
878                                                 break;
879                                         case CFProxyType.None:
880                                                 // no proxy should be used
881                                                 credentials = null;
882                                                 return targetUri;
883                                         }
884                                 }
885                                 
886                                 credentials = null;
887                                 
888                                 return null;
889                         }
890                         
891                         public Uri GetProxy (Uri targetUri)
892                         {
893                                 NetworkCredential credentials = null;
894                                 Uri proxy = null;
895                                 
896                                 if (targetUri == null)
897                                         throw new ArgumentNullException ("targetUri");
898                                 
899                                 try {
900                                         CFProxySettings settings = CFNetwork.GetSystemProxySettings ();
901                                         CFProxy[] proxies = CFNetwork.GetProxiesForUri (targetUri, settings);
902                                         
903                                         if (proxies != null) {
904                                                 for (int i = 0; i < proxies.Length && proxy == null; i++) {
905                                                         switch (proxies[i].ProxyType) {
906                                                         case CFProxyType.AutoConfigurationJavaScript:
907                                                                 proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
908                                                                 break;
909                                                         case CFProxyType.AutoConfigurationUrl:
910                                                                 // unsupported proxy type (requires fetching script from remote url)
911                                                                 break;
912                                                         case CFProxyType.HTTPS:
913                                                         case CFProxyType.HTTP:
914                                                         case CFProxyType.FTP:
915                                                                 // create a Uri based on the hostname/port/etc info
916                                                                 proxy = GetProxyUri (proxies[i], out credentials);
917                                                                 break;
918                                                         case CFProxyType.SOCKS:
919                                                                 // unsupported proxy type, try the next one
920                                                                 break;
921                                                         case CFProxyType.None:
922                                                                 // no proxy should be used
923                                                                 proxy = targetUri;
924                                                                 break;
925                                                         }
926                                                 }
927                                                 
928                                                 if (proxy == null) {
929                                                         // no supported proxies for this Uri, fall back to trying to connect to targetUri directly
930                                                         proxy = targetUri;
931                                                 }
932                                         } else {
933                                                 proxy = targetUri;
934                                         }
935                                 } catch {
936                                         // ignore errors while retrieving proxy data
937                                         proxy = targetUri;
938                                 }
939                                 
940                                 if (!userSpecified)
941                                         this.credentials = credentials;
942                                 
943                                 return proxy;
944                         }
945                         
946                         public bool IsBypassed (Uri targetUri)
947                         {
948                                 if (targetUri == null)
949                                         throw new ArgumentNullException ("targetUri");
950                                 
951                                 return GetProxy (targetUri) == targetUri;
952                         }
953                 }
954                 
955                 public static IWebProxy GetDefaultProxy ()
956                 {
957                         return new CFWebProxy ();
958                 }
959         }
960 }