Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / System / System.Net / WebClient.cs
index d1dea6d8ffced249024046f3ad89bada7079d534..f121547f9bce58090d29d3c418d9495f1288e35e 100644 (file)
@@ -6,9 +6,11 @@
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
 //     Atsushi Enomoto (atsushi@ximian.com)
 //     Miguel de Icaza (miguel@ximian.com)
+//      Martin Baulig (martin.baulig@googlemail.com)
 //
 // Copyright 2003 Ximian, Inc. (http://www.ximian.com)
 // Copyright 2006, 2010 Novell, Inc. (http://www.novell.com)
+// Copyright 2012 Xamarin Inc. (http://www.xamarin.com)
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining
@@ -67,6 +69,9 @@ using System.Runtime.Serialization;
 using System.Text;
 using System.Threading;
 using System.Net.Cache;
+#if NET_4_5
+using System.Threading.Tasks;
+#endif
 
 namespace System.Net 
 {
@@ -83,9 +88,14 @@ namespace System.Net
                NameValueCollection queryString;
                bool is_busy;
                bool async;
+               bool proxySet = false;
                Thread async_thread;
                Encoding encoding = Encoding.Default;
                IWebProxy proxy;
+//             RequestCachePolicy cache_policy;
+#if NET_4_5
+               CancellationTokenSource cts;
+#endif
 
                // Constructors
                static WebClient ()
@@ -130,25 +140,23 @@ namespace System.Net
                        return new NotImplementedException ();
                }
                
-               [MonoTODO]
+               [MonoTODO ("Value can be set but is currently ignored")]
                public RequestCachePolicy CachePolicy
                {
                        get {
                                throw GetMustImplement ();
                        }
-                       set {
-                               throw GetMustImplement ();
-                       }
+                       set { /*cache_policy = value;*/ }
                }
 
-               [MonoTODO]
+               [MonoTODO ("Value can be set but is ignored")]
                public bool UseDefaultCredentials
                {
                        get {
                                throw GetMustImplement ();
                        }
                        set {
-                               throw GetMustImplement ();
+                               // This makes no sense in mono
                        }
                }
                
@@ -191,19 +199,33 @@ namespace System.Net
                }
 
                public IWebProxy Proxy {
-                       get { return proxy; }
-                       set { proxy = value; }
+                       get {
+                               if (!proxySet)
+                                       return WebRequest.DefaultWebProxy;
+
+                               return proxy;
+                       }
+                       set {
+                               proxy = value;
+                               proxySet = true;
+                       }
                }
 
                public bool IsBusy {
-                       get { return is_busy; } 
+                       get {
+#if NET_4_5
+                               return is_busy || (cts != null);
+#else
+                               return is_busy;
+#endif
+                       }
                }
                // Methods
 
                void CheckBusy ()
                {
                        if (IsBusy)
-                               throw new NotSupportedException ("WebClient does not support conccurent I/O operations.");
+                               throw new NotSupportedException ("WebClient does not support concurrent I/O operations.");
                }
 
                void SetBusy ()
@@ -248,12 +270,11 @@ namespace System.Net
                        } catch (ThreadInterruptedException){
                                if (request != null)
                                        request.Abort ();
-                               throw;
+                               throw new WebException ("User canceled the request", WebExceptionStatus.RequestCanceled);
                        } catch (WebException) {
                                throw;
                        } catch (Exception ex) {
-                               throw new WebException ("An error occurred " +
-                                       "performing a WebClient request.", ex);
+                               throw new WebException ("An error occurred performing a WebClient request.", ex);
                        }
                }
 
@@ -521,8 +542,12 @@ namespace System.Net
                                fileCType = "application/octet-stream";
                        }
 
-                       string boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
-                       Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
+                       bool needs_boundary = (method != "PUT"); // only verified case so far
+                       string boundary = null;
+                       if (needs_boundary) {
+                               boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
+                               Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
+                       }
                        Stream reqStream = null;
                        Stream fStream = null;
                        byte [] resultBytes = null;
@@ -534,23 +559,59 @@ namespace System.Net
                                fStream = File.OpenRead (fileName);
                                request = SetupRequest (address, method, true);
                                reqStream = request.GetRequestStream ();
-                               byte [] realBoundary = Encoding.ASCII.GetBytes ("--" + boundary + "\r\n");
-                               reqStream.Write (realBoundary, 0, realBoundary.Length);
-                               string partHeaders = String.Format ("Content-Disposition: form-data; " +
-                                                                   "name=\"file\"; filename=\"{0}\"\r\n" +
-                                                                   "Content-Type: {1}\r\n\r\n",
-                                                                   Path.GetFileName (fileName), fileCType);
-
-                               byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
-                               reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);
+                               byte [] bytes_boundary = null;
+                               if (needs_boundary) {
+                                       bytes_boundary = Encoding.ASCII.GetBytes (boundary);
+                                       reqStream.WriteByte ((byte) '-');
+                                       reqStream.WriteByte ((byte) '-');
+                                       reqStream.Write (bytes_boundary, 0, bytes_boundary.Length);
+                                       reqStream.WriteByte ((byte) '\r');
+                                       reqStream.WriteByte ((byte) '\n');
+                                       string partHeaders = String.Format ("Content-Disposition: form-data; " +
+                                                                           "name=\"file\"; filename=\"{0}\"\r\n" +
+                                                                           "Content-Type: {1}\r\n\r\n",
+                                                                           Path.GetFileName (fileName), fileCType);
+
+                                       byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
+                                       reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);
+                               }
                                int nread;
