PageRenderTime 75ms CodeModel.GetById 53ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

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