2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / class / System / System.Net / WebClient.cs
index 945b21cea22db19fb87aace3d0cf79b2f4112dc9..4b46f4c18a47bfb1816c2a6e8815e500c87b3c66 100644 (file)
@@ -5,11 +5,11 @@
 //     Lawrence Pit (loz@cable.a2000.nl)
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
 //     Atsushi Enomoto (atsushi@ximian.com)
+//     Miguel de Icaza (miguel@ximian.com)
 //
-// (c) 2003 Ximian, Inc. (http://www.ximian.com)
-// (C) 2006 Novell, Inc. (http://www.novell.com)
+// Copyright 2003 Ximian, Inc. (http://www.ximian.com)
+// Copyright 2006, 2007 Novell, Inc. (http://www.novell.com)
 //
-
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-
+//
+// Notes on CancelAsync and Async methods:
+//
+//    WebClient.CancelAsync is implemented by calling Thread.Interrupt
+//    in our helper thread.   The various async methods have to cancel
+//    any ongoing requests by calling request.Abort () at that point.
+//    In a few places (UploadDataCore, UploadValuesCore,
+//    UploadFileCore) we catch the ThreadInterruptedException and
+//    abort the request there.
+//
+//    Higher level routines (the async callbacks) also need to catch
+//    the exception and raise the OnXXXXCompleted events there with
+//    the "canceled" flag set to true. 
+//
+//    In a few other places where these helper routines are not used
+//    (OpenReadAsync for example) catching the ThreadAbortException
+//    also must abort the request.
+//
+//    The Async methods currently differ in their implementation from
+//    the .NET implementation in that we manually catch any other
+//    exceptions and correctly raise the OnXXXXCompleted passing the
+//    Exception that caused the problem.   The .NET implementation
+//    does not seem to have a mechanism to flag errors that happen
+//    during downloads though.    We do this because we still need to
+//    catch the exception on these helper threads, or we would
+//    otherwise kill the application (on the 2.x profile, uncaught
+//    exceptions in threads terminate the application).
+//
 using System;
 using System.Collections.Specialized;
 using System.ComponentModel;
@@ -38,9 +65,9 @@ using System.IO;
 using System.Runtime.InteropServices;
 using System.Runtime.Serialization;
 using System.Text;
-#if NET_2_0
-using System.Collections.Generic;
 using System.Threading;
+#if NET_2_0
+using System.Net.Cache;
 #endif
 
 namespace System.Net 
@@ -60,8 +87,10 @@ namespace System.Net
                Uri baseAddress;
                string baseString;
                NameValueCollection queryString;
-               bool isBusy;
+               bool is_busy;
 #if NET_2_0
+               bool async;
+               Thread async_thread;
                Encoding encoding = Encoding.Default;
                IWebProxy proxy;
 #endif
@@ -103,6 +132,35 @@ namespace System.Net
                                }
                        }
                }
