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