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