+
+#if NET_2_0
+               static Exception GetMustImplement ()
+               {
+                       return new NotImplementedException ();
+               }
+               
+               [MonoTODO]
+               public RequestCachePolicy CachePolicy
+               {
+                       get {
+                               throw GetMustImplement ();
+                       }
+                       set {
+                               throw GetMustImplement ();
+                       }
+               }
+
+               [MonoTODO]
+               public bool UseDefaultCredentials
+               {
+                       get {
+                               throw GetMustImplement ();
+                       }
+                       set {
+                               throw GetMustImplement ();
+                       }
+               }
+#endif
                
                public ICredentials Credentials {
                        get { return credentials; }
@@ -151,11 +209,11 @@ namespace System.Net
 
 #if NET_2_0
                public bool IsBusy {
-                       get { return isBusy || wait_handles != null && wait_handles.Count > 0; }
+                       get { return is_busy; } 
                }
 #else
                bool IsBusy {
-                       get { return isBusy; }
+                       get { return is_busy; }
                }
 #endif
 
@@ -171,7 +229,7 @@ namespace System.Net
                {
                        lock (this) {
                                CheckBusy ();
-                               isBusy = true;
+                               is_busy = true;
                        }
                }
 
@@ -179,7 +237,12 @@ namespace System.Net
 
                public byte [] DownloadData (string address)
                {
-                       return DownloadData (MakeUri (address));
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       return DownloadData (CreateUri (address));
                }
 
 #if NET_2_0
@@ -187,27 +250,51 @@ namespace System.Net
 #endif
                byte [] DownloadData (Uri address)
                {
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
                        try {
                                SetBusy ();
-                               return DownloadDataCore (address);
+#if NET_2_0                            
+                               async = false;
+#endif                         
+                               return DownloadDataCore (address, null);
                        } finally {
-                               isBusy = false;
+                               is_busy = false;
                        }
                }
 
-               byte [] DownloadDataCore (Uri address)
+               byte [] DownloadDataCore (Uri address, object userToken)
                {
-                       WebRequest request = SetupRequest (address, "GET");
-                       WebResponse response = request.GetResponse ();
-                       Stream st = ProcessResponse (response);
-                       return ReadAll (st, (int) response.ContentLength);
+                       WebRequest request = null;
+                       
+                       try {
+                               request = SetupRequest (address);
+                               WebResponse response = request.GetResponse ();
+                               Stream st = ProcessResponse (response);
+                               return ReadAll (st, (int) response.ContentLength, userToken);
+                       } catch (ThreadInterruptedException){
+                               if (request != null)
+                                       request.Abort ();
+                               throw;
+                       } catch (Exception ex) {
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
+                       }
                }
 
                //   DownloadFile
 
                public void DownloadFile (string address, string fileName)
                {
-                       DownloadFile (MakeUri (address), fileName);
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       DownloadFile (CreateUri (address), fileName);
                }
 
 #if NET_2_0
@@ -215,37 +302,74 @@ namespace System.Net
 #endif
                void DownloadFile (Uri address, string fileName)
                {
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (fileName == null)
+                               throw new ArgumentNullException ("fileName");
+#endif
+
                        try {
                                SetBusy ();
-                               DownloadFileCore (address, fileName);
+#if NET_2_0                            
+                               async = false;
+#endif                         
+                               DownloadFileCore (address, fileName, null);
+                       } catch (Exception ex) {
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
                        } finally {
-                               isBusy = false;
+                               is_busy = false;
                        }
                }
 
-               void DownloadFileCore (Uri address, string fileName)
+               void DownloadFileCore (Uri address, string fileName, object userToken)
                {
-                       WebRequest request = SetupRequest (address);
-                       WebResponse response = request.GetResponse ();
-                       Stream st = ProcessResponse (response);
-
-                       int cLength = (int) response.ContentLength;
-                       int length = (cLength <= -1 || cLength > 8192) ? 8192 : cLength;
-                       byte [] buffer = new byte [length];
-                       FileStream f = new FileStream (fileName, FileMode.CreateNew);
-
-                       int nread = 0;
-                       while ((nread = st.Read (buffer, 0, length)) != 0)
-                               f.Write (buffer, 0, nread);
-
-                       f.Close ();
+                       WebRequest request = null;
+                       
+                       using (FileStream f = new FileStream (fileName, FileMode.Create)) {
+                               try {
+                                       request = SetupRequest (address);
+                                       WebResponse response = request.GetResponse ();
+                                       Stream st = ProcessResponse (response);
+                                       
+                                       int cLength = (int) response.ContentLength;
+                                       int length = (cLength <= -1 || cLength > 32*1024) ? 32*1024 : cLength;
+                                       byte [] buffer = new byte [length];
+                                       
+                                       int nread = 0;
+#if NET_2_0
+                                       long notify_total = 0;
+#endif                                 
+                                       while ((nread = st.Read (buffer, 0, length)) != 0){
+#if NET_2_0
+                                               if (async){
+                                                       notify_total += nread;
+                                                       OnDownloadProgressChanged (
+                                                               new DownloadProgressChangedEventArgs (notify_total, response.ContentLength, userToken));
+                                                                                                     
+                                               }
+#endif
+                                               f.Write (buffer, 0, nread);
+                                       }
+                               } catch (ThreadInterruptedException){
+                                       if (request != null)
+                                               request.Abort ();
+                                       throw;
+                               }
+                       }
                }
 
                //   OpenRead
 
                public Stream OpenRead (string address)
                {
-                       return OpenRead (MakeUri (address));
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       return OpenRead (CreateUri (address));
                }
 
 #if NET_2_0
@@ -253,13 +377,25 @@ namespace System.Net
 #endif
                Stream OpenRead (Uri address)
                {
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       WebRequest request = null;
                        try {
                                SetBusy ();
-                               WebRequest request = SetupRequest (address);
+#if NET_2_0                            
+                               async = false;
+#endif                         
+                               request = SetupRequest (address);
                                WebResponse response = request.GetResponse ();
                                return ProcessResponse (response);
+                       } catch (Exception ex) {
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
                        } finally {
-                               isBusy = false;
+                               is_busy = false;
                        }
                }
 
@@ -267,12 +403,22 @@ namespace System.Net
 
                public Stream OpenWrite (string address)
                {
-                       return OpenWrite (MakeUri (address));
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       return OpenWrite (CreateUri (address));
                }
                
                public Stream OpenWrite (string address, string method)
                {
-                       return OpenWrite (MakeUri (address), method);
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       return OpenWrite (CreateUri (address), method);
                }
 
 #if NET_2_0
@@ -280,7 +426,7 @@ namespace System.Net
 #endif
                Stream OpenWrite (Uri address)
                {
-                       return OpenWrite (address, DetermineMethod (address));
+                       return OpenWrite (address, (string) null);
                }
 
 #if NET_2_0
@@ -288,36 +434,58 @@ namespace System.Net
 #endif
                Stream OpenWrite (Uri address, string method)
                {
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
                        try {
                                SetBusy ();
-                               WebRequest request = SetupRequest (address, method);
+#if NET_2_0                            
+                               async = false;
+#endif                         
+                               WebRequest request = SetupRequest (address, method, true);
                                return request.GetRequestStream ();
+                       } catch (Exception ex) {
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
                        } finally {
-                               isBusy = false;
+                               is_busy = false;
                        }
                }
 
-               private string DetermineMethod (Uri address)
+               private string DetermineMethod (Uri address, string method, bool is_upload)
                {
-                       if (address == null)
-                               throw new ArgumentNullException ("address");
+                       if (method != null)
+                               return method;
+
 #if NET_2_0
                        if (address.Scheme == Uri.UriSchemeFtp)
-                               return "RETR";
+                               return (is_upload) ? "STOR" : "RETR";
 #endif
-                       return "POST";
+                       return (is_upload) ? "POST" : "GET";
                }
 
                //   UploadData
 
                public byte [] UploadData (string address, byte [] data)
                {
-                       return UploadData (MakeUri (address), data);
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       return UploadData (CreateUri (address), data);
                }
                
                public byte [] UploadData (string address, string method, byte [] data)
                {
-                       return UploadData (MakeUri (address), method, data);
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       return UploadData (CreateUri (address), method, data);
                }
 
 #if NET_2_0
@@ -325,7 +493,7 @@ namespace System.Net
 #endif
                byte [] UploadData (Uri address, byte [] data)
                {
-                       return UploadData (address, DetermineMethod (address), data);
+                       return UploadData (address, (string) null, data);
                }
 
 #if NET_2_0
@@ -333,35 +501,66 @@ namespace System.Net
 #endif
                byte [] UploadData (Uri address, string method, byte [] data)
                {
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+#endif
+
                        try {
                                SetBusy ();
-                               return UploadDataCore (address, method, data);
+#if NET_2_0                            
+                               async = false;
+#endif                         
+                               return UploadDataCore (address, method, data, null);
+                       } catch (WebException) {
+                               throw;
+                       } catch (Exception ex) {
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
                        } finally {
-                               isBusy = false;
+                               is_busy = false;
                        }
                }
 
-               byte [] UploadDataCore (Uri address, string method, byte [] data)
+               byte [] UploadDataCore (Uri address, string method, byte [] data, object userToken)
                {
+#if ONLY_1_1
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
                        if (data == null)
                                throw new ArgumentNullException ("data");
+#endif
 
-                       int contentLength = data.Length;
-                       WebRequest request = SetupRequest (address, method, contentLength);
-                       using (Stream stream = request.GetRequestStream ()) {
-                               stream.Write (data, 0, contentLength);
+                       WebRequest request = SetupRequest (address, method, true);
+                       try {
+                               int contentLength = data.Length;
+                               request.ContentLength = contentLength;
+                               using (Stream stream = request.GetRequestStream ()) {
+                                       stream.Write (data, 0, contentLength);
+                               }
+                               
+                               WebResponse response = request.GetResponse ();
+                               Stream st = ProcessResponse (response);
+                               return ReadAll (st, (int) response.ContentLength, userToken);
+                       } catch (ThreadInterruptedException){
+                               if (request != null)
+                                       request.Abort ();
+                               throw;
                        }
-
-                       WebResponse response = request.GetResponse ();
-                       Stream st = ProcessResponse (response);
-                       return ReadAll (st, (int) response.ContentLength);
                }
 
                //   UploadFile
 
                public byte [] UploadFile (string address, string fileName)
                {
-                       return UploadFile (MakeUri (address), fileName);
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       return UploadFile (CreateUri (address), fileName);
                }
 
 #if NET_2_0
@@ -369,12 +568,12 @@ namespace System.Net
 #endif
                byte [] UploadFile (Uri address, string fileName)
                {
-                       return UploadFile (address, DetermineMethod (address), fileName);
+                       return UploadFile (address, (string) null, fileName);
                }
                
                public byte [] UploadFile (string address, string method, string fileName)
                {
-                       return UploadFile (MakeUri (address), method, fileName);
+                       return UploadFile (CreateUri (address), method, fileName);
                }
 
 #if NET_2_0
@@ -382,16 +581,34 @@ namespace System.Net
 #endif
                byte [] UploadFile (Uri address, string method, string fileName)
                {
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (fileName == null)
+                               throw new ArgumentNullException ("fileName");
+#endif
+
                        try {
                                SetBusy ();
-                               return UploadFileCore (address, method, fileName);
+#if NET_2_0                            
+                               async = false;
+#endif                         
+                               return UploadFileCore (address, method, fileName, null);
+                       } catch (Exception ex) {
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
                        } finally {
-                               isBusy = false;
+                               is_busy = false;
                        }
                }
 
-               byte [] UploadFileCore (Uri address, string method, string fileName)
+               byte [] UploadFileCore (Uri address, string method, string fileName, object userToken)
                {
+#if ONLY_1_1
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
                        string fileCType = Headers ["Content-Type"];
                        if (fileCType != null) {
                                string lower = fileCType.ToLower ();
@@ -404,13 +621,16 @@ namespace System.Net
 
                        string boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
                        Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
-                       WebRequest request = SetupRequest (address, method);
                        Stream reqStream = null;
                        Stream fStream = null;
                        byte [] resultBytes = null;
 
+                       fileName = Path.GetFullPath (fileName);
+
+                       WebRequest request = null;
                        try {
                                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);
@@ -433,11 +653,11 @@ namespace System.Net
                                reqStream = null;
                                WebResponse response = request.GetResponse ();
                                Stream st = ProcessResponse (response);
-                               resultBytes = ReadAll (st, (int) response.ContentLength);
-                       } catch (WebException) {
+                               resultBytes = ReadAll (st, (int) response.ContentLength, userToken);
+                       } catch (ThreadInterruptedException){
+                               if (request != null)
+                                       request.Abort ();
                                throw;
-                       } catch (Exception e) {
-                               throw new WebException ("Error uploading file.", e);
                        } finally {
                                if (fStream != null)
                                        fStream.Close ();
@@ -446,17 +666,27 @@ namespace System.Net
                                        reqStream.Close ();
                        }
                        
-                       return resultBytes;     
+                       return resultBytes;
                }
                
                public byte[] UploadValues (string address, NameValueCollection data)
                {
-                       return UploadValues (MakeUri (address), data);
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       return UploadValues (CreateUri (address), data);
                }
                
                public byte[] UploadValues (string address, string method, NameValueCollection data)
                {
-                       return UploadValues (MakeUri (address), method, data);
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+#endif
+
+                       return UploadValues (CreateUri (address), method, data);
                }
 
 #if NET_2_0
@@ -464,26 +694,41 @@ namespace System.Net
 #endif
                byte[] UploadValues (Uri address, NameValueCollection data)
                {
-                       return UploadValues (address, DetermineMethod (address), data);
+                       return UploadValues (address, (string) null, data);
                }
 
 #if NET_2_0
                public
 #endif
-               byte[] UploadValues (Uri uri, string method, NameValueCollection data)
+               byte[] UploadValues (Uri address, string method, NameValueCollection data)
                {
+#if NET_2_0
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+#endif
+
                        try {
                                SetBusy ();
-                               return UploadValuesCore (uri, method, data);
+#if NET_2_0                            
+                               async = false;
+#endif                         
+                               return UploadValuesCore (address, method, data, null);
+                       } catch (Exception ex) {
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
                        } finally {
-                               isBusy = false;
+                               is_busy = false;
                        }
                }
 
-               byte[] UploadValuesCore (Uri uri, string method, NameValueCollection data)
+               byte[] UploadValuesCore (Uri uri, string method, NameValueCollection data, object userToken)
                {
+#if ONLY_1_1
                        if (data == null)
-                               throw new ArgumentNullException ("data"); // MS throws a nullref
+                               throw new ArgumentNullException ("data");
+#endif
 
                        string cType = Headers ["Content-Type"];
                        if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
@@ -491,66 +736,94 @@ namespace System.Net
                                                        "value for this request.");
 
                        Headers ["Content-Type"] = urlEncodedCType;
-                       WebRequest request = SetupRequest (uri, method);
-                       Stream rqStream = request.GetRequestStream ();
-                       MemoryStream tmpStream = new MemoryStream ();
-                       foreach (string key in data) {
-                               byte [] bytes = Encoding.ASCII.GetBytes (key);
-                               UrlEncodeAndWrite (tmpStream, bytes);
-                               tmpStream.WriteByte ((byte) '=');
-                               bytes = Encoding.ASCII.GetBytes (data [key]);
-                               UrlEncodeAndWrite (tmpStream, bytes);
-                               tmpStream.WriteByte ((byte) '&');
+                       WebRequest request = SetupRequest (uri, method, true);
+                       try {
+                               Stream rqStream = request.GetRequestStream ();
+                               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) '&');
+                               }
+                               
+                               int length = (int) tmpStream.Length;
+                               if (length > 0)
+                                       tmpStream.SetLength (--length); // remove trailing '&'
+                               
+                               byte [] buf = tmpStream.GetBuffer ();
+                               rqStream.Write (buf, 0, length);
+                               rqStream.Close ();
+                               tmpStream.Close ();
+                               
+                               WebResponse response = request.GetResponse ();
+                               Stream st = ProcessResponse (response);
+                               return ReadAll (st, (int) response.ContentLength, userToken);
+                       } catch (ThreadInterruptedException) {
+                               request.Abort ();
+                               throw;
                        }