+                               long bytes_sent = 0;
+                               long file_size = -1;
+                               long step = 16384; // every 16kB
+                               if (fStream.CanSeek) {
+                                       file_size = fStream.Length;
+                                       step = file_size / 100;
+                               }
+                               var upload_args = new UploadProgressChangedEventArgs (0, 0, bytes_sent, file_size, 0, userToken);
+                               OnUploadProgressChanged (upload_args);
                                byte [] buffer = new byte [4096];
-                               while ((nread = fStream.Read (buffer, 0, 4096)) != 0)
+                               long sum = 0;
+                               while ((nread = fStream.Read (buffer, 0, 4096)) > 0) {
                                        reqStream.Write (buffer, 0, nread);
+                                       bytes_sent += nread;
+                                       sum += nread;
+                                       if (sum >= step || nread < 4096) {
+                                               int percent = 0;
+                                               if (file_size > 0)
+                                                       percent = (int) (bytes_sent * 100 / file_size);
+                                               upload_args = new UploadProgressChangedEventArgs (0, 0, bytes_sent, file_size, percent, userToken);
+                                               OnUploadProgressChanged (upload_args);
+                                               sum = 0;
+                                       }
+                               }
 
-                               reqStream.WriteByte ((byte) '\r');
-                               reqStream.WriteByte ((byte) '\n');
-                               reqStream.Write (realBoundary, 0, realBoundary.Length);
+                               if (needs_boundary) {
+                                       reqStream.WriteByte ((byte) '\r');
+                                       reqStream.WriteByte ((byte) '\n');
+                                       reqStream.WriteByte ((byte) '-');
+                                       reqStream.WriteByte ((byte) '-');
+                                       reqStream.Write (bytes_boundary, 0, bytes_boundary.Length);
+                                       reqStream.WriteByte ((byte) '-');
+                                       reqStream.WriteByte ((byte) '-');
+                                       reqStream.WriteByte ((byte) '\r');
+                                       reqStream.WriteByte ((byte) '\n');
+                               }
                                reqStream.Close ();
                                reqStream = null;
                                resultBytes = ReadAll (request, userToken);
@@ -778,9 +839,16 @@ namespace System.Net
                WebRequest SetupRequest (Uri uri)
                {
                        WebRequest request = GetWebRequest (uri);
-                       if (Proxy != null)
+                       if (proxySet)
                                request.Proxy = Proxy;
-                       request.Credentials = credentials;
+                       if (credentials != null)
+                               request.Credentials = credentials;
+                       else if (!String.IsNullOrEmpty (uri.UserInfo)) {
+                               // Perhaps this should be done by the underlying URI handler?
+                               ICredentials creds = GetCredentials (uri.UserInfo);
+                               if (creds != null)
+                                       request.Credentials = creds;
+                       }
 
                        // Special headers. These are properties of HttpWebRequest.
                        // What do we do with other requests differnt from HttpWebRequest?
@@ -830,6 +898,21 @@ namespace System.Net
                        return request;
                }
 
+               static NetworkCredential GetCredentials (string user_info)
+               {
+                       string [] creds = user_info.Split (':');
+                       if (creds.Length != 2)
+                               return null;
+
+                       if (creds [0].IndexOf ('\\') != -1) {
+                               string [] user = creds [0].Split ('\\');
+                               if (user.Length != 2)
+                                       return null;
+                               return new NetworkCredential (user [1], creds [1], user [0]);
+                       }
+                       return new NetworkCredential (creds [0], creds [1]);
+               }
+
                byte [] ReadAll (WebRequest request, object userToken)
                {
                        WebResponse response = GetWebResponse (request);
@@ -850,7 +933,7 @@ namespace System.Net
                        if (nolength)
                                ms = new MemoryStream ();
 
-//                     long total = 0;
+                       long total = 0;
                        int nread = 0;
                        int offset = 0;
                        byte [] buffer = new byte [size];
@@ -862,8 +945,8 @@ namespace System.Net
                                        size -= nread;
                                }
                                if (async){
-//                                     total += nread;
-                                       OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (nread, length, userToken));
+                                       total += nread;
+                                       OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (total, length, userToken));
                                }
                        }
 
@@ -930,6 +1013,13 @@ namespace System.Net
                public void CancelAsync ()
                {
                        lock (this){
+#if NET_4_5
+                               if (cts != null) {
+                                       cts.Cancel ();
+                                       return;
+                               }
+#endif
+
                                if (async_thread == null)
                                        return;
 
@@ -947,9 +1037,14 @@ namespace System.Net
 
                void CompleteAsync ()
                {
-                       lock (this){
+                       lock (this) {
                                is_busy = false;
                                async_thread = null;
+#if NET_4_5
+                               if (cts != null)
+                                       cts.Dispose ();
+                               cts = null;
+#endif
                        }
                }
 
@@ -975,16 +1070,17 @@ namespace System.Net
                                                byte [] data = DownloadDataCore ((Uri) args [0], args [1]);
                                                OnDownloadDataCompleted (
                                                        new DownloadDataCompletedEventArgs (data, null, false, args [1]));
-                                       } catch (ThreadInterruptedException){
-                                               OnDownloadDataCompleted (
-                                                       new DownloadDataCompletedEventArgs (null, null, true, args [1]));
-                                               throw;
                                        } catch (Exception e){
+                                               bool canceled = false;
+                                               WebException we = e as WebException;
+                                               if (we != null)
+                                                       canceled = we.Status == WebExceptionStatus.RequestCanceled;
                                                OnDownloadDataCompleted (
-                                                       new DownloadDataCompletedEventArgs (null, e, false, args [1]));
+                                                       new DownloadDataCompletedEventArgs (null, e, canceled, args [1]));
                                        }
                                });
-                               object [] cb_args = new object [] {address, userToken};
+                               object [] cb_args = new object [] { CreateUri (address), userToken };
+                               async_thread.IsBackground = true;
                                async_thread.Start (cb_args);
                        }
                }
