HttpWebResponse.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. //
  2. // System.Net.HttpWebResponse
  3. //
  4. // Author:
  5. // Lawrence Pit ([email protected])
  6. //
  7. using System;
  8. using System.IO;
  9. using System.Runtime.Serialization;
  10. namespace System.Net
  11. {
  12. [Serializable]
  13. public class HttpWebResponse : WebResponse, ISerializable, IDisposable
  14. {
  15. private Uri uri;
  16. private WebHeaderCollection webHeaders;
  17. private CookieCollection cookieCollection = null;
  18. private string method = null;
  19. private Version version = null;
  20. private HttpStatusCode statusCode;
  21. private string statusDescription = null;
  22. private Stream responseStream;
  23. private bool disposed = false;
  24. // Constructors
  25. internal HttpWebResponse (Uri uri, string method, Stream responseStream)
  26. {
  27. Text.StringBuilder value = null;
  28. string last = null;
  29. string line = null;
  30. string[] protocol, header;
  31. this.uri = uri;
  32. this.method = method;
  33. this.responseStream = responseStream;
  34. this.webHeaders = new WebHeaderCollection();
  35. line = ReadHttpLine(responseStream);
  36. protocol = line.Split (' ');
  37. switch (protocol[0]) {
  38. case "HTTP/1.0":
  39. this.version = HttpVersion.Version10;
  40. break;
  41. case "HTTP/1.1":
  42. this.version = HttpVersion.Version11;
  43. break;
  44. default:
  45. throw new WebException ("Unrecognized HTTP Version");
  46. }
  47. this.statusCode = (HttpStatusCode) Int32.Parse (protocol[1]);
  48. while ((line = ReadHttpLine(responseStream)).Length != 0) {
  49. if (!Char.IsWhiteSpace (line[0])) { // new header
  50. header = line.Split (new char[] {':'}, 2);
  51. if (header.Length != 2)
  52. throw new WebException ("Bad HTTP Header");
  53. if (last != null) { // not the first header
  54. if (last.Equals ("Set-Cookie"))
  55. SetCookie (value.ToString());
  56. else if (last.Equals ("Set-Cookie2"))
  57. SetCookie2 (value.ToString());
  58. else //don't save Set-Cookie headers
  59. this.webHeaders[last] = value.ToString();
  60. }
  61. last = header[0];
  62. value = new Text.StringBuilder (header[1].Trim());
  63. }
  64. else
  65. value.Append (line.Trim());
  66. }
  67. this.webHeaders[last] = value.ToString(); // otherwise we miss the last header
  68. }
  69. protected HttpWebResponse (SerializationInfo serializationInfo, StreamingContext streamingContext)
  70. {
  71. throw new NotSupportedException ();
  72. }
  73. // Properties
  74. public string CharacterSet {
  75. // Content-Type = "Content-Type" ":" media-type
  76. // media-type = type "/" subtype *( ";" parameter )
  77. // parameter = attribute "=" value
  78. // 3.7.1. default is ISO-8859-1
  79. get {
  80. try {
  81. string contentType = ContentType;
  82. if (contentType == null)
  83. return "ISO-8859-1";
  84. string val = contentType.ToLower ();
  85. int pos = val.IndexOf ("charset=");
  86. if (pos == -1)
  87. return "ISO-8859-1";
  88. pos += 8;
  89. int pos2 = val.IndexOf (';', pos);
  90. return (pos2 == -1)
  91. ? contentType.Substring (pos)
  92. : contentType.Substring (pos, pos2 - pos);
  93. } finally {
  94. CheckDisposed ();
  95. }
  96. }
  97. }
  98. public string ContentEncoding {
  99. get {
  100. try { return webHeaders ["Content-Encoding"]; }
  101. finally { CheckDisposed (); }
  102. }
  103. }
  104. public override long ContentLength {
  105. get {
  106. try {
  107. return Int64.Parse (webHeaders ["Content-Length"]);
  108. } catch (Exception) {
  109. return -1;
  110. } finally {
  111. CheckDisposed ();
  112. }
  113. }
  114. }
  115. public override string ContentType {
  116. get {
  117. try { return webHeaders ["Content-Type"]; }
  118. finally { CheckDisposed (); }
  119. }
  120. }
  121. public CookieCollection Cookies {
  122. get {
  123. CheckDisposed ();
  124. if (cookieCollection == null)
  125. cookieCollection = new CookieCollection ();
  126. return cookieCollection;
  127. }
  128. set {
  129. CheckDisposed ();
  130. // ?? don't understand how you can set cookies on a response.
  131. throw new NotSupportedException ();
  132. }
  133. }
  134. public override WebHeaderCollection Headers {
  135. get {
  136. CheckDisposed ();
  137. return webHeaders;
  138. }
  139. }
  140. public DateTime LastModified {
  141. get {
  142. CheckDisposed ();
  143. try {
  144. string dtStr = webHeaders ["Last-Modified"];
  145. return MonoHttpDate.Parse (dtStr);
  146. } catch (Exception) {
  147. return DateTime.Now;
  148. }
  149. }
  150. }
  151. public string Method {
  152. get {
  153. CheckDisposed ();
  154. return method;
  155. }
  156. }
  157. public Version ProtocolVersion {
  158. get {
  159. CheckDisposed ();
  160. return version;
  161. }
  162. }
  163. public override Uri ResponseUri {
  164. get {
  165. CheckDisposed ();
  166. return uri;
  167. }
  168. }
  169. public string Server {
  170. get {
  171. try {
  172. return webHeaders ["Server"];
  173. } finally {
  174. CheckDisposed ();
  175. }
  176. }
  177. }
  178. public HttpStatusCode StatusCode {
  179. get {
  180. CheckDisposed ();
  181. return statusCode;
  182. }
  183. }
  184. public string StatusDescription {
  185. get {
  186. CheckDisposed ();
  187. return statusDescription;
  188. }
  189. }
  190. // Methods
  191. public override int GetHashCode ()
  192. {
  193. try {
  194. return base.GetHashCode ();
  195. } finally {
  196. CheckDisposed ();
  197. }
  198. }
  199. public string GetResponseHeader (string headerName)
  200. {
  201. try {
  202. return webHeaders [headerName];
  203. } finally {
  204. CheckDisposed ();
  205. }
  206. }
  207. public override Stream GetResponseStream ()
  208. {
  209. try {
  210. if (method.Equals ("HEAD")) // see par 4.3 & 9.4
  211. return Stream.Null;
  212. return responseStream;
  213. } finally {
  214. CheckDisposed ();
  215. }
  216. }
  217. [MonoTODO]
  218. void ISerializable.GetObjectData (SerializationInfo serializationInfo,
  219. StreamingContext streamingContext)
  220. {
  221. CheckDisposed ();
  222. throw new NotImplementedException ();
  223. }
  224. // Cleaning up stuff
  225. ~HttpWebResponse ()
  226. {
  227. Dispose (false);
  228. }
  229. public override void Close ()
  230. {
  231. ((IDisposable) this).Dispose ();
  232. }
  233. void IDisposable.Dispose ()
  234. {
  235. Dispose (true);
  236. GC.SuppressFinalize (this);
  237. }
  238. protected virtual void Dispose (bool disposing)
  239. {
  240. if (this.disposed)
  241. return;
  242. this.disposed = true;
  243. if (disposing) {
  244. // release managed resources
  245. uri = null;
  246. webHeaders = null;
  247. cookieCollection = null;
  248. method = null;
  249. version = null;
  250. // statusCode = null;
  251. statusDescription = null;
  252. }
  253. // release unmanaged resources
  254. Stream stream = responseStream;
  255. responseStream = null;
  256. if (stream != null)
  257. stream.Close (); // also closes webRequest
  258. }
  259. private void CheckDisposed ()
  260. {
  261. if (disposed)
  262. throw new ObjectDisposedException (GetType ().FullName);
  263. }
  264. private static string ReadHttpLine (Stream stream)
  265. {
  266. Text.StringBuilder line = new Text.StringBuilder();
  267. byte last = (byte)'\n';
  268. bool read_last = false;
  269. byte[] buf = new byte[1]; // one at a time to not snarf too much
  270. while (stream.Read (buf, 0, buf.Length) != 0) {
  271. if (buf[0] == '\r') {
  272. if ((last = (byte)stream.ReadByte ()) == '\n') // headers; not at EOS
  273. break;
  274. read_last = true;
  275. }
  276. line.Append (Convert.ToChar(buf[0]));
  277. if (read_last) {
  278. line.Append (Convert.ToChar (last));
  279. read_last = false;
  280. }
  281. }
  282. return line.ToString();
  283. }
  284. private void SetCookie (string cookie_str)
  285. {
  286. string[] parts = null;
  287. Collections.Queue options = null;
  288. Cookie cookie = null;
  289. options = new Collections.Queue (cookie_str.Split (';'));
  290. parts = ((string)options.Dequeue()).Split ('='); // NAME=VALUE must be first
  291. cookie = new Cookie (parts[0], parts[1]);
  292. while (options.Count > 0) {
  293. parts = ((string)options.Dequeue()).Split ('=');
  294. switch (parts[0].ToUpper()) { // cookie options are case-insensitive
  295. case "COMMENT":
  296. if (cookie.Comment == null)
  297. cookie.Comment = parts[1];
  298. break;
  299. case "COMMENTURL":
  300. if (cookie.CommentUri == null)
  301. cookie.CommentUri = new Uri(parts[1]);
  302. break;
  303. case "DISCARD":
  304. cookie.Discard = true;
  305. break;
  306. case "DOMAIN":
  307. if (cookie.Domain == null)
  308. cookie.Domain = parts[1];
  309. break;
  310. case "MAX-AGE": // RFC Style Set-Cookie2
  311. if (cookie.Expires == DateTime.MinValue)
  312. cookie.Expires = cookie.TimeStamp.AddSeconds (Int32.Parse (parts[1]));
  313. break;
  314. case "EXPIRES": // Netscape Style Set-Cookie
  315. if (cookie.Expires == DateTime.MinValue)
  316. cookie.Expires = DateTime.Parse (parts[1]);
  317. break;
  318. case "PATH":
  319. if (cookie.Path == null)
  320. cookie.Path = parts[1];
  321. break;
  322. case "PORT":
  323. if (cookie.Port == null)
  324. cookie.Port = parts[1];
  325. break;
  326. case "SECURE":
  327. cookie.Secure = true;
  328. break;
  329. case "VERSION":
  330. cookie.Version = Int32.Parse (parts[1]);
  331. break;
  332. } // switch
  333. } // while
  334. if (cookieCollection == null)
  335. cookieCollection = new CookieCollection();
  336. if (cookie.Domain == null)
  337. cookie.Domain = uri.Host;
  338. cookieCollection.Add (cookie);
  339. }
  340. private void SetCookie2 (string cookies_str)
  341. {
  342. string[] cookies = cookies_str.Split (',');
  343. foreach (string cookie_str in cookies)
  344. SetCookie (cookie_str);
  345. }
  346. }
  347. }