-
-                       int length = (int) tmpStream.Length;
-                       if (length > 0)
-                               tmpStream.SetLength (--length); // remove trailing '&'
-
-                       tmpStream.WriteByte ((byte) '\r');
-                       tmpStream.WriteByte ((byte) '\n');
-
-                       byte [] buf = tmpStream.GetBuffer ();
-                       rqStream.Write (buf, 0, length + 2);
-                       rqStream.Close ();
-                       tmpStream.Close ();
-
-                       WebResponse response = request.GetResponse ();
-                       Stream st = ProcessResponse (response);
-                       return ReadAll (st, (int) response.ContentLength);
                }
 
 #if NET_2_0
                public string DownloadString (string address)
                {
-                       return encoding.GetString (DownloadData (address));
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return encoding.GetString (DownloadData (CreateUri (address)));
                }
 
                public string DownloadString (Uri address)
                {
-                       return encoding.GetString (DownloadData (address));
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
+                       return encoding.GetString (DownloadData (CreateUri (address)));
                }
 
                public string UploadString (string address, string data)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
                        byte [] resp = UploadData (address, encoding.GetBytes (data));
                        return encoding.GetString (resp);
                }
 
                public string UploadString (string address, string method, string data)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
                        byte [] resp = UploadData (address, method, encoding.GetBytes (data));
                        return encoding.GetString (resp);
                }
 
                public string UploadString (Uri address, string data)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
                        byte [] resp = UploadData (address, encoding.GetBytes (data));
                        return encoding.GetString (resp);
                }
 
                public string UploadString (Uri address, string method, string data)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+
                        byte [] resp = UploadData (address, method, encoding.GetBytes (data));
                        return encoding.GetString (resp);
                }