@@ -1020,7 +1116,8 @@ namespace System.Net
                                                OnDownloadFileCompleted (
                                                        new AsyncCompletedEventArgs (e, false, args [2]));
                                        }});
-                               object [] cb_args = new object [] {address, fileName, userToken};
+                               object [] cb_args = new object [] { CreateUri (address), fileName, userToken };
+                               async_thread.IsBackground = true;
                                async_thread.Start (cb_args);
                        }
                }
@@ -1047,14 +1144,16 @@ namespace System.Net
                                                string data = encoding.GetString (DownloadDataCore ((Uri) args [0], args [1]));
                                                OnDownloadStringCompleted (
                                                        new DownloadStringCompletedEventArgs (data, null, false, args [1]));
-                                       } catch (ThreadInterruptedException){
-                                               OnDownloadStringCompleted (
-                                                       new DownloadStringCompletedEventArgs (null, null, true, args [1]));
                                        } catch (Exception e){
+                                               bool canceled = false;
+                                               WebException we = e as WebException;
+                                               if (we != null)
+                                                       canceled = we.Status == WebExceptionStatus.RequestCanceled;
                                                OnDownloadStringCompleted (
-                                                       new DownloadStringCompletedEventArgs (null, e, false, args [1]));
+                                                       new DownloadStringCompletedEventArgs (null, e, canceled, args [1]));
                                        }});
-                               object [] cb_args = new object [] {address, userToken};
+                               object [] cb_args = new object [] { CreateUri (address), userToken };
+                               async_thread.IsBackground = true;
                                async_thread.Start (cb_args);
                        }
                }
@@ -1092,7 +1191,8 @@ namespace System.Net
                                        } catch (Exception e){
                                                OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, e, false, args [1]));
                                        } });
-                               object [] cb_args = new object [] {address, userToken};
+                               object [] cb_args = new object [] { CreateUri (address), userToken };
+                               async_thread.IsBackground = true;
                                async_thread.Start (cb_args);
                        }
                }
@@ -1135,7 +1235,8 @@ namespace System.Net
                                                OnOpenWriteCompleted (
                                                        new OpenWriteCompletedEventArgs (null, e, false, args [2]));
                                        }});
-                               object [] cb_args = new object [] {address, method, userToken};
+                               object [] cb_args = new object [] { CreateUri (address), method, userToken };
+                               async_thread.IsBackground = true;
                                async_thread.Start (cb_args);
                        }
                }
@@ -1179,7 +1280,8 @@ namespace System.Net
                                                OnUploadDataCompleted (
                                                        new UploadDataCompletedEventArgs (null, e, false, args [3]));
                                        }});
-                               object [] cb_args = new object [] {address, method, data,  userToken};
+                               object [] cb_args = new object [] { CreateUri (address), method, data,  userToken };
+                               async_thread.IsBackground = true;
                                async_thread.Start (cb_args);
                        }
                }
@@ -1222,7 +1324,8 @@ namespace System.Net
                                                OnUploadFileCompleted (
                                                        new UploadFileCompletedEventArgs (null, e, false, args [3]));
                                        }});
-                               object [] cb_args = new object [] {address, method, fileName,  userToken};
+                               object [] cb_args = new object [] { CreateUri (address), method, fileName,  userToken };
+                               async_thread.IsBackground = true;
                                async_thread.Start (cb_args);
                        }
                }
@@ -1264,29 +1367,30 @@ namespace System.Net
                                                OnUploadStringCompleted (
                                                        new UploadStringCompletedEventArgs (null, e, false, args [3]));
                                        }});
-                               object [] cb_args = new object [] {address, method, data, userToken};
+                               object [] cb_args = new object [] { CreateUri (address), method, data, userToken };
+                               async_thread.IsBackground = true;
                                async_thread.Start (cb_args);
                        }
                }
 
                //    UploadValuesAsync
 
-               public void UploadValuesAsync (Uri address, NameValueCollection values)
+               public void UploadValuesAsync (Uri address, NameValueCollection data)
                {
-                       UploadValuesAsync (address, null, values);
+                       UploadValuesAsync (address, null, data);
                }
 
-               public void UploadValuesAsync (Uri address, string method, NameValueCollection values)
+               public void UploadValuesAsync (Uri address, string method, NameValueCollection data)
                {
-                       UploadValuesAsync (address, method, values, null);
+                       UploadValuesAsync (address, method, data, null);
                }
 
-               public void UploadValuesAsync (Uri address, string method, NameValueCollection values, object userToken)
+               public void UploadValuesAsync (Uri address, string method, NameValueCollection data, object userToken)
                {
                        if (address == null)
                                throw new ArgumentNullException ("address");
-                       if (values == null)
-                               throw new ArgumentNullException ("values");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
 
                        lock (this) {
                                CheckBusy ();
@@ -1295,9 +1399,9 @@ namespace System.Net
                                async_thread = new Thread (delegate (object state) {
                                        object [] args = (object []) state;
                                        try {
-                                               byte [] data = UploadValuesCore ((Uri) args [0], (string) args [1], (NameValueCollection) args [2], args [3]);
+                                               byte [] values = UploadValuesCore ((Uri) args [0], (string) args [1], (NameValueCollection) args [2], args [3]);
                                                OnUploadValuesCompleted (
-                                                       new UploadValuesCompletedEventArgs (data, null, false, args [3]));
+                                                       new UploadValuesCompletedEventArgs (values, null, false, args [3]));
                                        } catch (ThreadInterruptedException){
                                                OnUploadValuesCompleted (
                                                        new UploadValuesCompletedEventArgs (null, null, true, args [3]));
@@ -1305,23 +1409,24 @@ namespace System.Net
                                                OnUploadValuesCompleted (
                                                        new UploadValuesCompletedEventArgs (null, e, false, args [3]));
                                        }});
-                               object [] cb_args = new object [] {address, method, values,  userToken};
+                               object [] cb_args = new object [] { CreateUri (address), method, data,  userToken };
+                               async_thread.IsBackground = true;
                                async_thread.Start (cb_args);
                        }
                }
 
