WebClient.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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, string method)
  126. {
  127. WebRequest request = SetupRequest (address, method);
  128. request.Method = method;
  129. WebResponse response = request.GetResponse ();
  130. Stream st = ProcessResponse (response);
  131. return ReadAll (st, (int) response.ContentLength);
  132. }
  133. #if NET_2_0
  134. public
  135. #endif
  136. byte [] DownloadData (string address, string method)
  137. {
  138. return DownloadData (MakeUri (address), method);
  139. }
  140. #if NET_2_0
  141. public byte [] DownloadData (Uri address)
  142. {
  143. return DownloadData (address, "GET");
  144. }
  145. #endif
  146. public void DownloadFile (string address, string fileName)
  147. {
  148. DownloadFile (MakeUri (address), fileName);
  149. }
  150. #if NET_2_0
  151. public
  152. #endif
  153. void DownloadFile (Uri address, string fileName)
  154. {
  155. WebRequest request = SetupRequest (address);
  156. WebResponse response = request.GetResponse ();
  157. Stream st = ProcessResponse (response);
  158. int cLength = (int) response.ContentLength;
  159. int length = (cLength <= -1 || cLength > 8192) ? 8192 : cLength;
  160. byte [] buffer = new byte [length];
  161. FileStream f = new FileStream (fileName, FileMode.CreateNew);
  162. int nread = 0;
  163. while ((nread = st.Read (buffer, 0, length)) != 0)
  164. f.Write (buffer, 0, nread);
  165. f.Close ();
  166. }
  167. public Stream OpenRead (string address)
  168. {
  169. return OpenRead (MakeUri (address));
  170. }
  171. #if NET_2_0
  172. public
  173. #endif
  174. Stream OpenRead (Uri address)
  175. {
  176. WebRequest request = SetupRequest (address);
  177. WebResponse response = request.GetResponse ();
  178. return ProcessResponse (response);
  179. }
  180. public Stream OpenWrite (string address)
  181. {
  182. return OpenWrite (address, "POST");
  183. }
  184. public Stream OpenWrite (string address, string method)
  185. {
  186. return OpenWrite (MakeUri (address), method);
  187. }
  188. #if NET_2_0
  189. public
  190. #endif
  191. Stream OpenWrite (Uri address, string method)
  192. {
  193. WebRequest request = SetupRequest (address, method);
  194. return request.GetRequestStream ();
  195. }
  196. public byte [] UploadData (string address, byte [] data)
  197. {
  198. return UploadData (address, "POST", data);
  199. }
  200. public byte [] UploadData (string address, string method, byte [] data)
  201. {
  202. return UploadData (MakeUri (address), method, data);
  203. }
  204. #if NET_2_0
  205. public byte [] UploadData (Uri address, byte [] data)
  206. {
  207. return UploadData (address, "POST", data);
  208. }
  209. #endif
  210. #if NET_2_0
  211. public
  212. #endif
  213. byte [] UploadData (Uri address, string method, byte [] data)
  214. {
  215. if (data == null)
  216. throw new ArgumentNullException ("data");
  217. int contentLength = data.Length;
  218. WebRequest request = SetupRequest (address, method, contentLength);
  219. using (Stream stream = request.GetRequestStream ()) {
  220. stream.Write (data, 0, contentLength);
  221. }
  222. WebResponse response = request.GetResponse ();
  223. Stream st = ProcessResponse (response);
  224. return ReadAll (st, (int) response.ContentLength);
  225. }
  226. public byte [] UploadFile (string address, string fileName)
  227. {
  228. return UploadFile (address, "POST", fileName);
  229. }
  230. public byte [] UploadFile (string address, string method, string fileName)
  231. {
  232. return UploadFile (MakeUri (address), method, fileName);
  233. }
  234. #if NET_2_0
  235. public
  236. #endif
  237. byte [] UploadFile (Uri address, string method, string fileName)
  238. {
  239. string fileCType = Headers ["Content-Type"];
  240. if (fileCType != null) {
  241. string lower = fileCType.ToLower ();
  242. if (lower.StartsWith ("multipart/"))
  243. throw new WebException ("Content-Type cannot be set to a multipart" +
  244. " type for this request.");
  245. } else {
  246. fileCType = "application/octet-stream";
  247. }
  248. string boundary = "------------" + DateTime.Now.Ticks.ToString ("x");
  249. Headers ["Content-Type"] = String.Format ("multipart/form-data; boundary={0}", boundary);
  250. WebRequest request = SetupRequest (address, method);
  251. Stream reqStream = null;
  252. Stream fStream = null;
  253. byte [] resultBytes = null;
  254. try {
  255. fStream = File.OpenRead (fileName);
  256. reqStream = request.GetRequestStream ();
  257. byte [] realBoundary = Encoding.ASCII.GetBytes ("--" + boundary + "\r\n");
  258. reqStream.Write (realBoundary, 0, realBoundary.Length);
  259. string partHeaders = String.Format ("Content-Disposition: form-data; " +
  260. "name=\"file\"; filename=\"{0}\"\r\n" +
  261. "Content-Type: {1}\r\n\r\n",
  262. Path.GetFileName (fileName), fileCType);
  263. byte [] partHeadersBytes = Encoding.UTF8.GetBytes (partHeaders);
  264. reqStream.Write (partHeadersBytes, 0, partHeadersBytes.Length);
  265. int nread;
  266. byte [] buffer = new byte [4096];
  267. while ((nread = fStream.Read (buffer, 0, 4096)) != 0)
  268. reqStream.Write (buffer, 0, nread);
  269. reqStream.WriteByte ((byte) '\r');
  270. reqStream.WriteByte ((byte) '\n');
  271. reqStream.Write (realBoundary, 0, realBoundary.Length);
  272. reqStream.Close ();
  273. reqStream = null;
  274. WebResponse response = request.GetResponse ();
  275. Stream st = ProcessResponse (response);
  276. resultBytes = ReadAll (st, (int) response.ContentLength);
  277. } catch (WebException) {
  278. throw;
  279. } catch (Exception e) {
  280. throw new WebException ("Error uploading file.", e);
  281. } finally {
  282. if (fStream != null)
  283. fStream.Close ();
  284. if (reqStream != null)
  285. reqStream.Close ();
  286. }
  287. return resultBytes;
  288. }
  289. public byte[] UploadValues (string address, NameValueCollection data)
  290. {
  291. return UploadValues (address, "POST", data);
  292. }
  293. public byte[] UploadValues (string address, string method, NameValueCollection data)
  294. {
  295. return UploadValues (MakeUri (address), method, data);
  296. }
  297. #if NET_2_0
  298. public
  299. #endif
  300. byte[] UploadValues (Uri uri, string method, NameValueCollection data)
  301. {
  302. if (data == null)
  303. throw new ArgumentNullException ("data"); // MS throws a nullref
  304. string cType = Headers ["Content-Type"];
  305. if (cType != null && String.Compare (cType, urlEncodedCType, true) != 0)
  306. throw new WebException ("Content-Type header cannot be changed from its default " +
  307. "value for this request.");
  308. Headers ["Content-Type"] = urlEncodedCType;
  309. WebRequest request = SetupRequest (uri, method);
  310. Stream rqStream = request.GetRequestStream ();
  311. MemoryStream tmpStream = new MemoryStream ();
  312. foreach (string key in data) {
  313. byte [] bytes = Encoding.ASCII.GetBytes (key);
  314. UrlEncodeAndWrite (tmpStream, bytes);
  315. tmpStream.WriteByte ((byte) '=');
  316. bytes = Encoding.ASCII.GetBytes (data [key]);
  317. UrlEncodeAndWrite (tmpStream, bytes);
  318. tmpStream.WriteByte ((byte) '&');
  319. }
  320. int length = (int) tmpStream.Length;
  321. if (length > 0)
  322. tmpStream.SetLength (--length); // remove trailing '&'
  323. tmpStream.WriteByte ((byte) '\r');
  324. tmpStream.WriteByte ((byte) '\n');
  325. byte [] buf = tmpStream.GetBuffer ();
  326. rqStream.Write (buf, 0, length + 2);
  327. rqStream.Close ();
  328. tmpStream.Close ();
  329. WebResponse response = request.GetResponse ();
  330. Stream st = ProcessResponse (response);
  331. return ReadAll (st, (int) response.ContentLength);
  332. }
  333. #if NET_2_0
  334. public string DownloadString (string address)
  335. {
  336. return encoding.GetString (DownloadData (address));
  337. }
  338. public string DownloadString (string address, string method)
  339. {
  340. return encoding.GetString (DownloadData (address, method));
  341. }
  342. public string DownloadString (Uri address)
  343. {
  344. return encoding.GetString (DownloadData (address));
  345. }
  346. public string DownloadString (Uri address, string method)
  347. {
  348. return encoding.GetString (DownloadData (address, method));
  349. }
  350. public string UploadString (string address, string data)
  351. {
  352. byte [] resp = UploadData (address, encoding.GetBytes (data));
  353. return encoding.GetString (resp);
  354. }
  355. public string UploadString (string address, string method, string data)
  356. {
  357. byte [] resp = UploadData (address, method, encoding.GetBytes (data));
  358. return encoding.GetString (resp);
  359. }
  360. public string UploadString (Uri address, string data)
  361. {
  362. byte [] resp = UploadData (address, encoding.GetBytes (data));
  363. return encoding.GetString (resp);
  364. }
  365. public string UploadString (Uri address, string method, string data)
  366. {
  367. byte [] resp = UploadData (address, method, encoding.GetBytes (data));
  368. return encoding.GetString (resp);
  369. }
  370. #endif
  371. Uri MakeUri (string path)
  372. {
  373. string query = null;
  374. if (queryString != null && queryString.Count != 0) {
  375. // This is not the same as UploadValues, because these 'keys' are not
  376. // urlencoded here.
  377. StringBuilder sb = new StringBuilder ();
  378. sb.Append ('?');
  379. foreach (string key in queryString)
  380. sb.AppendFormat ("{0}={1}&", key, UrlEncode (queryString [key]));
  381. if (sb.Length != 0) {
  382. sb.Length--; // remove trailing '&'
  383. query = sb.ToString ();
  384. }
  385. }
  386. if (baseAddress == null && query == null) {
  387. try {
  388. return new Uri (path);
  389. }
  390. catch (System.UriFormatException) {
  391. if ((path[0] == Path.DirectorySeparatorChar) || (path[1] == ':' && Char.ToLower(path[0]) > 'a' && Char.ToLower(path[0]) < 'z')) {
  392. return new Uri ("file://" + path);
  393. }
  394. else {
  395. return new Uri ("file://" + Environment.CurrentDirectory + Path.DirectorySeparatorChar + path);
  396. }
  397. }
  398. }
  399. if (baseAddress == null)
  400. return new Uri (path + query, (query != null));
  401. if (query == null)
  402. return new Uri (baseAddress, path);
  403. return new Uri (baseAddress, path + query, (query != null));
  404. }
  405. WebRequest SetupRequest (Uri uri)
  406. {
  407. WebRequest request = WebRequest.Create (uri);
  408. request.Credentials = credentials;
  409. // Special headers. These are properties of HttpWebRequest.
  410. // What do we do with other requests differnt from HttpWebRequest?
  411. if (headers != null && headers.Count != 0 && (request is HttpWebRequest)) {
  412. HttpWebRequest req = (HttpWebRequest) request;
  413. string expect = headers ["Expect"];
  414. string contentType = headers ["Content-Type"];
  415. string accept = headers ["Accept"];
  416. string connection = headers ["Connection"];
  417. string userAgent = headers ["User-Agent"];
  418. string referer = headers ["Referer"];
  419. headers.RemoveInternal ("Expect");
  420. headers.RemoveInternal ("Content-Type");
  421. headers.RemoveInternal ("Accept");
  422. headers.RemoveInternal ("Connection");
  423. headers.RemoveInternal ("Referer");
  424. headers.RemoveInternal ("User-Agent");
  425. request.Headers = headers;
  426. if (expect != null && expect != "")
  427. req.Expect = expect;
  428. if (accept != null && accept != "")
  429. req.Accept = accept;
  430. if (contentType != null && contentType != "")
  431. req.ContentType = contentType;
  432. if (connection != null && connection != "")
  433. req.Connection = connection;
  434. if (userAgent != null && userAgent != "")
  435. req.UserAgent = userAgent;
  436. if (referer != null && referer != "")
  437. req.Referer = referer;
  438. }
  439. responseHeaders = null;
  440. return request;
  441. }
  442. WebRequest SetupRequest (Uri uri, string method)
  443. {
  444. WebRequest request = SetupRequest (uri);
  445. request.Method = method;
  446. return request;
  447. }
  448. WebRequest SetupRequest (Uri uri, string method, int contentLength)
  449. {
  450. WebRequest request = SetupRequest (uri, method);
  451. request.ContentLength = contentLength;
  452. return request;
  453. }
  454. Stream ProcessResponse (WebResponse response)
  455. {
  456. responseHeaders = response.Headers;
  457. return response.GetResponseStream ();
  458. }
  459. static byte [] ReadAll (Stream stream, int length)
  460. {
  461. MemoryStream ms = null;
  462. bool nolength = (length == -1);
  463. int size = ((nolength) ? 8192 : length);
  464. if (nolength)
  465. ms = new MemoryStream ();
  466. int nread = 0;
  467. int offset = 0;
  468. byte [] buffer = new byte [size];
  469. while ((nread = stream.Read (buffer, offset, size)) != 0) {
  470. if (nolength) {
  471. ms.Write (buffer, 0, nread);
  472. } else {
  473. offset += nread;
  474. size -= nread;
  475. }
  476. }
  477. if (nolength)
  478. return ms.ToArray ();
  479. return buffer;
  480. }
  481. string UrlEncode (string str)
  482. {
  483. StringBuilder result = new StringBuilder ();
  484. int len = str.Length;
  485. for (int i = 0; i < len; i++) {
  486. char c = str [i];
  487. if (c == ' ')
  488. result.Append ('+');
  489. else if ((c < '0' && c != '-' && c != '.') ||
  490. (c < 'A' && c > '9') ||
  491. (c > 'Z' && c < 'a' && c != '_') ||
  492. (c > 'z')) {
  493. result.Append ('%');
  494. int idx = ((int) c) >> 4;
  495. result.Append ((char) hexBytes [idx]);
  496. idx = ((int) c) & 0x0F;
  497. result.Append ((char) hexBytes [idx]);
  498. } else {
  499. result.Append (c);
  500. }
  501. }
  502. return result.ToString ();
  503. }
  504. static void UrlEncodeAndWrite (Stream stream, byte [] bytes)
  505. {
  506. if (bytes == null)
  507. return;
  508. int len = bytes.Length;
  509. if (len == 0)
  510. return;
  511. for (int i = 0; i < len; i++) {
  512. char c = (char) bytes [i];
  513. if (c == ' ')
  514. stream.WriteByte ((byte) '+');
  515. else if ((c < '0' && c != '-' && c != '.') ||
  516. (c < 'A' && c > '9') ||
  517. (c > 'Z' && c < 'a' && c != '_') ||
  518. (c > 'z')) {
  519. stream.WriteByte ((byte) '%');
  520. int idx = ((int) c) >> 4;
  521. stream.WriteByte (hexBytes [idx]);
  522. idx = ((int) c) & 0x0F;
  523. stream.WriteByte (hexBytes [idx]);
  524. } else {
  525. stream.WriteByte ((byte) c);
  526. }
  527. }
  528. }
  529. }
  530. }