* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / System / System.Net / WebClient.cs
1 //
2 // System.Net.WebClient
3 //
4 // Authors:
5 //      Lawrence Pit (loz@cable.a2000.nl)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //      Atsushi Enomoto (atsushi@ximian.com)
8 //
9 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
10 // (C) 2006 Novell, Inc. (http://www.novell.com)
11 //
12
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Collections.Specialized;
36 using System.ComponentModel;
37 using System.IO;
38 using System.Runtime.InteropServices;
39 using System.Runtime.Serialization;
40 using System.Text;
41 #if NET_2_0
42 using System.Collections.Generic;
43 using System.Threading;
44 #endif
45
46 namespace System.Net 
47 {
48         [ComVisible(true)]
49         public
50 #if !NET_2_0
51         sealed
52 #endif
53         class WebClient : Component
54         {
55                 static readonly string urlEncodedCType = "application/x-www-form-urlencoded";
56                 static byte [] hexBytes;
57                 ICredentials credentials;
58                 WebHeaderCollection headers;
59                 WebHeaderCollection responseHeaders;
60                 Uri baseAddress;
61                 string baseString;
62                 NameValueCollection queryString;
63                 bool isBusy;
64 #if NET_2_0
65                 Encoding encoding = Encoding.Default;
66                 IWebProxy proxy;
67 #endif
68
69                 // Constructors
70                 static WebClient ()
71                 {
72                         hexBytes = new byte [16];
73                         int index = 0;
74                         for (int i = '0'; i <= '9'; i++, index++)
75                                 hexBytes [index] = (byte) i;
76
77                         for (int i = 'A'; i <= 'F'; i++, index++)
78                                 hexBytes [index] = (byte) i;
79                 }
80                 
81                 public WebClient ()
82                 {
83                 }
84                 
85                 // Properties
86                 
87                 public string BaseAddress {
88                         get {
89                                 if (baseString == null) {
90                                         if (baseAddress == null)
91                                                 return "";
92                                 }
93
94                                 baseString = baseAddress.ToString ();
95                                 return baseString;
96                         }
97                         
98                         set {
99                                 if (value == null || value == "") {
100                                         baseAddress = null;
101                                 } else {
102                                         baseAddress = new Uri (value);
103                                 }
104                         }
105                 }
106                 
107                 public ICredentials Credentials {
108                         get { return credentials; }
109                         set { credentials = value; }
110                 }
111
112                 public WebHeaderCollection Headers {
113                         get {
114                                 if (headers == null)
115                                         headers = new WebHeaderCollection ();
116
117                                 return headers;
118                         }
119                         set { headers = value; }
120                 }
121                 
122                 public NameValueCollection QueryString {
123                         get {
124                                 if (queryString == null)
125                                         queryString = new NameValueCollection ();
126
127                                 return queryString;
128                         }
129                         set { queryString = value; }
130                 }
131                 
132                 public WebHeaderCollection ResponseHeaders {
133                         get { return responseHeaders; }
134                 }
135
136 #if NET_2_0
137                 public Encoding Encoding {
138                         get { return encoding; }
139                         set {
140                                 if (value == null)
141                                         throw new ArgumentNullException ("value");
142                                 encoding = value;
143                         }
144                 }
145
146                 public IWebProxy Proxy {
147                         get { return proxy; }
148                         set { proxy = value; }
149                 }
150 #endif
151
152 #if NET_2_0
153                 public bool IsBusy {
154                         get { return isBusy || wait_handles != null && wait_handles.Count > 0; }
155                 }
156 #else
157                 bool IsBusy {
158                         get { return isBusy; }
159                 }
160 #endif
161
162                 // Methods
163
164                 void CheckBusy ()
165                 {
166                         if (IsBusy)
167                                 throw new NotSupportedException ("WebClient does not support conccurent I/O operations.");
168                 }
169
170                 void SetBusy ()
171                 {
172                         lock (this) {
173                                 CheckBusy ();
174                                 isBusy = true;
175                         }
176                 }
177
178                 //   DownloadData
179
180                 public byte [] DownloadData (string address)
181                 {
182                         return DownloadData (MakeUri (address));
183                 }
184
185 #if NET_2_0
186                 public
187 #endif
188                 byte [] DownloadData (Uri address)
189                 {
190                         try {
191                                 SetBusy ();
192                                 return DownloadDataCore (address);
193                         } finally {
194                                 isBusy = false;
195                         }
196                 }
197
198                 byte [] DownloadDataCore (Uri address)
199                 {
200                         WebRequest request = SetupRequest (address, "GET");
201                         WebResponse response = request.GetResponse ();
202                         Stream st = ProcessResponse (response);
203                         return ReadAll (st, (int) response.ContentLength);
204                 }
205
206                 //   DownloadFile
207
208                 public void DownloadFile (string address, string fileName)
209                 {
210                         DownloadFile (MakeUri (address), fileName);
211                 }
212
213 #if NET_2_0
214                 public
215 #endif
216                 void DownloadFile (Uri address, string fileName)
217                 {
218                         try {
219                                 SetBusy ();
220                                 DownloadFileCore (address, fileName);
221                         } finally {
222                                 isBusy = false;
223                         }
224                 }
225
226                 void DownloadFileCore (Uri address, string fileName)
227                 {
228                         WebRequest request = SetupRequest (address);
229                         WebResponse response = request.GetResponse ();
230                         Stream st = ProcessResponse (response);
231
232                         int cLength = (int) response.ContentLength;
233                         int length = (cLength <= -1 || cLength > 8192) ? 8192 : cLength;
234                         byte [] buffer = new byte [length];
235                         FileStream f = new FileStream (fileName, FileMode.CreateNew);
236
237                         int nread = 0;
238                         while ((nread = st.Read (buffer, 0, length)) != 0)
239                                 f.Write (buffer, 0, nread);
240
241                         f.Close ();
242                 }
243
244                 //   OpenRead
245
246                 public Stream OpenRead (string address)
247                 {
248                         return OpenRead (MakeUri (address));
249                 }
250
251 #if NET_2_0
252                 public
253 #endif
254                 Stream OpenRead (Uri address)
255                 {
256                         try {
257                                 SetBusy ();
258                                 WebRequest request = SetupRequest (address);
259                                 WebResponse response = request.GetResponse ();
260                                 return ProcessResponse (response);
261                         } finally {
262                                 isBusy = false;
263                         }
264                 }
265
266                 //   OpenWrite
267
268                 public Stream OpenWrite (string address)
269                 {
270                         return OpenWrite (MakeUri (address));
271                 }
272                 
273                 public Stream OpenWrite (string address, string method)
274                 {
275                         return OpenWrite (MakeUri (address), method);
276                 }
277
278 #if NET_2_0
279                 public
280 #endif
281                 Stream OpenWrite (Uri address)
282                 {
283                         return OpenWrite (address, DetermineMethod (address));
284                 }
285
286 #if NET_2_0
287                 public
288 #endif
289                 Stream OpenWrite (Uri address, string method)
290                 {
291                         try {
292                                 SetBusy ();
293                                 WebRequest request = SetupRequest (address, method);
294                                 return request.GetRequestStream ();
295                         } finally {
296                                 isBusy = false;
297                         }
298                 }
299
300                 private string DetermineMethod (Uri address)
301                 {
302                         if (address == null)
303                                 throw new ArgumentNullException ("address");
304 #if NET_2_0
305                         if (address.Scheme == Uri.UriSchemeFtp)
306                                 return "RETR";
307 #endif
308                         return "POST";
309                 }
310
311                 //   UploadData
312
313                 public byte [] UploadData (string address, byte [] data)
314                 {
315                         return UploadData (MakeUri (address), data);
316                 }
317                 
318                 public byte [] UploadData (string address, string method, byte [] data)
319                 {
320                         return UploadData (MakeUri (address), method, data);
321                 }
322
323 #if NET_2_0
324                 public
325 #endif
326                 byte [] UploadData (Uri address, byte [] data)
327                 {
328                         return UploadData (address, DetermineMethod (address), data);
329                 }
330
331 #if NET_2_0
332                 public
333 #endif
334                 byte [] UploadData (Uri address, string method, byte [] data)
335                 {
336                         try {
337                                 SetBusy ();
338                                 return UploadDataCore (address, method, data);
339                         } finally {
340                                 isBusy = false;
341                         }
342                 }
343
344                 byte [] UploadDataCore (Uri address, string method, byte [] data)
345                 {
346                         if (data == null)
347                                 throw new ArgumentNullException ("data");
348
349                         int contentLength = data.Length;
350                         WebRequest request = SetupRequest (address, method, contentLength);
351                         using (Stream stream = request.GetRequestStream ()) {
352                                 stream.Write (data, 0, contentLength);
353                         }
354
355                         WebResponse response = request.GetResponse ();
356                         Stream st = ProcessResponse (response);
357                         return ReadAll (st, (int) response.ContentLength);
358                 }
359
360                 //   UploadFile
361
362                 public byte [] UploadFile (string address, string fileName)
363                 {
364                         return UploadFile (MakeUri (address), fileName);
365                 }
366
367 #if NET_2_0
368                 public
369 #endif
370                 byte [] UploadFile (Uri address, string fileName)
371                 {
372                         return UploadFile (address, DetermineMethod (address), fileName);
373                 }
374                 
375                 public byte [] UploadFile (string address, string method, string fileName)
376                 {
377                         return UploadFile (MakeUri (address), method, fileName);
378                 }
379
380 #if NET_2_0
381                 public
382 #endif
383                 byte [] UploadFile (Uri address, string method, string fileName)
384                 {
385                         try {
386                                 SetBusy ();
387                                 return UploadFileCore (address, method, fileName);
388                         } finally {
389                                 isBusy = false;
390                         }
391                 }
392
393                 byte [] UploadFileCore (Uri address, string method, string fileName)
394                 {
395                         string fileCType = Headers ["Content-Type"];
396                         if (fileCType != null) {
397                                 string lower = fileCType.ToLower ();
398                                 if (lower.StartsWith ("multipart/"))
399                                         throw new WebException ("Content-Type cannot be set to a multipart" +
400                                                                 " type for this request.");
401                         } else {
402                                 fileCType = "application/octet-stream";
403                         }
404
405                         string boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
406                         Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
407                         WebRequest request = SetupRequest (address, method);
408                         Stream reqStream = null;
409                         Stream fStream = null;
410                         byte [] resultBytes = null;
411
412                         try {
413                                 fStream = File.OpenRead (fileName);
414                                 reqStream = request.GetRequestStream ();
415                                 byte [] realBoundary = Encoding.ASCII.GetBytes ("--" + boundary + "\r\n");
416                                 reqStream.Write (realBoundary, 0, realBoundary.Length);
417                                 string partHeaders = String.Format ("Content-Disposition: form-data; " +
418                                                                     "name=\"file\"; filename=\"{0}\"\r\n" +
419                                                                     "Content-Type: {1}\r\n\r\n",
420                                                                     Path.GetFileName (fileName), fileCType);
421
422                                 byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
423                                 reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);
424                                 int nread;
425                                 byte [] buffer = new byte [4096];
426                                 while ((nread = fStream.Read (buffer, 0, 4096)) != 0)
427                                         reqStream.Write (buffer, 0, nread);
428
429                                 reqStream.WriteByte ((byte) '\r');
430                                 reqStream.WriteByte ((byte) '\n');
431                                 reqStream.Write (realBoundary, 0, realBoundary.Length);
432                                 reqStream.Close ();
433                                 reqStream = null;
434                                 WebResponse response = request.GetResponse ();
435                                 Stream st = ProcessResponse (response);
436                                 resultBytes = ReadAll (st, (int) response.ContentLength);
437                         } catch (WebException) {
438                                 throw;
439                         } catch (Exception e) {
440                                 throw new WebException ("Error uploading file.", e);
441                         } finally {
442                                 if (fStream != null)
443                                         fStream.Close ();
444
445                                 if (reqStream != null)
446                                         reqStream.Close ();
447                         }
448                         
449                         return resultBytes;     
450                 }
451                 
452                 public byte[] UploadValues (string address, NameValueCollection data)
453                 {
454                         return UploadValues (MakeUri (address), data);
455                 }
456                 
457                 public byte[] UploadValues (string address, string method, NameValueCollection data)
458                 {
459                         return UploadValues (MakeUri (address), method, data);
460                 }
461
462 #if NET_2_0
463                 public
464 #endif
465                 byte[] UploadValues (Uri address, NameValueCollection data)
466                 {
467                         return UploadValues (address, DetermineMethod (address), data);
468                 }
469
470 #if NET_2_0
471                 public
472 #endif
473                 byte[] UploadValues (Uri uri, string method, NameValueCollection data)
474                 {
475                         try {
476                                 SetBusy ();
477                                 return UploadValuesCore (uri, method, data);
478                         } finally {
479                                 isBusy = false;
480                         }
481                 }
482
483                 byte[] UploadValuesCore (Uri uri, string method, NameValueCollection data)
484                 {
485                         if (data == null)
486                                 throw new ArgumentNullException ("data"); // MS throws a nullref
487
488                         string cType = Headers ["Content-Type"];
489                         if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
490                                 throw new WebException ("Content-Type header cannot be changed from its default " +
491                                                         "value for this request.");
492
493                         Headers ["Content-Type"] = urlEncodedCType;
494                         WebRequest request = SetupRequest (uri, method);
495                         Stream rqStream = request.GetRequestStream ();
496                         MemoryStream tmpStream = new MemoryStream ();
497                         foreach (string key in data) {
498                                 byte [] bytes = Encoding.ASCII.GetBytes (key);
499                                 UrlEncodeAndWrite (tmpStream, bytes);
500                                 tmpStream.WriteByte ((byte) '=');
501                                 bytes = Encoding.ASCII.GetBytes (data [key]);
502                                 UrlEncodeAndWrite (tmpStream, bytes);
503                                 tmpStream.WriteByte ((byte) '&');
504                         }
505
506                         int length = (int) tmpStream.Length;
507                         if (length > 0)
508                                 tmpStream.SetLength (--length); // remove trailing '&'
509
510                         tmpStream.WriteByte ((byte) '\r');
511                         tmpStream.WriteByte ((byte) '\n');
512
513                         byte [] buf = tmpStream.GetBuffer ();
514                         rqStream.Write (buf, 0, length + 2);
515                         rqStream.Close ();
516                         tmpStream.Close ();
517
518                         WebResponse response = request.GetResponse ();
519                         Stream st = ProcessResponse (response);
520                         return ReadAll (st, (int) response.ContentLength);
521                 }
522
523 #if NET_2_0
524                 public string DownloadString (string address)
525                 {
526                         return encoding.GetString (DownloadData (address));
527                 }
528
529                 public string DownloadString (Uri address)
530                 {
531                         return encoding.GetString (DownloadData (address));
532                 }
533
534                 public string UploadString (string address, string data)
535                 {
536                         byte [] resp = UploadData (address, encoding.GetBytes (data));
537                         return encoding.GetString (resp);
538                 }
539
540                 public string UploadString (string address, string method, string data)
541                 {
542                         byte [] resp = UploadData (address, method, encoding.GetBytes (data));
543                         return encoding.GetString (resp);
544                 }
545
546                 public string UploadString (Uri address, string data)
547                 {
548                         byte [] resp = UploadData (address, encoding.GetBytes (data));
549                         return encoding.GetString (resp);
550                 }
551
552                 public string UploadString (Uri address, string method, string data)
553                 {
554                         byte [] resp = UploadData (address, method, encoding.GetBytes (data));
555                         return encoding.GetString (resp);
556                 }
557
558                 public event DownloadDataCompletedEventHandler DownloadDataCompleted;
559                 public event AsyncCompletedEventHandler DownloadFileCompleted;
560                 public event DownloadProgressChangedEventHandler DownloadProgressChanged;
561                 public event DownloadStringCompletedEventHandler DownloadStringCompleted;
562                 public event OpenReadCompletedEventHandler OpenReadCompleted;
563                 public event OpenWriteCompletedEventHandler OpenWriteCompleted;
564                 public event UploadDataCompletedEventHandler UploadDataCompleted;
565                 public event UploadFileCompletedEventHandler UploadFileCompleted;
566                 public event UploadProgressChangedEventHandler UploadProgressChanged;
567                 public event UploadStringCompletedEventHandler UploadStringCompleted;
568                 public event UploadValuesCompletedEventHandler UploadValuesCompleted;
569 #endif
570
571                 Uri MakeUri (string path)
572                 {
573                         string query = null;
574                         if (queryString != null && queryString.Count != 0) {
575                                 // This is not the same as UploadValues, because these 'keys' are not
576                                 // urlencoded here.
577                                 StringBuilder sb = new StringBuilder ();
578                                 sb.Append ('?');
579                                 foreach (string key in queryString)
580                                         sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));
581
582                                 if (sb.Length != 0) {
583                                         sb.Length--; // remove trailing '&'
584                                         query = sb.ToString ();
585                                 }
586                         }
587                         
588
589                         if (baseAddress == null && query == null) {
590                                 try {
591                                         return new Uri (path);
592                                 }
593                                 catch (System.UriFormatException) {
594                                         if ((path[0] == Path.DirectorySeparatorChar) || (path[1] == ':' && Char.ToLower(path[0]) > 'a' && Char.ToLower(path[0]) < 'z')) {
595                                                 return new Uri ("file://" + path);
596                                         }
597                                         else {
598                                                 return new Uri ("file://" + Environment.CurrentDirectory + Path.DirectorySeparatorChar + path);
599                                         }
600                                 }
601                         }
602
603                         if (baseAddress == null)
604                                 return new Uri (path + query, (query != null));
605
606                         if (query == null)
607                                 return new Uri (baseAddress, path);
608
609                         return new Uri (baseAddress, path + query, (query != null));
610                 }
611                 
612                 WebRequest SetupRequest (Uri uri)
613                 {
614                         WebRequest request = WebRequest.Create (uri);
615 #if NET_2_0
616                         if (Proxy != null)
617                                 request.Proxy = Proxy;
618 #endif
619                         request.Credentials = credentials;
620
621                         // Special headers. These are properties of HttpWebRequest.
622                         // What do we do with other requests differnt from HttpWebRequest?
623                         if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {
624                                 HttpWebRequest req = (HttpWebRequest) request;
625                                 string expect = headers ["Expect"];
626                                 string contentType = headers ["Content-Type"];
627                                 string accept = headers ["Accept"];
628                                 string connection = headers ["Connection"];
629                                 string userAgent = headers ["User-Agent"];
630                                 string referer = headers ["Referer"];
631                                 headers.RemoveInternal ("Expect");
632                                 headers.RemoveInternal ("Content-Type");
633                                 headers.RemoveInternal ("Accept");
634                                 headers.RemoveInternal ("Connection");
635                                 headers.RemoveInternal ("Referer");
636                                 headers.RemoveInternal ("User-Agent");
637                                 request.Headers = headers;
638
639                                 if (expect != null && expect != "")
640                                         req.Expect = expect;
641
642                                 if (accept != null && accept != "")
643                                         req.Accept = accept;
644
645                                 if (contentType != null && contentType != "")
646                                         req.ContentType = contentType;
647
648                                 if (connection != null && connection != "")
649                                         req.Connection = connection;
650
651                                 if (userAgent != null && userAgent != "")
652                                         req.UserAgent = userAgent;
653
654                                 if (referer != null && referer != "")
655                                         req.Referer = referer;
656                         }
657
658                         responseHeaders = null;
659                         return request;
660                 }
661
662                 WebRequest SetupRequest (Uri uri, string method)
663                 {
664                         WebRequest request = SetupRequest (uri);
665                         request.Method = method;
666                         return request;
667                 }
668
669                 WebRequest SetupRequest (Uri uri, string method, int contentLength)
670                 {
671                         WebRequest request = SetupRequest (uri, method);
672                         request.ContentLength = contentLength;
673                         return request;
674                 }
675
676                 Stream ProcessResponse (WebResponse response)
677                 {
678                         responseHeaders = response.Headers;
679                         return response.GetResponseStream ();
680                 }
681
682                 static byte [] ReadAll (Stream stream, int length)
683                 {
684                         MemoryStream ms = null;
685                         
686                         bool nolength = (length == -1);
687                         int size = ((nolength) ? 8192 : length);
688                         if (nolength)
689                                 ms = new MemoryStream ();
690
691                         int nread = 0;
692                         int offset = 0;
693                         byte [] buffer = new byte [size];
694                         while ((nread = stream.Read (buffer, offset, size)) != 0) {
695                                 if (nolength) {
696                                         ms.Write (buffer, 0, nread);
697                                 } else {
698                                         offset += nread;
699                                         size -= nread;
700                                 }
701                         }
702
703                         if (nolength)
704                                 return ms.ToArray ();
705
706                         return buffer;
707                 }
708
709                 string UrlEncode (string str)
710                 {
711                         StringBuilder result = new StringBuilder ();
712
713                         int len = str.Length;
714                         for (int i = 0; i < len; i++) {
715                                 char c = str [i];
716                                 if (c == ' ')
717                                         result.Append ('+');
718                                 else if ((c < '0' && c != '-' && c != '.') ||
719                                          (c < 'A' && c > '9') ||
720                                          (c > 'Z' && c < 'a' && c != '_') ||
721                                          (c > 'z')) {
722                                         result.Append ('%');
723                                         int idx = ((int) c) >> 4;
724                                         result.Append ((char) hexBytes [idx]);
725                                         idx = ((int) c) & 0x0F;
726                                         result.Append ((char) hexBytes [idx]);
727                                 } else {
728                                         result.Append (c);
729                                 }
730                         }
731
732                         return result.ToString ();
733                 }
734
735                 static void UrlEncodeAndWrite (Stream stream, byte [] bytes)
736                 {
737                         if (bytes == null)
738                                 return;
739
740                         int len = bytes.Length;
741                         if (len == 0)
742                                 return;
743
744                         for (int i = 0; i < len; i++) {
745                                 char c = (char) bytes [i];
746                                 if (c == ' ')
747                                         stream.WriteByte ((byte) '+');
748                                 else if ((c < '0' && c != '-' && c != '.') ||
749                                          (c < 'A' && c > '9') ||
750                                          (c > 'Z' && c < 'a' && c != '_') ||
751                                          (c > 'z')) {
752                                         stream.WriteByte ((byte) '%');
753                                         int idx = ((int) c) >> 4;
754                                         stream.WriteByte (hexBytes [idx]);
755                                         idx = ((int) c) & 0x0F;
756                                         stream.WriteByte (hexBytes [idx]);
757                                 } else {
758                                         stream.WriteByte ((byte) c);
759                                 }
760                         }
761                 }
762
763 #if NET_2_0
764                 List<RegisteredWaitHandle> wait_handles;
765
766                 List<RegisteredWaitHandle> WaitHandles {
767                         get {
768                                 if (wait_handles == null)
769                                         wait_handles = new List<RegisteredWaitHandle> ();
770                                 return wait_handles;
771                         }
772                 }
773
774                 [MonoTODO ("Is it enough to just unregister wait handles from ThreadPool?")]
775                 public void CancelAsync ()
776                 {
777                         if (wait_handles == null)
778                                 return;
779                         lock (wait_handles) {
780                                 foreach (RegisteredWaitHandle handle in wait_handles)
781                                         handle.Unregister (null);
782                                 wait_handles.Clear ();
783                         }
784                 }
785
786                 //    DownloadDataAsync
787
788                 public void DownloadDataAsync (Uri uri)
789                 {
790                         DownloadDataAsync (uri, null);
791                 }
792
793                 public void DownloadDataAsync (Uri uri, object asyncState)
794                 {
795                         lock (this) {
796                                 CheckBusy ();
797
798                                 object [] cbArgs = new object [] {uri, asyncState};
799                                 WaitOrTimerCallback cb = delegate (object state, bool timedOut) {
800                                         object [] args = (object []) state;
801                                         byte [] data = timedOut ? null : DownloadData ((Uri) args [0]);
802                                         OnDownloadDataCompleted (
803                                                 new DownloadDataCompletedEventArgs (data, null, timedOut, args [1]));
804                                         };
805                                 AutoResetEvent ev = new AutoResetEvent (true);
806                                 WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
807                         }
808                 }
809
810                 //    DownloadFileAsync
811
812                 public void DownloadFileAsync (Uri uri, string method)
813                 {
814                         DownloadFileAsync (uri, method, null);
815                 }
816
817                 public void DownloadFileAsync (Uri uri, string method, object asyncState)
818                 {
819                         lock (this) {
820                                 CheckBusy ();
821
822                                 object [] cbArgs = new object [] {uri, method, asyncState};
823                                 WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
824                                         object [] args = (object []) innerState;
825                                         if (!timedOut)
826                                                 DownloadFile ((Uri) args [0], (string) args [1]);
827                                         OnDownloadFileCompleted (
828                                                 new AsyncCompletedEventArgs (null, timedOut, args [2]));
829                                         };
830                                 AutoResetEvent ev = new AutoResetEvent (true);
831                                 WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
832                         }
833                 }
834
835                 //    DownloadStringAsync
836
837                 public void DownloadStringAsync (Uri uri)
838                 {
839                         DownloadStringAsync (uri, null);
840                 }
841
842                 public void DownloadStringAsync (Uri uri, object asyncState)
843                 {
844                         lock (this) {
845                                 CheckBusy ();
846
847                                 object [] cbArgs = new object [] {uri, asyncState};
848                                 WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
849                                         object [] args = (object []) innerState;
850                                         string data = timedOut ? null : DownloadString ((Uri) args [0]);
851                                         OnDownloadStringCompleted (
852                                                 new DownloadStringCompletedEventArgs (data, null, timedOut, args [1]));
853                                         };
854                                 AutoResetEvent ev = new AutoResetEvent (true);
855                                 WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
856                         }
857                 }
858
859                 //    OpenReadAsync
860
861                 public void OpenReadAsync (Uri uri)
862                 {
863                         OpenReadAsync (uri, null);
864                 }
865
866                 public void OpenReadAsync (Uri uri, object asyncState)
867                 {
868                         lock (this) {
869                                 CheckBusy ();
870
871                                 object [] cbArgs = new object [] {uri, asyncState};
872                                 WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
873                                         object [] args = (object []) innerState;
874                                         Stream stream = timedOut ? null : OpenRead ((Uri) args [0]);
875                                         OnOpenReadCompleted (
876                                                 new OpenReadCompletedEventArgs (stream, null, timedOut, args [1]));
877                                         };
878                                 AutoResetEvent ev = new AutoResetEvent (true);
879                                 WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
880                         }
881                 }
882
883                 //    OpenWriteAsync
884
885                 public void OpenWriteAsync (Uri uri)
886                 {
887                         OpenWriteAsync (uri, null);
888                 }
889
890                 public void OpenWriteAsync (Uri uri, string method)
891                 {
892                         OpenWriteAsync (uri, method, null);
893                 }
894
895                 public void OpenWriteAsync (Uri uri, string method, object asyncState)
896                 {
897                         lock (this) {
898                                 CheckBusy ();
899
900                                 object [] cbArgs = new object [] {uri, method, asyncState};
901                                 WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
902                                         object [] args = (object []) innerState;
903                                         Stream stream = timedOut ? null : OpenWrite ((Uri) args [0], (string) args [1]);
904                                         OnOpenWriteCompleted (
905                                                 new OpenWriteCompletedEventArgs (stream, null, timedOut, args [2]));
906                                         };
907                                 AutoResetEvent ev = new AutoResetEvent (true);
908                                 WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
909                         }
910                 }
911
912                 //    UploadDataAsync
913
914                 public void UploadDataAsync (Uri uri, byte [] data)
915                 {
916                         UploadDataAsync (uri, null, data);
917                 }
918
919                 public void UploadDataAsync (Uri uri, string method, byte [] data)
920                 {
921                         UploadDataAsync (uri, method, data, null);
922                 }
923
924                 public void UploadDataAsync (Uri uri, string method, byte [] data, object asyncState)
925                 {
926                         lock (this) {
927                                 CheckBusy ();
928
929                                 object [] cbArgs = new object [] {uri, method, data,  asyncState};
930                                 WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
931                                         object [] args = (object []) innerState;
932                                         byte [] data = timedOut ? null : UploadData ((Uri) args [0], (string) args [1], (byte []) args [2]);
933                                         OnUploadDataCompleted (
934                                                 new UploadDataCompletedEventArgs (data, null, timedOut, args [3]));
935                                         };
936                                 AutoResetEvent ev = new AutoResetEvent (true);
937                                 WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
938                         }
939                 }
940
941                 //    UploadFileAsync
942
943                 public void UploadFileAsync (Uri uri, string file)
944                 {
945                         UploadFileAsync (uri, null, file);
946                 }
947
948                 public void UploadFileAsync (Uri uri, string method, string file)
949                 {
950                         UploadFileAsync (uri, method, file, null);
951                 }
952
953                 public void UploadFileAsync (Uri uri, string method, string file, object asyncState)
954                 {
955                         lock (this) {
956                                 CheckBusy ();
957
958                                 object [] cbArgs = new object [] {uri, method, file,  asyncState};
959                                 WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
960                                         object [] args = (object []) innerState;
961                                         byte [] data = timedOut ? null : UploadFile ((Uri) args [0], (string) args [1], (string) args [2]);
962                                         OnUploadFileCompleted (
963                                                 new UploadFileCompletedEventArgs (data, null, timedOut, args [3]));
964                                         };
965                                 AutoResetEvent ev = new AutoResetEvent (true);
966                                 WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
967                         }
968                 }
969
970                 //    UploadStringAsync
971
972                 public void UploadStringAsync (Uri uri, string data)
973                 {
974                         UploadStringAsync (uri, null, data);
975                 }
976
977                 public void UploadStringAsync (Uri uri, string method, string data)
978                 {
979                         UploadStringAsync (uri, method, data, null);
980                 }
981
982                 public void UploadStringAsync (Uri uri, string method, string data, object asyncState)
983                 {
984                         lock (this) {
985                                 CheckBusy ();
986
987                                 object [] cbArgs = new object [] {uri, method, data, asyncState};
988                                 WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
989                                         object [] args = (object []) innerState;
990                                         string data = timedOut ? null : UploadString ((Uri) args [0], (string) args [1], (string) args [2]);
991                                         OnUploadStringCompleted (
992                                                 new UploadStringCompletedEventArgs (data, null, timedOut, args [3]));
993                                         };
994                                 AutoResetEvent ev = new AutoResetEvent (true);
995                                 WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
996                         }
997                 }
998
999                 //    UploadValuesAsync
1000
1001                 public void UploadValuesAsync (Uri uri, NameValueCollection values)
1002                 {
1003                         UploadValuesAsync (uri, null, values);
1004                 }
1005
1006                 public void UploadValuesAsync (Uri uri, string method, NameValueCollection values)
1007                 {
1008                         UploadValuesAsync (uri, method, values, null);
1009                 }
1010
1011                 public void UploadValuesAsync (Uri uri, string method, NameValueCollection values, object asyncState)
1012                 {
1013                         lock (this) {
1014                                 CheckBusy ();
1015
1016                                 object [] cbArgs = new object [] {uri, method, values,  asyncState};
1017                                 WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
1018                                         object [] args = (object []) innerState;
1019                                         byte [] data = timedOut ? null : UploadValues ((Uri) args [0], (string) args [1], (NameValueCollection) args [2]);
1020                                         OnUploadValuesCompleted (
1021                                                 new UploadValuesCompletedEventArgs (data, null, timedOut, args [3]));
1022                                         };
1023                                 AutoResetEvent ev = new AutoResetEvent (true);
1024                                 WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
1025                         }
1026                 }
1027
1028                 protected virtual void OnDownloadDataCompleted (
1029                         DownloadDataCompletedEventArgs args)
1030                 {
1031                         if (DownloadDataCompleted != null)
1032                                 DownloadDataCompleted (this, args);
1033                 }
1034
1035                 protected virtual void OnDownloadFileCompleted (
1036                         AsyncCompletedEventArgs args)
1037                 {
1038                         if (DownloadFileCompleted != null)
1039                                 DownloadFileCompleted (this, args);
1040                 }
1041
1042                 protected virtual void OnDownloadStringCompleted (
1043                         DownloadStringCompletedEventArgs args)
1044                 {
1045                         if (DownloadStringCompleted != null)
1046                                 DownloadStringCompleted (this, args);
1047                 }
1048
1049                 protected virtual void OnOpenReadCompleted (
1050                         OpenReadCompletedEventArgs args)
1051                 {
1052                         if (OpenReadCompleted != null)
1053                                 OpenReadCompleted (this, args);
1054                 }
1055
1056                 protected virtual void OnOpenWriteCompleted (
1057                         OpenWriteCompletedEventArgs args)
1058                 {
1059                         if (OpenWriteCompleted != null)
1060                                 OpenWriteCompleted (this, args);
1061                 }
1062
1063                 protected virtual void OnUploadDataCompleted (
1064                         UploadDataCompletedEventArgs args)
1065                 {
1066                         if (UploadDataCompleted != null)
1067                                 UploadDataCompleted (this, args);
1068                 }
1069
1070                 protected virtual void OnUploadFileCompleted (
1071                         UploadFileCompletedEventArgs args)
1072                 {
1073                         if (UploadFileCompleted != null)
1074                                 UploadFileCompleted (this, args);
1075                 }
1076
1077                 protected virtual void OnUploadStringCompleted (
1078                         UploadStringCompletedEventArgs args)
1079                 {
1080                         if (UploadStringCompleted != null)
1081                                 UploadStringCompleted (this, args);
1082                 }
1083
1084                 protected virtual void OnUploadValuesCompleted (
1085                         UploadValuesCompletedEventArgs args)
1086                 {
1087                         if (UploadValuesCompleted != null)
1088                                 UploadValuesCompleted (this, args);
1089                 }
1090 #endif
1091         }
1092 }
1093