-               protected virtual void OnDownloadDataCompleted (DownloadDataCompletedEventArgs args)
+               protected virtual void OnDownloadDataCompleted (DownloadDataCompletedEventArgs e)
                {
                        CompleteAsync ();
                        if (DownloadDataCompleted != null)
-                               DownloadDataCompleted (this, args);
+                               DownloadDataCompleted (this, e);
                }
 
-               protected virtual void OnDownloadFileCompleted (AsyncCompletedEventArgs args)
+               protected virtual void OnDownloadFileCompleted (AsyncCompletedEventArgs e)
                {
                        CompleteAsync ();
                        if (DownloadFileCompleted != null)
-                               DownloadFileCompleted (this, args);
+                               DownloadFileCompleted (this, e);
                }
 
                protected virtual void OnDownloadProgressChanged (DownloadProgressChangedEventArgs e)
@@ -1330,39 +1435,39 @@ namespace System.Net
                                DownloadProgressChanged (this, e);
                }
 
-               protected virtual void OnDownloadStringCompleted (DownloadStringCompletedEventArgs args)
+               protected virtual void OnDownloadStringCompleted (DownloadStringCompletedEventArgs e)
                {
                        CompleteAsync ();
                        if (DownloadStringCompleted != null)
-                               DownloadStringCompleted (this, args);
+                               DownloadStringCompleted (this, e);
                }
 
-               protected virtual void OnOpenReadCompleted (OpenReadCompletedEventArgs args)
+               protected virtual void OnOpenReadCompleted (OpenReadCompletedEventArgs e)
                {
                        CompleteAsync ();
                        if (OpenReadCompleted != null)
-                               OpenReadCompleted (this, args);
+                               OpenReadCompleted (this, e);
                }
 
-               protected virtual void OnOpenWriteCompleted (OpenWriteCompletedEventArgs args)
+               protected virtual void OnOpenWriteCompleted (OpenWriteCompletedEventArgs e)
                {
                        CompleteAsync ();
                        if (OpenWriteCompleted != null)
-                               OpenWriteCompleted (this, args);
+                               OpenWriteCompleted (this, e);
                }
 
-               protected virtual void OnUploadDataCompleted (UploadDataCompletedEventArgs args)
+               protected virtual void OnUploadDataCompleted (UploadDataCompletedEventArgs e)
                {
                        CompleteAsync ();
                        if (UploadDataCompleted != null)
-                               UploadDataCompleted (this, args);
+                               UploadDataCompleted (this, e);
                }
 
-               protected virtual void OnUploadFileCompleted (UploadFileCompletedEventArgs args)
+               protected virtual void OnUploadFileCompleted (UploadFileCompletedEventArgs e)
                {
                        CompleteAsync ();
                        if (UploadFileCompleted != null)
-                               UploadFileCompleted (this, args);
+                               UploadFileCompleted (this, e);
                }
 
                protected virtual void OnUploadProgressChanged (UploadProgressChangedEventArgs e)
@@ -1371,18 +1476,18 @@ namespace System.Net
                                UploadProgressChanged (this, e);
                }
 
-               protected virtual void OnUploadStringCompleted (UploadStringCompletedEventArgs args)
+               protected virtual void OnUploadStringCompleted (UploadStringCompletedEventArgs e)
                {
                        CompleteAsync ();
                        if (UploadStringCompleted != null)
-                               UploadStringCompleted (this, args);
+                               UploadStringCompleted (this, e);
                }
 
-               protected virtual void OnUploadValuesCompleted (UploadValuesCompletedEventArgs args)
+               protected virtual void OnUploadValuesCompleted (UploadValuesCompletedEventArgs e)
                {
                        CompleteAsync ();
                        if (UploadValuesCompleted != null)
-                               UploadValuesCompleted (this, args);
+                               UploadValuesCompleted (this, e);
                }
 
                protected virtual WebResponse GetWebResponse (WebRequest request, IAsyncResult result)
@@ -1403,6 +1508,699 @@ namespace System.Net
                        responseHeaders = response.Headers;
                        return response;
                }
