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