context: //
  // System.Net.HttpConnection
  //
  // Author:
  //	Gonzalo Paniagua Javier (gonzalo.mono@gmail.com)
  //
  // Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com)
  // Copyright (c) 2012 Xamarin, Inc. (http://xamarin.com)
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  #if SECURITY_DEP
  
  #if MONOTOUCH
  using Mono.Security.Protocol.Tls;
  #else
  extern alias MonoSecurity;
  using MonoSecurity::Mono.Security.Protocol.Tls;
  #endif
  
  using System.IO;
  using System.Net.Sockets;
  using System.Reflection;
  using System.Text;
  using System.Threading;
  using System.Security.Cryptography;
  using System.Security.Cryptography.X509Certificates;
  
  namespace System.Net {
  	sealed class HttpConnection
  	{
  		static AsyncCallback onread_cb = new AsyncCallback (OnRead);
  		const int BufferSize = 8192;
  		Socket sock;
  		Stream stream;
  		EndPointListener epl;
  		MemoryStream ms;
  		byte [] buffer;
  		HttpListenerContext context;
  		StringBuilder current_line;
  		ListenerPrefix prefix;
  		RequestStream i_stream;
  		ResponseStream o_stream;
  		bool chunked;
  		int reuses;
  		bool context_bound;
  		bool secure;
  		AsymmetricAlgorithm key;
  		int s_timeout = 90000; // 90k ms for first request, 15k ms from then on
  		Timer timer;
  		IPEndPoint local_ep;
  		HttpListener last_listener;
  		int [] client_cert_errors;
  		X509Certificate2 client_cert;
  
  		public HttpConnection (Socket sock, EndPointListener epl, bool secure, X509Certificate2 cert, AsymmetricAlgorithm key)
  		{
  			this.sock = sock;
  			this.epl = epl;
  			this.secure = secure;
  			this.key = key;
  			if (secure == false) {
  				stream = new NetworkStream (sock, false);
  			} else {
  				SslServerStream ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, true, false);
  				ssl_stream.PrivateKeyCertSelectionDelegate += OnPVKSelection;
  				ssl_stream.ClientCertValidationDelegate += OnClientCertificateValidation;
  				stream = ssl_stream;
  			}
  			timer = new Timer (OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
  			Init ();
  		}
  
  		internal int [] ClientCertificateErrors {
  			get { return client_cert_errors; }
  		}
  
  		internal X509Certificate2 ClientCertificate {
  			get { return client_cert; }
  		}
  
  		bool OnClientCertificateValidation (X509Certificate certificate, int[] errors)
  		{
  			if (certificate == null)
  				return true;
  			X509Certificate2 cert = certificate as X509Certificate2;
  			if (cert == null)
  				cert = new X509Certificate2 (certificate.GetRawCertData ());
  			client_cert = cert;
  			client_cert_errors = errors;
  			return true;
  		}
  
  		AsymmetricAlgorithm OnPVKSelection (X509Certificate certificate, string targetHost)
  		{
  			return key;
  		}
  
  		void Init ()
  		{
  			context_bound = false;
  			i_stream = null;
  			o_stream = null;
  			prefix = null;
  			chunked = false;
  			ms = new MemoryStream ();
  			position = 0;
  			input_state = InputState.RequestLine;
  			line_state = LineState.None;
  			context = new HttpListenerContext (this);
  		}
  
  		public bool IsClosed {
  			get { return (sock == null); }
  		}
  
  		public int Reuses {
  			get { return reuses; }
  		}
  
  		public IPEndPoint LocalEndPoint {
  			get {
  				if (local_ep != null)
  					return local_ep;
  
  				local_ep = (IPEndPoint) sock.LocalEndPoint;
  				return local_ep;
  			}
  		}
  
  		public IPEndPoint RemoteEndPoint {
  			get { return (IPEndPoint) sock.RemoteEndPoint; }
  		}
  
  		public bool IsSecure {
  			get { return secure; }
  		}
  
  		public ListenerPrefix Prefix {
  			get { return prefix; }
  			set { prefix = value; }
  		}
  
  		void OnTimeout (object unused)
  		{
  			CloseSocket ();
  			Unbind ();
  		}
  
  		public void BeginReadRequest ()
  		{
  			if (buffer == null)
  				buffer = new byte [BufferSize];
  			try {
  				if (reuses == 1)
  					s_timeout = 15000;
  				timer.Change (s_timeout, Timeout.Infinite);
  				stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
  			} catch {
  				timer.Change (Timeout.Infinite, Timeout.Infinite);
  				CloseSocket ();
  				Unbind ();
  			}
  		}
  
  		public RequestStream GetRequestStream (bool chunked, long contentlength)
  		{
  			if (i_stream == null) {
  				byte [] buffer = ms.GetBuffer ();
  				int length = (int) ms.Length;
  				ms = null;
  				if (chunked) {
  					this.chunked = true;
  					context.Response.SendChunked = true;
  					i_stream = new ChunkedInputStream (context, stream, buffer, position, length - position);
  				} else {
  					i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
  				}
  			}
  			return i_stream;
  		}
  
  		public ResponseStream GetResponseStream ()
  		{
  			// TODO: can we get this stream before reading the input?
  			if (o_stream == null) {
  				HttpListener listener = context.Listener;
  				bool ign = (listener == null) ? true : listener.IgnoreWriteExceptions;
  				o_stream = new ResponseStream (stream, context.Response, ign);
  			}
  			return o_stream;
  		}
  
  		static void OnRead (IAsyncResult ares)
  		{
  			HttpConnection cnc = (HttpConnection) ares.AsyncState;
  			cnc.OnReadInternal (ares);
  		}
  
  		void OnReadInternal (IAsyncResult ares)
  		{
  			timer.Change (Timeout.Infinite, Timeout.Infinite);
  			int nread = -1;
  			try {
  				nread = stream.EndRead (ares);
  				ms.Write (buffer, 0, nread);
  				if (ms.Length > 32768) {
  					SendError ("Bad request", 400);
  					Close (true);
  					return;
  				}
  			} catch {
  				if (ms != null && ms.Length > 0)
  					SendError ();
  				if (sock != null) {
  					CloseSocket ();
  					Unbind ();
  				}
  				return;
  			}
  
  			if (nread == 0) {
  				//if (ms.Length > 0)
  				//	SendError (); // Why bother?
  				CloseSocket ();
  				Unbind ();
  				return;
  			}
  
  			if (ProcessInput (ms)) {
  				if (!context.HaveError)
  					context.Request.FinishInitialization ();
  
  				if (context.HaveError) {
  					SendError ();
  					Close (true);
  					return;
  				}
  
  				if (!epl.BindContext (context)) {
  					SendError ("Invalid host", 400);
  					Close (true);
  					return;
  				}
  				HttpListener listener = context.Listener;
  				if (last_listener != listener) {
  					RemoveConnection ();
  					listener.AddConnection (this);
  					last_listener = listener;
  				}
  
  				context_bound = true;
  				listener.RegisterContext (context);
  				return;
  			}
  			stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
  		}
  
  		void RemoveConnection ()
  		{
  			if (last_listener == null)
  				epl.RemoveConnection (this);
  			else
  				last_listener.RemoveConnection (this);
  		}
  
  		enum InputState {
  			RequestLine,
  			Headers
  		}
  
  		enum LineState {
  			None,
  			CR,
  			LF
  		}
  
  		InputState input_state = InputState.RequestLine;
  		LineState line_state = LineState.None;
  		int position;
  
  		// true -> done processing
  		// false -> need more input
  		bool ProcessInput (MemoryStream ms)
  		{
  			byte [] buffer = ms.GetBuffer ();
  			int len = (int) ms.Length;
  			int used = 0;
  			string line;
  
  			try {
  				line = ReadLine (buffer, position, len - position, ref used);
  				position += used;
  			} catch {
  				context.ErrorMessage = "Bad request";
  				context.ErrorStatus = 400;
  				return true;
  			}
  
  			do {
  				if (line == null)
  					break;
  				if (line == "") {
  					if (input_state == InputState.RequestLine)
  						continue;
  					current_line = null;
  					ms = null;
  					return true;
  				}
  
  
gt: 				if (input_state == InputState.RequestLine) {