@@ -568,35 +841,76 @@ namespace System.Net
                public event UploadValuesCompletedEventHandler UploadValuesCompleted;
 #endif
 
-               Uri MakeUri (string path)
+               Uri CreateUri (string address)
                {
-                       string query = null;
-                       if (queryString != null && queryString.Count != 0) {
-                               // This is not the same as UploadValues, because these 'keys' are not
-                               // urlencoded here.
-                               StringBuilder sb = new StringBuilder ();
+#if ONLY_1_1
+                       try {
+                               return MakeUri (address);
+                       } catch (Exception ex) {
+                               throw new WebException ("An error occurred " +
+                                       "performing a WebClient request.", ex);
+                       }
+#else
+                       return MakeUri (address);
+#endif
+               }
+
+#if NET_2_0
+               Uri CreateUri (Uri address)
+               {
+                       string query = address.Query;
+                       if (String.IsNullOrEmpty (query))
+                               query = GetQueryString (true);
+
+                       if (baseAddress == null && query == null)
+                               return address;
+
+                       if (baseAddress == null)
+                               return new Uri (address.ToString () + query, (query != null));
+
+                       if (query == null)
+                               return new Uri (baseAddress, address.ToString ());
+
+                       return new Uri (baseAddress, address.ToString () + query, (query != null));
+
+               }
+#endif
+
+               string GetQueryString (bool add_qmark)
+               {
+                       if (queryString == null || queryString.Count == 0)
+                               return null;
+
+                       StringBuilder sb = new StringBuilder ();
+                       if (add_qmark)
                                sb.Append ('?');
-                               foreach (string key in queryString)
-                                       sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));
 
-                               if (sb.Length != 0) {
-                                       sb.Length--; // remove trailing '&'
-                                       query = sb.ToString ();
-                               }
-                       }
-                       
+                       foreach (string key in queryString)
+                               sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));
 
+                       if (sb.Length != 0)
+                               sb.Length--; // removes last '&' or the '?' if empty.
+
+                       if (sb.Length == 0)
+                               return null;
+
+                       return sb.ToString ();
+               }
+
+               Uri MakeUri (string path)
+               {
+                       string query = GetQueryString (true);
                        if (baseAddress == null && query == null) {
                                try {
                                        return new Uri (path);
-                               }
-                               catch (System.UriFormatException) {
-                                       if ((path[0] == Path.DirectorySeparatorChar) || (path[1] == ':' && Char.ToLower(path[0]) > 'a' && Char.ToLower(path[0]) < 'z')) {
-                                               return new Uri ("file://" + path);
-                                       }
-                                       else {
-                                               return new Uri ("file://" + Environment.CurrentDirectory + Path.DirectorySeparatorChar + path);
-                                       }
+#if NET_2_0
+                               } catch (ArgumentNullException) {
+                                       path = Path.GetFullPath (path);
+                                       return new Uri ("file://" + path);
+#endif
+                               } catch (UriFormatException) {
+                                       path = Path.GetFullPath (path);
+                                       return new Uri ("file://" + path);
                                }
                        }
 
@@ -636,22 +950,22 @@ namespace System.Net
                                headers.RemoveInternal ("User-Agent");
                                request.Headers = headers;
 
-                               if (expect != null && expect != "")
+                               if (expect != null && expect.Length > 0)
                                        req.Expect = expect;
 
-                               if (accept != null && accept != "")
+                               if (accept != null && accept.Length > 0)
                                        req.Accept = accept;
 
-                               if (contentType != null && contentType != "")
+                               if (contentType != null && contentType.Length > 0)
                                        req.ContentType = contentType;
 
-                               if (connection != null && connection != "")
+                               if (connection != null && connection.Length > 0)
                                        req.Connection = connection;
 
-                               if (userAgent != null && userAgent != "")
+                               if (userAgent != null && userAgent.Length > 0)
                                        req.UserAgent = userAgent;
 
-                               if (referer != null && referer != "")
+                               if (referer != null && referer.Length > 0)
                                        req.Referer = referer;
                        }
 
