/src/Manos/Manos.Http/HttpResponse.cs

http://github.com/jacksonh/manos · C# · 282 lines · 205 code · 53 blank · 24 comment · 22 complexity · 2e08798ee1525e2f690134f6c934015b MD5 · raw file

  1. //
  2. // Copyright (C) 2010 Jackson Harper (jackson@manosdemono.com)
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining
  5. // a copy of this software and associated documentation files (the
  6. // "Software"), to deal in the Software without restriction, including
  7. // without limitation the rights to use, copy, modify, merge, publish,
  8. // distribute, sublicense, and/or sell copies of the Software, and to
  9. // permit persons to whom the Software is furnished to do so, subject to
  10. // the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be
  13. // included in all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. //
  23. //
  24. using System;
  25. using System.IO;
  26. using System.Text;
  27. using System.Collections;
  28. using System.Collections.Generic;
  29. using Libev;
  30. using Manos.IO;
  31. using Manos.Collections;
  32. namespace Manos.Http {
  33. public class HttpResponse : HttpEntity, IHttpResponse {
  34. private StreamWriter writer;
  35. private Dictionary<string, HttpCookie> cookies;
  36. public HttpResponse (Context context, IHttpRequest request, ITcpSocket socket)
  37. : base (context)
  38. {
  39. Request = request;
  40. Socket = socket;
  41. StatusCode = 200;
  42. WriteHeaders = true;
  43. Stream = new HttpStream (this, socket.GetSocketStream ());
  44. Stream.Chunked = (request.MajorVersion > 0 && request.MinorVersion > 0);
  45. }
  46. public IHttpRequest Request {
  47. get;
  48. private set;
  49. }
  50. public StreamWriter Writer {
  51. get {
  52. if (writer == null)
  53. writer = new StreamWriter (new HttpStreamWriterWrapper (Stream));
  54. return writer;
  55. }
  56. }
  57. public int StatusCode {
  58. get;
  59. set;
  60. }
  61. public bool WriteHeaders {
  62. get;
  63. set;
  64. }
  65. public Dictionary<string,HttpCookie> Cookies {
  66. get {
  67. if (cookies == null)
  68. cookies = new Dictionary<string, HttpCookie> ();
  69. return cookies;
  70. }
  71. }
  72. public void Redirect (string url)
  73. {
  74. StatusCode = 302;
  75. Headers.SetNormalizedHeader ("Location", url);
  76. // WriteMetadata ();
  77. End ();
  78. }
  79. public override void WriteMetadata (StringBuilder builder)
  80. {
  81. WriteStatusLine (builder);
  82. if (WriteHeaders) {
  83. Headers.Write (builder, cookies == null ? null : Cookies.Values, Encoding.ASCII);
  84. }
  85. }
  86. public void SetHeader (string name, string value)
  87. {
  88. Headers.SetHeader (name, value);
  89. }
  90. public void SetCookie (string name, HttpCookie cookie)
  91. {
  92. Cookies [name] = cookie;
  93. }
  94. public HttpCookie SetCookie (string name, string value)
  95. {
  96. if (name == null)
  97. throw new ArgumentNullException ("name");
  98. if (value == null)
  99. throw new ArgumentNullException ("value");
  100. var cookie = new HttpCookie (name, value);
  101. SetCookie (name, cookie);
  102. return cookie;
  103. }
  104. public HttpCookie SetCookie (string name, string value, string domain)
  105. {
  106. if (name == null)
  107. throw new ArgumentNullException ("name");
  108. if (value == null)
  109. throw new ArgumentNullException ("value");
  110. var cookie = new HttpCookie (name, value);
  111. cookie.Domain = domain;
  112. SetCookie (name, cookie);
  113. return cookie;
  114. }
  115. public HttpCookie SetCookie (string name, string value, DateTime expires)
  116. {
  117. return SetCookie (name, value, null, expires);
  118. }
  119. public HttpCookie SetCookie (string name, string value, string domain, DateTime expires)
  120. {
  121. if (name == null)
  122. throw new ArgumentNullException ("name");
  123. if (value == null)
  124. throw new ArgumentNullException ("value");
  125. var cookie = new HttpCookie (name, value);
  126. cookie.Domain = domain;
  127. cookie.Expires = expires;
  128. SetCookie (name, cookie);
  129. return cookie;
  130. }
  131. public HttpCookie SetCookie (string name, string value, TimeSpan max_age)
  132. {
  133. return SetCookie (name, value, DateTime.Now + max_age);
  134. }
  135. public HttpCookie SetCookie (string name, string value, string domain, TimeSpan max_age)
  136. {
  137. return SetCookie (name, value, domain, DateTime.Now + max_age);
  138. }
  139. public void RemoveCookie(string name)
  140. {
  141. var cookie = new HttpCookie (name, "");
  142. cookie.Expires = DateTime.Now.AddYears(-1);
  143. SetCookie (name, cookie);
  144. }
  145. public override void Reset ()
  146. {
  147. cookies = null;
  148. base.Reset ();
  149. }
  150. public override ParserSettings CreateParserSettings ()
  151. {
  152. ParserSettings settings = new ParserSettings ();
  153. settings.OnBody = OnBody;
  154. return settings;
  155. }
  156. protected override int OnHeadersComplete (HttpParser parser)
  157. {
  158. base.OnHeadersComplete (parser);
  159. StatusCode = parser.StatusCode;
  160. if (Request.Method == HttpMethod.HTTP_HEAD)
  161. return 1;
  162. return 0;
  163. }
  164. private void WriteStatusLine (StringBuilder builder)
  165. {
  166. builder.Append ("HTTP/");
  167. builder.Append (Request.MajorVersion);
  168. builder.Append (".");
  169. builder.Append (Request.MinorVersion);
  170. builder.Append (" ");
  171. builder.Append (StatusCode);
  172. builder.Append (" ");
  173. builder.Append (GetStatusDescription (StatusCode));
  174. builder.Append ("\r\n");
  175. }
  176. private static string GetStatusDescription (int code)
  177. {
  178. switch (code){
  179. case 100: return "Continue";
  180. case 101: return "Switching Protocols";
  181. case 102: return "Processing";
  182. case 200: return "OK";
  183. case 201: return "Created";
  184. case 202: return "Accepted";
  185. case 203: return "Non-Authoritative Information";
  186. case 204: return "No Content";
  187. case 205: return "Reset Content";
  188. case 206: return "Partial Content";
  189. case 207: return "Multi-Status";
  190. case 300: return "Multiple Choices";
  191. case 301: return "Moved Permanently";
  192. case 302: return "Found";
  193. case 303: return "See Other";
  194. case 304: return "Not Modified";
  195. case 305: return "Use Proxy";
  196. case 307: return "Temporary Redirect";
  197. case 400: return "Bad Request";
  198. case 401: return "Unauthorized";
  199. case 402: return "Payment Required";
  200. case 403: return "Forbidden";
  201. case 404: return "Not Found";
  202. case 405: return "Method Not Allowed";
  203. case 406: return "Not Acceptable";
  204. case 407: return "Proxy Authentication Required";
  205. case 408: return "Request Timeout";
  206. case 409: return "Conflict";
  207. case 410: return "Gone";
  208. case 411: return "Length Required";
  209. case 412: return "Precondition Failed";
  210. case 413: return "Request Entity Too Large";
  211. case 414: return "Request-Uri Too Long";
  212. case 415: return "Unsupported Media Type";
  213. case 416: return "Requested Range Not Satisfiable";
  214. case 417: return "Expectation Failed";
  215. case 422: return "Unprocessable Entity";
  216. case 423: return "Locked";
  217. case 424: return "Failed Dependency";
  218. case 500: return "Internal Server Error";
  219. case 501: return "Not Implemented";
  220. case 502: return "Bad Gateway";
  221. case 503: return "Service Unavailable";
  222. case 504: return "Gateway Timeout";
  223. case 505: return "Http Version Not Supported";
  224. case 507: return "Insufficient Storage";
  225. }
  226. return "";
  227. }
  228. }
  229. }