WebClient.cs 16 KB

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