@@ -659,17 +973,10 @@ namespace System.Net
                        return request;
                }
 
-               WebRequest SetupRequest (Uri uri, string method)
+               WebRequest SetupRequest (Uri uri, string method, bool is_upload)
                {
                        WebRequest request = SetupRequest (uri);
-                       request.Method = method;
-                       return request;
-               }
-
-               WebRequest SetupRequest (Uri uri, string method, int contentLength)
-               {
-                       WebRequest request = SetupRequest (uri, method);
-                       request.ContentLength = contentLength;
+                       request.Method = DetermineMethod (uri, method, is_upload);
                        return request;
                }
 
@@ -679,7 +986,7 @@ namespace System.Net
                        return response.GetResponseStream ();
                }
 
-               static byte [] ReadAll (Stream stream, int length)
+               byte [] ReadAll (Stream stream, int length, object userToken)
                {
                        MemoryStream ms = null;
                        
@@ -688,6 +995,7 @@ namespace System.Net
                        if (nolength)
                                ms = new MemoryStream ();
 
+//                     long total = 0;
                        int nread = 0;
                        int offset = 0;
                        byte [] buffer = new byte [size];
@@ -698,6 +1006,12 @@ namespace System.Net
                                        offset += nread;
                                        size -= nread;
                                }
+#if NET_2_0
+                               if (async){
+//                                     total += nread;
+                                       OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (nread, length, userToken));
+                               }
+#endif
                        }
 
                        if (nolength)
@@ -761,333 +1075,479 @@ namespace System.Net
                }
 
 #if NET_2_0
-               List<RegisteredWaitHandle> wait_handles;
-
-               List<RegisteredWaitHandle> WaitHandles {
-                       get {
-                               if (wait_handles == null)
-                                       wait_handles = new List<RegisteredWaitHandle> ();
-                               return wait_handles;
+               public void CancelAsync ()
+               {
+                       lock (this){
+                               if (async_thread == null)
+                                       return;
+
+                               //
+                               // We first flag things as done, in case the Interrupt hangs
+                               // or the thread decides to hang in some other way inside the
+                               // event handlers, or if we are stuck somewhere else.  This
+                               // ensures that the WebClient object is reusable immediately
+                               //
+                               Thread t = async_thread;
+                               CompleteAsync ();
+                               t.Interrupt ();
                        }
                }
 
-               [MonoTODO ("Is it enough to just unregister wait handles from ThreadPool?")]
-               public void CancelAsync ()
+               void CompleteAsync ()
                {
-                       if (wait_handles == null)
-                               return;
-                       lock (wait_handles) {
-                               foreach (RegisteredWaitHandle handle in wait_handles)
-                                       handle.Unregister (null);
-                               wait_handles.Clear ();
+                       lock (this){
+                               is_busy = false;
+                               async_thread = null;
                        }
                }
 
                //    DownloadDataAsync
 
-               public void DownloadDataAsync (Uri uri)
+               public void DownloadDataAsync (Uri address)
                {
-                       DownloadDataAsync (uri, null);
+                       DownloadDataAsync (address, null);
                }
 
-               public void DownloadDataAsync (Uri uri, object asyncState)
+               public void DownloadDataAsync (Uri address, object userToken)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       
                        lock (this) {
-                               CheckBusy ();
-
-                               object [] cbArgs = new object [] {uri, asyncState};
-                               WaitOrTimerCallback cb = delegate (object state, bool timedOut) {
+                               SetBusy ();
+                               async = true;
+                               
+                               async_thread = new Thread (delegate (object state) {
                                        object [] args = (object []) state;
-                                       byte [] data = timedOut ? null : DownloadData ((Uri) args [0]);
-                                       OnDownloadDataCompleted (
-                                               new DownloadDataCompletedEventArgs (data, null, timedOut, args [1]));
-                                       };
-                               AutoResetEvent ev = new AutoResetEvent (true);
-                               WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
+                                       try {
+                                               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){
+                                               OnDownloadDataCompleted (
+                                                       new DownloadDataCompletedEventArgs (null, e, false, args [1]));
+                                       }
+                               });
+                               object [] cb_args = new object [] {address, userToken};
+                               async_thread.Start (cb_args);
                        }
                }
 
                //    DownloadFileAsync
 
-               public void DownloadFileAsync (Uri uri, string method)
+               public void DownloadFileAsync (Uri address, string fileName)
                {
-                       DownloadFileAsync (uri, method, null);
+                       DownloadFileAsync (address, fileName, null);
                }
 
-               public void DownloadFileAsync (Uri uri, string method, object asyncState)
+               public void DownloadFileAsync (Uri address, string fileName, object userToken)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (fileName == null)
+                               throw new ArgumentNullException ("fileName");
+                       
                        lock (this) {
-                               CheckBusy ();
+                               SetBusy ();
+                               async = true;
 
-                               object [] cbArgs = new object [] {uri, method, asyncState};
-                               WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
-                                       object [] args = (object []) innerState;
-                                       if (!timedOut)
-                                               DownloadFile ((Uri) args [0], (string) args [1]);
-                                       OnDownloadFileCompleted (
-                                               new AsyncCompletedEventArgs (null, timedOut, args [2]));
-                                       };
-                               AutoResetEvent ev = new AutoResetEvent (true);
-                               WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
+                               async_thread = new Thread (delegate (object state) {
+                                       object [] args = (object []) state;
+                                       try {
+                                               DownloadFileCore ((Uri) args [0], (string) args [1], args [2]);
+                                               OnDownloadFileCompleted (
+                                                       new AsyncCompletedEventArgs (null, false, args [2]));
+                                       } catch (ThreadInterruptedException){
+                                               OnDownloadFileCompleted (
+                                                       new AsyncCompletedEventArgs (null, true, args [2]));
+                                       } catch (Exception e){
+                                               OnDownloadFileCompleted (
+                                                       new AsyncCompletedEventArgs (e, false, args [2]));
+                                       }});
+                               object [] cb_args = new object [] {address, fileName, userToken};
+                               async_thread.Start (cb_args);
                        }
                }
 
                //    DownloadStringAsync
 
