Add TimeZoneInfo Serialization
[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 //      Miguel de Icaza (miguel@ximian.com)
9 //      Martin Baulig (martin.baulig@googlemail.com)
10 //      Marek Safar (marek.safar@gmail.com)
11 //
12 // Copyright 2003 Ximian, Inc. (http://www.ximian.com)
13 // Copyright 2006, 2010 Novell, Inc. (http://www.novell.com)
14 // Copyright 2012 Xamarin Inc. (http://www.xamarin.com)
15 //
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 // 
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 // 
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36 //
37 // Notes on CancelAsync and Async methods:
38 //
39 //    WebClient.CancelAsync is implemented by calling Thread.Interrupt
40 //    in our helper thread.   The various async methods have to cancel
41 //    any ongoing requests by calling request.Abort () at that point.
42 //    In a few places (UploadDataCore, UploadValuesCore,
43 //    UploadFileCore) we catch the ThreadInterruptedException and
44 //    abort the request there.
45 //
46 //    Higher level routines (the async callbacks) also need to catch
47 //    the exception and raise the OnXXXXCompleted events there with
48 //    the "canceled" flag set to true. 
49 //
50 //    In a few other places where these helper routines are not used
51 //    (OpenReadAsync for example) catching the ThreadAbortException
52 //    also must abort the request.
53 //
54 //    The Async methods currently differ in their implementation from
55 //    the .NET implementation in that we manually catch any other
56 //    exceptions and correctly raise the OnXXXXCompleted passing the
57 //    Exception that caused the problem.   The .NET implementation
58 //    does not seem to have a mechanism to flag errors that happen
59 //    during downloads though.    We do this because we still need to
60 //    catch the exception on these helper threads, or we would
61 //    otherwise kill the application (on the 2.x profile, uncaught
62 //    exceptions in threads terminate the application).
63 //
64 using System;
65 using System.Collections.Specialized;
66 using System.ComponentModel;
67 using System.IO;
68 using System.Runtime.InteropServices;
69 using System.Runtime.Serialization;
70 using System.Text;
71 using System.Threading;
72 using System.Net.Cache;
73 #if NET_4_5
74 using System.Threading.Tasks;
75 #endif
76
77 namespace System.Net 
78 {
79         [ComVisible(true)]
80         public class WebClient : Component
81         {
82                 static readonly string urlEncodedCType = "application/x-www-form-urlencoded";
83                 static byte [] hexBytes;
84                 ICredentials credentials;
85                 WebHeaderCollection headers;
86                 WebHeaderCollection responseHeaders;
87                 Uri baseAddress;
88                 string baseString;
89                 NameValueCollection queryString;
90                 bool is_busy;
91                 bool async;
92                 bool proxySet = false;
93                 Thread async_thread;
94                 Encoding encoding = Encoding.Default;
95                 IWebProxy proxy;
96 //              RequestCachePolicy cache_policy;
97 #if NET_4_5
98                 CancellationTokenSource cts;
99 #endif
100
101                 // Constructors
102                 static WebClient ()
103                 {
104                         hexBytes = new byte [16];
105                         int index = 0;
106                         for (int i = '0'; i <= '9'; i++, index++)
107                                 hexBytes [index] = (byte) i;
108
109                         for (int i = 'a'; i <= 'f'; i++, index++)
110                                 hexBytes [index] = (byte) i;
111                 }
112                 
113                 public WebClient ()
114                 {
115                 }
116                 
117                 // Properties
118                 
119                 public string BaseAddress {
120                         get {
121                                 if (baseString == null) {
122                                         if (baseAddress == null)
123                                                 return string.Empty;
124                                 }
125
126                                 baseString = baseAddress.ToString ();
127                                 return baseString;
128                         }
129                         
130                         set {
131                                 if (value == null || value.Length == 0) {
132                                         baseAddress = null;
133                                 } else {
134                                         baseAddress = new Uri (value);
135                                 }
136                         }
137                 }
138
139                 static Exception GetMustImplement ()
140                 {
141                         return new NotImplementedException ();
142                 }
143                 
144                 [MonoTODO ("Value can be set but is currently ignored")]
145                 public RequestCachePolicy CachePolicy
146                 {
147                         get {
148                                 throw GetMustImplement ();
149                         }
150                         set { /*cache_policy = value;*/ }
151                 }
152
153                 [MonoTODO ("Value can be set but is ignored")]
154                 public bool UseDefaultCredentials
155                 {
156                         get {
157                                 throw GetMustImplement ();
158                         }
159                         set {
160                                 // This makes no sense in mono
161                         }
162                 }
163                 
164                 public ICredentials Credentials {
165                         get { return credentials; }
166                         set { credentials = value; }
167                 }
168
169                 public WebHeaderCollection Headers {
170                         get {
171                                 if (headers == null)
172                                         headers = new WebHeaderCollection ();
173
174                                 return headers;
175                         }
176                         set { headers = value; }
177                 }
178                 
179                 public NameValueCollection QueryString {
180                         get {
181                                 if (queryString == null)
182                                         queryString = new NameValueCollection ();
183
184                                 return queryString;
185                         }
186                         set { queryString = value; }
187                 }
188                 
189                 public WebHeaderCollection ResponseHeaders {
190                         get { return responseHeaders; }
191                 }
192
193                 public Encoding Encoding {
194                         get { return encoding; }
195                         set {
196                                 if (value == null)
197                                         throw new ArgumentNullException ("Encoding");
198                                 encoding = value;
199                         }
200                 }
201
202                 public IWebProxy Proxy {
203                         get {
204                                 if (!proxySet)
205                                         return WebRequest.DefaultWebProxy;
206
207                                 return proxy;
208                         }
209                         set {
210                                 proxy = value;
211                                 proxySet = true;
212                         }
213                 }
214
215                 public bool IsBusy {
216                         get {
217 #if NET_4_5
218                                 return is_busy || (cts != null);
219 #else
220                                 return is_busy;
221 #endif
222                         }
223                 }
224                 // Methods
225
226                 void CheckBusy ()
227                 {
228                         if (IsBusy)
229                                 throw new NotSupportedException ("WebClient does not support concurrent I/O operations.");
230                 }
231
232                 void SetBusy ()
233                 {
234                         lock (this) {
235                                 CheckBusy ();
236                                 is_busy = true;
237                         }
238                 }
239
240                 //   DownloadData
241
242                 public byte [] DownloadData (string address)
243                 {
244                         if (address == null)
245                                 throw new ArgumentNullException ("address");
246
247                         return DownloadData (CreateUri (address));
248                 }
249
250                 public byte [] DownloadData (Uri address)
251                 {
252                         if (address == null)
253                                 throw new ArgumentNullException ("address");
254
255                         try {
256                                 SetBusy ();
257                                 async = false;
258                                 return DownloadDataCore (address, null);
259                         } finally {
260                                 is_busy = false;
261                         }
262                 }
263
264                 byte [] DownloadDataCore (Uri address, object userToken)
265                 {
266                         WebRequest request = null;
267                         
268                         try {
269                                 request = SetupRequest (address);
270                                 return ReadAll (request, userToken);
271                         } catch (ThreadInterruptedException){
272                                 if (request != null)
273                                         request.Abort ();
274                                 throw new WebException ("User canceled the request", WebExceptionStatus.RequestCanceled);
275                         } catch (WebException) {
276                                 throw;
277                         } catch (Exception ex) {
278                                 throw new WebException ("An error occurred performing a WebClient request.", ex);
279                         }
280                 }
281
282                 //   DownloadFile
283
284                 public void DownloadFile (string address, string fileName)
285                 {
286                         if (address == null)
287                                 throw new ArgumentNullException ("address");
288
289                         DownloadFile (CreateUri (address), fileName);
290                 }
291
292                 public void DownloadFile (Uri address, string fileName)
293                 {
294                         if (address == null)
295                                 throw new ArgumentNullException ("address");
296                         if (fileName == null)
297                                 throw new ArgumentNullException ("fileName");
298
299                         try {
300                                 SetBusy ();
301                                 async = false;
302                                 DownloadFileCore (address, fileName, null);
303                         } catch (WebException) {
304                                 throw;
305                         } catch (Exception ex) {
306                                 throw new WebException ("An error occurred " +
307                                         "performing a WebClient request.", ex);
308                         } finally {
309                                 is_busy = false;
310                         }
311                 }
312
313                 void DownloadFileCore (Uri address, string fileName, object userToken)
314                 {
315                         WebRequest request = null;
316                         
317                         using (FileStream f = new FileStream (fileName, FileMode.Create)) {
318                                 try {
319                                         request = SetupRequest (address);
320                                         WebResponse response = GetWebResponse (request);
321                                         Stream st = response.GetResponseStream ();
322                                         
323                                         int cLength = (int) response.ContentLength;
324                                         int length = (cLength <= -1 || cLength > 32*1024) ? 32*1024 : cLength;
325                                         byte [] buffer = new byte [length];
326                                         
327                                         int nread = 0;
328                                         long notify_total = 0;
329                                         while ((nread = st.Read (buffer, 0, length)) != 0){
330                                                 if (async){
331                                                         notify_total += nread;
332                                                         OnDownloadProgressChanged (
333                                                                 new DownloadProgressChangedEventArgs (notify_total, response.ContentLength, userToken));
334                                                                                                       
335                                                 }
336                                                 f.Write (buffer, 0, nread);
337                                         }
338                                 } catch (ThreadInterruptedException){
339                                         if (request != null)
340                                                 request.Abort ();
341                                         throw;
342                                 }
343                         }
344                 }
345
346                 //   OpenRead
347
348                 public Stream OpenRead (string address)
349                 {
350                         if (address == null)
351                                 throw new ArgumentNullException ("address");
352                         return OpenRead (CreateUri (address));
353                 }
354
355                 public Stream OpenRead (Uri address)
356                 {
357                         if (address == null)
358                                 throw new ArgumentNullException ("address");
359
360                         WebRequest request = null;
361                         try {
362                                 SetBusy ();
363                                 async = false;
364                                 request = SetupRequest (address);
365                                 WebResponse response = GetWebResponse (request);
366                                 return response.GetResponseStream ();
367                         } catch (WebException) {
368                                 throw;
369                         } catch (Exception ex) {
370                                 throw new WebException ("An error occurred " +
371                                         "performing a WebClient request.", ex);
372                         } finally {
373                                 is_busy = false;
374                         }
375                 }
376
377                 //   OpenWrite
378
379                 public Stream OpenWrite (string address)
380                 {
381                         if (address == null)
382                                 throw new ArgumentNullException ("address");
383
384                         return OpenWrite (CreateUri (address));
385                 }
386                 
387                 public Stream OpenWrite (string address, string method)
388                 {
389                         if (address == null)
390                                 throw new ArgumentNullException ("address");
391
392                         return OpenWrite (CreateUri (address), method);
393                 }
394
395                 public Stream OpenWrite (Uri address)
396                 {
397                         return OpenWrite (address, (string) null);
398                 }
399
400                 public Stream OpenWrite (Uri address, string method)
401                 {
402                         if (address == null)
403                                 throw new ArgumentNullException ("address");
404
405                         try {
406                                 SetBusy ();
407                                 async = false;
408                                 WebRequest request = SetupRequest (address, method, true);
409                                 return request.GetRequestStream ();
410                         } catch (WebException) {
411                                 throw;
412                         } catch (Exception ex) {
413                                 throw new WebException ("An error occurred " +
414                                         "performing a WebClient request.", ex);
415                         } finally {
416                                 is_busy = false;
417                         }
418                 }
419
420                 private string DetermineMethod (Uri address, string method, bool is_upload)
421                 {
422                         if (method != null)
423                                 return method;
424
425                         if (address.Scheme == Uri.UriSchemeFtp)
426                                 return (is_upload) ? "STOR" : "RETR";
427
428                         return (is_upload) ? "POST" : "GET";
429                 }
430
431                 //   UploadData
432
433                 public byte [] UploadData (string address, byte [] data)
434                 {
435                         if (address == null)
436                                 throw new ArgumentNullException ("address");
437
438                         return UploadData (CreateUri (address), data);
439                 }
440                 
441                 public byte [] UploadData (string address, string method, byte [] data)
442                 {
443                         if (address == null)
444                                 throw new ArgumentNullException ("address");
445
446                         return UploadData (CreateUri (address), method, data);
447                 }
448
449                 public byte [] UploadData (Uri address, byte [] data)
450                 {
451                         return UploadData (address, (string) null, data);
452                 }
453
454                 public byte [] UploadData (Uri address, string method, byte [] data)
455                 {
456                         if (address == null)
457                                 throw new ArgumentNullException ("address");
458                         if (data == null)
459                                 throw new ArgumentNullException ("data");
460
461                         try {
462                                 SetBusy ();
463                                 async = false;
464                                 return UploadDataCore (address, method, data, null);
465                         } catch (WebException) {
466                                 throw;
467                         } catch (Exception ex) {
468                                 throw new WebException ("An error occurred " +
469                                         "performing a WebClient request.", ex);
470                         } finally {
471                                 is_busy = false;
472                         }
473                 }
474
475                 byte [] UploadDataCore (Uri address, string method, byte [] data, object userToken)
476                 {
477                         WebRequest request = SetupRequest (address, method, true);
478                         try {
479                                 int contentLength = data.Length;
480                                 request.ContentLength = contentLength;
481                                 using (Stream stream = request.GetRequestStream ()) {
482                                         stream.Write (data, 0, contentLength);
483                                 }
484                                 
485                                 return ReadAll (request, userToken);
486                         } catch (ThreadInterruptedException){
487                                 if (request != null)
488                                         request.Abort ();
489                                 throw;
490                         }
491                 }
492
493                 //   UploadFile
494
495                 public byte [] UploadFile (string address, string fileName)
496                 {
497                         if (address == null)
498                                 throw new ArgumentNullException ("address");
499
500                         return UploadFile (CreateUri (address), fileName);
501                 }
502
503                 public byte [] UploadFile (Uri address, string fileName)
504                 {
505                         return UploadFile (address, (string) null, fileName);
506                 }
507                 
508                 public byte [] UploadFile (string address, string method, string fileName)
509                 {
510                         return UploadFile (CreateUri (address), method, fileName);
511                 }
512
513                 public byte [] UploadFile (Uri address, string method, string fileName)
514                 {
515                         if (address == null)
516                                 throw new ArgumentNullException ("address");
517                         if (fileName == null)
518                                 throw new ArgumentNullException ("fileName");
519
520                         try {
521                                 SetBusy ();
522                                 async = false;
523                                 return UploadFileCore (address, method, fileName, null);
524                         } catch (WebException) {
525                                 throw;
526                         } catch (Exception ex) {
527                                 throw new WebException ("An error occurred " +
528                                         "performing a WebClient request.", ex);
529                         } finally {
530                                 is_busy = false;
531                         }
532                 }
533
534                 byte [] UploadFileCore (Uri address, string method, string fileName, object userToken)
535                 {
536                         string fileCType = Headers ["Content-Type"];
537                         if (fileCType != null) {
538                                 string lower = fileCType.ToLower ();
539                                 if (lower.StartsWith ("multipart/"))
540                                         throw new WebException ("Content-Type cannot be set to a multipart" +
541                                                                 " type for this request.");
542                         } else {
543                                 fileCType = "application/octet-stream";
544                         }
545
546                         bool needs_boundary = (method != "PUT"); // only verified case so far
547                         string boundary = null;
548                         if (needs_boundary) {
549                                 boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
550                                 Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
551                         }
552                         Stream reqStream = null;
553                         Stream fStream = null;
554                         byte [] resultBytes = null;
555
556                         fileName = Path.GetFullPath (fileName);
557
558                         WebRequest request = null;
559                         try {
560                                 fStream = File.OpenRead (fileName);
561                                 request = SetupRequest (address, method, true);
562                                 reqStream = request.GetRequestStream ();
563                                 byte [] bytes_boundary = null;
564                                 if (needs_boundary) {
565                                         bytes_boundary = Encoding.ASCII.GetBytes (boundary);
566                                         reqStream.WriteByte ((byte) '-');
567                                         reqStream.WriteByte ((byte) '-');
568                                         reqStream.Write (bytes_boundary, 0, bytes_boundary.Length);
569                                         reqStream.WriteByte ((byte) '\r');
570                                         reqStream.WriteByte ((byte) '\n');
571                                         string partHeaders = String.Format ("Content-Disposition: form-data; " +
572                                                                             "name=\"file\"; filename=\"{0}\"\r\n" +
573                                                                             "Content-Type: {1}\r\n\r\n",
574                                                                             Path.GetFileName (fileName), fileCType);
575
576                                         byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
577                                         reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);
578                                 }
579                                 int nread;
580                                 long bytes_sent = 0;
581                                 long file_size = -1;
582                                 long step = 16384; // every 16kB
583                                 if (fStream.CanSeek) {
584                                         file_size = fStream.Length;
585                                         step = file_size / 100;
586                                 }
587                                 var upload_args = new UploadProgressChangedEventArgs (0, 0, bytes_sent, file_size, 0, userToken);
588                                 OnUploadProgressChanged (upload_args);
589                                 byte [] buffer = new byte [4096];
590                                 long sum = 0;
591                                 while ((nread = fStream.Read (buffer, 0, 4096)) > 0) {
592                                         reqStream.Write (buffer, 0, nread);
593                                         bytes_sent += nread;
594                                         sum += nread;
595                                         if (sum >= step || nread < 4096) {
596                                                 int percent = 0;
597                                                 if (file_size > 0)
598                                                         percent = (int) (bytes_sent * 100 / file_size);
599                                                 upload_args = new UploadProgressChangedEventArgs (0, 0, bytes_sent, file_size, percent, userToken);
600                                                 OnUploadProgressChanged (upload_args);
601                                                 sum = 0;
602                                         }
603                                 }
604
605                                 if (needs_boundary) {
606                                         reqStream.WriteByte ((byte) '\r');
607                                         reqStream.WriteByte ((byte) '\n');
608                                         reqStream.WriteByte ((byte) '-');
609                                         reqStream.WriteByte ((byte) '-');
610                                         reqStream.Write (bytes_boundary, 0, bytes_boundary.Length);
611                                         reqStream.WriteByte ((byte) '-');
612                                         reqStream.WriteByte ((byte) '-');
613                                         reqStream.WriteByte ((byte) '\r');
614                                         reqStream.WriteByte ((byte) '\n');
615                                 }
616                                 reqStream.Close ();
617                                 reqStream = null;
618                                 resultBytes = ReadAll (request, userToken);
619                         } catch (ThreadInterruptedException){
620                                 if (request != null)
621                                         request.Abort ();
622                                 throw;
623                         } finally {
624                                 if (fStream != null)
625                                         fStream.Close ();
626
627                                 if (reqStream != null)
628                                         reqStream.Close ();
629                         }
630                         
631                         return resultBytes;
632                 }
633                 
634                 public byte[] UploadValues (string address, NameValueCollection data)
635                 {
636                         if (address == null)
637                                 throw new ArgumentNullException ("address");
638
639                         return UploadValues (CreateUri (address), data);
640                 }
641                 
642                 public byte[] UploadValues (string address, string method, NameValueCollection data)
643                 {
644                         if (address == null)
645                                 throw new ArgumentNullException ("address");
646                         return UploadValues (CreateUri (address), method, data);
647                 }
648
649                 public byte[] UploadValues (Uri address, NameValueCollection data)
650                 {
651                         return UploadValues (address, (string) null, data);
652                 }
653
654                 public byte[] UploadValues (Uri address, string method, NameValueCollection data)
655                 {
656                         if (address == null)
657                                 throw new ArgumentNullException ("address");
658                         if (data == null)
659                                 throw new ArgumentNullException ("data");
660
661                         try {
662                                 SetBusy ();
663                                 async = false;
664                                 return UploadValuesCore (address, method, data, null);
665                         } catch (WebException) {
666                                 throw;
667                         } catch (Exception ex) {
668                                 throw new WebException ("An error occurred " +
669                                         "performing a WebClient request.", ex);
670                         } finally {
671                                 is_busy = false;
672                         }
673                 }
674
675                 byte[] UploadValuesCore (Uri uri, string method, NameValueCollection data, object userToken)
676                 {
677                         string cType = Headers ["Content-Type"];
678                         if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
679                                 throw new WebException ("Content-Type header cannot be changed from its default " +
680                                                         "value for this request.");
681
682                         Headers ["Content-Type"] = urlEncodedCType;
683                         WebRequest request = SetupRequest (uri, method, true);
684                         try {
685                                 MemoryStream tmpStream = new MemoryStream ();
686                                 foreach (string key in data) {
687                                         byte [] bytes = Encoding.UTF8.GetBytes (key);
688                                         UrlEncodeAndWrite (tmpStream, bytes);
689                                         tmpStream.WriteByte ((byte) '=');
690                                         bytes = Encoding.UTF8.GetBytes (data [key]);
691                                         UrlEncodeAndWrite (tmpStream, bytes);
692                                         tmpStream.WriteByte ((byte) '&');
693                                 }
694                                 
695                                 int length = (int) tmpStream.Length;
696                                 if (length > 0)
697                                         tmpStream.SetLength (--length); // remove trailing '&'
698                                 
699                                 byte [] buf = tmpStream.GetBuffer ();
700                                 request.ContentLength = length;
701                                 using (Stream rqStream = request.GetRequestStream ()) {
702                                         rqStream.Write (buf, 0, length);
703                                 }
704                                 tmpStream.Close ();
705                                 
706                                 return ReadAll (request, userToken);
707                         } catch (ThreadInterruptedException) {
708                                 request.Abort ();
709                                 throw;
710                         }
711                 }
712
713                 public string DownloadString (string address)
714                 {
715                         if (address == null)
716                                 throw new ArgumentNullException ("address");
717
718                         return encoding.GetString (DownloadData (CreateUri (address)));
719                 }
720
721                 public string DownloadString (Uri address)
722                 {
723                         if (address == null)
724                                 throw new ArgumentNullException ("address");
725
726                         return encoding.GetString (DownloadData (CreateUri (address)));
727                 }
728
729                 public string UploadString (string address, string data)
730                 {
731                         if (address == null)
732                                 throw new ArgumentNullException ("address");
733                         if (data == null)
734                                 throw new ArgumentNullException ("data");
735
736                         byte [] resp = UploadData (address, encoding.GetBytes (data));
737                         return encoding.GetString (resp);
738                 }
739
740                 public string UploadString (string address, string method, string data)
741                 {
742                         if (address == null)
743                                 throw new ArgumentNullException ("address");
744                         if (data == null)
745                                 throw new ArgumentNullException ("data");
746
747                         byte [] resp = UploadData (address, method, encoding.GetBytes (data));
748                         return encoding.GetString (resp);
749                 }
750
751                 public string UploadString (Uri address, string data)
752                 {
753                         if (address == null)
754                                 throw new ArgumentNullException ("address");
755                         if (data == null)
756                                 throw new ArgumentNullException ("data");
757
758                         byte [] resp = UploadData (address, encoding.GetBytes (data));
759                         return encoding.GetString (resp);
760                 }
761
762                 public string UploadString (Uri address, string method, string data)
763                 {
764                         if (address == null)
765                                 throw new ArgumentNullException ("address");
766                         if (data == null)
767                                 throw new ArgumentNullException ("data");
768
769                         byte [] resp = UploadData (address, method, encoding.GetBytes (data));
770                         return encoding.GetString (resp);
771                 }
772
773                 public event DownloadDataCompletedEventHandler DownloadDataCompleted;
774                 public event AsyncCompletedEventHandler DownloadFileCompleted;
775                 public event DownloadProgressChangedEventHandler DownloadProgressChanged;
776                 public event DownloadStringCompletedEventHandler DownloadStringCompleted;
777                 public event OpenReadCompletedEventHandler OpenReadCompleted;
778                 public event OpenWriteCompletedEventHandler OpenWriteCompleted;
779                 public event UploadDataCompletedEventHandler UploadDataCompleted;
780                 public event UploadFileCompletedEventHandler UploadFileCompleted;
781                 public event UploadProgressChangedEventHandler UploadProgressChanged;
782                 public event UploadStringCompletedEventHandler UploadStringCompleted;
783                 public event UploadValuesCompletedEventHandler UploadValuesCompleted;
784
785                 Uri CreateUri (string address)
786                 {
787                         Uri uri;
788                         try {
789                                 if (baseAddress == null)
790                                         uri = new Uri (address);
791                                 else
792                                         uri = new Uri (baseAddress, address);
793                                 return CreateUri (uri);
794                         } catch {
795                         }
796                         return new Uri (Path.GetFullPath (address));
797                 }
798
799                 Uri CreateUri (Uri address)
800                 {
801                         Uri result = address;
802                         if (baseAddress != null && !result.IsAbsoluteUri) {
803                                 try {
804                                         result = new Uri (baseAddress, result.OriginalString);
805                                 } catch {
806                                         return result; // Not much we can do here.
807                                 }
808                         }
809
810                         string query = result.Query;
811                         if (String.IsNullOrEmpty (query))
812                                 query = GetQueryString (true);
813                         UriBuilder builder = new UriBuilder (address);
814                         if (!String.IsNullOrEmpty (query))
815                                 builder.Query = query.Substring (1);
816                         return builder.Uri;
817                 }
818
819                 string GetQueryString (bool add_qmark)
820                 {
821                         if (queryString == null || queryString.Count == 0)
822                                 return null;
823
824                         StringBuilder sb = new StringBuilder ();
825                         if (add_qmark)
826                                 sb.Append ('?');
827
828                         foreach (string key in queryString)
829                                 sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));
830
831                         if (sb.Length != 0)
832                                 sb.Length--; // removes last '&' or the '?' if empty.
833
834                         if (sb.Length == 0)
835                                 return null;
836
837                         return sb.ToString ();
838                 }
839
840                 WebRequest SetupRequest (Uri uri)
841                 {
842                         WebRequest request = GetWebRequest (uri);
843                         if (proxySet)
844                                 request.Proxy = Proxy;
845                         if (credentials != null)
846                                 request.Credentials = credentials;
847                         else if (!String.IsNullOrEmpty (uri.UserInfo)) {
848                                 // Perhaps this should be done by the underlying URI handler?
849                                 ICredentials creds = GetCredentials (uri.UserInfo);
850                                 if (creds != null)
851                                         request.Credentials = creds;
852                         }
853
854                         // Special headers. These are properties of HttpWebRequest.
855                         // What do we do with other requests differnt from HttpWebRequest?
856                         if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {
857                                 HttpWebRequest req = (HttpWebRequest) request;
858                                 string expect = headers ["Expect"];
859                                 string contentType = headers ["Content-Type"];
860                                 string accept = headers ["Accept"];
861                                 string connection = headers ["Connection"];
862                                 string userAgent = headers ["User-Agent"];
863                                 string referer = headers ["Referer"];
864                                 headers.RemoveInternal ("Expect");
865                                 headers.RemoveInternal ("Content-Type");
866                                 headers.RemoveInternal ("Accept");
867                                 headers.RemoveInternal ("Connection");
868                                 headers.RemoveInternal ("Referer");
869                                 headers.RemoveInternal ("User-Agent");
870                                 request.Headers = headers;
871
872                                 if (expect != null && expect.Length > 0)
873                                         req.Expect = expect;
874
875                                 if (accept != null && accept.Length > 0)
876                                         req.Accept = accept;
877
878                                 if (contentType != null && contentType.Length > 0)
879                                         req.ContentType = contentType;
880
881                                 if (connection != null && connection.Length > 0)
882                                         req.Connection = connection;
883
884                                 if (userAgent != null && userAgent.Length > 0)
885                                         req.UserAgent = userAgent;
886
887                                 if (referer != null && referer.Length > 0)
888                                         req.Referer = referer;
889                         }
890
891                         responseHeaders = null;
892                         return request;
893                 }
894
895                 WebRequest SetupRequest (Uri uri, string method, bool is_upload)
896                 {
897                         WebRequest request = SetupRequest (uri);
898                         request.Method = DetermineMethod (uri, method, is_upload);
899                         return request;
900                 }
901
902                 static NetworkCredential GetCredentials (string user_info)
903                 {
904                         string [] creds = user_info.Split (':');
905                         if (creds.Length != 2)
906                                 return null;
907
908                         if (creds [0].IndexOf ('\\') != -1) {
909                                 string [] user = creds [0].Split ('\\');
910                                 if (user.Length != 2)
911                                         return null;
912                                 return new NetworkCredential (user [1], creds [1], user [0]);
913                         }
914                         return new NetworkCredential (creds [0], creds [1]);
915                 }
916
917                 byte [] ReadAll (WebRequest request, object userToken)
918                 {
919                         WebResponse response = GetWebResponse (request);
920                         Stream stream = response.GetResponseStream ();
921                         int length = (int) response.ContentLength;
922                         HttpWebRequest wreq = request as HttpWebRequest;
923
924                         if (length > -1 && wreq != null && (int) wreq.AutomaticDecompression != 0) {
925                                 string content_encoding = ((HttpWebResponse) response).ContentEncoding;
926                                 if (((content_encoding == "gzip" && (wreq.AutomaticDecompression & DecompressionMethods.GZip) != 0)) ||
927                                         ((content_encoding == "deflate" && (wreq.AutomaticDecompression & DecompressionMethods.Deflate) != 0)))
928                                         length = -1;
929                         }
930
931                         MemoryStream ms = null;
932                         bool nolength = (length == -1);
933                         int size = ((nolength) ? 8192 : length);
934                         if (nolength)
935                                 ms = new MemoryStream ();
936
937                         long total = 0;
938                         int nread = 0;
939                         int offset = 0;
940                         byte [] buffer = new byte [size];
941                         while ((nread = stream.Read (buffer, offset, size)) != 0) {
942                                 if (nolength) {
943                                         ms.Write (buffer, 0, nread);
944                                 } else {
945                                         offset += nread;
946                                         size -= nread;
947                                 }
948                                 if (async){
949                                         total += nread;
950                                         OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (total, length, userToken));
951                                 }
952                         }
953
954                         if (nolength)
955                                 return ms.ToArray ();
956
957                         return buffer;
958                 }
959
960                 string UrlEncode (string str)
961                 {
962                         StringBuilder result = new StringBuilder ();
963
964                         int len = str.Length;
965                         for (int i = 0; i < len; i++) {
966                                 char c = str [i];
967                                 if (c == ' ')
968                                         result.Append ('+');
969                                 else if ((c < '0' && c != '-' && c != '.') ||
970                                          (c < 'A' && c > '9') ||
971                                          (c > 'Z' && c < 'a' && c != '_') ||
972                                          (c > 'z')) {
973                                         result.Append ('%');
974                                         int idx = ((int) c) >> 4;
975                                         result.Append ((char) hexBytes [idx]);
976                                         idx = ((int) c) & 0x0F;
977                                         result.Append ((char) hexBytes [idx]);
978                                 } else {
979                                         result.Append (c);
980                                 }
981                         }
982
983                         return result.ToString ();
984                 }
985
986                 static void UrlEncodeAndWrite (Stream stream, byte [] bytes)
987                 {
988                         if (bytes == null)
989                                 return;
990
991                         int len = bytes.Length;
992                         if (len == 0)
993                                 return;
994
995                         for (int i = 0; i < len; i++) {
996                                 char c = (char) bytes [i];
997                                 if (c == ' ')
998                                         stream.WriteByte ((byte) '+');
999                                 else if ((c < '0' && c != '-' && c != '.') ||
1000                                          (c < 'A' && c > '9') ||
1001                                          (c > 'Z' && c < 'a' && c != '_') ||
1002                                          (c > 'z')) {
1003                                         stream.WriteByte ((byte) '%');
1004                                         int idx = ((int) c) >> 4;
1005                                         stream.WriteByte (hexBytes [idx]);
1006                                         idx = ((int) c) & 0x0F;
1007                                         stream.WriteByte (hexBytes [idx]);
1008                                 } else {
1009                                         stream.WriteByte ((byte) c);
1010                                 }
1011                         }
1012                 }
1013
1014                 public void CancelAsync ()
1015                 {
1016                         lock (this){
1017 #if NET_4_5
1018                                 if (cts != null) {
1019                                         cts.Cancel ();
1020                                         return;
1021                                 }
1022 #endif
1023
1024                                 if (async_thread == null)
1025                                         return;
1026
1027                                 //
1028                                 // We first flag things as done, in case the Interrupt hangs
1029                                 // or the thread decides to hang in some other way inside the
1030                                 // event handlers, or if we are stuck somewhere else.  This
1031                                 // ensures that the WebClient object is reusable immediately
1032                                 //
1033                                 Thread t = async_thread;
1034                                 CompleteAsync ();
1035                                 t.Interrupt ();
1036                         }
1037                 }
1038
1039                 void CompleteAsync ()
1040                 {
1041                         lock (this) {
1042                                 is_busy = false;
1043                                 async_thread = null;
1044 #if NET_4_5
1045                                 if (cts != null)
1046                                         cts.Dispose ();
1047                                 cts = null;
1048 #endif
1049                         }
1050                 }
1051
1052                 //    DownloadDataAsync
1053
1054                 public void DownloadDataAsync (Uri address)
1055                 {
1056                         DownloadDataAsync (address, null);
1057                 }
1058
1059                 public void DownloadDataAsync (Uri address, object userToken)
1060                 {
1061                         if (address == null)
1062                                 throw new ArgumentNullException ("address");
1063                         
1064                         lock (this) {
1065                                 SetBusy ();
1066                                 async = true;
1067                                 
1068                                 async_thread = new Thread (delegate (object state) {
1069                                         object [] args = (object []) state;
1070                                         try {
1071                                                 byte [] data = DownloadDataCore ((Uri) args [0], args [1]);
1072                                                 OnDownloadDataCompleted (
1073                                                         new DownloadDataCompletedEventArgs (data, null, false, args [1]));
1074                                         } catch (Exception e){
1075                                                 bool canceled = false;
1076                                                 WebException we = e as WebException;
1077                                                 if (we != null)
1078                                                         canceled = we.Status == WebExceptionStatus.RequestCanceled;
1079                                                 OnDownloadDataCompleted (
1080                                                         new DownloadDataCompletedEventArgs (null, e, canceled, args [1]));
1081                                         }
1082                                 });
1083                                 object [] cb_args = new object [] { CreateUri (address), userToken };
1084                                 async_thread.IsBackground = true;
1085                                 async_thread.Start (cb_args);
1086                         }
1087                 }
1088
1089                 //    DownloadFileAsync
1090
1091                 public void DownloadFileAsync (Uri address, string fileName)
1092                 {
1093                         DownloadFileAsync (address, fileName, null);
1094                 }
1095
1096                 public void DownloadFileAsync (Uri address, string fileName, object userToken)
1097                 {
1098                         if (address == null)
1099                                 throw new ArgumentNullException ("address");
1100                         if (fileName == null)
1101                                 throw new ArgumentNullException ("fileName");
1102                         
1103                         lock (this) {
1104                                 SetBusy ();
1105                                 async = true;
1106
1107                                 async_thread = new Thread (delegate (object state) {
1108                                         object [] args = (object []) state;
1109                                         try {
1110                                                 DownloadFileCore ((Uri) args [0], (string) args [1], args [2]);
1111                                                 OnDownloadFileCompleted (
1112                                                         new AsyncCompletedEventArgs (null, false, args [2]));
1113                                         } catch (ThreadInterruptedException){
1114                                                 OnDownloadFileCompleted (
1115                                                         new AsyncCompletedEventArgs (null, true, args [2]));
1116                                         } catch (Exception e){
1117                                                 OnDownloadFileCompleted (
1118                                                         new AsyncCompletedEventArgs (e, false, args [2]));
1119                                         }});
1120                                 object [] cb_args = new object [] { CreateUri (address), fileName, userToken };
1121                                 async_thread.IsBackground = true;
1122                                 async_thread.Start (cb_args);
1123                         }
1124                 }
1125
1126                 //    DownloadStringAsync
1127
1128                 public void DownloadStringAsync (Uri address)
1129                 {
1130                         DownloadStringAsync (address, null);
1131                 }
1132
1133                 public void DownloadStringAsync (Uri address, object userToken)
1134                 {
1135                         if (address == null)
1136                                 throw new ArgumentNullException ("address");
1137                         
1138                         lock (this) {
1139                                 SetBusy ();
1140                                 async = true;
1141
1142                                 async_thread = new Thread (delegate (object state) {
1143                                         object [] args = (object []) state;
1144                                         try {
1145                                                 string data = encoding.GetString (DownloadDataCore ((Uri) args [0], args [1]));
1146                                                 OnDownloadStringCompleted (
1147                                                         new DownloadStringCompletedEventArgs (data, null, false, args [1]));
1148                                         } catch (Exception e){
1149                                                 bool canceled = false;
1150                                                 WebException we = e as WebException;
1151                                                 if (we != null)
1152                                                         canceled = we.Status == WebExceptionStatus.RequestCanceled;
1153                                                 OnDownloadStringCompleted (
1154                                                         new DownloadStringCompletedEventArgs (null, e, canceled, args [1]));
1155                                         }});
1156                                 object [] cb_args = new object [] { CreateUri (address), userToken };
1157                                 async_thread.IsBackground = true;
1158                                 async_thread.Start (cb_args);
1159                         }
1160                 }
1161
1162                 //    OpenReadAsync
1163
1164                 public void OpenReadAsync (Uri address)
1165                 {
1166                         OpenReadAsync (address, null);
1167                 }
1168
1169                 public void OpenReadAsync (Uri address, object userToken)
1170                 {
1171                         if (address == null)
1172                                 throw new ArgumentNullException ("address");
1173                         
1174                         lock (this) {
1175                                 SetBusy ();
1176                                 async = true;
1177
1178                                 async_thread = new Thread (delegate (object state) {
1179                                         object [] args = (object []) state;
1180                                         WebRequest request = null;
1181                                         try {
1182                                                 request = SetupRequest ((Uri) args [0]);
1183                                                 WebResponse response = GetWebResponse (request);
1184                                                 Stream stream = response.GetResponseStream ();
1185                                                 OnOpenReadCompleted (
1186                                                         new OpenReadCompletedEventArgs (stream, null, false, args [1]));
1187                                         } catch (ThreadInterruptedException){
1188                                                 if (request != null)
1189                                                         request.Abort ();
1190                                                 
1191                                                 OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, null, true, args [1]));
1192                                         } catch (Exception e){
1193                                                 OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, e, false, args [1]));
1194                                         } });
1195                                 object [] cb_args = new object [] { CreateUri (address), userToken };
1196                                 async_thread.IsBackground = true;
1197                                 async_thread.Start (cb_args);
1198                         }
1199                 }
1200
1201                 //    OpenWriteAsync
1202
1203                 public void OpenWriteAsync (Uri address)
1204                 {
1205                         OpenWriteAsync (address, null);
1206                 }
1207
1208                 public void OpenWriteAsync (Uri address, string method)
1209                 {
1210                         OpenWriteAsync (address, method, null);
1211                 }
1212
1213                 public void OpenWriteAsync (Uri address, string method, object userToken)
1214                 {
1215                         if (address == null)
1216                                 throw new ArgumentNullException ("address");
1217
1218                         lock (this) {
1219                                 SetBusy ();
1220                                 async = true;
1221
1222                                 async_thread = new Thread (delegate (object state) {
1223                                         object [] args = (object []) state;
1224                                         WebRequest request = null;
1225                                         try {
1226                                                 request = SetupRequest ((Uri) args [0], (string) args [1], true);
1227                                                 Stream stream = request.GetRequestStream ();
1228                                                 OnOpenWriteCompleted (
1229                                                         new OpenWriteCompletedEventArgs (stream, null, false, args [2]));
1230                                         } catch (ThreadInterruptedException){
1231                                                 if (request != null)
1232                                                         request.Abort ();
1233                                                 OnOpenWriteCompleted (
1234                                                         new OpenWriteCompletedEventArgs (null, null, true, args [2]));
1235                                         } catch (Exception e){
1236                                                 OnOpenWriteCompleted (
1237                                                         new OpenWriteCompletedEventArgs (null, e, false, args [2]));
1238                                         }});
1239                                 object [] cb_args = new object [] { CreateUri (address), method, userToken };
1240                                 async_thread.IsBackground = true;
1241                                 async_thread.Start (cb_args);
1242                         }
1243                 }
1244
1245                 //    UploadDataAsync
1246
1247                 public void UploadDataAsync (Uri address, byte [] data)
1248                 {
1249                         UploadDataAsync (address, null, data);
1250                 }
1251
1252                 public void UploadDataAsync (Uri address, string method, byte [] data)
1253                 {
1254                         UploadDataAsync (address, method, data, null);
1255                 }
1256
1257                 public void UploadDataAsync (Uri address, string method, byte [] data, object userToken)
1258                 {
1259                         if (address == null)
1260                                 throw new ArgumentNullException ("address");
1261                         if (data == null)
1262                                 throw new ArgumentNullException ("data");
1263                         
1264                         lock (this) {
1265                                 SetBusy ();
1266                                 async = true;
1267
1268                                 async_thread = new Thread (delegate (object state) {
1269                                         object [] args = (object []) state;
1270                                         byte [] data2;
1271
1272                                         try {
1273                                                 data2 = UploadDataCore ((Uri) args [0], (string) args [1], (byte []) args [2], args [3]);
1274                                         
1275                                                 OnUploadDataCompleted (
1276                                                         new UploadDataCompletedEventArgs (data2, null, false, args [3]));
1277                                         } catch (ThreadInterruptedException){
1278                                                 OnUploadDataCompleted (
1279                                                         new UploadDataCompletedEventArgs (null, null, true, args [3]));
1280                                         } catch (Exception e){
1281                                                 OnUploadDataCompleted (
1282                                                         new UploadDataCompletedEventArgs (null, e, false, args [3]));
1283                                         }});
1284                                 object [] cb_args = new object [] { CreateUri (address), method, data,  userToken };
1285                                 async_thread.IsBackground = true;
1286                                 async_thread.Start (cb_args);
1287                         }
1288                 }
1289
1290                 //    UploadFileAsync
1291
1292                 public void UploadFileAsync (Uri address, string fileName)
1293                 {
1294                         UploadFileAsync (address, null, fileName);
1295                 }
1296
1297                 public void UploadFileAsync (Uri address, string method, string fileName)
1298                 {
1299                         UploadFileAsync (address, method, fileName, null);
1300                 }
1301
1302                 public void UploadFileAsync (Uri address, string method, string fileName, object userToken)
1303                 {
1304                         if (address == null)
1305                                 throw new ArgumentNullException ("address");
1306                         if (fileName == null)
1307                                 throw new ArgumentNullException ("fileName");
1308
1309                         lock (this) {
1310                                 SetBusy ();
1311                                 async = true;
1312
1313                                 async_thread = new Thread (delegate (object state) {
1314                                         object [] args = (object []) state;
1315                                         byte [] data;
1316
1317                                         try {
1318                                                 data = UploadFileCore ((Uri) args [0], (string) args [1], (string) args [2], args [3]);
1319                                                 OnUploadFileCompleted (
1320                                                         new UploadFileCompletedEventArgs (data, null, false, args [3]));
1321                                         } catch (ThreadInterruptedException){
1322                                                 OnUploadFileCompleted (
1323                                                         new UploadFileCompletedEventArgs (null, null, true, args [3]));
1324                                         } catch (Exception e){
1325                                                 OnUploadFileCompleted (
1326                                                         new UploadFileCompletedEventArgs (null, e, false, args [3]));
1327                                         }});
1328                                 object [] cb_args = new object [] { CreateUri (address), method, fileName,  userToken };
1329                                 async_thread.IsBackground = true;
1330                                 async_thread.Start (cb_args);
1331                         }
1332                 }
1333
1334                 //    UploadStringAsync
1335
1336                 public void UploadStringAsync (Uri address, string data)
1337                 {
1338                         UploadStringAsync (address, null, data);
1339                 }
1340
1341                 public void UploadStringAsync (Uri address, string method, string data)
1342                 {
1343                         UploadStringAsync (address, method, data, null);
1344                 }
1345
1346                 public void UploadStringAsync (Uri address, string method, string data, object userToken)
1347                 {
1348                         if (address == null)
1349                                 throw new ArgumentNullException ("address");
1350                         if (data == null)
1351                                 throw new ArgumentNullException ("data");
1352                         
1353                         lock (this) {
1354                                 CheckBusy ();
1355                                 async = true;
1356                                 
1357                                 async_thread = new Thread (delegate (object state) {
1358                                         object [] args = (object []) state;
1359
1360                                         try {
1361                                                 string data2 = UploadString ((Uri) args [0], (string) args [1], (string) args [2]);
1362                                                 OnUploadStringCompleted (
1363                                                         new UploadStringCompletedEventArgs (data2, null, false, args [3]));
1364                                         } catch (ThreadInterruptedException){
1365                                                 OnUploadStringCompleted (
1366                                                         new UploadStringCompletedEventArgs (null, null, true, args [3]));
1367                                         } catch (Exception e){
1368                                                 OnUploadStringCompleted (
1369                                                         new UploadStringCompletedEventArgs (null, e, false, args [3]));
1370                                         }});
1371                                 object [] cb_args = new object [] { CreateUri (address), method, data, userToken };
1372                                 async_thread.IsBackground = true;
1373                                 async_thread.Start (cb_args);
1374                         }
1375                 }
1376
1377                 //    UploadValuesAsync
1378
1379                 public void UploadValuesAsync (Uri address, NameValueCollection data)
1380                 {
1381                         UploadValuesAsync (address, null, data);
1382                 }
1383
1384                 public void UploadValuesAsync (Uri address, string method, NameValueCollection data)
1385                 {
1386                         UploadValuesAsync (address, method, data, null);
1387                 }
1388
1389                 public void UploadValuesAsync (Uri address, string method, NameValueCollection data, object userToken)
1390                 {
1391                         if (address == null)
1392                                 throw new ArgumentNullException ("address");
1393                         if (data == null)
1394                                 throw new ArgumentNullException ("data");
1395
1396                         lock (this) {
1397                                 CheckBusy ();
1398                                 async = true;
1399
1400                                 async_thread = new Thread (delegate (object state) {
1401                                         object [] args = (object []) state;
1402                                         try {
1403                                                 byte [] values = UploadValuesCore ((Uri) args [0], (string) args [1], (NameValueCollection) args [2], args [3]);
1404                                                 OnUploadValuesCompleted (
1405                                                         new UploadValuesCompletedEventArgs (values, null, false, args [3]));
1406                                         } catch (ThreadInterruptedException){
1407                                                 OnUploadValuesCompleted (
1408                                                         new UploadValuesCompletedEventArgs (null, null, true, args [3]));
1409                                         } catch (Exception e){
1410                                                 OnUploadValuesCompleted (
1411                                                         new UploadValuesCompletedEventArgs (null, e, false, args [3]));
1412                                         }});
1413                                 object [] cb_args = new object [] { CreateUri (address), method, data,  userToken };
1414                                 async_thread.IsBackground = true;
1415                                 async_thread.Start (cb_args);
1416                         }
1417                 }
1418
1419                 protected virtual void OnDownloadDataCompleted (DownloadDataCompletedEventArgs e)
1420                 {
1421                         CompleteAsync ();
1422                         if (DownloadDataCompleted != null)
1423                                 DownloadDataCompleted (this, e);
1424                 }
1425
1426                 protected virtual void OnDownloadFileCompleted (AsyncCompletedEventArgs e)
1427                 {
1428                         CompleteAsync ();
1429                         if (DownloadFileCompleted != null)
1430                                 DownloadFileCompleted (this, e);
1431                 }
1432
1433                 protected virtual void OnDownloadProgressChanged (DownloadProgressChangedEventArgs e)
1434                 {
1435                         if (DownloadProgressChanged != null)
1436                                 DownloadProgressChanged (this, e);
1437                 }
1438
1439                 protected virtual void OnDownloadStringCompleted (DownloadStringCompletedEventArgs e)
1440                 {
1441                         CompleteAsync ();
1442                         if (DownloadStringCompleted != null)
1443                                 DownloadStringCompleted (this, e);
1444                 }
1445
1446                 protected virtual void OnOpenReadCompleted (OpenReadCompletedEventArgs e)
1447                 {
1448                         CompleteAsync ();
1449                         if (OpenReadCompleted != null)
1450                                 OpenReadCompleted (this, e);
1451                 }
1452
1453                 protected virtual void OnOpenWriteCompleted (OpenWriteCompletedEventArgs e)
1454                 {
1455                         CompleteAsync ();
1456                         if (OpenWriteCompleted != null)
1457                                 OpenWriteCompleted (this, e);
1458                 }
1459
1460                 protected virtual void OnUploadDataCompleted (UploadDataCompletedEventArgs e)
1461                 {
1462                         CompleteAsync ();
1463                         if (UploadDataCompleted != null)
1464                                 UploadDataCompleted (this, e);
1465                 }
1466
1467                 protected virtual void OnUploadFileCompleted (UploadFileCompletedEventArgs e)
1468                 {
1469                         CompleteAsync ();
1470                         if (UploadFileCompleted != null)
1471                                 UploadFileCompleted (this, e);
1472                 }
1473
1474                 protected virtual void OnUploadProgressChanged (UploadProgressChangedEventArgs e)
1475                 {
1476                         if (UploadProgressChanged != null)
1477                                 UploadProgressChanged (this, e);
1478                 }
1479
1480                 protected virtual void OnUploadStringCompleted (UploadStringCompletedEventArgs e)
1481                 {
1482                         CompleteAsync ();
1483                         if (UploadStringCompleted != null)
1484                                 UploadStringCompleted (this, e);
1485                 }
1486
1487                 protected virtual void OnUploadValuesCompleted (UploadValuesCompletedEventArgs e)
1488                 {
1489                         CompleteAsync ();
1490                         if (UploadValuesCompleted != null)
1491                                 UploadValuesCompleted (this, e);
1492                 }
1493
1494                 protected virtual WebResponse GetWebResponse (WebRequest request, IAsyncResult result)
1495                 {
1496                         WebResponse response = request.EndGetResponse (result);
1497                         responseHeaders = response.Headers;
1498                         return response;
1499                 }
1500
1501                 protected virtual WebRequest GetWebRequest (Uri address)
1502                 {
1503                         return WebRequest.Create (address);
1504                 }
1505
1506                 protected virtual WebResponse GetWebResponse (WebRequest request)
1507                 {
1508                         WebResponse response = request.GetResponse ();
1509                         responseHeaders = response.Headers;
1510                         return response;
1511                 }
1512                 
1513 #if NET_4_5
1514
1515                 // DownloadDataTaskAsync
1516                 
1517                 public Task<byte[]> DownloadDataTaskAsync (string address)
1518                 {
1519                         return DownloadDataTaskAsync (CreateUri (address));
1520                 }
1521
1522                 public async Task<byte[]> DownloadDataTaskAsync (Uri address)
1523                 {
1524                         WebRequest request = null;
1525                         WebResponse response = null;
1526                         try {
1527                                 SetBusy ();
1528                                 cts = new CancellationTokenSource ();
1529                                 request = await SetupRequestAsync (address);
1530                                 response = await GetWebResponseTaskAsync (request, cts.Token);
1531                                 var result = await ReadAllTaskAsync (request, response, cts.Token);
1532
1533                                 // Has to run on original context
1534                                 OnDownloadDataCompleted (new DownloadDataCompletedEventArgs (result, null, false, null));
1535                                 return result;
1536                         } catch (WebException ex) {
1537                                 OnDownloadDataCompleted (new DownloadDataCompletedEventArgs (null, ex, false, null));
1538                                 throw;
1539                         } catch (OperationCanceledException) {
1540                                 if (request != null)
1541                                         request.Abort ();
1542                                 OnDownloadDataCompleted (new DownloadDataCompletedEventArgs (null, null, true, null));
1543                                 throw;
1544                         } catch (Exception ex) {
1545                                 OnDownloadDataCompleted (new DownloadDataCompletedEventArgs (null, ex, true, null));
1546                                 throw new WebException ("An error occurred performing a WebClient request.", ex);
1547                         } finally {
1548                                 if (response != null)
1549                                         response.Close ();
1550                         }
1551                 }
1552
1553                 Task<WebRequest> SetupRequestAsync (Uri address)
1554                 {
1555                         return Task.Factory.StartNew (() => SetupRequest (address));
1556                 }
1557
1558                 async Task<WebRequest> SetupRequestAsync (Uri address, string method, bool is_upload)
1559                 {
1560                         WebRequest request = await SetupRequestAsync (address).ConfigureAwait (false);
1561                         request.Method = DetermineMethod (address, method, is_upload);
1562                         return request;
1563                 }
1564                 
1565                 async Task<WebResponse> GetWebResponseTaskAsync (WebRequest request, CancellationToken token)
1566                 {
1567                         token.ThrowIfCancellationRequested ();
1568                         WebResponse response = await request.GetResponseAsync ().ConfigureAwait (false);
1569                         token.ThrowIfCancellationRequested ();
1570                         responseHeaders = response.Headers;
1571                         return response;
1572                 }
1573                 
1574                 async Task<byte[]> ReadAllTaskAsync (WebRequest request, WebResponse response, CancellationToken token)
1575                 {
1576                         Stream stream = response.GetResponseStream ();
1577                         int length = (int)response.ContentLength;
1578                         HttpWebRequest wreq = request as HttpWebRequest;
1579
1580                         if (length > -1 && wreq != null && (int)wreq.AutomaticDecompression != 0) {
1581                                 string content_encoding = ((HttpWebResponse)response).ContentEncoding;
1582                                 if (((content_encoding == "gzip" && (wreq.AutomaticDecompression & DecompressionMethods.GZip) != 0)) ||
1583                                         ((content_encoding == "deflate" && (wreq.AutomaticDecompression & DecompressionMethods.Deflate) != 0)))
1584                                         length = -1;
1585                         }
1586
1587                         MemoryStream ms = null;
1588                         bool nolength = (length == -1);
1589                         int size = ((nolength) ? 8192 : length);
1590                         if (nolength)
1591                                 ms = new MemoryStream ();
1592
1593                         long total = 0;
1594                         int nread = 0;
1595                         int offset = 0;
1596                         byte [] buffer = new byte [size];
1597                         token.ThrowIfCancellationRequested ();
1598                         while ((nread = await stream.ReadAsync (buffer, offset, size, token)) != 0) {
1599                                 if (nolength) {
1600                                         ms.Write (buffer, 0, nread);
1601                                 } else {
1602                                         offset += nread;
1603                                         size -= nread;
1604                                 }
1605                                 total += nread;
1606                                 OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (total, length, null));
1607                                 token.ThrowIfCancellationRequested ();
1608                         }
1609                         
1610                         return nolength ? ms.ToArray () : buffer;
1611                 }
1612                 
1613                 // DownloadFileTaskAsync
1614                 
1615                 public Task DownloadFileTaskAsync (string address, string fileName)
1616                 {
1617                         if (address == null)
1618                                 throw new ArgumentNullException ("address");
1619
1620                         return DownloadFileTaskAsync (CreateUri (address), fileName);
1621                 }
1622
1623                 public async Task DownloadFileTaskAsync (Uri address, string fileName)
1624                 {
1625                         if (address == null)
1626                                 throw new ArgumentNullException ("address");
1627                         if (fileName == null)
1628                                 throw new ArgumentNullException ("fileName");
1629                         
1630                         WebRequest request = null;
1631                         WebResponse response = null;
1632
1633                         try {
1634                                 SetBusy ();
1635                                 cts = new CancellationTokenSource ();
1636                                 request = await SetupRequestAsync (address);
1637                                 response = await GetWebResponseTaskAsync (request, cts.Token);
1638                                 await DownloadFileTaskAsyncCore (request, response, fileName, cts.Token);
1639                                 OnDownloadFileCompleted (new AsyncCompletedEventArgs (null, false, null));
1640                         } catch (WebException ex) {
1641                                 OnDownloadFileCompleted (new AsyncCompletedEventArgs (ex, false, null));
1642                                 throw;
1643                         } catch (OperationCanceledException) {
1644                                 if (request != null)
1645                                         request.Abort ();
1646                                 OnDownloadFileCompleted (new AsyncCompletedEventArgs (null, true, null));
1647                                 throw;
1648                         } catch (Exception ex) {
1649                                 OnDownloadFileCompleted (new AsyncCompletedEventArgs (ex, false, null));
1650                                 throw new WebException ("An error occurred " +
1651                                         "performing a WebClient request.", ex);
1652                         } finally {
1653                                 if (response != null)
1654                                         response.Close ();
1655                         }
1656                 }
1657
1658                 async Task DownloadFileTaskAsyncCore (WebRequest request, WebResponse response,
1659                                                       string fileName, CancellationToken token)
1660                 {
1661                         using (FileStream f = new FileStream (fileName, FileMode.Create)) {
1662                                 Stream st = response.GetResponseStream ();
1663                                         
1664                                 int cLength = (int)response.ContentLength;
1665                                 int length = (cLength <= -1 || cLength > 32 * 1024) ? 32 * 1024 : cLength;
1666                                 byte [] buffer = new byte [length];
1667                                         
1668                                 int nread = 0;
1669                                 long notify_total = 0;
1670                                 token.ThrowIfCancellationRequested ();
1671                                 while ((nread = await st.ReadAsync (buffer, 0, length, token)) != 0) {
1672                                         notify_total += nread;
1673                                         OnDownloadProgressChanged (
1674                                                 new DownloadProgressChangedEventArgs (notify_total, response.ContentLength, null));
1675                                         token.ThrowIfCancellationRequested ();
1676                                         await f.WriteAsync (buffer, 0, nread, token);
1677                                         token.ThrowIfCancellationRequested ();
1678                                 }
1679                         }
1680                 }
1681                 
1682                 // OpenReadTaskAsync
1683                 
1684                 public Task<Stream> OpenReadTaskAsync (string address)
1685                 {
1686                         if (address == null)
1687                                 throw new ArgumentNullException ("address");
1688                         return OpenReadTaskAsync (CreateUri (address));
1689                 }
1690
1691                 public async Task<Stream> OpenReadTaskAsync (Uri address)
1692                 {
1693                         if (address == null)
1694                                 throw new ArgumentNullException ("address");
1695
1696                         WebRequest request = null;
1697                         try {
1698                                 SetBusy ();
1699                                 cts = new CancellationTokenSource ();
1700                                 request = await SetupRequestAsync (address);
1701                                 WebResponse response = await GetWebResponseTaskAsync (request, cts.Token);
1702                                 var result = response.GetResponseStream ();
1703                                 cts.Token.ThrowIfCancellationRequested ();
1704                                 OnOpenReadCompleted (new OpenReadCompletedEventArgs (result, null, false, null));
1705                                 return result;
1706                         } catch (WebException ex) {
1707                                 OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, ex, false, null));
1708                                 throw;
1709                         } catch (OperationCanceledException) {
1710                                 if (request != null)
1711                                         request.Abort ();
1712                                 OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, null, true, null));
1713                                 throw;
1714                         } catch (Exception ex) {
1715                                 OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, ex, false, null));
1716                                 throw new WebException ("An error occurred " +
1717                                         "performing a WebClient request.", ex);
1718                         }
1719                 }
1720                 
1721                 // DownloadStringTaskAsync
1722                 
1723                 public Task<string> DownloadStringTaskAsync (string address)
1724                 {
1725                         if (address == null)
1726                                 throw new ArgumentNullException ("address");
1727
1728                         return DownloadStringTaskAsync (CreateUri (address));
1729                 }
1730
1731                 public async Task<string> DownloadStringTaskAsync (Uri address)
1732                 {
1733                         if (address == null)
1734                                 throw new ArgumentNullException ("address");
1735
1736                         WebRequest request = null;
1737                         WebResponse response = null;
1738
1739                         try {
1740                                 SetBusy ();
1741                                 cts = new CancellationTokenSource ();
1742                                 request = await SetupRequestAsync (address);
1743                                 response = await GetWebResponseTaskAsync (request, cts.Token);
1744                                 var data = await ReadAllTaskAsync (request, response, cts.Token);
1745                                 cts.Token.ThrowIfCancellationRequested ();
1746                                 var text = encoding.GetString (data);
1747                                 OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (text, null, false, null));
1748                                 return text;
1749                         } catch (WebException ex) {
1750                                 OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (null, ex, false, null));
1751                                 throw;
1752                         } catch (OperationCanceledException) {
1753                                 if (request != null)
1754                                         request.Abort ();
1755                                 OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (null, null, true, null));
1756                                 throw;
1757                         } catch (Exception ex) {
1758                                 OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (null, ex, true, null));
1759                                 throw new WebException ("An error occurred performing a WebClient request.", ex);
1760                         } finally {
1761                                 if (response != null)
1762                                         response.Close ();
1763                         }
1764                 }
1765
1766                 // OpenWriteTaskAsync
1767                 
1768                 public Task<Stream> OpenWriteTaskAsync (string address)
1769                 {
1770                         if (address == null)
1771                                 throw new ArgumentNullException ("address");
1772
1773                         return OpenWriteTaskAsync (CreateUri (address));
1774                 }
1775                 
1776                 public Task<Stream> OpenWriteTaskAsync (string address, string method)
1777                 {
1778                         if (address == null)
1779                                 throw new ArgumentNullException ("address");
1780
1781                         return OpenWriteTaskAsync (CreateUri (address), method);
1782                 }
1783
1784                 public Task<Stream> OpenWriteTaskAsync (Uri address)
1785                 {
1786                         return OpenWriteTaskAsync (address, (string) null);
1787                 }
1788
1789                 public async Task<Stream> OpenWriteTaskAsync (Uri address, string method)
1790                 {
1791                         if (address == null)
1792                                 throw new ArgumentNullException ("address");
1793
1794                         WebRequest request = null;
1795                         try {
1796                                 SetBusy ();
1797                                 cts = new CancellationTokenSource ();
1798                                 request = SetupRequest (address);
1799                                 return await request.GetRequestStreamAsync ().ConfigureAwait (false);
1800                         } catch (WebException) {
1801                                 throw;
1802                         } catch (OperationCanceledException) {
1803                                 if (request != null)
1804                                         request.Abort ();
1805                                 throw;
1806                         } catch (Exception ex) {
1807                                 throw new WebException ("An error occurred " +
1808                                         "performing a WebClient request.", ex);
1809                         } finally {
1810                                 CompleteAsync ();
1811                         }
1812                 }
1813
1814                 // UploadDataTaskAsync
1815                 
1816                 public Task<byte[]> UploadDataTaskAsync (string address, byte [] data)
1817                 {
1818                         if (address == null)
1819                                 throw new ArgumentNullException ("address");
1820
1821                         return UploadDataTaskAsync (CreateUri (address), data);
1822                 }
1823                 
1824                 public Task<byte[]> UploadDataTaskAsync (string address, string method, byte [] data)
1825                 {
1826                         if (address == null)
1827                                 throw new ArgumentNullException ("address");
1828
1829                         return UploadDataTaskAsync (CreateUri (address), method, data);
1830                 }
1831
1832                 public Task<byte[]> UploadDataTaskAsync (Uri address, byte [] data)
1833                 {
1834                         return UploadDataTaskAsync (address, (string) null, data);
1835                 }
1836
1837                 public async Task<byte[]> UploadDataTaskAsync (Uri address, string method, byte [] data)
1838                 {
1839                         if (address == null)
1840                                 throw new ArgumentNullException ("address");
1841                         if (data == null)
1842                                 throw new ArgumentNullException ("data");
1843
1844                         WebRequest request = null;
1845                         try {
1846                                 SetBusy ();
1847                                 cts = new CancellationTokenSource ();
1848                                 request = await SetupRequestAsync (address, method, true).ConfigureAwait (false);
1849                                 var result = await UploadDataTaskAsyncCore (request, data, cts.Token).ConfigureAwait (false);
1850                                 OnUploadDataCompleted (new UploadDataCompletedEventArgs (result, null, false, null));
1851                                 return result;
1852                         } catch (WebException ex) {
1853                                 OnUploadDataCompleted (new UploadDataCompletedEventArgs (null, ex, false, null));
1854                                 throw;
1855                         } catch (OperationCanceledException) {
1856                                 if (request != null)
1857                                         request.Abort ();
1858                                 OnUploadDataCompleted (new UploadDataCompletedEventArgs (null, null, true, null));
1859                                 throw;
1860                         } catch (Exception ex) {
1861                                 OnUploadDataCompleted (new UploadDataCompletedEventArgs (null, ex, true, null));
1862                                 throw new WebException ("An error occurred performing a WebClient request.", ex);
1863                         }
1864                 }
1865
1866                 async Task<byte[]> UploadDataTaskAsyncCore (WebRequest request, byte[] data, CancellationToken token)
1867                 {
1868                         token.ThrowIfCancellationRequested ();
1869
1870                         int contentLength = data.Length;
1871                         request.ContentLength = contentLength;
1872                         using (Stream stream = await request.GetRequestStreamAsync ().ConfigureAwait (false)) {
1873                                 token.ThrowIfCancellationRequested ();
1874                                 await stream.WriteAsync (data, 0, contentLength, token).ConfigureAwait (false);
1875                                 token.ThrowIfCancellationRequested ();
1876                         }
1877
1878                         WebResponse response = null;
1879
1880                         try {
1881                                 response = await GetWebResponseTaskAsync (request, token).ConfigureAwait (false);
1882                                 return await ReadAllTaskAsync (request, response, token).ConfigureAwait (false);
1883                         } finally {
1884                                 if (response != null)
1885                                         response.Close ();
1886                         }
1887                 }
1888
1889                 // UploadFileTaskAsync
1890
1891                 public Task<byte[]> UploadFileTaskAsync (string address, string fileName)
1892                 {
1893                         if (address == null)
1894                                 throw new ArgumentNullException ("address");
1895
1896                         return UploadFileTaskAsync (CreateUri (address), fileName);
1897                 }
1898
1899                 public Task<byte[]> UploadFileTaskAsync (Uri address, string fileName)
1900                 {
1901                         return UploadFileTaskAsync (address, (string) null, fileName);
1902                 }
1903                 
1904                 public Task<byte[]> UploadFileTaskAsync (string address, string method, string fileName)
1905                 {
1906                         return UploadFileTaskAsync (CreateUri (address), method, fileName);
1907                 }
1908
1909                 public async Task<byte[]> UploadFileTaskAsync (Uri address, string method, string fileName)
1910                 {
1911                         if (address == null)
1912                                 throw new ArgumentNullException ("address");
1913                         if (fileName == null)
1914                                 throw new ArgumentNullException ("fileName");
1915
1916                         WebRequest request = null;
1917                         try {
1918                                 SetBusy ();
1919                                 cts = new CancellationTokenSource ();
1920                                 request = await SetupRequestAsync (address, method, true).ConfigureAwait (false);
1921                                 var result = await UploadFileTaskAsyncCore (request, method, fileName, cts.Token).ConfigureAwait (false);
1922                                 OnUploadFileCompleted (new UploadFileCompletedEventArgs (result, null, false, null));
1923                                 return result;
1924                         } catch (WebException ex) {
1925                                 OnUploadFileCompleted (new UploadFileCompletedEventArgs (null, ex, false, null));
1926                                 throw;
1927                         } catch (OperationCanceledException) {
1928                                 if (request != null)
1929                                         request.Abort ();
1930                                 OnUploadFileCompleted (new UploadFileCompletedEventArgs (null, null, true, null));
1931                                 throw;
1932                         } catch (Exception ex) {
1933                                 OnUploadFileCompleted (new UploadFileCompletedEventArgs (null, ex, true, null));
1934                                 throw new WebException ("An error occurred performing a WebClient request.", ex);
1935                         }
1936                 }
1937
1938                 async Task<byte[]> UploadFileTaskAsyncCore (WebRequest request, string method,
1939                                                             string fileName, CancellationToken token)
1940                 {
1941                         token.ThrowIfCancellationRequested ();
1942
1943                         string fileCType = Headers ["Content-Type"];
1944                         if (fileCType != null) {
1945                                 string lower = fileCType.ToLower ();
1946                                 if (lower.StartsWith ("multipart/"))
1947                                         throw new WebException ("Content-Type cannot be set to a multipart" +
1948                                                                 " type for this request.");
1949                         } else {
1950                                 fileCType = "application/octet-stream";
1951                         }
1952
1953                         bool needs_boundary = (method != "PUT"); // only verified case so far
1954                         string boundary = null;
1955                         if (needs_boundary) {
1956                                 boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
1957                                 Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
1958                         }
1959                         Stream reqStream = null;
1960                         Stream fStream = null;
1961                         WebResponse response = null;
1962
1963                         fileName = Path.GetFullPath (fileName);
1964
1965                         try {
1966                                 fStream = File.OpenRead (fileName);
1967                                 token.ThrowIfCancellationRequested ();
1968                                 reqStream = await request.GetRequestStreamAsync ().ConfigureAwait (false);
1969                                 token.ThrowIfCancellationRequested ();
1970                                 byte [] bytes_boundary = null;
1971                                 if (needs_boundary) {
1972                                         bytes_boundary = Encoding.ASCII.GetBytes (boundary);
1973                                         using (MemoryStream ms = new MemoryStream ()) {
1974                                                 ms.WriteByte ((byte) '-');
1975                                                 ms.WriteByte ((byte) '-');
1976                                                 ms.Write (bytes_boundary, 0, bytes_boundary.Length);
1977                                                 ms.WriteByte ((byte) '\r');
1978                                                 ms.WriteByte ((byte) '\n');
1979                                                 string partHeaders = String.Format (
1980                                                         "Content-Disposition: form-data; " +
1981                                                         "name=\"file\"; filename=\"{0}\"\r\n" +
1982                                                         "Content-Type: {1}\r\n\r\n",
1983                                                         Path.GetFileName (fileName), fileCType);
1984                                                 byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
1985                                                 ms.Write (partHeadersBytes, 0, partHeadersBytes.Length);
1986                                                 await ms.CopyToAsync (reqStream, (int)ms.Position, token).ConfigureAwait (false);
1987                                         }
1988                                 }
1989                                 int nread;
1990                                 long bytes_sent = 0;
1991                                 long file_size = -1;
1992                                 long step = 16384; // every 16kB
1993                                 if (fStream.CanSeek) {
1994                                         file_size = fStream.Length;
1995                                         step = file_size / 100;
1996                                 }
1997                                 var upload_args = new UploadProgressChangedEventArgs (0, 0, bytes_sent, file_size, 0, null);
1998                                 OnUploadProgressChanged (upload_args);
1999                                 byte [] buffer = new byte [4096];
2000                                 long sum = 0;
2001                                 token.ThrowIfCancellationRequested ();
2002                                 while ((nread = await fStream.ReadAsync (buffer, 0, 4096, token).ConfigureAwait (false)) > 0) {
2003                                         token.ThrowIfCancellationRequested ();
2004                                         await reqStream.WriteAsync (buffer, 0, nread, token).ConfigureAwait (false);
2005                                         bytes_sent += nread;
2006                                         sum += nread;
2007                                         if (sum >= step || nread < 4096) {
2008                                                 int percent = 0;
2009                                                 if (file_size > 0)
2010                                                         percent = (int) (bytes_sent * 100 / file_size);
2011                                                 upload_args = new UploadProgressChangedEventArgs (0, 0, bytes_sent, file_size, percent, null);
2012                                                 OnUploadProgressChanged (upload_args);
2013                                                 sum = 0;
2014                                         }
2015                                 }
2016
2017                                 if (needs_boundary) {
2018                                         using (MemoryStream ms = new MemoryStream ()) {
2019                                                 ms.WriteByte ((byte) '\r');
2020                                                 ms.WriteByte ((byte) '\n');
2021                                                 ms.WriteByte ((byte) '-');
2022                                                 ms.WriteByte ((byte) '-');
2023                                                 ms.Write (bytes_boundary, 0, bytes_boundary.Length);
2024                                                 ms.WriteByte ((byte) '-');
2025                                                 ms.WriteByte ((byte) '-');
2026                                                 ms.WriteByte ((byte) '\r');
2027                                                 ms.WriteByte ((byte) '\n');
2028                                                 await ms.CopyToAsync (reqStream, (int)ms.Position, token).ConfigureAwait (false);
2029                                         }
2030                                 }
2031                                 reqStream.Close ();
2032                                 reqStream = null;
2033
2034                                 response = await GetWebResponseTaskAsync (request, token).ConfigureAwait (false);
2035                                 return await ReadAllTaskAsync (request, response, token).ConfigureAwait (false);
2036                         } finally {
2037                                 if (fStream != null)
2038                                         fStream.Close ();
2039
2040                                 if (reqStream != null)
2041                                         reqStream.Close ();
2042
2043                                 if (response != null)
2044                                         response.Close ();
2045                         }
2046                 }
2047
2048                 // UploadStringTaskAsync
2049
2050                 public Task<string> UploadStringTaskAsync (string address, string data)
2051                 {
2052                         if (address == null)
2053                                 throw new ArgumentNullException ("address");
2054                         if (data == null)
2055                                 throw new ArgumentNullException ("data");
2056
2057                         return UploadStringTaskAsync (CreateUri (address), null, data);
2058                 }
2059
2060                 public Task<string> UploadStringTaskAsync (string address, string method, string data)
2061                 {
2062                         if (address == null)
2063                                 throw new ArgumentNullException ("address");
2064                         if (data == null)
2065                                 throw new ArgumentNullException ("data");
2066
2067                         return UploadStringTaskAsync (CreateUri (address), method, data);
2068                 }
2069
2070                 public Task<string> UploadStringTaskAsync (Uri address, string data)
2071                 {
2072                         if (address == null)
2073                                 throw new ArgumentNullException ("address");
2074                         if (data == null)
2075                                 throw new ArgumentNullException ("data");
2076
2077                         return UploadStringTaskAsync (address, null, data);
2078                 }
2079
2080                 public async Task<string> UploadStringTaskAsync (Uri address, string method, string data)
2081                 {
2082                         if (address == null)
2083                                 throw new ArgumentNullException ("address");
2084                         if (data == null)
2085                                 throw new ArgumentNullException ("data");
2086
2087                         WebRequest request = null;
2088                         try {
2089                                 SetBusy ();
2090                                 cts = new CancellationTokenSource ();
2091                                 request = await SetupRequestAsync (address, method, true).ConfigureAwait (false);
2092                                 var result = await UploadDataTaskAsyncCore (request, encoding.GetBytes (data), cts.Token).ConfigureAwait (false);
2093                                 var result_str = encoding.GetString (result);
2094                                 OnUploadStringCompleted (new UploadStringCompletedEventArgs (result_str, null, false, null));
2095                                 return result_str;
2096                         } catch (WebException ex) {
2097                                 OnUploadStringCompleted (new UploadStringCompletedEventArgs (null, ex, false, null));
2098                                 throw;
2099                         } catch (OperationCanceledException) {
2100                                 if (request != null)
2101                                         request.Abort ();
2102                                 OnUploadStringCompleted (new UploadStringCompletedEventArgs (null, null, true, null));
2103                                 throw;
2104                         } catch (Exception ex) {
2105                                 OnUploadStringCompleted (new UploadStringCompletedEventArgs (null, ex, true, null));
2106                                 throw new WebException ("An error occurred performing a WebClient request.", ex);
2107                         }
2108                 }
2109
2110                 // UploadValuesTaskAsync
2111
2112                 public Task<byte[]> UploadValuesTaskAsync (string address, NameValueCollection data)
2113                 {
2114                         if (address == null)
2115                                 throw new ArgumentNullException ("address");
2116
2117                         return UploadValuesTaskAsync (CreateUri (address), data);
2118                 }
2119                 
2120                 public Task<byte[]> UploadValuesTaskAsync (string address, string method, NameValueCollection data)
2121                 {
2122                         if (address == null)
2123                                 throw new ArgumentNullException ("address");
2124
2125                         return UploadValuesTaskAsync (CreateUri (address), method, data);
2126                 }
2127
2128                 public Task<byte[]> UploadValuesTaskAsync (Uri address, NameValueCollection data)
2129                 {
2130                         return UploadValuesTaskAsync (address, (string) null, data);
2131                 }
2132
2133                 public async Task<byte[]> UploadValuesTaskAsync (Uri address, string method, NameValueCollection data)
2134                 {
2135                         if (address == null)
2136                                 throw new ArgumentNullException ("address");
2137                         if (data == null)
2138                                 throw new ArgumentNullException ("data");
2139
2140                         WebRequest request = null;
2141                         try {
2142                                 SetBusy ();
2143                                 cts = new CancellationTokenSource ();
2144                                 request = await SetupRequestAsync (address, method, true).ConfigureAwait (false);
2145                                 var result = await UploadValuesTaskAsyncCore (request, data, cts.Token).ConfigureAwait (false);
2146                                 OnUploadValuesCompleted (new UploadValuesCompletedEventArgs (result, null, false, null));
2147                                 return result;
2148                         } catch (WebException ex) {
2149                                 OnUploadValuesCompleted (new UploadValuesCompletedEventArgs (null, ex, false, null));
2150                                 throw;
2151                         } catch (OperationCanceledException) {
2152                                 if (request != null)
2153                                         request.Abort ();
2154                                 OnUploadValuesCompleted (new UploadValuesCompletedEventArgs (null, null, true, null));
2155                                 throw;
2156                         } catch (Exception ex) {
2157                                 OnUploadValuesCompleted (new UploadValuesCompletedEventArgs (null, ex, true, null));
2158                                 throw new WebException ("An error occurred performing a WebClient request.", ex);
2159                         }
2160                 }
2161
2162                 async Task<byte[]> UploadValuesTaskAsyncCore (WebRequest request, NameValueCollection data,
2163                                                               CancellationToken token)
2164                 {
2165                         token.ThrowIfCancellationRequested ();
2166                         string cType = Headers ["Content-Type"];
2167                         if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
2168                                 throw new WebException ("Content-Type header cannot be changed from its default " +
2169                                                         "value for this request.");
2170
2171                         WebResponse response = null;
2172
2173                         Headers ["Content-Type"] = urlEncodedCType;
2174                         try {
2175                                 MemoryStream tmpStream = new MemoryStream ();
2176                                 foreach (string key in data) {
2177                                         byte [] bytes = Encoding.UTF8.GetBytes (key);
2178                                         UrlEncodeAndWrite (tmpStream, bytes);
2179                                         tmpStream.WriteByte ((byte) '=');
2180                                         bytes = Encoding.UTF8.GetBytes (data [key]);
2181                                         UrlEncodeAndWrite (tmpStream, bytes);
2182                                         tmpStream.WriteByte ((byte) '&');
2183                                 }
2184
2185                                 token.ThrowIfCancellationRequested ();
2186                                 
2187                                 int length = (int) tmpStream.Length;
2188                                 if (length > 0)
2189                                         tmpStream.SetLength (--length); // remove trailing '&'
2190                                 
2191                                 byte [] buf = tmpStream.GetBuffer ();
2192                                 request.ContentLength = length;
2193                                 using (Stream rqStream = await request.GetRequestStreamAsync ().ConfigureAwait (false)) {
2194                                         await rqStream.WriteAsync (buf, 0, length, token).ConfigureAwait (false);
2195                                 }
2196                                 tmpStream.Close ();
2197
2198                                 response = await GetWebResponseTaskAsync (request, token).ConfigureAwait (false);
2199                                 return await ReadAllTaskAsync (request, response, token).ConfigureAwait (false);
2200                         } finally {
2201                                 if (response != null)
2202                                         response.Close ();
2203                         }
2204                 }
2205
2206 #endif
2207
2208         }
2209 }
2210