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