-               public void DownloadStringAsync (Uri uri)
+               public void DownloadStringAsync (Uri address)
                {
-                       DownloadStringAsync (uri, null);
+                       DownloadStringAsync (address, null);
                }
 
-               public void DownloadStringAsync (Uri uri, object asyncState)
+               public void DownloadStringAsync (Uri address, object userToken)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       
                        lock (this) {
-                               CheckBusy ();
+                               SetBusy ();
+                               async = true;
 
-                               object [] cbArgs = new object [] {uri, asyncState};
-                               WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
-                                       object [] args = (object []) innerState;
-                                       string data = timedOut ? null : DownloadString ((Uri) args [0]);
-                                       OnDownloadStringCompleted (
-                                               new DownloadStringCompletedEventArgs (data, null, timedOut, args [1]));
-                                       };
-                               AutoResetEvent ev = new AutoResetEvent (true);
-                               WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
+                               async_thread = new Thread (delegate (object state) {
+                                       object [] args = (object []) state;
+                                       try {
+                                               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){
+                                               OnDownloadStringCompleted (
+                                                       new DownloadStringCompletedEventArgs (null, e, false, args [1]));
+                                       }});
+                               object [] cb_args = new object [] {address, userToken};
+                               async_thread.Start (cb_args);
                        }
                }
 
                //    OpenReadAsync
 
-               public void OpenReadAsync (Uri uri)
+               public void OpenReadAsync (Uri address)
                {
-                       OpenReadAsync (uri, null);
+                       OpenReadAsync (address, null);
                }
 
-               public void OpenReadAsync (Uri uri, object asyncState)
+               public void OpenReadAsync (Uri address, object userToken)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       
                        lock (this) {
-                               CheckBusy ();
+                               SetBusy ();
+                               async = true;
 
-                               object [] cbArgs = new object [] {uri, asyncState};
-                               WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
-                                       object [] args = (object []) innerState;
-                                       Stream stream = timedOut ? null : OpenRead ((Uri) args [0]);
-                                       OnOpenReadCompleted (
-                                               new OpenReadCompletedEventArgs (stream, null, timedOut, args [1]));
-                                       };
-                               AutoResetEvent ev = new AutoResetEvent (true);
-                               WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
+                               async_thread = new Thread (delegate (object state) {
+                                       object [] args = (object []) state;
+                                       WebRequest request = null;
+                                       try {
+                                               request = SetupRequest ((Uri) args [0]);
+                                               WebResponse response = request.GetResponse ();
+                                               Stream stream = ProcessResponse (response);
+                                               OnOpenReadCompleted (
+                                                       new OpenReadCompletedEventArgs (stream, null, false, args [1]));
+                                       } catch (ThreadInterruptedException){
+                                               if (request != null)
+                                                       request.Abort ();
+                                               
+                                               OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, null, true, args [1]));
+                                       } catch (Exception e){
+                                               OnOpenReadCompleted (new OpenReadCompletedEventArgs (null, e, false, args [1]));
+                                       } });
+                               object [] cb_args = new object [] {address, userToken};
+                               async_thread.Start (cb_args);
                        }
                }
 
                //    OpenWriteAsync
 
-               public void OpenWriteAsync (Uri uri)
+               public void OpenWriteAsync (Uri address)
                {
-                       OpenWriteAsync (uri, null);
+                       OpenWriteAsync (address, null);
                }
 
-               public void OpenWriteAsync (Uri uri, string method)
+               public void OpenWriteAsync (Uri address, string method)
                {
-                       OpenWriteAsync (uri, method, null);
+                       OpenWriteAsync (address, method, null);
                }
 
-               public void OpenWriteAsync (Uri uri, string method, object asyncState)
+               public void OpenWriteAsync (Uri address, string method, object userToken)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+
                        lock (this) {
-                               CheckBusy ();
+                               SetBusy ();
+                               async = true;
 
-                               object [] cbArgs = new object [] {uri, method, asyncState};
-                               WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
-                                       object [] args = (object []) innerState;
-                                       Stream stream = timedOut ? null : OpenWrite ((Uri) args [0], (string) args [1]);
-                                       OnOpenWriteCompleted (
-                                               new OpenWriteCompletedEventArgs (stream, null, timedOut, args [2]));
-                                       };
-                               AutoResetEvent ev = new AutoResetEvent (true);
-                               WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
+                               async_thread = new Thread (delegate (object state) {
+                                       object [] args = (object []) state;
+                                       WebRequest request = null;
+                                       try {
+                                               request = SetupRequest ((Uri) args [0], (string) args [1], true);
+                                               Stream stream = request.GetRequestStream ();
+                                               OnOpenWriteCompleted (
+                                                       new OpenWriteCompletedEventArgs (stream, null, false, args [2]));
+                                       } catch (ThreadInterruptedException){
+                                               if (request != null)
+                                                       request.Abort ();
+                                               OnOpenWriteCompleted (
+                                                       new OpenWriteCompletedEventArgs (null, null, true, args [2]));
+                                       } catch (Exception e){
+                                               OnOpenWriteCompleted (
+                                                       new OpenWriteCompletedEventArgs (null, e, false, args [2]));
+                                       }});
+                               object [] cb_args = new object [] {address, method, userToken};
+                               async_thread.Start (cb_args);
                        }
                }
 
                //    UploadDataAsync
 
-               public void UploadDataAsync (Uri uri, byte [] data)
+               public void UploadDataAsync (Uri address, byte [] data)
                {
-                       UploadDataAsync (uri, null, data);
+                       UploadDataAsync (address, null, data);
                }
 
-               public void UploadDataAsync (Uri uri, string method, byte [] data)
+               public void UploadDataAsync (Uri address, string method, byte [] data)
                {
-                       UploadDataAsync (uri, method, data, null);
+                       UploadDataAsync (address, method, data, null);
                }
 
