Removed Consoles and ^Ms
[mono.git] / mcs / class / System / System.Net / WebClient.cs
index 1e9a8cb87f6345b47764e449efc1e2149312859e..ca424855e3ffefb5c669487e150c12b6e65d8bd1 100644 (file)
@@ -1,27 +1,46 @@
 //\r
 // System.Net.WebClient\r
 //\r
-// Author:\r
-//   Lawrence Pit (loz@cable.a2000.nl)\r
+// Authors:\r
+//     Lawrence Pit (loz@cable.a2000.nl)\r
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)\r
+//\r
+// (c) 2003 Ximian, Inc. (http://www.ximian.com)\r
 //\r
 \r
 using System;\r
-using System.Collections;\r
 using System.Collections.Specialized;\r
 using System.ComponentModel;\r
 using System.IO;\r
 using System.Runtime.InteropServices;\r
 using System.Runtime.Serialization;\r
+using System.Text;\r
 \r
 namespace System.Net \r
 {\r
        [ComVisible(true)]\r
        public sealed class WebClient : Component\r
        {\r
-\r
+               static readonly string urlEncodedCType = "application/x-www-form-urlencoded";\r
+               static byte [] hexBytes;\r
                ICredentials credentials;\r
+               WebHeaderCollection headers;\r
+               WebHeaderCollection responseHeaders;\r
+               Uri baseAddress;\r
+               string baseString;\r
+               NameValueCollection queryString;\r
        \r
                // Constructors\r
+               static WebClient ()\r
+               {\r
+                       hexBytes = new byte [16];\r
+                       int index = 0;\r
+                       for (int i = '0'; i <= '9'; i++, index++)\r
+                               hexBytes [index] = (byte) i;\r
+\r
+                       for (int i = 'A'; i <= 'F'; i++, index++)\r
+                               hexBytes [index] = (byte) i;\r
+               }\r
                \r
                public WebClient ()\r
                {\r
@@ -29,10 +48,24 @@ namespace System.Net
                \r
                // Properties\r
                \r
-               [MonoTODO]\r
                public string BaseAddress {\r
-                       get { throw new NotImplementedException (); }\r
-                       set { throw new NotImplementedException (); }\r
+                       get {\r
+                               if (baseString == null) {\r
+                                       if (baseAddress == null)\r
+                                               return "";\r
+                               }\r
+\r
+                               baseString = baseAddress.ToString ();\r
+                               return baseString;\r
+                       }\r
+                       \r
+                       set {\r
+                               if (value == null || value == "") {\r
+                                       baseAddress = null;\r
+                               } else {\r
+                                       baseAddress = new Uri (value);\r
+                               }\r
+                       }\r
                }\r
                \r
                public ICredentials Credentials {\r
@@ -40,74 +73,63 @@ namespace System.Net
                        set { credentials = value; }\r
                }\r
                \r
-               [MonoTODO]\r
                public WebHeaderCollection Headers {\r
-                       get { throw new NotImplementedException (); }\r
-                       set { throw new NotImplementedException (); }\r
+                       get {\r
+                               if (headers == null)\r
+                                       headers = new WebHeaderCollection ();\r
+\r
+                               return headers;\r
+                       }\r
+                       set { headers = value; }\r
                }\r
                \r
-               [MonoTODO]\r
                public NameValueCollection QueryString {\r
-                       get { throw new NotImplementedException (); }\r
-                       set { throw new NotImplementedException (); }\r
+                       get {\r
+                               if (queryString == null)\r
+                                       queryString = new NameValueCollection ();\r
+\r
+                               return queryString;\r
+                       }\r
+                       set { queryString = value; }\r
                }\r
                \r
-               [MonoTODO]\r
                public WebHeaderCollection ResponseHeaders {\r
-                       get { throw new NotImplementedException (); }\r
+                       get { return responseHeaders; }\r
                }\r
 \r
                // Methods\r
                \r
-               [MonoTODO("depends on OpenRead")]\r
                public byte [] DownloadData (string address)\r
                {\r
-                       const int readSize = 8192;\r
-                       Stream networkStream = OpenRead (address);\r
-                       ArrayList chunks = new ArrayList ();\r
-                       byte[] buf = new byte [readSize];\r
-                       int size = 0;\r
-                       int total_size = 0;\r
-                       do {\r
-                               size = networkStream.Read (buf, 0, readSize);\r
-                               byte [] copy = new byte [size];\r
-                               Array.Copy (buf, 0, copy,0, size);\r
-                               chunks.Add (copy);\r
-                               total_size += size;\r
-                       } while (size != 0);\r
-\r
-                       byte [] result = new byte [total_size];\r
-                       int target = 0;\r
-                       foreach (byte [] block in chunks){\r
-                               int len = block.Length;\r
-                               Array.Copy (block, 0, result, target, len);\r
-                               target += len;\r
-                       }\r
-                       networkStream.Close ();\r
-                       return result;\r
+                       WebRequest request = SetupRequest (address);\r
+                       WebResponse response = request.GetResponse ();\r
+                       Stream st = ProcessResponse (response);\r
+                       return ReadAll (st, (int) response.ContentLength);\r
                }\r
                \r
-               [MonoTODO("depends on DownloadData")]\r
                public void DownloadFile (string address, string fileName)\r
                {\r
-                       byte[] buf = DownloadData (address);\r
-                       new FileStream (fileName, FileMode.CreateNew).Write (buf, 0, buf.Length);\r
+                       WebRequest request = SetupRequest (address);\r
+                       WebResponse response = request.GetResponse ();\r
+                       Stream st = ProcessResponse (response);\r
+\r
+                       int cLength = (int) response.ContentLength;\r
+                       int length = (cLength <= -1 || cLength > 8192) ? 8192 : cLength;\r
+                       byte [] buffer = new byte [length];\r
+                       FileStream f = new FileStream (fileName, FileMode.CreateNew);\r
+\r
+                       int nread = 0;\r
+                       while ((nread = st.Read (buffer, 0, length)) != 0)\r
+                               f.Write (buffer, 0, nread);\r
+\r
+                       f.Close ();\r
                }\r
                \r
-               [MonoTODO("some tests are required")]\r
                public Stream OpenRead (string address)\r
                {\r
-                       Uri uri = new Uri (address);\r
-                       WebRequest request = null;\r
-\r
-                       if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)\r
-                               request = new HttpWebRequest (uri);\r
-                       else if(uri.Scheme == Uri.UriSchemeFile)\r
-                               request = new FileWebRequest (uri);\r
-                       else\r
-                               throw new NotImplementedException ();\r
-\r
-                       return request.GetResponse ().GetResponseStream ();\r
+                       WebRequest request = SetupRequest (address);\r
+                       WebResponse response = request.GetResponse ();\r
+                       return ProcessResponse (response);\r
                }\r
                \r
                public Stream OpenWrite (string address)\r
@@ -115,10 +137,10 @@ namespace System.Net
                        return OpenWrite (address, "POST");\r
                }\r
                \r
-               [MonoTODO]\r
                public Stream OpenWrite (string address, string method)\r
                {\r
-                       throw new NotImplementedException ();\r
+                       WebRequest request = SetupRequest (address, method);\r
+                       return request.GetRequestStream ();\r
                }\r
                                \r
                public byte [] UploadData (string address, byte [] data)\r
@@ -126,10 +148,20 @@ namespace System.Net
                        return UploadData (address, "POST", data);\r
                }\r
                \r
