2003-07-20 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[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                                 return new Uri (path);\r
296 \r
297                         if (baseAddress == null)\r
298                                 return new Uri (path + query, (query != null));\r
299 \r
300                         if (query == null)\r
301                                 return new Uri (baseAddress, path);\r
302 \r
303                         return new Uri (baseAddress, path + query, (query != null));\r
304                 }\r
305                 \r
306                 WebRequest SetupRequest (string address)\r
307                 {\r
308                         Uri uri = MakeUri (address);\r
309                         WebRequest request = WebRequest.Create (uri);\r
310                         request.Credentials = credentials;\r
311 \r
312                         // Special headers. These are properties of HttpWebRequest.\r
313                         // What do we do with other requests differnt from HttpWebRequest?\r
314                         if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {\r
315                                 HttpWebRequest req = (HttpWebRequest) request;\r
316                                 string expect = headers ["Expect"];\r
317                                 string contentType = headers ["Content-Type"];\r
318                                 string accept = headers ["Accept"];\r
319                                 string connection = headers ["Connection"];\r
320                                 string userAgent = headers ["User-Agent"];\r
321                                 string referer = headers ["Referer"];\r
322                                 headers.RemoveInternal ("Expect");\r
323                                 headers.RemoveInternal ("Content-Type");\r
324                                 headers.RemoveInternal ("Accept");\r
325                                 headers.RemoveInternal ("Connection");\r
326                                 headers.RemoveInternal ("Referer");\r
327                                 headers.RemoveInternal ("User-Agent");\r
328                                 request.Headers = headers;\r
329 \r
330                                 if (expect != null && expect != "")\r
331                                         req.Expect = expect;\r
332 \r
333                                 if (accept != null && accept != "")\r
334                                         req.Accept = accept;\r
335 \r
336                                 if (contentType != null && contentType != "")\r
337                                         req.ContentType = contentType;\r
338 \r
339                                 if (connection != null && connection != "")\r
340                                         req.Connection = connection;\r
341 \r
342                                 if (userAgent != null && userAgent != "")\r
343                                         req.UserAgent = userAgent;\r
344 \r
345                                 if (referer != null && referer != "")\r
346                                         req.Referer = referer;\r
347                         }\r
348 \r
349                         responseHeaders = null;\r
350                         return request;\r
351                 }\r
352 \r
353                 WebRequest SetupRequest (string address, string method)\r
354                 {\r
355                         WebRequest request = SetupRequest (address);\r
356                         request.Method = method;\r
357                         return request;\r
358                 }\r
359 \r
360                 WebRequest SetupRequest (string address, string method, int contentLength)\r
361                 {\r
362                         WebRequest request = SetupRequest (address, method);\r
363                         request.ContentLength = contentLength;\r
364                         return request;\r
365                 }\r
366 \r
367                 Stream ProcessResponse (WebResponse response)\r
368                 {\r
369                         responseHeaders = response.Headers;\r
370                         return response.GetResponseStream ();\r
371                 }\r
372 \r
373                 static byte [] ReadAll (Stream stream, int length)\r
374                 {\r
375                         MemoryStream ms = null;\r
376                         \r
377                         bool nolength = (length == -1);\r
378                         int size = ((nolength) ? 8192 : length);\r
379                         if (nolength)\r
380                                 ms = new MemoryStream ();\r
381 \r
382                         int nread = 0;\r
383                         int offset = 0;\r
384                         byte [] buffer = new byte [size];\r
385                         while ((nread = stream.Read (buffer, offset, size)) != 0) {\r
386                                 if (nolength) {\r
387                                         ms.Write (buffer, 0, nread);\r
388                                 } else {\r
389                                         offset += nread;\r
390                                         size -= nread;\r
391                                 }\r
392                         }\r
393 \r
394                         if (nolength)\r
395                                 return ms.ToArray ();\r
396 \r
397                         return buffer;\r
398                 }\r
399 \r
400                 string UrlEncode (string str)\r
401                 {\r
402                         StringBuilder result = new StringBuilder ();\r
403 \r
404                         int len = str.Length;\r
405                         for (int i = 0; i < len; i++) {\r
406                                 char c = str [i];\r
407                                 if (c == ' ')\r
408                                         result.Append ('+');\r
409                                 else if ((c < '0' && c != '-' && c != '.') ||\r
410                                          (c < 'A' && c > '9') ||\r
411                                          (c > 'Z' && c < 'a' && c != '_') ||\r
412                                          (c > 'z')) {\r
413                                         result.Append ('%');\r
414                                         int idx = ((int) c) >> 4;\r
415                                         result.Append ((char) hexBytes [idx]);\r
416                                         idx = ((int) c) & 0x0F;\r
417                                         result.Append ((char) hexBytes [idx]);\r
418                                 } else {\r
419                                         result.Append (c);\r
420                                 }\r
421                         }\r
422 \r
423                         return result.ToString ();\r
424                 }\r
425 \r
426                 static void UrlEncodeAndWrite (Stream stream, byte [] bytes)\r
427                 {\r
428                         if (bytes == null)\r
429                                 return;\r
430 \r
431                         int len = bytes.Length;\r
432                         if (len == 0)\r
433                                 return;\r
434 \r
435                         for (int i = 0; i < len; i++) {\r
436                                 char c = (char) bytes [i];\r
437                                 if (c == ' ')\r
438                                         stream.WriteByte ((byte) '+');\r
439                                 else if ((c < '0' && c != '-' && c != '.') ||\r
440                                          (c < 'A' && c > '9') ||\r
441                                          (c > 'Z' && c < 'a' && c != '_') ||\r
442                                          (c > 'z')) {\r
443                                         stream.WriteByte ((byte) '%');\r
444                                         int idx = ((int) c) >> 4;\r
445                                         stream.WriteByte (hexBytes [idx]);\r
446                                         idx = ((int) c) & 0x0F;\r
447                                         stream.WriteByte (hexBytes [idx]);\r
448                                 } else {\r
449                                         stream.WriteByte ((byte) c);\r
450                                 }\r
451                         }\r
452                 }\r
453         }\r
454 }\r
455 \r