-               public void UploadDataAsync (Uri uri, string method, byte [] data, object asyncState)
+               public void UploadDataAsync (Uri address, string method, byte [] data, object userToken)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+                       
                        lock (this) {
-                               CheckBusy ();
+                               SetBusy ();
+                               async = true;
 
-                               object [] cbArgs = new object [] {uri, method, data,  asyncState};
-                               WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
-                                       object [] args = (object []) innerState;
-                                       byte [] data2 = timedOut ? null : UploadData ((Uri) args [0], (string) args [1], (byte []) args [2]);
-                                       OnUploadDataCompleted (
-                                               new UploadDataCompletedEventArgs (data2, null, timedOut, args [3]));
-                                       };
-                               AutoResetEvent ev = new AutoResetEvent (true);
-                               WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
+                               async_thread = new Thread (delegate (object state) {
+                                       object [] args = (object []) state;
+                                       byte [] data2;
+
+                                       try {
+                                               data2 = UploadDataCore ((Uri) args [0], (string) args [1], (byte []) args [2], args [3]);
+                                       
+                                               OnUploadDataCompleted (
+                                                       new UploadDataCompletedEventArgs (data2, null, false, args [3]));
+                                       } catch (ThreadInterruptedException){
+                                               OnUploadDataCompleted (
+                                                       new UploadDataCompletedEventArgs (null, null, true, args [3]));
+                                       } catch (Exception e){
+                                               OnUploadDataCompleted (
+                                                       new UploadDataCompletedEventArgs (null, e, false, args [3]));
+                                       }});
+                               object [] cb_args = new object [] {address, method, data,  userToken};
+                               async_thread.Start (cb_args);
                        }
                }
 
                //    UploadFileAsync
 
-               public void UploadFileAsync (Uri uri, string file)
+               public void UploadFileAsync (Uri address, string fileName)
                {
-                       UploadFileAsync (uri, null, file);
+                       UploadFileAsync (address, null, fileName);
                }
 
-               public void UploadFileAsync (Uri uri, string method, string file)
+               public void UploadFileAsync (Uri address, string method, string fileName)
                {
-                       UploadFileAsync (uri, method, file, null);
+                       UploadFileAsync (address, method, fileName, null);
                }
 
-               public void UploadFileAsync (Uri uri, string method, string file, object asyncState)
+               public void UploadFileAsync (Uri address, string method, string fileName, object userToken)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (fileName == null)
+                               throw new ArgumentNullException ("fileName");
+
                        lock (this) {
-                               CheckBusy ();
+                               SetBusy ();
+                               async = true;
 
-                               object [] cbArgs = new object [] {uri, method, file,  asyncState};
-                               WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
-                                       object [] args = (object []) innerState;
-                                       byte [] data = timedOut ? null : UploadFile ((Uri) args [0], (string) args [1], (string) args [2]);
-                                       OnUploadFileCompleted (
-                                               new UploadFileCompletedEventArgs (data, null, timedOut, args [3]));
-                                       };
-                               AutoResetEvent ev = new AutoResetEvent (true);
-                               WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
+                               async_thread = new Thread (delegate (object state) {
+                                       object [] args = (object []) state;
+                                       byte [] data;
+
+                                       try {
+                                               data = UploadFileCore ((Uri) args [0], (string) args [1], (string) args [2], args [3]);
+                                               OnUploadFileCompleted (
+                                                       new UploadFileCompletedEventArgs (data, null, false, args [3]));
+                                       } catch (ThreadInterruptedException){
+                                               OnUploadFileCompleted (
+                                                       new UploadFileCompletedEventArgs (null, null, true, args [3]));
+                                       } catch (Exception e){
+                                               OnUploadFileCompleted (
+                                                       new UploadFileCompletedEventArgs (null, e, false, args [3]));
+                                       }});
+                               object [] cb_args = new object [] {address, method, fileName,  userToken};
+                               async_thread.Start (cb_args);
                        }
                }
 
                //    UploadStringAsync
 
-               public void UploadStringAsync (Uri uri, string data)
+               public void UploadStringAsync (Uri address, string data)
                {
-                       UploadStringAsync (uri, null, data);
+                       UploadStringAsync (address, null, data);
                }
 
-               public void UploadStringAsync (Uri uri, string method, string data)
+               public void UploadStringAsync (Uri address, string method, string data)
                {
-                       UploadStringAsync (uri, method, data, null);
+                       UploadStringAsync (address, method, data, null);
                }
 
-               public void UploadStringAsync (Uri uri, string method, string data, object asyncState)
+               public void UploadStringAsync (Uri address, string method, string data, object userToken)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (data == null)
+                               throw new ArgumentNullException ("data");
+                       
                        lock (this) {
-                               CheckBusy ();
+                               SetBusy ();
+                               async = true;
+                               
+                               async_thread = new Thread (delegate (object state) {
+                                       object [] args = (object []) state;
 
-                               object [] cbArgs = new object [] {uri, method, data, asyncState};
-                               WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
-                                       object [] args = (object []) innerState;
-                                       string data2 = timedOut ? null : UploadString ((Uri) args [0], (string) args [1], (string) args [2]);
-                                       OnUploadStringCompleted (
-                                               new UploadStringCompletedEventArgs (data2, null, timedOut, args [3]));
-                                       };
-                               AutoResetEvent ev = new AutoResetEvent (true);
-                               WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
+                                       try {
+                                               string data2 = UploadString ((Uri) args [0], (string) args [1], (string) args [2]);
+                                               OnUploadStringCompleted (
+                                                       new UploadStringCompletedEventArgs (data2, null, false, args [3]));
+                                       } catch (ThreadInterruptedException){
+                                               OnUploadStringCompleted (
+                                                       new UploadStringCompletedEventArgs (null, null, true, args [3]));
+                                       } catch (Exception e){
+                                               OnUploadStringCompleted (
+                                                       new UploadStringCompletedEventArgs (null, e, false, args [3]));
+                                       }});
+                               object [] cb_args = new object [] {address, method, data, userToken};
+                               async_thread.Start (cb_args);
                        }
                }
 
                //    UploadValuesAsync
 
