WebClient.cs 14 KB

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