-               [MonoTODO]\r
                public byte [] UploadData (string address, string method, byte [] data)\r
                {\r
-                       throw new NotImplementedException ();\r
+                       if (data == null)\r
+                               throw new ArgumentNullException ("data");\r
+\r
+                       int contentLength = data.Length;\r
+                       WebRequest request = SetupRequest (address, method, contentLength);\r
+                       using (Stream stream = request.GetRequestStream ()) {\r
+                               stream.Write (data, 0, contentLength);\r
+                       }\r
+\r
+                       WebResponse response = request.GetResponse ();\r
+                       Stream st = ProcessResponse (response);\r
+                       return ReadAll (st, (int) response.ContentLength);\r
                }\r
                \r
                public byte [] UploadFile (string address, string fileName)\r
@@ -137,10 +169,63 @@ namespace System.Net
                        return UploadFile (address, "POST", fileName);\r
                }\r
                \r
-               [MonoTODO]\r
-               public byte[] UploadFile (string address, string method, string fileName)\r
+               public byte [] UploadFile (string address, string method, string fileName)\r
                {\r
-                       throw new NotImplementedException ();\r
+                       string fileCType = Headers ["Content-Type"];\r
+                       if (fileCType != null) {\r
+                               string lower = fileCType.ToLower ();\r
+                               if (lower.StartsWith ("multipart/"))\r
+                                       throw new WebException ("Content-Type cannot be set to a multipart" +\r
+                                                               " type for this request.");\r
+                       } else {\r
+                               fileCType = "application/octet-stream";\r
+                       }\r
+\r
+                       string boundary = "------------" + DateTime.Now.Ticks.ToString ("x");\r
+                       Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);\r
+                       WebRequest request = SetupRequest (address, method);\r
+                       Stream reqStream = null;\r
+                       Stream fStream = null;\r
+                       byte [] resultBytes = null;\r
+\r
+                       try {\r
+                               fStream = File.OpenRead (fileName);\r
+                               reqStream = request.GetRequestStream ();\r
+                               byte [] realBoundary = Encoding.ASCII.GetBytes ("--" + boundary + "\r\n");\r
+                               reqStream.Write (realBoundary, 0, realBoundary.Length);\r
+                               string partHeaders = String.Format ("Content-Disposition: form-data; " +\r
+                                                                   "name=\"file\"; filename=\"{0}\"\r\n" +\r
+                                                                   "Content-Type: {1}\r\n\r\n",\r
+                                                                   Path.GetFileName (fileName), fileCType);\r
+\r
+                               byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);\r
+                               reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);\r
+                               int nread;\r
+                               byte [] buffer = new byte [4096];\r
+                               while ((nread = fStream.Read (buffer, 0, 4096)) != 0)\r
+                                       reqStream.Write (buffer, 0, nread);\r
+\r
+                               reqStream.WriteByte ((byte) '\r');\r
+                               reqStream.WriteByte ((byte) '\n');\r
+                               reqStream.Write (realBoundary, 0, realBoundary.Length);\r
+                               reqStream.Close ();\r
+                               reqStream = null;\r
+                               WebResponse response = request.GetResponse ();\r
+                               Stream st = ProcessResponse (response);\r
+                               resultBytes = ReadAll (st, (int) response.ContentLength);\r
+                       } catch (WebException) {\r
+                               throw;\r
+                       } catch (Exception e) {\r
+                               throw new WebException ("Error uploading file.", e);\r
+                       } finally {\r
+                               if (fStream != null)\r
+                                       fStream.Close ();\r
+\r
+                               if (reqStream != null)\r
+                                       reqStream.Close ();\r
+                       }\r
+                       \r
+                       return resultBytes;     \r
                }\r
                \r
                public byte[] UploadValues (string address, NameValueCollection data)\r
