2 // System.Net.WebClient
5 // Lawrence Pit (loz@cable.a2000.nl)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
33 using System.Collections.Specialized;
34 using System.ComponentModel;
36 using System.Runtime.InteropServices;
37 using System.Runtime.Serialization;
43 public sealed class WebClient : Component
45 static readonly string urlEncodedCType = "application/x-www-form-urlencoded";
46 static byte [] hexBytes;
47 ICredentials credentials;
48 WebHeaderCollection headers;
49 WebHeaderCollection responseHeaders;
52 NameValueCollection queryString;
54 Encoding encoding = Encoding.Default;
60 hexBytes = new byte [16];
62 for (int i = '0'; i <= '9'; i++, index++)
63 hexBytes [index] = (byte) i;
65 for (int i = 'A'; i <= 'F'; i++, index++)
66 hexBytes [index] = (byte) i;
75 public string BaseAddress {
77 if (baseString == null) {
78 if (baseAddress == null)
82 baseString = baseAddress.ToString ();
87 if (value == null || value == "") {
90 baseAddress = new Uri (value);
95 public ICredentials Credentials {
96 get { return credentials; }
97 set { credentials = value; }
100 public WebHeaderCollection Headers {
103 headers = new WebHeaderCollection ();
107 set { headers = value; }
110 public NameValueCollection QueryString {
112 if (queryString == null)
113 queryString = new NameValueCollection ();
117 set { queryString = value; }
120 public WebHeaderCollection ResponseHeaders {
121 get { return responseHeaders; }
125 public Encoding Encoding {
126 get { return encoding; }
129 throw new ArgumentNullException ("value");
137 public byte [] DownloadData (string address)
139 return DownloadData (address, "GET");
145 byte [] DownloadData (Uri address)
147 return DownloadData (address, "GET");
153 byte [] DownloadData (string address, string method)
155 return DownloadData (MakeUri (address), method);
161 byte [] DownloadData (Uri address, string method)
163 WebRequest request = SetupRequest (address, method);
164 request.Method = method;
165 WebResponse response = request.GetResponse ();
166 Stream st = ProcessResponse (response);
167 return ReadAll (st, (int) response.ContentLength);
170 public void DownloadFile (string address, string fileName)
172 DownloadFile (MakeUri (address), fileName);
178 void DownloadFile (Uri address, string fileName)
180 WebRequest request = SetupRequest (address);
181 WebResponse response = request.GetResponse ();
182 Stream st = ProcessResponse (response);
184 int cLength = (int) response.ContentLength;
185 int length = (cLength <= -1 || cLength > 8192) ? 8192 : cLength;
186 byte [] buffer = new byte [length];
187 FileStream f = new FileStream (fileName, FileMode.CreateNew);
190 while ((nread = st.Read (buffer, 0, length)) != 0)
191 f.Write (buffer, 0, nread);
196 public Stream OpenRead (string address)
198 return OpenRead (MakeUri (address));
204 Stream OpenRead (Uri address)
206 WebRequest request = SetupRequest (address);
207 WebResponse response = request.GetResponse ();
208 return ProcessResponse (response);
211 public Stream OpenWrite (string address)
213 return OpenWrite (address, "POST");
216 public Stream OpenWrite (string address, string method)
218 return OpenWrite (MakeUri (address), method);
224 Stream OpenWrite (Uri address, string method)
226 WebRequest request = SetupRequest (address, method);
227 return request.GetRequestStream ();
230 public byte [] UploadData (string address, byte [] data)
232 return UploadData (address, "POST", data);
235 public byte [] UploadData (string address, string method, byte [] data)
237 return UploadData (MakeUri (address), method, data);
241 public byte [] UploadData (Uri address, byte [] data)
243 return UploadData (address, "POST", data);
250 byte [] UploadData (Uri address, string method, byte [] data)
253 throw new ArgumentNullException ("data");
255 int contentLength = data.Length;
256 WebRequest request = SetupRequest (address, method, contentLength);
257 using (Stream stream = request.GetRequestStream ()) {
258 stream.Write (data, 0, contentLength);
261 WebResponse response = request.GetResponse ();
262 Stream st = ProcessResponse (response);
263 return ReadAll (st, (int) response.ContentLength);
266 public byte [] UploadFile (string address, string fileName)
268 return UploadFile (address, "POST", fileName);
271 public byte [] UploadFile (string address, string method, string fileName)
273 return UploadFile (MakeUri (address), method, fileName);
279 byte [] UploadFile (Uri address, string method, string fileName)
281 string fileCType = Headers ["Content-Type"];
282 if (fileCType != null) {
283 string lower = fileCType.ToLower ();
284 if (lower.StartsWith ("multipart/"))
285 throw new WebException ("Content-Type cannot be set to a multipart" +
286 " type for this request.");
288 fileCType = "application/octet-stream";
291 string boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
292 Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
293 WebRequest request = SetupRequest (address, method);
294 Stream reqStream = null;
295 Stream fStream = null;
296 byte [] resultBytes = null;
299 fStream = File.OpenRead (fileName);
300 reqStream = request.GetRequestStream ();
301 byte [] realBoundary = Encoding.ASCII.GetBytes ("--" + boundary + "\r\n");
302 reqStream.Write (realBoundary, 0, realBoundary.Length);
303 string partHeaders = String.Format ("Content-Disposition: form-data; " +
304 "name=\"file\"; filename=\"{0}\"\r\n" +
305 "Content-Type: {1}\r\n\r\n",
306 Path.GetFileName (fileName), fileCType);
308 byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
309 reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);
311 byte [] buffer = new byte [4096];
312 while ((nread = fStream.Read (buffer, 0, 4096)) != 0)
313 reqStream.Write (buffer, 0, nread);
315 reqStream.WriteByte ((byte) '\r');
316 reqStream.WriteByte ((byte) '\n');
317 reqStream.Write (realBoundary, 0, realBoundary.Length);
320 WebResponse response = request.GetResponse ();
321 Stream st = ProcessResponse (response);
322 resultBytes = ReadAll (st, (int) response.ContentLength);
323 } catch (WebException) {
325 } catch (Exception e) {
326 throw new WebException ("Error uploading file.", e);
331 if (reqStream != null)
338 public byte[] UploadValues (string address, NameValueCollection data)
340 return UploadValues (address, "POST", data);
343 public byte[] UploadValues (string address, string method, NameValueCollection data)
345 return UploadValues (MakeUri (address), method, data);
351 byte[] UploadValues (Uri uri, string method, NameValueCollection data)
354 throw new ArgumentNullException ("data"); // MS throws a nullref
356 string cType = Headers ["Content-Type"];
357 if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
358 throw new WebException ("Content-Type header cannot be changed from its default " +
359 "value for this request.");
361 Headers ["Content-Type"] = urlEncodedCType;
362 WebRequest request = SetupRequest (uri, method);
363 Stream rqStream = request.GetRequestStream ();
364 MemoryStream tmpStream = new MemoryStream ();
365 foreach (string key in data) {
366 byte [] bytes = Encoding.ASCII.GetBytes (key);
367 UrlEncodeAndWrite (tmpStream, bytes);
368 tmpStream.WriteByte ((byte) '=');
369 bytes = Encoding.ASCII.GetBytes (data [key]);
370 UrlEncodeAndWrite (tmpStream, bytes);
371 tmpStream.WriteByte ((byte) '&');
374 int length = (int) tmpStream.Length;
376 tmpStream.SetLength (--length); // remove trailing '&'
378 tmpStream.WriteByte ((byte) '\r');
379 tmpStream.WriteByte ((byte) '\n');
381 byte [] buf = tmpStream.GetBuffer ();
382 rqStream.Write (buf, 0, length + 2);
386 WebResponse response = request.GetResponse ();
387 Stream st = ProcessResponse (response);
388 return ReadAll (st, (int) response.ContentLength);
392 public string DownloadString (string address)
394 return encoding.GetString (DownloadData (address));
397 public string DownloadString (string address, string method)
399 return encoding.GetString (DownloadData (address, method));
402 public string DownloadString (Uri address)
404 return encoding.GetString (DownloadData (address));
407 public string DownloadString (Uri address, string method)
409 return encoding.GetString (DownloadData (address, method));
412 public string UploadString (string address, string data)
414 byte [] resp = UploadData (address, encoding.GetBytes (data));
415 return encoding.GetString (resp);
418 public string UploadString (string address, string method, string data)
420 byte [] resp = UploadData (address, method, encoding.GetBytes (data));
421 return encoding.GetString (resp);
424 public string UploadString (Uri address, string data)
426 byte [] resp = UploadData (address, encoding.GetBytes (data));
427 return encoding.GetString (resp);
430 public string UploadString (Uri address, string method, string data)
432 byte [] resp = UploadData (address, method, encoding.GetBytes (data));
433 return encoding.GetString (resp);
437 Uri MakeUri (string path)
440 if (queryString != null && queryString.Count != 0) {
441 // This is not the same as UploadValues, because these 'keys' are not
443 StringBuilder sb = new StringBuilder ();
445 foreach (string key in queryString)
446 sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));
448 if (sb.Length != 0) {
449 sb.Length--; // remove trailing '&'
450 query = sb.ToString ();
455 if (baseAddress == null && query == null) {
457 return new Uri (path);
459 catch (System.UriFormatException) {
460 if ((path[0] == Path.DirectorySeparatorChar) || (path[1] == ':' && Char.ToLower(path[0]) > 'a' && Char.ToLower(path[0]) < 'z')) {
461 return new Uri ("file://" + path);
464 return new Uri ("file://" + Environment.CurrentDirectory + Path.DirectorySeparatorChar + path);
469 if (baseAddress == null)
470 return new Uri (path + query, (query != null));
473 return new Uri (baseAddress, path);
475 return new Uri (baseAddress, path + query, (query != null));
478 WebRequest SetupRequest (Uri uri)
480 WebRequest request = WebRequest.Create (uri);
481 request.Credentials = credentials;
483 // Special headers. These are properties of HttpWebRequest.
484 // What do we do with other requests differnt from HttpWebRequest?
485 if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {
486 HttpWebRequest req = (HttpWebRequest) request;
487 string expect = headers ["Expect"];
488 string contentType = headers ["Content-Type"];
489 string accept = headers ["Accept"];
490 string connection = headers ["Connection"];
491 string userAgent = headers ["User-Agent"];
492 string referer = headers ["Referer"];
493 headers.RemoveInternal ("Expect");
494 headers.RemoveInternal ("Content-Type");
495 headers.RemoveInternal ("Accept");
496 headers.RemoveInternal ("Connection");
497 headers.RemoveInternal ("Referer");
498 headers.RemoveInternal ("User-Agent");
499 request.Headers = headers;
501 if (expect != null && expect != "")
504 if (accept != null && accept != "")
507 if (contentType != null && contentType != "")
508 req.ContentType = contentType;
510 if (connection != null && connection != "")
511 req.Connection = connection;
513 if (userAgent != null && userAgent != "")
514 req.UserAgent = userAgent;
516 if (referer != null && referer != "")
517 req.Referer = referer;
520 responseHeaders = null;
524 WebRequest SetupRequest (Uri uri, string method)
526 WebRequest request = SetupRequest (uri);
527 request.Method = method;
531 WebRequest SetupRequest (Uri uri, string method, int contentLength)
533 WebRequest request = SetupRequest (uri, method);
534 request.ContentLength = contentLength;
538 Stream ProcessResponse (WebResponse response)
540 responseHeaders = response.Headers;
541 return response.GetResponseStream ();
544 static byte [] ReadAll (Stream stream, int length)
546 MemoryStream ms = null;
548 bool nolength = (length == -1);
549 int size = ((nolength) ? 8192 : length);
551 ms = new MemoryStream ();
555 byte [] buffer = new byte [size];
556 while ((nread = stream.Read (buffer, offset, size)) != 0) {
558 ms.Write (buffer, 0, nread);
566 return ms.ToArray ();
571 string UrlEncode (string str)
573 StringBuilder result = new StringBuilder ();
575 int len = str.Length;
576 for (int i = 0; i < len; i++) {
580 else if ((c < '0' && c != '-' && c != '.') ||
581 (c < 'A' && c > '9') ||
582 (c > 'Z' && c < 'a' && c != '_') ||
585 int idx = ((int) c) >> 4;
586 result.Append ((char) hexBytes [idx]);
587 idx = ((int) c) & 0x0F;
588 result.Append ((char) hexBytes [idx]);
594 return result.ToString ();
597 static void UrlEncodeAndWrite (Stream stream, byte [] bytes)
602 int len = bytes.Length;
606 for (int i = 0; i < len; i++) {
607 char c = (char) bytes [i];
609 stream.WriteByte ((byte) '+');
610 else if ((c < '0' && c != '-' && c != '.') ||
611 (c < 'A' && c > '9') ||
612 (c > 'Z' && c < 'a' && c != '_') ||
614 stream.WriteByte ((byte) '%');
615 int idx = ((int) c) >> 4;
616 stream.WriteByte (hexBytes [idx]);
617 idx = ((int) c) & 0x0F;
618 stream.WriteByte (hexBytes [idx]);
620 stream.WriteByte ((byte) c);