+               
+#if NET_4_5
+
+               // DownloadDataTaskAsync
+               
+               public Task<byte[]> DownloadDataTaskAsync (string address)
+               {
+                       return DownloadDataTaskAsync (CreateUri (address));
+               }
+
+               public async Task<byte[]> DownloadDataTaskAsync (Uri address)
+               {
+                       WebRequest request = null;
+                       WebResponse response = null;
+                       try {
+                               SetBusy ();
+                               cts = new CancellationTokenSource ();
+                               request = await SetupRequestAsync (address);
+                               response = await GetWebResponseTaskAsync (request, cts.Token);
+                               var result = await ReadAllTaskAsync (request, response, cts.Token);
+                               OnDownloadDataCompleted (new DownloadDataCompletedEventArgs (result, null, false, null));
+                               return result;
+                       } catch (WebException ex) {
+                               OnDownloadDataCompleted (new DownloadDataCompletedEventArgs (null, ex, false, null));
+                               throw;
+                       } catch (OperationCanceledException) {
+                               if (request != null)
+                                       request.Abort ();
+                               OnDownloadDataCompleted (new DownloadDataCompletedEventArgs (null, null, true, null));
+                               throw;
+                       } catch (Exception ex) {
+                               OnDownloadDataCompleted (new DownloadDataCompletedEventArgs (null, ex, true, null));
+                               throw new WebException ("An error occurred performing a WebClient request.", ex);
+                       } finally {
+                               if (response != null)
+                                       response.Close ();
+                       }
+               }
+
+               Task<WebRequest> SetupRequestAsync (Uri address)
+               {
+                       return Task.Factory.StartNew (() => SetupRequest (address));
+               }
+
+               async Task<WebRequest> SetupRequestAsync (Uri address, string method, bool is_upload)
+               {
+                       WebRequest request = await SetupRequestAsync (address);
+                       request.Method = DetermineMethod (address, method, is_upload);
+                       return request;
+               }
+               
+               async Task<WebResponse> GetWebResponseTaskAsync (WebRequest request, CancellationToken token)
+               {
+                       token.ThrowIfCancellationRequested ();
+                       WebResponse response = await request.GetResponseAsync ();
+                       token.ThrowIfCancellationRequested ();
+                       responseHeaders = response.Headers;
+                       return response;
+               }
+               
+               async Task<byte[]> ReadAllTaskAsync (WebRequest request, WebResponse response, CancellationToken token)
+               {
+                       Stream stream = response.GetResponseStream ();
+                       int length = (int)response.ContentLength;
+                       HttpWebRequest wreq = request as HttpWebRequest;
+
+                       if (length > -1 && wreq != null && (int)wreq.AutomaticDecompression != 0) {
+                               string content_encoding = ((HttpWebResponse)response).ContentEncoding;
+                               if (((content_encoding == "gzip" && (wreq.AutomaticDecompression & DecompressionMethods.GZip) != 0)) ||
+                                       ((content_encoding == "deflate" && (wreq.AutomaticDecompression & DecompressionMethods.Deflate) != 0)))
+                                       length = -1;
+                       }
+
+                       MemoryStream ms = null;
+                       bool nolength = (length == -1);
+                       int size = ((nolength) ? 8192 : length);
+                       if (nolength)
+                               ms = new MemoryStream ();
+
+                       long total = 0;
+                       int nread = 0;
+                       int offset = 0;
+                       byte [] buffer = new byte [size];
+                       token.ThrowIfCancellationRequested ();
+                       while ((nread = await stream.ReadAsync (buffer, offset, size, token)) != 0) {
+                               if (nolength) {
+                                       ms.Write (buffer, 0, nread);
+                               } else {
+                                       offset += nread;
+                                       size -= nread;
+                               }
+                               total += nread;
+                               OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (total, length, null));
+                               token.ThrowIfCancellationRequested ();
+                       }
+                       
+                       return nolength ? ms.ToArray () : buffer;
+               }
+               
+               // DownloadFileTaskAsync
+               
+               public Task DownloadFileTaskAsync (string address, string fileName)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return DownloadFileTaskAsync (CreateUri (address), fileName);
+               }
+
+               public async Task DownloadFileTaskAsync (Uri address, string fileName)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (fileName == null)
+                               throw new ArgumentNullException ("fileName");
+                       
+                       WebRequest request = null;
+                       WebResponse response = null;
+
+                       try {
+                               SetBusy ();
+                               cts = new CancellationTokenSource ();
+                               request = await SetupRequestAsync (address);
+                               response = await GetWebResponseTaskAsync (request, cts.Token);
+                               await DownloadFileTaskAsyncCore (request, response, fileName, cts.Token);
+                               OnDownloadFileCompleted (new AsyncCompletedEventArgs (null, false, null));
+                       } catch (WebException ex) {
+                               OnDownloadFileCompleted (new AsyncCompletedEventArgs (ex, false, null));
+                               throw;
+                       } catch (OperationCanceledException) {
+                               if (request != null)
+                                       request.Abort ();
+                               OnDownloadFileCompleted (new AsyncCompletedEventArgs (null, true, null));
+                               throw;
+                       } catch (Exception ex) {
+                               OnDownloadFileCompleted (new AsyncCompletedEventArgs (ex, false, null));
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
+                       } finally {
+                               if (response != null)
+                                       response.Close ();
+                       }
+               }
+
+               async Task DownloadFileTaskAsyncCore (WebRequest request, WebResponse response,
+                                                     string fileName, CancellationToken token)
+               {
+                       using (FileStream f = new FileStream (fileName, FileMode.Create)) {
+                               Stream st = response.GetResponseStream ();
+                                       
+                               int cLength = (int)response.ContentLength;
+                               int length = (cLength <= -1 || cLength > 32 * 1024) ? 32 * 1024 : cLength;
+                               byte [] buffer = new byte [length];
+                                       
+                               int nread = 0;
+                               long notify_total = 0;
+                               token.ThrowIfCancellationRequested ();
+                               while ((nread = await st.ReadAsync (buffer, 0, length, token)) != 0) {
+                                       notify_total += nread;
+                                       OnDownloadProgressChanged (
+                                               new DownloadProgressChangedEventArgs (notify_total, response.ContentLength, null));
+                                       token.ThrowIfCancellationRequested ();
+                                       await f.WriteAsync (buffer, 0, nread, token);
+                                       token.ThrowIfCancellationRequested ();
+                               }
+                       }
+               }
+               
+               // OpenReadTaskAsync
+               
+               public Task<Stream> OpenReadTaskAsync (string address)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       return OpenReadTaskAsync (CreateUri (address));
+               }
+
+               public async Task<Stream> OpenReadTaskAsync (Uri address)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       WebRequest request = null;
+                       try {
+                               SetBusy ();
+                               cts = new CancellationTokenSource ();
+                               request = await SetupRequestAsync (address);
+                               WebResponse response = await GetWebResponseTaskAsync (request, cts.Token);
+                               var result = response.GetResponseStream ();
+                               cts.Token.ThrowIfCancellationRequested ();
+                               OnOpenReadCompleted (new OpenReadCompletedEventArgs (result, null, false, null));
+                               return result;
+                       } catch (WebException ex) {
+                               OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, ex, false, null));
+                               throw;
+                       } catch (OperationCanceledException) {
+                               if (request != null)
+                                       request.Abort ();
+                               OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, null, true, null));
+                               throw;
+                       } catch (Exception ex) {
+                               OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, ex, false, null));
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
+                       }
+               }
+               
+               // DownloadStringTaskAsync
+               
+               public Task<string> DownloadStringTaskAsync (string address)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return DownloadStringTaskAsync (CreateUri (address));
+               }
+
+               public async Task<string> DownloadStringTaskAsync (Uri address)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       WebRequest request = null;
+                       WebResponse response = null;
+
+                       try {
+                               SetBusy ();
+                               cts = new CancellationTokenSource ();
+                               request = await SetupRequestAsync (address);
+                               response = await GetWebResponseTaskAsync (request, cts.Token);
+                               var data = await ReadAllTaskAsync (request, response, cts.Token);
+                               cts.Token.ThrowIfCancellationRequested ();
+                               var text = encoding.GetString (data);
+                               OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (text, null, false, null));
+                               return text;
+                       } catch (WebException ex) {
+                               OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (null, ex, false, null));
+                               throw;
+                       } catch (OperationCanceledException) {
+                               if (request != null)
+                                       request.Abort ();
+                               OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (null, null, true, null));
+                               throw;
+                       } catch (Exception ex) {
+                               OnDownloadStringCompleted (new DownloadStringCompletedEventArgs (null, ex, true, null));
+                               throw new WebException ("An error occurred performing a WebClient request.", ex);
+                       } finally {
+                               if (response != null)
+                                       response.Close ();
+                       }
+               }
+
+               // OpenWriteTaskAsync
+               
+               public Task<Stream> OpenWriteTaskAsync (string address)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return OpenWriteTaskAsync (CreateUri (address));
+               }
+               
+               public Task<Stream> OpenWriteTaskAsync (string address, string method)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return OpenWriteTaskAsync (CreateUri (address), method);
+               }
+
+               public Task<Stream> OpenWriteTaskAsync (Uri address)
+               {
+                       return OpenWriteTaskAsync (address, (string) null);
+               }
+
+               public async Task<Stream> OpenWriteTaskAsync (Uri address, string method)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       WebRequest request = null;
+                       try {
+                               SetBusy ();
+                               cts = new CancellationTokenSource ();
+                               request = SetupRequest (address);
+                               return await request.GetRequestStreamAsync ();
+                       } catch (WebException) {
+                               throw;
+                       } catch (OperationCanceledException) {
+                               if (request != null)
+                                       request.Abort ();
+                               throw;
+                       } catch (Exception ex) {
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
+                       } finally {
+                               CompleteAsync ();
+                       }
+               }
+
+               // UploadDataTaskAsync
+               
+               public Task<byte[]> UploadDataTaskAsync (string address, byte [] data)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return UploadDataTaskAsync (CreateUri (address), data);
+               }
+               
+               public Task<byte[]> UploadDataTaskAsync (string address, string method, byte [] data)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return UploadDataTaskAsync (CreateUri (address), method, data);
+               }
+
+               public Task<byte[]> UploadDataTaskAsync (Uri address, byte [] data)
+               {
+                       return UploadDataTaskAsync (address, (string) null, data);
+               }
+
+               public async Task<byte[]> UploadDataTaskAsync (Uri address, string method, byte [] data)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
+                       WebRequest request = null;
+                       try {
+                               SetBusy ();
+                               cts = new CancellationTokenSource ();
+                               request = await SetupRequestAsync (address, method, true);
+                               var result = await UploadDataTaskAsyncCore (request, data, cts.Token);
+                               OnUploadDataCompleted (new UploadDataCompletedEventArgs (result, null, false, null));
+                               return result;
+                       } catch (WebException ex) {
+                               OnUploadDataCompleted (new UploadDataCompletedEventArgs (null, ex, false, null));
+                               throw;
+                       } catch (OperationCanceledException) {
+                               if (request != null)
+                                       request.Abort ();
+                               OnUploadDataCompleted (new UploadDataCompletedEventArgs (null, null, true, null));
+                               throw;
+                       } catch (Exception ex) {
+                               OnUploadDataCompleted (new UploadDataCompletedEventArgs (null, ex, true, null));
+                               throw new WebException ("An error occurred performing a WebClient request.", ex);
+                       }
+               }
+
+               async Task<byte[]> UploadDataTaskAsyncCore (WebRequest request, byte[] data, CancellationToken token)
+               {
+                       token.ThrowIfCancellationRequested ();
+
+                       int contentLength = data.Length;
+                       request.ContentLength = contentLength;
+                       using (Stream stream = await request.GetRequestStreamAsync ()) {
+                               token.ThrowIfCancellationRequested ();
+                               await stream.WriteAsync (data, 0, contentLength, token);
+                               token.ThrowIfCancellationRequested ();
+                       }
+
+                       WebResponse response = null;
+
+                       try {
+                               response = await GetWebResponseTaskAsync (request, token);
+                               return await ReadAllTaskAsync (request, response, token);
+                       } finally {
+                               if (response != null)
+                                       response.Close ();
+                       }
+               }
+
+               // UploadFileTaskAsync
+
+               public Task<byte[]> UploadFileTaskAsync (string address, string fileName)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return UploadFileTaskAsync (CreateUri (address), fileName);
+               }
+
+               public Task<byte[]> UploadFileTaskAsync (Uri address, string fileName)
+               {
+                       return UploadFileTaskAsync (address, (string) null, fileName);
+               }
+               
+               public Task<byte[]> UploadFileTaskAsync (string address, string method, string fileName)
+               {
+                       return UploadFileTaskAsync (CreateUri (address), method, fileName);
+               }
+
+               public async Task<byte[]> UploadFileTaskAsync (Uri address, string method, string fileName)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (fileName == null)
+                               throw new ArgumentNullException ("fileName");
+
+                       WebRequest request = null;
+                       try {
+                               SetBusy ();
+                               cts = new CancellationTokenSource ();
+                               request = await SetupRequestAsync (address, method, true);
+                               var result = await UploadFileTaskAsyncCore (request, method, fileName, cts.Token);
+                               OnUploadFileCompleted (new UploadFileCompletedEventArgs (result, null, false, null));
+                               return result;
+                       } catch (WebException ex) {
+                               OnUploadFileCompleted (new UploadFileCompletedEventArgs (null, ex, false, null));
+                               throw;
+                       } catch (OperationCanceledException) {
+                               if (request != null)
+                                       request.Abort ();
+                               OnUploadFileCompleted (new UploadFileCompletedEventArgs (null, null, true, null));
+                               throw;
+                       } catch (Exception ex) {
+                               OnUploadFileCompleted (new UploadFileCompletedEventArgs (null, ex, true, null));
+                               throw new WebException ("An error occurred performing a WebClient request.", ex);
+                       }
+               }
+
+               async Task<byte[]> UploadFileTaskAsyncCore (WebRequest request, string method,
+                                                           string fileName, CancellationToken token)
+               {
+                       token.ThrowIfCancellationRequested ();
+
+                       string fileCType = Headers ["Content-Type"];
+                       if (fileCType != null) {
+                               string lower = fileCType.ToLower ();
+                               if (lower.StartsWith ("multipart/"))
+                                       throw new WebException ("Content-Type cannot be set to a multipart" +
+                                                               " type for this request.");
+                       } else {
+                               fileCType = "application/octet-stream";
+                       }
+
+                       bool needs_boundary = (method != "PUT"); // only verified case so far
+                       string boundary = null;
+                       if (needs_boundary) {
+                               boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
+                               Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
+                       }
+                       Stream reqStream = null;
+                       Stream fStream = null;
+                       WebResponse response = null;
+
+                       fileName = Path.GetFullPath (fileName);
+
+                       try {
+                               fStream = File.OpenRead (fileName);
+                               token.ThrowIfCancellationRequested ();
+                               reqStream = await request.GetRequestStreamAsync ();
+                               token.ThrowIfCancellationRequested ();
+                               byte [] bytes_boundary = null;
+                               if (needs_boundary) {
+                                       bytes_boundary = Encoding.ASCII.GetBytes (boundary);
+                                       using (MemoryStream ms = new MemoryStream ()) {
+                                               ms.WriteByte ((byte) '-');
+                                               ms.WriteByte ((byte) '-');
+                                               ms.Write (bytes_boundary, 0, bytes_boundary.Length);
+                                               ms.WriteByte ((byte) '\r');
+                                               ms.WriteByte ((byte) '\n');
+                                               string partHeaders = String.Format (
+                                                       "Content-Disposition: form-data; " +
+                                                       "name=\"file\"; filename=\"{0}\"\r\n" +
+                                                       "Content-Type: {1}\r\n\r\n",
+                                                       Path.GetFileName (fileName), fileCType);
+                                               byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
+                                               ms.Write (partHeadersBytes, 0, partHeadersBytes.Length);
+                                               await ms.CopyToAsync (reqStream, (int)ms.Position, token);
+                                       }
+                               }
+                               int nread;
+                               long bytes_sent = 0;
+                               long file_size = -1;
+                               long step = 16384; // every 16kB
+                               if (fStream.CanSeek) {
+                                       file_size = fStream.Length;
+                                       step = file_size / 100;
+                               }
+                               var upload_args = new UploadProgressChangedEventArgs (0, 0, bytes_sent, file_size, 0, null);
+                               OnUploadProgressChanged (upload_args);
+                               byte [] buffer = new byte [4096];
+                               long sum = 0;
+                               token.ThrowIfCancellationRequested ();
+                               while ((nread = await fStream.ReadAsync (buffer, 0, 4096, token)) > 0) {
+                                       token.ThrowIfCancellationRequested ();
+                                       await reqStream.WriteAsync (buffer, 0, nread, token);
+                                       bytes_sent += nread;
+                                       sum += nread;
+                                       if (sum >= step || nread < 4096) {
+                                               int percent = 0;
+                                               if (file_size > 0)
+                                                       percent = (int) (bytes_sent * 100 / file_size);
+                                               upload_args = new UploadProgressChangedEventArgs (0, 0, bytes_sent, file_size, percent, null);
+                                               OnUploadProgressChanged (upload_args);
+                                               sum = 0;
+                                       }
+                               }
+
+                               if (needs_boundary) {
+                                       using (MemoryStream ms = new MemoryStream ()) {
+                                               ms.WriteByte ((byte) '\r');
+                                               ms.WriteByte ((byte) '\n');
+                                               ms.WriteByte ((byte) '-');
+                                               ms.WriteByte ((byte) '-');
+                                               ms.Write (bytes_boundary, 0, bytes_boundary.Length);
+                                               ms.WriteByte ((byte) '-');
+                                               ms.WriteByte ((byte) '-');
+                                               ms.WriteByte ((byte) '\r');
+                                               ms.WriteByte ((byte) '\n');
+                                               await ms.CopyToAsync (reqStream, (int)ms.Position, token);
+                                       }
+                               }
+                               reqStream.Close ();
+                               reqStream = null;
+
+                               response = await GetWebResponseTaskAsync (request, token);
+                               return await ReadAllTaskAsync (request, response, token);
+                       } finally {
+                               if (fStream != null)
+                                       fStream.Close ();
+
+                               if (reqStream != null)
+                                       reqStream.Close ();
+
+                               if (response != null)
+                                       response.Close ();
+                       }
+               }
+
+               // UploadStringTaskAsync
+
+               public Task<string> UploadStringTaskAsync (string address, string data)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
+                       return UploadStringTaskAsync (CreateUri (address), null, data);
+               }
+
+               public Task<string> UploadStringTaskAsync (string address, string method, string data)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
+                       return UploadStringTaskAsync (CreateUri (address), method, data);
+               }
+
+               public Task<string> UploadStringTaskAsync (Uri address, string data)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
+                       return UploadStringTaskAsync (address, null, data);
+               }
+
+               public async Task<string> UploadStringTaskAsync (Uri address, string method, string data)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
+                       WebRequest request = null;
+                       try {
+                               SetBusy ();
+                               cts = new CancellationTokenSource ();
+                               request = await SetupRequestAsync (address, method, true);
+                               var result = await UploadDataTaskAsyncCore (request, encoding.GetBytes (data), cts.Token);
+                               var result_str = encoding.GetString (result);
+                               OnUploadStringCompleted (new UploadStringCompletedEventArgs (result_str, null, false, null));
+                               return result_str;
+                       } catch (WebException ex) {
+                               OnUploadStringCompleted (new UploadStringCompletedEventArgs (null, ex, false, null));
+                               throw;
+                       } catch (OperationCanceledException) {
+                               if (request != null)
+                                       request.Abort ();
+                               OnUploadStringCompleted (new UploadStringCompletedEventArgs (null, null, true, null));
+                               throw;
+                       } catch (Exception ex) {
+                               OnUploadStringCompleted (new UploadStringCompletedEventArgs (null, ex, true, null));
+                               throw new WebException ("An error occurred performing a WebClient request.", ex);
+                       }
+               }
+
+               // UploadValuesTaskAsync
+
+               public Task<byte[]> UploadValuesTaskAsync (string address, NameValueCollection data)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return UploadValuesTaskAsync (CreateUri (address), data);
+               }
+               
+               public Task<byte[]> UploadValuesTaskAsync (string address, string method, NameValueCollection data)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return UploadValuesTaskAsync (CreateUri (address), method, data);
+               }
+
+               public Task<byte[]> UploadValuesTaskAsync (Uri address, NameValueCollection data)
+               {
+                       return UploadValuesTaskAsync (address, (string) null, data);
+               }
+
+               public async Task<byte[]> UploadValuesTaskAsync (Uri address, string method, NameValueCollection data)
+               {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
+                       WebRequest request = null;
+                       try {
+                               SetBusy ();
+                               cts = new CancellationTokenSource ();
+                               request = await SetupRequestAsync (address, method, true);
+                               var result = await UploadValuesTaskAsyncCore (request, data, cts.Token);
+                               OnUploadValuesCompleted (new UploadValuesCompletedEventArgs (result, null, false, null));
+                               return result;
+                       } catch (WebException ex) {
+                               OnUploadValuesCompleted (new UploadValuesCompletedEventArgs (null, ex, false, null));
+                               throw;
+                       } catch (OperationCanceledException) {
+                               if (request != null)
+                                       request.Abort ();
+                               OnUploadValuesCompleted (new UploadValuesCompletedEventArgs (null, null, true, null));
+                               throw;
+                       } catch (Exception ex) {
+                               OnUploadValuesCompleted (new UploadValuesCompletedEventArgs (null, ex, true, null));
+                               throw new WebException ("An error occurred performing a WebClient request.", ex);
+                       }
+               }
+
+               async Task<byte[]> UploadValuesTaskAsyncCore (WebRequest request, NameValueCollection data,
+                                                             CancellationToken token)
+               {
+                       token.ThrowIfCancellationRequested ();
+                       string cType = Headers ["Content-Type"];
+                       if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
+                               throw new WebException ("Content-Type header cannot be changed from its default " +
+                                                       "value for this request.");
+
+                       WebResponse response = null;
+
+                       Headers ["Content-Type"] = urlEncodedCType;
+                       try {
+                               MemoryStream tmpStream = new MemoryStream ();
+                               foreach (string key in data) {
+                                       byte [] bytes = Encoding.UTF8.GetBytes (key);
+                                       UrlEncodeAndWrite (tmpStream, bytes);
+                                       tmpStream.WriteByte ((byte) '=');
+                                       bytes = Encoding.UTF8.GetBytes (data [key]);
+                                       UrlEncodeAndWrite (tmpStream, bytes);
+                                       tmpStream.WriteByte ((byte) '&');
+                               }
+
+                               token.ThrowIfCancellationRequested ();
+                               
+                               int length = (int) tmpStream.Length;
+                               if (length > 0)
+                                       tmpStream.SetLength (--length); // remove trailing '&'
+                               
+                               byte [] buf = tmpStream.GetBuffer ();
+                               request.ContentLength = length;
+                               using (Stream rqStream = await request.GetRequestStreamAsync ()) {
+                                       await rqStream.WriteAsync (buf, 0, length, token);
+                               }
+                               tmpStream.Close ();
+
+                               response = await GetWebResponseTaskAsync (request, token);
+                               return await ReadAllTaskAsync (request, response, token);
+                       } finally {
+                               if (response != null)
+                                       response.Close ();
+                       }
+               }
+
+#endif
 
        }
 }