1 //
2 // System.Net.HttpConnection
3 //
4 // Author:
5 //	Gonzalo Paniagua Javier (gonzalo.mono@gmail.com)
6 //
7 // Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com)
8 // Copyright (c) 2012 Xamarin, Inc. (http://xamarin.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 
30 using System.IO;
31 using System.Net.Sockets;
32 using System.Text;
33 using System.Threading;
34 using System.Net.Security;
35 using System.Security.Authentication;
36 using System.Security.Cryptography;
37 using System.Security.Cryptography.X509Certificates;
38 
39 namespace System.Net {
40 	sealed class HttpConnection {
41 		static AsyncCallback onread_cb = new AsyncCallback (OnRead);
42 		const int BufferSize = 8192;
43 		Socket sock;
44 		Stream stream;
45 		EndPointListener epl;
46 		MemoryStream ms;
47 		byte [] buffer;
48 		HttpListenerContext context;
49 		StringBuilder current_line;
50 		ListenerPrefix prefix;
51 		RequestStream i_stream;
52 		ResponseStream o_stream;
53 		bool chunked;
54 		int reuses;
55 		bool context_bound;
56 		bool secure;
57 		X509Certificate cert;
58 		int s_timeout = 90000; // 90k ms for first request, 15k ms from then on
59 		Timer timer;
60 		IPEndPoint local_ep;
61 		HttpListener last_listener;
62 		int [] client_cert_errors;
63 		X509Certificate2 client_cert;
64 		SslStream ssl_stream;
65 
HttpConnection(Socket sock, EndPointListener epl, bool secure, X509Certificate cert)66 		public HttpConnection (Socket sock, EndPointListener epl, bool secure, X509Certificate cert)
67 		{
68 			this.sock = sock;
69 			this.epl = epl;
70 			this.secure = secure;
71 			this.cert = cert;
72 			if (secure == false) {
73 				stream = new NetworkStream (sock, false);
74 			} else {
75 				ssl_stream = epl.Listener.CreateSslStream (new NetworkStream (sock, false), false, (t, c, ch, e) => {
76 					if (c == null)
77 						return true;
78 					var c2 = c as X509Certificate2;
79 					if (c2 == null)
80 						c2 = new X509Certificate2 (c.GetRawCertData ());
81 					client_cert = c2;
82 					client_cert_errors = new int[] { (int)e };
83 					return true;
84 				});
85 				stream = ssl_stream;
86 			}
87 			timer = new Timer (OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
88 			if (ssl_stream != null)
89 				ssl_stream.AuthenticateAsServer (cert, true, (SslProtocols)ServicePointManager.SecurityProtocol, false);
90 			Init ();
91 		}
92 
93 		internal SslStream SslStream {
94 			get { return ssl_stream; }
95 		}
96 
97 		internal int [] ClientCertificateErrors {
98 			get { return client_cert_errors; }
99 		}
100 
101 		internal X509Certificate2 ClientCertificate {
102 			get { return client_cert; }
103 		}
104 
Init()105 		void Init ()
106 		{
107 			context_bound = false;
108 			i_stream = null;
109 			o_stream = null;
110 			prefix = null;
111 			chunked = false;
112 			ms = new MemoryStream ();
113 			position = 0;
114 			input_state = InputState.RequestLine;
115 			line_state = LineState.None;
116 			context = new HttpListenerContext (this);
117 		}
118 
119 		public bool IsClosed {
120 			get { return (sock == null); }
121 		}
122 
123 		public int Reuses {
124 			get { return reuses; }
125 		}
126 
127 		public IPEndPoint LocalEndPoint {
128 			get {
129 				if (local_ep != null)
130 					return local_ep;
131 
132 				local_ep = (IPEndPoint) sock.LocalEndPoint;
133 				return local_ep;
134 			}
135 		}
136 
137 		public IPEndPoint RemoteEndPoint {
138 			get { return (IPEndPoint) sock.RemoteEndPoint; }
139 		}
140 
141 		public bool IsSecure {
142 			get { return secure; }
143 		}
144 
145 		public ListenerPrefix Prefix {
146 			get { return prefix; }
147 			set { prefix = value; }
148 		}
149 
OnTimeout(object unused)150 		void OnTimeout (object unused)
151 		{
152 			CloseSocket ();
153 			Unbind ();
154 		}
155 
BeginReadRequest()156 		public void BeginReadRequest ()
157 		{
158 			if (buffer == null)
159 				buffer = new byte [BufferSize];
160 			try {
161 				if (reuses == 1)
162 					s_timeout = 15000;
163 				timer.Change (s_timeout, Timeout.Infinite);
164 				stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
165 			} catch {
166 				timer.Change (Timeout.Infinite, Timeout.Infinite);
167 				CloseSocket ();
168 				Unbind ();
169 			}
170 		}
171 
GetRequestStream(bool chunked, long contentlength)172 		public RequestStream GetRequestStream (bool chunked, long contentlength)
173 		{
174 			if (i_stream == null) {
175 				byte [] buffer = ms.GetBuffer ();
176 				int length = (int) ms.Length;
177 				ms = null;
178 				if (chunked) {
179 					this.chunked = true;
180 					context.Response.SendChunked = true;
181 					i_stream = new ChunkedInputStream (context, stream, buffer, position, length - position);
182 				} else {
183 					i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
184 				}
185 			}
186 			return i_stream;
187 		}
188 
GetResponseStream()189 		public ResponseStream GetResponseStream ()
190 		{
191 			// TODO: can we get this stream before reading the input?
192 			if (o_stream == null) {
193 				HttpListener listener = context.Listener;
194 
195 				if(listener == null)
196 					return new ResponseStream (stream, context.Response, true);
197 
198 				o_stream = new ResponseStream (stream, context.Response, listener.IgnoreWriteExceptions);
199 			}
200 			return o_stream;
201 		}
202 
OnRead(IAsyncResult ares)203 		static void OnRead (IAsyncResult ares)
204 		{
205 			HttpConnection cnc = (HttpConnection) ares.AsyncState;
206 			cnc.OnReadInternal (ares);
207 		}
208 
OnReadInternal(IAsyncResult ares)209 		void OnReadInternal (IAsyncResult ares)
210 		{
211 			timer.Change (Timeout.Infinite, Timeout.Infinite);
212 			int nread = -1;
213 			try {
214 				nread = stream.EndRead (ares);
215 				ms.Write (buffer, 0, nread);
216 				if (ms.Length > 32768) {
217 					SendError ("Bad request", 400);
218 					Close (true);
219 					return;
220 				}
221 			} catch {
222 				if (ms != null && ms.Length > 0)
223 					SendError ();
224 				if (sock != null) {
225 					CloseSocket ();
226 					Unbind ();
227 				}
228 				return;
229 			}
230 
231 			if (nread == 0) {
232 				//if (ms.Length > 0)
233 				//	SendError (); // Why bother?
234 				CloseSocket ();
235 				Unbind ();
236 				return;
237 			}
238 
239 			if (ProcessInput (ms)) {
240 				if (!context.HaveError)
241 					context.Request.FinishInitialization ();
242 
243 				if (context.HaveError) {
244 					SendError ();
245 					Close (true);
246 					return;
247 				}
248 
249 				if (!epl.BindContext (context)) {
250 					SendError ("Invalid host", 400);
251 					Close (true);
252 					return;
253 				}
254 				HttpListener listener = context.Listener;
255 				if (last_listener != listener) {
256 					RemoveConnection ();
257 					listener.AddConnection (this);
258 					last_listener = listener;
259 				}
260 
261 				context_bound = true;
262 				listener.RegisterContext (context);
263 				return;
264 			}
265 			stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
266 		}
267 
RemoveConnection()268 		void RemoveConnection ()
269 		{
270 			if (last_listener == null)
271 				epl.RemoveConnection (this);
272 			else
273 				last_listener.RemoveConnection (this);
274 		}
275 
276 		enum InputState {
277 			RequestLine,
278 			Headers
279 		}
280 
281 		enum LineState {
282 			None,
283 			CR,
284 			LF
285 		}
286 
287 		InputState input_state = InputState.RequestLine;
288 		LineState line_state = LineState.None;
289 		int position;
290 
291 		// true -> done processing
292 		// false -> need more input
ProcessInput(MemoryStream ms)293 		bool ProcessInput (MemoryStream ms)
294 		{
295 			byte [] buffer = ms.GetBuffer ();
296 			int len = (int) ms.Length;
297 			int used = 0;
298 			string line;
299 
300 			while (true) {
301 				if (context.HaveError)
302 					return true;
303 
304 				if (position >= len)
305 					break;
306 
307 				try {
308 					line = ReadLine (buffer, position, len - position, ref used);
309 					position += used;
310 				} catch {
311 					context.ErrorMessage = "Bad request";
312 					context.ErrorStatus = 400;
313 					return true;
314 				}
315 
316 				if (line == null)
317 					break;
318 
319 				if (line == "") {
320 					if (input_state == InputState.RequestLine)
321 						continue;
322 					current_line = null;
323 					ms = null;
324 					return true;
325 				}
326 
327 				if (input_state == InputState.RequestLine) {
328 					context.Request.SetRequestLine (line);
329 					input_state = InputState.Headers;
330 				} else {
331 					try {
332 						context.Request.AddHeader (line);
333 					} catch (Exception e) {
334 						context.ErrorMessage = e.Message;
335 						context.ErrorStatus = 400;
336 						return true;
337 					}
338 				}
339 			}
340 
341 			if (used == len) {
342 				ms.SetLength (0);
343 				position = 0;
344 			}
345 			return false;
346 		}
347 
ReadLine(byte [] buffer, int offset, int len, ref int used)348 		string ReadLine (byte [] buffer, int offset, int len, ref int used)
349 		{
350 			if (current_line == null)
351 				current_line = new StringBuilder (128);
352 			int last = offset + len;
353 			used = 0;
354 			for (int i = offset; i < last && line_state != LineState.LF; i++) {
355 				used++;
356 				byte b = buffer [i];
357 				if (b == 13) {
358 					line_state = LineState.CR;
359 				} else if (b == 10) {
360 					line_state = LineState.LF;
361 				} else {
362 					current_line.Append ((char) b);
363 				}
364 			}
365 
366 			string result = null;
367 			if (line_state == LineState.LF) {
368 				line_state = LineState.None;
369 				result = current_line.ToString ();
370 				current_line.Length = 0;
371 			}
372 
373 			return result;
374 		}
375 
SendError(string msg, int status)376 		public void SendError (string msg, int status)
377 		{
378 			try {
379 				HttpListenerResponse response = context.Response;
380 				response.StatusCode = status;
381 				response.ContentType = "text/html";
382 				string description = HttpStatusDescription.Get (status);
383 				string str;
384 				if (msg != null)
385 					str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
386 				else
387 					str = String.Format ("<h1>{0}</h1>", description);
388 
389 				byte [] error = context.Response.ContentEncoding.GetBytes (str);
390 				response.Close (error, false);
391 			} catch {
392 				// response was already closed
393 			}
394 		}
395 
SendError()396 		public void SendError ()
397 		{
398 			SendError (context.ErrorMessage, context.ErrorStatus);
399 		}
400 
Unbind()401 		void Unbind ()
402 		{
403 			if (context_bound) {
404 				epl.UnbindContext (context);
405 				context_bound = false;
406 			}
407 		}
408 
Close()409 		public void Close ()
410 		{
411 			Close (false);
412 		}
413 
CloseSocket()414 		void CloseSocket ()
415 		{
416 			if (sock == null)
417 				return;
418 
419 			try {
420 				sock.Close ();
421 			} catch {
422 			} finally {
423 				sock = null;
424 			}
425 			RemoveConnection ();
426 		}
427 
Close(bool force_close)428 		internal void Close (bool force_close)
429 		{
430 			if (sock != null) {
431 				Stream st = GetResponseStream ();
432 				if (st != null)
433 					st.Close ();
434 
435 				o_stream = null;
436 			}
437 
438 			if (sock != null) {
439 				force_close |= !context.Request.KeepAlive;
440 				if (!force_close)
441 					force_close = (context.Response.Headers ["connection"] == "close");
442 				/*
443 				if (!force_close) {
444 //					bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 ||
445 //							status_code == 413 || status_code == 414 || status_code == 500 ||
446 //							status_code == 503);
447 
448 					force_close |= (context.Request.ProtocolVersion <= HttpVersion.Version10);
449 				}
450 				*/
451 
452 				if (!force_close && context.Request.FlushInput ()) {
453 					if (chunked && context.Response.ForceCloseChunked == false) {
454 						// Don't close. Keep working.
455 						reuses++;
456 						Unbind ();
457 						Init ();
458 						BeginReadRequest ();
459 						return;
460 					}
461 
462 					reuses++;
463 					Unbind ();
464 					Init ();
465 					BeginReadRequest ();
466 					return;
467 				}
468 
469 				Socket s = sock;
470 				sock = null;
471 				try {
472 					if (s != null)
473 						s.Shutdown (SocketShutdown.Both);
474 				} catch {
475 				} finally {
476 					if (s != null)
477 						s.Close ();
478 				}
479 				Unbind ();
480 				RemoveConnection ();
481 				return;
482 			}
483 		}
484 	}
485 }
486 
487