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