HttpWebResponse.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. //
  2. // System.Net.HttpWebResponse
  3. //
  4. // Authors:
  5. // Lawrence Pit ([email protected])
  6. // Gonzalo Paniagua Javier ([email protected])
  7. //
  8. // (c) 2002 Lawrence Pit
  9. // (c) 2003 Ximian, Inc. (http://www.ximian.com)
  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. using System;
  32. using System.Collections;
  33. using System.Globalization;
  34. using System.IO;
  35. using System.Net.Sockets;
  36. using System.Runtime.Serialization;
  37. using System.Text;
  38. namespace System.Net
  39. {
  40. [Serializable]
  41. public class HttpWebResponse : WebResponse, ISerializable, IDisposable
  42. {
  43. Uri uri;
  44. WebHeaderCollection webHeaders;
  45. CookieCollection cookieCollection;
  46. string method;
  47. Version version;
  48. HttpStatusCode statusCode;
  49. string statusDescription;
  50. long contentLength = -1;
  51. string contentType;
  52. CookieContainer cookieContainer;
  53. bool disposed = false;
  54. Stream stream;
  55. // Constructors
  56. internal HttpWebResponse (Uri uri, string method, WebConnectionData data, CookieContainer container)
  57. {
  58. this.uri = uri;
  59. this.method = method;
  60. webHeaders = data.Headers;
  61. version = data.Version;
  62. statusCode = (HttpStatusCode) data.StatusCode;
  63. statusDescription = data.StatusDescription;
  64. stream = data.stream;
  65. if (container != null) {
  66. this.cookieContainer = container;
  67. FillCookies ();
  68. } else if (webHeaders != null) {
  69. webHeaders.RemoveInternal ("Set-Cookie");
  70. webHeaders.RemoveInternal ("Set-Cookie2");
  71. }
  72. }
  73. protected HttpWebResponse (SerializationInfo serializationInfo, StreamingContext streamingContext)
  74. {
  75. SerializationInfo info = serializationInfo;
  76. uri = (Uri) info.GetValue ("uri", typeof (Uri));
  77. contentLength = info.GetInt64 ("contentLength");
  78. contentType = info.GetString ("contentType");
  79. method = info.GetString ("method");
  80. statusDescription = info.GetString ("statusDescription");
  81. cookieCollection = (CookieCollection) info.GetValue ("cookieCollection", typeof (CookieCollection));
  82. version = (Version) info.GetValue ("version", typeof (Version));
  83. statusCode = (HttpStatusCode) info.GetValue ("statusCode", typeof (HttpStatusCode));
  84. }
  85. // Properties
  86. public string CharacterSet {
  87. // Content-Type = "Content-Type" ":" media-type
  88. // media-type = type "/" subtype *( ";" parameter )
  89. // parameter = attribute "=" value
  90. // 3.7.1. default is ISO-8859-1
  91. get {
  92. CheckDisposed ();
  93. string contentType = ContentType;
  94. if (contentType == null)
  95. return "ISO-8859-1";
  96. string val = contentType.ToLower ();
  97. int pos = val.IndexOf ("charset=");
  98. if (pos == -1)
  99. return "ISO-8859-1";
  100. pos += 8;
  101. int pos2 = val.IndexOf (';', pos);
  102. return (pos2 == -1)
  103. ? contentType.Substring (pos)
  104. : contentType.Substring (pos, pos2 - pos);
  105. }
  106. }
  107. public string ContentEncoding {
  108. get {
  109. CheckDisposed ();
  110. return webHeaders ["Content-Encoding"];
  111. }
  112. }
  113. public override long ContentLength {
  114. get {
  115. CheckDisposed ();
  116. if (contentLength != -1)
  117. return contentLength;
  118. try {
  119. contentLength = (long) UInt64.Parse (webHeaders ["Content-Length"]);
  120. } catch (Exception) {
  121. return -1;
  122. }
  123. return contentLength;
  124. }
  125. }
  126. public override string ContentType {
  127. get {
  128. CheckDisposed ();
  129. if (contentType == null)
  130. contentType = webHeaders ["Content-Type"];
  131. return contentType;
  132. }
  133. }
  134. public CookieCollection Cookies {
  135. get {
  136. CheckDisposed ();
  137. if (cookieCollection == null)
  138. cookieCollection = new CookieCollection ();
  139. return cookieCollection;
  140. }
  141. set {
  142. CheckDisposed ();
  143. cookieCollection = value;
  144. }
  145. }
  146. public override WebHeaderCollection Headers {
  147. get {
  148. CheckDisposed ();
  149. return webHeaders;
  150. }
  151. }
  152. public DateTime LastModified {
  153. get {
  154. CheckDisposed ();
  155. try {
  156. string dtStr = webHeaders ["Last-Modified"];
  157. return MonoHttpDate.Parse (dtStr);
  158. } catch (Exception) {
  159. return DateTime.Now;
  160. }
  161. }
  162. }
  163. public string Method {
  164. get {
  165. CheckDisposed ();
  166. return method;
  167. }
  168. }
  169. public Version ProtocolVersion {
  170. get {
  171. CheckDisposed ();
  172. return version;
  173. }
  174. }
  175. public override Uri ResponseUri {
  176. get {
  177. CheckDisposed ();
  178. return uri;
  179. }
  180. }
  181. public string Server {
  182. get {
  183. CheckDisposed ();
  184. return webHeaders ["Server"];
  185. }
  186. }
  187. public HttpStatusCode StatusCode {
  188. get {
  189. CheckDisposed ();
  190. return statusCode;
  191. }
  192. }
  193. public string StatusDescription {
  194. get {
  195. CheckDisposed ();
  196. return statusDescription;
  197. }
  198. }
  199. // Methods
  200. #if !NET_2_0
  201. public override int GetHashCode ()
  202. {
  203. CheckDisposed ();
  204. return base.GetHashCode ();
  205. }
  206. #endif
  207. public string GetResponseHeader (string headerName)
  208. {
  209. CheckDisposed ();
  210. string value = webHeaders [headerName];
  211. return (value != null) ? value : "";
  212. }
  213. internal void ReadAll ()
  214. {
  215. WebConnectionStream wce = stream as WebConnectionStream;
  216. if (wce == null)
  217. return;
  218. try {
  219. wce.ReadAll ();
  220. } catch {}
  221. }
  222. public override Stream GetResponseStream ()
  223. {
  224. CheckDisposed ();
  225. if (stream == null)
  226. return Stream.Null;
  227. if (0 == String.Compare (method, "HEAD", true)) // see par 4.3 & 9.4
  228. return Stream.Null;
  229. return stream;
  230. }
  231. void ISerializable.GetObjectData (SerializationInfo serializationInfo,
  232. StreamingContext streamingContext)
  233. {
  234. SerializationInfo info = serializationInfo;
  235. info.AddValue ("uri", uri);
  236. info.AddValue ("contentLength", contentLength);
  237. info.AddValue ("contentType", contentType);
  238. info.AddValue ("method", method);
  239. info.AddValue ("statusDescription", statusDescription);
  240. info.AddValue ("cookieCollection", cookieCollection);
  241. info.AddValue ("version", version);
  242. info.AddValue ("statusCode", statusCode);
  243. }
  244. // Cleaning up stuff
  245. public override void Close ()
  246. {
  247. ((IDisposable) this).Dispose ();
  248. }
  249. void IDisposable.Dispose ()
  250. {
  251. Dispose (true);
  252. GC.SuppressFinalize (this);
  253. }
  254. #if !NET_2_0
  255. protected virtual
  256. #endif
  257. void Dispose (bool disposing)
  258. {
  259. if (this.disposed)
  260. return;
  261. this.disposed = true;
  262. if (disposing) {
  263. // release managed resources
  264. uri = null;
  265. webHeaders = null;
  266. cookieCollection = null;
  267. method = null;
  268. version = null;
  269. statusDescription = null;
  270. }
  271. // release unmanaged resources
  272. Stream st = stream;
  273. stream = null;
  274. if (st != null)
  275. st.Close ();
  276. }
  277. private void CheckDisposed ()
  278. {
  279. if (disposed)
  280. throw new ObjectDisposedException (GetType ().FullName);
  281. }
  282. void FillCookies ()
  283. {
  284. if (webHeaders == null)
  285. return;
  286. string [] values = webHeaders.GetValues ("Set-Cookie");
  287. if (values != null) {
  288. foreach (string va in values)
  289. SetCookie (va);
  290. }
  291. values = webHeaders.GetValues ("Set-Cookie2");
  292. if (values != null) {
  293. foreach (string va in values)
  294. SetCookie2 (va);
  295. }
  296. }
  297. void SetCookie (string header)
  298. {
  299. string name, val;
  300. Cookie cookie = null;
  301. CookieParser parser = new CookieParser (header);
  302. while (parser.GetNextNameValue (out name, out val)) {
  303. if (name == null || name == "")
  304. continue;
  305. if (cookie == null) {
  306. cookie = new Cookie (name, val);
  307. continue;
  308. }
  309. name = name.ToUpper ();
  310. switch (name) {
  311. case "COMMENT":
  312. if (cookie.Comment == null)
  313. cookie.Comment = val;
  314. break;
  315. case "COMMENTURL":
  316. if (cookie.CommentUri == null)
  317. cookie.CommentUri = new Uri (val);
  318. break;
  319. case "DISCARD":
  320. cookie.Discard = true;
  321. break;
  322. case "DOMAIN":
  323. if (cookie.Domain == "")
  324. cookie.Domain = val;
  325. break;
  326. case "MAX-AGE": // RFC Style Set-Cookie2
  327. if (cookie.Expires == DateTime.MinValue)
  328. cookie.Expires = cookie.TimeStamp.AddSeconds (Int32.Parse (val));
  329. break;
  330. case "EXPIRES": // Netscape Style Set-Cookie
  331. if (cookie.Expires != DateTime.MinValue)
  332. break;
  333. try {
  334. cookie.Expires = DateTime.ParseExact (val, "r", CultureInfo.InvariantCulture);
  335. } catch {
  336. try {
  337. cookie.Expires = DateTime.ParseExact (val,
  338. "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'",
  339. CultureInfo.InvariantCulture);
  340. } catch {
  341. cookie.Expires = DateTime.Now.AddDays (1);
  342. }
  343. }
  344. break;
  345. case "PATH":
  346. cookie.Path = val;
  347. break;
  348. case "PORT":
  349. if (cookie.Port == null)
  350. cookie.Port = val;
  351. break;
  352. case "SECURE":
  353. cookie.Secure = true;
  354. break;
  355. case "VERSION":
  356. cookie.Version = Int32.Parse (val);
  357. break;
  358. }
  359. }
  360. if (cookieCollection == null)
  361. cookieCollection = new CookieCollection ();
  362. if (cookie.Domain == "")
  363. cookie.Domain = uri.Host;
  364. cookieCollection.Add (cookie);
  365. if (cookieContainer != null)
  366. cookieContainer.Add (uri, cookie);
  367. }
  368. void SetCookie2 (string cookies_str)
  369. {
  370. string [] cookies = cookies_str.Split (',');
  371. foreach (string cookie_str in cookies)
  372. SetCookie (cookie_str);
  373. }
  374. }
  375. class CookieParser {
  376. string header;
  377. int pos;
  378. int length;
  379. public CookieParser (string header) : this (header, 0)
  380. {
  381. }
  382. public CookieParser (string header, int position)
  383. {
  384. this.header = header;
  385. this.pos = position;
  386. this.length = header.Length;
  387. }
  388. public bool GetNextNameValue (out string name, out string val)
  389. {
  390. name = null;
  391. val = null;
  392. if (pos >= length)
  393. return false;
  394. name = GetCookieName ();
  395. if (pos < header.Length && header [pos] == '=') {
  396. pos++;
  397. val = GetCookieValue ();
  398. }
  399. if (pos < length && header [pos] == ';')
  400. pos++;
  401. return true;
  402. }
  403. string GetCookieName ()
  404. {
  405. int k = pos;
  406. while (k < length && Char.IsWhiteSpace (header [k]))
  407. k++;
  408. int begin = k;
  409. while (k < length && header [k] != ';' && header [k] != '=')
  410. k++;
  411. pos = k;
  412. return header.Substring (begin, k - begin).Trim ();
  413. }
  414. string GetCookieValue ()
  415. {
  416. if (pos >= length)
  417. return null;
  418. int k = pos;
  419. while (k < length && Char.IsWhiteSpace (header [k]))
  420. k++;
  421. int begin;
  422. if (header [k] == '"'){
  423. int j;
  424. begin = ++k;
  425. while (k < length && header [k] != '"')
  426. k++;
  427. for (j = k; j < length && header [j] != ';'; j++)
  428. ;
  429. pos = j;
  430. } else {
  431. begin = k;
  432. while (k < length && header [k] != ';')
  433. k++;
  434. pos = k;
  435. }
  436. return header.Substring (begin, k - begin).Trim ();
  437. }
  438. }
  439. }