@@ -148,10 +233,234 @@ namespace System.Net
                        return UploadValues (address, "POST", data);\r
                }\r
                \r
-               [MonoTODO]\r
                public byte[] UploadValues (string address, string method, NameValueCollection data)\r
                {\r
-                       throw new NotImplementedException ();\r
+                       if (data == null)\r
+                               throw new ArgumentNullException ("data"); // MS throws a nullref\r
+\r
+                       string cType = Headers ["Content-Type"];\r
+                       if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)\r
+                               throw new WebException ("Content-Type header cannot be changed from its default " +\r
+                                                       "value for this request.");\r
+\r
+                       Headers ["Content-Type"] = urlEncodedCType;\r
+                       WebRequest request = SetupRequest (address, method);\r
+                       Stream rqStream = request.GetRequestStream ();\r
+                       MemoryStream tmpStream = new MemoryStream ();\r
+                       foreach (string key in data) {\r
+                               byte [] bytes = Encoding.ASCII.GetBytes (key);\r
+                               UrlEncodeAndWrite (tmpStream, bytes);\r
+                               tmpStream.WriteByte ((byte) '=');\r
+                               bytes = Encoding.ASCII.GetBytes (data [key]);\r
+                               UrlEncodeAndWrite (tmpStream, bytes);\r
+                               tmpStream.WriteByte ((byte) '&');\r
+                       }\r
+\r
+                       int length = (int) tmpStream.Length;\r
+                       if (length > 0)\r
+                               tmpStream.SetLength (--length); // remove trailing '&'\r
+\r
+                       tmpStream.WriteByte ((byte) '\r');\r
+                       tmpStream.WriteByte ((byte) '\n');\r
+\r
+                       byte [] buf = tmpStream.GetBuffer ();\r
+                       rqStream.Write (buf, 0, length + 2);\r
+                       rqStream.Close ();\r
+                       tmpStream.Close ();\r
+\r
+                       WebResponse response = request.GetResponse ();\r
+                       Stream st = ProcessResponse (response);\r
+                       return ReadAll (st, (int) response.ContentLength);\r
+               }\r
+\r
+               Uri MakeUri (string path)\r
+               {\r
+                       string query = null;\r
+                       if (queryString != null && queryString.Count != 0) {\r
+                               // This is not the same as UploadValues, because these 'keys' are not\r
+                               // urlencoded here.\r
+                               StringBuilder sb = new StringBuilder ();\r
+                               sb.Append ('?');\r
+                               foreach (string key in queryString)\r
+                                       sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));\r
+\r
+                               if (sb.Length != 0) {\r
+                                       sb.Length--; // remove trailing '&'\r
+                                       query = sb.ToString ();\r
+                               }\r
+                       }\r
+                       \r
+\r
+                       if (baseAddress == null && query == null) {\r
+                               try {\r
+                                       return new Uri (path);\r
+                               }\r
+                               catch (System.UriFormatException) {\r
+                                       if ((path[0] == Path.DirectorySeparatorChar) || (path[1] == ':' && Char.ToLower(path[0]) > 'a' && Char.ToLower(path[0]) < 'z')) {\r
+                                               return new Uri ("file://" + path);\r
+                                       }\r
+                                       else {\r
+                                               return new Uri ("file://" + Environment.CurrentDirectory + Path.DirectorySeparatorChar + path);\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if (baseAddress == null)\r
+                               return new Uri (path + query, (query != null));\r
+\r
+                       if (query == null)\r
+                               return new Uri (baseAddress, path);\r
+\r
+                       return new Uri (baseAddress, path + query, (query != null));\r
+               }\r
+               \r
+               WebRequest SetupRequest (string address)\r
+               {\r
+                       Uri uri = MakeUri (address);\r
+                       WebRequest request = WebRequest.Create (uri);\r
+                       request.Credentials = credentials;\r
+\r
+                       // Special headers. These are properties of HttpWebRequest.\r
+                       // What do we do with other requests differnt from HttpWebRequest?\r
+                       if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {\r
+                               HttpWebRequest req = (HttpWebRequest) request;\r
+                               string expect = headers ["Expect"];\r
+                               string contentType = headers ["Content-Type"];\r
+                               string accept = headers ["Accept"];\r
+                               string connection = headers ["Connection"];\r
+                               string userAgent = headers ["User-Agent"];\r
+                               string referer = headers ["Referer"];\r
+                               headers.RemoveInternal ("Expect");\r
+                               headers.RemoveInternal ("Content-Type");\r
+                               headers.RemoveInternal ("Accept");\r
+                               headers.RemoveInternal ("Connection");\r
+                               headers.RemoveInternal ("Referer");\r
+                               headers.RemoveInternal ("User-Agent");\r
+                               request.Headers = headers;\r
+\r
+                               if (expect != null && expect != "")\r
+                                       req.Expect = expect;\r
+\r
+                               if (accept != null && accept != "")\r
+                                       req.Accept = accept;\r
+\r
+                               if (contentType != null && contentType != "")\r
+                                       req.ContentType = contentType;\r
+\r
+                               if (connection != null && connection != "")\r
+                                       req.Connection = connection;\r
+\r
+                               if (userAgent != null && userAgent != "")\r
+                                       req.UserAgent = userAgent;\r
+\r
+                               if (referer != null && referer != "")\r
+                                       req.Referer = referer;\r
+                       }\r
+\r
+                       responseHeaders = null;\r
+                       return request;\r
+               }\r
+\r
+               WebRequest SetupRequest (string address, string method)\r
+               {\r
+                       WebRequest request = SetupRequest (address);\r
+                       request.Method = method;\r
+                       return request;\r
+               }\r
+\r
+               WebRequest SetupRequest (string address, string method, int contentLength)\r
+               {\r
+                       WebRequest request = SetupRequest (address, method);\r
+                       request.ContentLength = contentLength;\r
+                       return request;\r
+               }\r
+\r
+               Stream ProcessResponse (WebResponse response)\r
+               {\r
+                       responseHeaders = response.Headers;\r
+                       return response.GetResponseStream ();\r
+               }\r
+\r
+               static byte [] ReadAll (Stream stream, int length)\r
+               {\r
+                       MemoryStream ms = null;\r
+                       \r
+                       bool nolength = (length == -1);\r
+                       int size = ((nolength) ? 8192 : length);\r
+                       if (nolength)\r
+                               ms = new MemoryStream ();\r
+\r
+                       int nread = 0;\r
+                       int offset = 0;\r
+                       byte [] buffer = new byte [size];\r
+                       while ((nread = stream.Read (buffer, offset, size)) != 0) {\r
+                               if (nolength) {\r
+                                       ms.Write (buffer, 0, nread);\r
+                               } else {\r
+                                       offset += nread;\r
+                                       size -= nread;\r
+                               }\r
+                       }\r
+\r
+                       if (nolength)\r
+                               return ms.ToArray ();\r
+\r
+                       return buffer;\r
+               }\r
+\r
+               string UrlEncode (string str)\r
+               {\r
+                       StringBuilder result = new StringBuilder ();\r
+\r
+                       int len = str.Length;\r
+                       for (int i = 0; i < len; i++) {\r
+                               char c = str [i];\r
+                               if (c == ' ')\r
+                                       result.Append ('+');\r
+                               else if ((c < '0' && c != '-' && c != '.') ||\r
+                                        (c < 'A' && c > '9') ||\r
+                                        (c > 'Z' && c < 'a' && c != '_') ||\r
+                                        (c > 'z')) {\r
+                                       result.Append ('%');\r
+                                       int idx = ((int) c) >> 4;\r
+                                       result.Append ((char) hexBytes [idx]);\r
+                                       idx = ((int) c) & 0x0F;\r
+                                       result.Append ((char) hexBytes [idx]);\r
+                               } else {\r
+                                       result.Append (c);\r
+                               }\r
+                       }\r
+\r
+                       return result.ToString ();\r
+               }\r
+\r
+               static void UrlEncodeAndWrite (Stream stream, byte [] bytes)\r
+               {\r
+                       if (bytes == null)\r
+                               return;\r
+\r
+                       int len = bytes.Length;\r
+                       if (len == 0)\r
+                               return;\r
+\r
+                       for (int i = 0; i < len; i++) {\r
+                               char c = (char) bytes [i];\r
+                               if (c == ' ')\r
+                                       stream.WriteByte ((byte) '+');\r
+                               else if ((c < '0' && c != '-' && c != '.') ||\r
+                                        (c < 'A' && c > '9') ||\r
+                                        (c > 'Z' && c < 'a' && c != '_') ||\r
+                                        (c > 'z')) {\r
+                                       stream.WriteByte ((byte) '%');\r
+                                       int idx = ((int) c) >> 4;\r
+                                       stream.WriteByte (hexBytes [idx]);\r
+                                       idx = ((int) c) & 0x0F;\r
+                                       stream.WriteByte (hexBytes [idx]);\r
+                               } else {\r
+                                       stream.WriteByte ((byte) c);\r
+                               }\r
+                       }\r
                }\r
        }\r
 }\r
+\r