2004-01-27 Nick Drochak <ndrochak@ieee.org>
[mono.git] / mcs / class / System / System.Net / WebClient.cs
1 //\r
2 // System.Net.WebClient\r
3 //\r
4 // Authors:\r
5 //      Lawrence Pit (loz@cable.a2000.nl)\r
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)\r
7 //\r
8 // (c) 2003 Ximian, Inc. (http://www.ximian.com)\r
9 //\r
10 \r
11 using System;\r
12 using System.Collections.Specialized;\r
13 using System.ComponentModel;\r
14 using System.IO;\r
15 using System.Runtime.InteropServices;\r
16 using System.Runtime.Serialization;\r
17 using System.Text;\r
18 \r
19 namespace System.Net \r
20 {\r
21         [ComVisible(true)]\r
22         public sealed class WebClient : Component\r
23         {\r
24                 static readonly string urlEncodedCType = "application/x-www-form-urlencoded";\r
25                 static byte [] hexBytes;\r
26                 ICredentials credentials;\r
27                 WebHeaderCollection headers;\r
28                 WebHeaderCollection responseHeaders;\r
29                 Uri baseAddress;\r
30                 string baseString;\r
31                 NameValueCollection queryString;\r
32         \r
33                 // Constructors\r
34                 static WebClient ()\r
35                 {\r
36                         hexBytes = new byte [16];\r
37                         int index = 0;\r
38                         for (int i = '0'; i <= '9'; i++, index++)\r
39                                 hexBytes [index] = (byte) i;\r
40 \r
41                         for (int i = 'A'; i <= 'F'; i++, index++)\r
42                                 hexBytes [index] = (byte) i;\r
43                 }\r
44                 \r
45                 public WebClient ()\r
46                 {\r
47                 }\r
48                 \r
49                 // Properties\r
50                 \r
51                 public string BaseAddress {\r
52                         get {\r
53                                 if (baseString == null) {\r
54                                         if (baseAddress == null)\r
55                                                 return "";\r
56                                 }\r
57 \r
58                                 baseString = baseAddress.ToString ();\r
59                                 return baseString;\r
60                         }\r
61                         \r
62                         set {\r
63                                 if (value == null || value == "") {\r
64                                         baseAddress = null;\r
65                                 } else {\r
66                                         baseAddress = new Uri (value);\r
67                                 }\r
68                         }\r
69                 }\r
70                 \r
71                 public ICredentials Credentials {\r
72                         get { return credentials; }\r
73                         set { credentials = value; }\r
74                 }\r
75                 \r
76                 public WebHeaderCollection Headers {\r
77                         get {\r
78                                 if (headers == null)\r
79                                         headers = new WebHeaderCollection ();\r
80 \r
81                                 return headers;\r
82                         }\r
83                         set { headers = value; }\r
84                 }\r
85                 \r
86                 public NameValueCollection QueryString {\r
87                         get {\r
88                                 if (queryString == null)\r
89                                         queryString = new NameValueCollection ();\r
90 \r
91                                 return queryString;\r
92                         }\r
93                         set { queryString = value; }\r
94                 }\r
95                 \r
96                 public WebHeaderCollection ResponseHeaders {\r
97                         get { return responseHeaders; }\r
98                 }\r
99 \r
100                 // Methods\r
101                 \r
102                 public byte [] DownloadData (string address)\r
103                 {\r
104                         WebRequest request = SetupRequest (address);\r
105                         WebResponse response = request.GetResponse ();\r
106                         Stream st = ProcessResponse (response);\r
107                         return ReadAll (st, (int) response.ContentLength);\r
108                 }\r
109                 \r
110                 public void DownloadFile (string address, string fileName)\r
111                 {\r
112                         WebRequest request = SetupRequest (address);\r
113                         WebResponse response = request.GetResponse ();\r
114                         Stream st = ProcessResponse (response);\r
115 \r
116                         int cLength = (int) response.ContentLength;\r
117                         int length = (cLength <= -1 || cLength > 8192) ? 8192 : cLength;\r
118                         byte [] buffer = new byte [length];\r
119                         FileStream f = new FileStream (fileName, FileMode.CreateNew);\r
120 \r
121                         int nread = 0;\r
122                         while ((nread = st.Read (buffer, 0, length)) != 0)\r
123                                 f.Write (buffer, 0, nread);\r
124 \r
125                         f.Close ();\r
126                 }\r
127                 \r
128                 public Stream OpenRead (string address)\r
129                 {\r
130                         WebRequest request = SetupRequest (address);\r
131                         WebResponse response = request.GetResponse ();\r
132                         return ProcessResponse (response);\r
133                 }\r
134                 \r
135                 public Stream OpenWrite (string address)\r
136                 {\r
137                         return OpenWrite (address, "POST");\r
138                 }\r
139                 \r
140                 public Stream OpenWrite (string address, string method)\r
141                 {\r
142                         WebRequest request = SetupRequest (address, method);\r
143                         return request.GetRequestStream ();\r
144                 }\r
145                                 \r
146                 public byte [] UploadData (string address, byte [] data)\r
147                 {\r
148                         return UploadData (address, "POST", data);\r
149                 }\r
150                 \r
151                 public byte [] UploadData (string address, string method, byte [] data)\r
152                 {\r
153                         if (data == null)\r
154                                 throw new ArgumentNullException ("data");\r
155 \r
156                         int contentLength = data.Length;\r
157                         WebRequest request = SetupRequest (address, method, contentLength);\r
158                         using (Stream stream = request.GetRequestStream ()) {\r
159                                 stream.Write (data, 0, contentLength);\r
160                         }\r
161 \r
162                         WebResponse response = request.GetResponse ();\r
163                         Stream st = ProcessResponse (response);\r
164                         return ReadAll (st, (int) response.ContentLength);\r
165                 }\r
166                 \r
167                 public byte [] UploadFile (string address, string fileName)\r
168                 {\r
169                         return UploadFile (address, "POST", fileName);\r
170                 }\r
171                 \r
172                 public byte [] UploadFile (string address, string method, string fileName)\r
173                 {\r
174                         string fileCType = Headers ["Content-Type"];\r
175                         if (fileCType != null) {\r
176                                 string lower = fileCType.ToLower ();\r
177                                 if (lower.StartsWith ("multipart/"))\r
178                                         throw new WebException ("Content-Type cannot be set to a multipart" +\r
179                                                                 " type for this request.");\r
180                         } else {\r
181                                 fileCType = "application/octet-stream";\r
182                         }\r
183 \r
184                         string boundary = "------------" + DateTime.Now.Ticks.ToString ("x");\r
185                         Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);\r
186                         WebRequest request = SetupRequest (address, method);\r
187                         Stream reqStream = null;\r
188                         Stream fStream = null;\r
189                         byte [] resultBytes = null;\r
190 \r
191                         try {\r
192                                 fStream = File.OpenRead (fileName);\r
193                                 reqStream = request.GetRequestStream ();\r
194                                 byte [] realBoundary = Encoding.ASCII.GetBytes ("--" + boundary + "\r\n");\r
195                                 reqStream.Write (realBoundary, 0, realBoundary.Length);\r
196                                 string partHeaders = String.Format ("Content-Disposition: form-data; " +\r
197                                                                     "name=\"file\"; filename=\"{0}\"\r\n" +\r
198                                                                     "Content-Type: {1}\r\n\r\n",\r
199                                                                     Path.GetFileName (fileName), fileCType);\r
200 \r
201                                 byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);\r
202                                 reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);\r
203                                 int nread;\r
204                                 byte [] buffer = new byte [4096];\r
205                                 while ((nread = fStream.Read (buffer, 0, 4096)) != 0)\r
206                                         reqStream.Write (buffer, 0, nread);\r
207 \r
208                                 reqStream.WriteByte ((byte) '\r');\r
209                                 reqStream.WriteByte ((byte) '\n');\r
210                                 reqStream.Write (realBoundary, 0, realBoundary.Length);\r
211                                 reqStream.Close ();\r
212                                 reqStream = null;\r
213                                 WebResponse response = request.GetResponse ();\r
214                                 Stream st = ProcessResponse (response);\r
215                                 resultBytes = ReadAll (st, (int) response.ContentLength);\r
216                         } catch (WebException) {\r
217                                 throw;\r
218                         } catch (Exception e) {\r
219                                 throw new WebException ("Error uploading file.", e);\r
220                         } finally {\r
221                                 if (fStream != null)\r
222                                         fStream.Close ();\r
223 \r
224                                 if (reqStream != null)\r
225                                         reqStream.Close ();\r
226                         }\r
227                         \r
228                         return resultBytes;     \r
229                 }\r
230                 \r
231                 public byte[] UploadValues (string address, NameValueCollection data)\r
232                 {\r
233                         return UploadValues (address, "POST", data);\r
234                 }\r
235                 \r
236                 public byte[] UploadValues (string address, string method, NameValueCollection data)\r
237                 {\r
238                         if (data == null)\r
239                                 throw new ArgumentNullException ("data"); // MS throws a nullref\r
240 \r
241                         string cType = Headers ["Content-Type"];\r
242                         if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)\r
243                                 throw new WebException ("Content-Type header cannot be changed from its default " +\r
244                                                         "value for this request.");\r
245 \r
246                         Headers ["Content-Type"] = urlEncodedCType;\r
247                         WebRequest request = SetupRequest (address, method);\r
248                         Stream rqStream = request.GetRequestStream ();\r
249                         MemoryStream tmpStream = new MemoryStream ();\r
250                         foreach (string key in data) {\r
251                                 byte [] bytes = Encoding.ASCII.GetBytes (key);\r
252                                 UrlEncodeAndWrite (tmpStream, bytes);\r
253                                 tmpStream.WriteByte ((byte) '=');\r
254                                 bytes = Encoding.ASCII.GetBytes (data [key]);\r
255                                 UrlEncodeAndWrite (tmpStream, bytes);\r
256                                 tmpStream.WriteByte ((byte) '&');\r
257                         }\r
258 \r
259                         int length = (int) tmpStream.Length;\r
260                         if (length > 0)\r
261                                 tmpStream.SetLength (--length); // remove trailing '&'\r
262 \r
263                         tmpStream.WriteByte ((byte) '\r');\r
264                         tmpStream.WriteByte ((byte) '\n');\r
265 \r
266                         byte [] buf = tmpStream.GetBuffer ();\r
267                         rqStream.Write (buf, 0, length + 2);\r
268                         rqStream.Close ();\r
269                         tmpStream.Close ();\r
270 \r
271                         WebResponse response = request.GetResponse ();\r
272                         Stream st = ProcessResponse (response);\r
273                         return ReadAll (st, (int) response.ContentLength);\r
274                 }\r
275 \r
276                 Uri MakeUri (string path)\r
277                 {\r
278                         string query = null;\r
279                         if (queryString != null && queryString.Count != 0) {\r
280                                 // This is not the same as UploadValues, because these 'keys' are not\r
281                                 // urlencoded here.\r
282                                 StringBuilder sb = new StringBuilder ();\r
283                                 sb.Append ('?');\r
284                                 foreach (string key in queryString)\r
285                                         sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));\r
286 \r
287                                 if (sb.Length != 0) {\r
288                                         sb.Length--; // remove trailing '&'\r
289                                         query = sb.ToString ();\r
290                                 }\r
291                         }\r
292                         \r
293 \r
294                         if (baseAddress == null && query == null) {\r
295                                 try {\r
296                                         return new Uri (path);\r
297                                 }\r
298                                 catch (System.UriFormatException) {\r
299                                         if ((path[0] == Path.DirectorySeparatorChar) || (path[1] == ':' && Char.ToLower(path[0]) > 'a' && Char.ToLower(path[0]) < 'z')) {\r
300                                                 return new Uri ("file://" + path);\r
301                                         }\r
302                                         else {\r
303                                                 return new Uri ("file://" + Environment.CurrentDirectory + Path.DirectorySeparatorChar + path);\r
304                                         }\r
305                                 }\r
306                         }\r
307 \r
308                         if (baseAddress == null)\r
309                                 return new Uri (path + query, (query != null));\r
310 \r
311                         if (query == null)\r
312                                 return new Uri (baseAddress, path);\r
313 \r
314                         return new Uri (baseAddress, path + query, (query != null));\r
315                 }\r
316                 \r
317                 WebRequest SetupRequest (string address)\r
318                 {\r
319                         Uri uri = MakeUri (address);\r
320                         WebRequest request = WebRequest.Create (uri);\r
321                         request.Credentials = credentials;\r
322 \r
323                         // Special headers. These are properties of HttpWebRequest.\r
324                         // What do we do with other requests differnt from HttpWebRequest?\r
325                         if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {\r
326                                 HttpWebRequest req = (HttpWebRequest) request;\r
327                                 string expect = headers ["Expect"];\r
328                                 string contentType = headers ["Content-Type"];\r
329                                 string accept = headers ["Accept"];\r
330                                 string connection = headers ["Connection"];\r
331                                 string userAgent = headers ["User-Agent"];\r
332                                 string referer = headers ["Referer"];\r
333                                 headers.RemoveInternal ("Expect");\r
334                                 headers.RemoveInternal ("Content-Type");\r
335                                 headers.RemoveInternal ("Accept");\r
336                                 headers.RemoveInternal ("Connection");\r
337                                 headers.RemoveInternal ("Referer");\r
338                                 headers.RemoveInternal ("User-Agent");\r
339                                 request.Headers = headers;\r
340 \r
341                                 if (expect != null && expect != "")\r
342                                         req.Expect = expect;\r
343 \r
344                                 if (accept != null && accept != "")\r
345                                         req.Accept = accept;\r
346 \r
347                                 if (contentType != null && contentType != "")\r
348                                         req.ContentType = contentType;\r
349 \r
350                                 if (connection != null && connection != "")\r
351                                         req.Connection = connection;\r
352 \r
353                                 if (userAgent != null && userAgent != "")\r
354                                         req.UserAgent = userAgent;\r
355 \r
356                                 if (referer != null && referer != "")\r
357                                         req.Referer = referer;\r
358                         }\r
359 \r
360                         responseHeaders = null;\r
361                         return request;\r
362                 }\r
363 \r
364                 WebRequest SetupRequest (string address, string method)\r
365                 {\r
366                         WebRequest request = SetupRequest (address);\r
367                         request.Method = method;\r
368                         return request;\r
369                 }\r
370 \r
371                 WebRequest SetupRequest (string address, string method, int contentLength)\r
372                 {\r
373                         WebRequest request = SetupRequest (address, method);\r
374                         request.ContentLength = contentLength;\r
375                         return request;\r
376                 }\r
377 \r
378                 Stream ProcessResponse (WebResponse response)\r
379                 {\r
380                         responseHeaders = response.Headers;\r
381                         return response.GetResponseStream ();\r
382                 }\r
383 \r
384                 static byte [] ReadAll (Stream stream, int length)\r
385                 {\r
386                         MemoryStream ms = null;\r
387                         \r
388                         bool nolength = (length == -1);\r
389                         int size = ((nolength) ? 8192 : length);\r
390                         if (nolength)\r
391                                 ms = new MemoryStream ();\r
392 \r
393                         int nread = 0;\r
394                         int offset = 0;\r
395                         byte [] buffer = new byte [size];\r
396                         while ((nread = stream.Read (buffer, offset, size)) != 0) {\r
397                                 if (nolength) {\r
398                                         ms.Write (buffer, 0, nread);\r
399                                 } else {\r
400                                         offset += nread;\r
401                                         size -= nread;\r
402                                 }\r
403                         }\r
404 \r
405                         if (nolength)\r
406                                 return ms.ToArray ();\r
407 \r
408                         return buffer;\r
409                 }\r
410 \r
411                 string UrlEncode (string str)\r
412                 {\r
413                         StringBuilder result = new StringBuilder ();\r
414 \r
415                         int len = str.Length;\r
416                         for (int i = 0; i < len; i++) {\r
417                                 char c = str [i];\r
418                                 if (c == ' ')\r
419                                         result.Append ('+');\r
420                                 else if ((c < '0' && c != '-' && c != '.') ||\r
421                                          (c < 'A' && c > '9') ||\r
422                                          (c > 'Z' && c < 'a' && c != '_') ||\r
423                                          (c > 'z')) {\r
424                                         result.Append ('%');\r
425                                         int idx = ((int) c) >> 4;\r
426                                         result.Append ((char) hexBytes [idx]);\r
427                                         idx = ((int) c) & 0x0F;\r
428                                         result.Append ((char) hexBytes [idx]);\r
429                                 } else {\r
430                                         result.Append (c);\r
431                                 }\r
432                         }\r
433 \r
434                         return result.ToString ();\r
435                 }\r
436 \r
437                 static void UrlEncodeAndWrite (Stream stream, byte [] bytes)\r
438                 {\r
439                         if (bytes == null)\r
440                                 return;\r
441 \r
442                         int len = bytes.Length;\r
443                         if (len == 0)\r
444                                 return;\r
445 \r
446                         for (int i = 0; i < len; i++) {\r
447                                 char c = (char) bytes [i];\r
448                                 if (c == ' ')\r
449                                         stream.WriteByte ((byte) '+');\r
450                                 else if ((c < '0' && c != '-' && c != '.') ||\r
451                                          (c < 'A' && c > '9') ||\r
452                                          (c > 'Z' && c < 'a' && c != '_') ||\r
453                                          (c > 'z')) {\r
454                                         stream.WriteByte ((byte) '%');\r
455                                         int idx = ((int) c) >> 4;\r
456                                         stream.WriteByte (hexBytes [idx]);\r
457                                         idx = ((int) c) & 0x0F;\r
458                                         stream.WriteByte (hexBytes [idx]);\r
459                                 } else {\r
460                                         stream.WriteByte ((byte) c);\r
461                                 }\r
462                         }\r
463                 }\r
464         }\r
465 }\r
466 \r