/src/Manos/Manos.Http/HttpRequest.cs

http://github.com/jacksonh/manos · C# · 242 lines · 166 code · 48 blank · 28 comment · 15 complexity · eac4b83e8dcf010dde945b7f586c5e74 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.Text;
  26. using System.Globalization;
  27. using System.Collections;
  28. using System.Collections.Generic;
  29. using System.Collections.Specialized;
  30. using Libev;
  31. using Manos.IO;
  32. using Manos.Collections;
  33. namespace Manos.Http {
  34. public class HttpRequest : HttpEntity, IHttpRequest {
  35. private StringBuilder query_data_builder = new StringBuilder ();
  36. private DataDictionary uri_data;
  37. private DataDictionary query_data;
  38. private DataDictionary cookies;
  39. public HttpRequest (Context context, string address)
  40. : base (context)
  41. {
  42. Uri uri = null;
  43. if (!Uri.TryCreate (address, UriKind.Absolute, out uri))
  44. throw new Exception ("Invalid URI: '" + address + "'.");
  45. RemoteAddress = uri.Host;
  46. RemotePort = uri.Port;
  47. Path = uri.AbsolutePath;
  48. Method = HttpMethod.HTTP_GET;
  49. MajorVersion = 1;
  50. MinorVersion = 1;
  51. }
  52. public HttpRequest (Context context, string remote_address, int port)
  53. : this (context, remote_address)
  54. {
  55. RemotePort = port;
  56. }
  57. public HttpRequest (IHttpTransaction transaction, ITcpSocket stream)
  58. : base (transaction.Context)
  59. {
  60. Transaction = transaction;
  61. Socket = stream;
  62. RemoteAddress = stream.RemoteEndpoint.Address.ToString();
  63. RemotePort = stream.RemoteEndpoint.Port;
  64. }
  65. public IHttpTransaction Transaction {
  66. get;
  67. private set;
  68. }
  69. public DataDictionary QueryData {
  70. get {
  71. if (query_data == null) {
  72. query_data = new DataDictionary ();
  73. Data.Children.Add (query_data);
  74. }
  75. return query_data;
  76. }
  77. set {
  78. SetDataDictionary (query_data, value);
  79. query_data = value;
  80. }
  81. }
  82. public DataDictionary UriData {
  83. get {
  84. if (uri_data == null) {
  85. uri_data = new DataDictionary ();
  86. Data.Children.Add (uri_data);
  87. }
  88. return uri_data;
  89. }
  90. set {
  91. SetDataDictionary (uri_data, value);
  92. uri_data = value;
  93. }
  94. }
  95. public DataDictionary Cookies {
  96. get {
  97. if (cookies == null)
  98. cookies = ParseCookies ();
  99. return cookies;
  100. }
  101. }
  102. public override void Reset ()
  103. {
  104. Path = null;
  105. uri_data = null;
  106. query_data = null;
  107. cookies = null;
  108. base.Reset ();
  109. }
  110. public void SetWwwFormData (DataDictionary data)
  111. {
  112. PostData = data;
  113. }
  114. private DataDictionary ParseCookies ()
  115. {
  116. string cookie_header;
  117. if (!Headers.TryGetValue ("Cookie", out cookie_header))
  118. return new DataDictionary ();
  119. return HttpCookie.FromHeader (cookie_header);
  120. }
  121. private int OnPath (HttpParser parser, ByteBuffer data, int pos, int len)
  122. {
  123. string str = Encoding.ASCII.GetString (data.Bytes, pos, len);
  124. str = HttpUtility.UrlDecode (str, Encoding.ASCII);
  125. Path = Path == null ? str : String.Concat (Path, str);
  126. return 0;
  127. }
  128. private int OnQueryString (HttpParser parser, ByteBuffer data, int pos, int len)
  129. {
  130. string str = Encoding.ASCII.GetString (data.Bytes, pos, len);
  131. query_data_builder.Append (str);
  132. return 0;
  133. }
  134. protected override void OnFinishedReading (HttpParser parser)
  135. {
  136. base.OnFinishedReading (parser);
  137. MajorVersion = parser.Major;
  138. MinorVersion = parser.Minor;
  139. Method = parser.HttpMethod;
  140. if (query_data_builder.Length != 0) {
  141. QueryData = HttpUtility.ParseUrlEncodedData (query_data_builder.ToString ());
  142. query_data_builder.Length = 0;
  143. }
  144. Transaction.OnRequestReady ();
  145. }
  146. public override ParserSettings CreateParserSettings ()
  147. {
  148. ParserSettings settings = new ParserSettings ();
  149. settings.OnPath = OnPath;
  150. settings.OnQueryString = OnQueryString;
  151. return settings;
  152. }
  153. public void Execute ()
  154. {
  155. var remote = new IPEndPoint(IPAddress.Parse (RemoteAddress), RemotePort);
  156. Socket = this.Context.CreateTcpSocket (remote.AddressFamily);
  157. Socket.Connect (remote, delegate {
  158. Stream = new HttpStream (this, Socket.GetSocketStream ());
  159. Stream.Chunked = false;
  160. Stream.AddHeaders = false;
  161. byte [] body = GetBody ();
  162. if (body != null) {
  163. Headers.ContentLength = body.Length;
  164. Stream.Write (body, 0, body.Length);
  165. }
  166. Stream.End (() => {
  167. HttpResponse response = new HttpResponse (Context, this, Socket);
  168. // response.OnCompleted += () => {
  169. // if (OnResponse != null)
  170. // OnResponse (response);
  171. // };
  172. response.Read (() => {
  173. if (OnResponse != null) OnResponse (response);
  174. });
  175. });
  176. }, ex => {
  177. // TODO: figure out what to do here
  178. });
  179. }
  180. public override void WriteMetadata (StringBuilder builder)
  181. {
  182. builder.Append (Encoding.ASCII.GetString (HttpMethodBytes.GetBytes (Method)));
  183. builder.Append (" ");
  184. builder.Append (Path);
  185. builder.Append (" HTTP/");
  186. builder.Append (MajorVersion.ToString (CultureInfo.InvariantCulture));
  187. builder.Append (".");
  188. builder.Append (MinorVersion.ToString (CultureInfo.InvariantCulture));
  189. builder.Append ("\r\n");
  190. Headers.Write (builder, null, Encoding.ASCII);
  191. }
  192. public event Action<IHttpResponse> OnResponse;
  193. }
  194. }