HttpWebResponse.cs 11 KB

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