-               public void UploadValuesAsync (Uri uri, NameValueCollection values)
+               public void UploadValuesAsync (Uri address, NameValueCollection values)
                {
-                       UploadValuesAsync (uri, null, values);
+                       UploadValuesAsync (address, null, values);
                }
 
-               public void UploadValuesAsync (Uri uri, string method, NameValueCollection values)
+               public void UploadValuesAsync (Uri address, string method, NameValueCollection values)
                {
-                       UploadValuesAsync (uri, method, values, null);
+                       UploadValuesAsync (address, method, values, null);
                }
 
-               public void UploadValuesAsync (Uri uri, string method, NameValueCollection values, object asyncState)
+               public void UploadValuesAsync (Uri address, string method, NameValueCollection values, object userToken)
                {
+                       if (address == null)
+                               throw new ArgumentNullException ("address");
+                       if (values == null)
+                               throw new ArgumentNullException ("values");
+
                        lock (this) {
                                CheckBusy ();
+                               async = true;
 
-                               object [] cbArgs = new object [] {uri, method, values,  asyncState};
-                               WaitOrTimerCallback cb = delegate (object innerState, bool timedOut) {
-                                       object [] args = (object []) innerState;
-                                       byte [] data = timedOut ? null : UploadValues ((Uri) args [0], (string) args [1], (NameValueCollection) args [2]);
-                                       OnUploadValuesCompleted (
-                                               new UploadValuesCompletedEventArgs (data, null, timedOut, args [3]));
-                                       };
-                               AutoResetEvent ev = new AutoResetEvent (true);
-                               WaitHandles.Add (ThreadPool.RegisterWaitForSingleObject (ev, cb, cbArgs, -1, true));
+                               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]);
+                                               OnUploadValuesCompleted (
+                                                       new UploadValuesCompletedEventArgs (data, null, false, args [3]));
+                                       } catch (ThreadInterruptedException){
+                                               OnUploadValuesCompleted (
+                                                       new UploadValuesCompletedEventArgs (null, null, true, args [3]));
+                                       } catch (Exception e){
+                                               OnUploadValuesCompleted (
+                                                       new UploadValuesCompletedEventArgs (null, e, false, args [3]));
+                                       }});
+                               object [] cb_args = new object [] {address, method, values,  userToken};
+                               async_thread.Start (cb_args);
                        }
                }
 
-               protected virtual void OnDownloadDataCompleted (
-                       DownloadDataCompletedEventArgs args)
+               protected virtual void OnDownloadDataCompleted (DownloadDataCompletedEventArgs args)
                {
+                       CompleteAsync ();
                        if (DownloadDataCompleted != null)
                                DownloadDataCompleted (this, args);
                }
 
-               protected virtual void OnDownloadFileCompleted (
-                       AsyncCompletedEventArgs args)
+               protected virtual void OnDownloadFileCompleted (AsyncCompletedEventArgs args)
                {
+                       CompleteAsync ();
                        if (DownloadFileCompleted != null)
                                DownloadFileCompleted (this, args);
                }
 
-               protected virtual void OnDownloadStringCompleted (
-                       DownloadStringCompletedEventArgs args)
+               protected virtual void OnDownloadProgressChanged (DownloadProgressChangedEventArgs e)
+               {
+                       if (DownloadProgressChanged != null)
+                               DownloadProgressChanged (this, e);
+               }
+
+               protected virtual void OnDownloadStringCompleted (DownloadStringCompletedEventArgs args)
                {
+                       CompleteAsync ();
                        if (DownloadStringCompleted != null)
                                DownloadStringCompleted (this, args);
                }
 
-               protected virtual void OnOpenReadCompleted (
-                       OpenReadCompletedEventArgs args)
+               protected virtual void OnOpenReadCompleted (OpenReadCompletedEventArgs args)
                {
+                       CompleteAsync ();
                        if (OpenReadCompleted != null)
                                OpenReadCompleted (this, args);
                }
 
-               protected virtual void OnOpenWriteCompleted (
-                       OpenWriteCompletedEventArgs args)
+               protected virtual void OnOpenWriteCompleted (OpenWriteCompletedEventArgs args)
                {
+                       CompleteAsync ();
                        if (OpenWriteCompleted != null)
                                OpenWriteCompleted (this, args);
                }
 
-               protected virtual void OnUploadDataCompleted (
-                       UploadDataCompletedEventArgs args)
+               protected virtual void OnUploadDataCompleted (UploadDataCompletedEventArgs args)
                {
+                       CompleteAsync ();
                        if (UploadDataCompleted != null)
                                UploadDataCompleted (this, args);
                }
 
-               protected virtual void OnUploadFileCompleted (
-                       UploadFileCompletedEventArgs args)
+               protected virtual void OnUploadFileCompleted (UploadFileCompletedEventArgs args)
                {
+                       CompleteAsync ();
                        if (UploadFileCompleted != null)
                                UploadFileCompleted (this, args);
                }
 
-               protected virtual void OnUploadStringCompleted (
-                       UploadStringCompletedEventArgs args)
+               protected virtual void OnUploadProgressChanged (UploadProgressChangedEventArgs e)
                {
+                       if (UploadProgressChanged != null)
+                               UploadProgressChanged (this, e);
+               }
+
+               protected virtual void OnUploadStringCompleted (UploadStringCompletedEventArgs args)
+               {
+                       CompleteAsync ();
                        if (UploadStringCompleted != null)
                                UploadStringCompleted (this, args);
                }
 
-               protected virtual void OnUploadValuesCompleted (
-                       UploadValuesCompletedEventArgs args)
+               protected virtual void OnUploadValuesCompleted (UploadValuesCompletedEventArgs args)
                {
+                       CompleteAsync ();
                        if (UploadValuesCompleted != null)
                                UploadValuesCompleted (this, args);
                }
+
+               [MonoNotSupported("")]
+               protected virtual WebRequest GetWebRequest (Uri address)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               protected virtual WebResponse GetWebResponse (WebRequest request)
+               {
+                       return request.GetResponse ();
+               }
+
+               protected virtual WebResponse GetWebResponse (WebRequest request, IAsyncResult result)
+               {
+                       return request.EndGetResponse (result);
+               }
 #endif
        }
 }
-