소스 검색

2005-11-17 Gonzalo Paniagua Javier <[email protected]>

	* HttpListenerPrefixCollection.cs:
	* EndPointListener.cs:
	* HttpStreamAsyncResult.cs:
	* HttpListenerContext.cs:
	* HttpListenerBasicIdentity.cs:
	* AuthenticationSchemeSelector.cs:
	* HttpListener.cs:
	* WebHeaderCollection.cs:
	* ListenerPrefix.cs:
	* HttpListenerException.cs:
	* HttpResponseHeader.cs:
	* EndPointManager.cs:
	* HttpListenerRequest.cs:
	* HttpRequestHeader.cs:
	* BindIPEndPoint.cs:
	* ResponseStream.cs:
	* ChunkedInputStream.cs:
	* HttpStatusCode.cs:
	* ListenerAsyncResult.cs:
	* ChunkStream.cs:
	* HttpConnection.cs:
	* HttpUtility.cs:
	* HttpListenerResponse.cs:
	* RequestStream.cs: initial implementation of HttpListener. There are
	a few TODOs left (cookies, HTTPS) but almost everything else is in.


svn path=/trunk/mcs/; revision=53169
Gonzalo Paniagua Javier 20 년 전
부모
커밋
3e700ee551
25개의 변경된 파일3725개의 추가작업 그리고 443개의 파일을 삭제
  1. 33 0
      mcs/class/System/System.Net/AuthenticationSchemeSelector.cs
  2. 34 0
      mcs/class/System/System.Net/BindIPEndPoint.cs
  3. 28 0
      mcs/class/System/System.Net/ChangeLog
  4. 7 2
      mcs/class/System/System.Net/ChunkStream.cs
  5. 157 0
      mcs/class/System/System.Net/ChunkedInputStream.cs
  6. 261 0
      mcs/class/System/System.Net/EndPointListener.cs
  7. 142 0
      mcs/class/System/System.Net/EndPointManager.cs
  8. 303 0
      mcs/class/System/System.Net/HttpConnection.cs
  9. 310 0
      mcs/class/System/System.Net/HttpListener.cs
  10. 45 0
      mcs/class/System/System.Net/HttpListenerBasicIdentity.cs
  11. 82 0
      mcs/class/System/System.Net/HttpListenerContext.cs
  12. 57 0
      mcs/class/System/System.Net/HttpListenerException.cs
  13. 117 0
      mcs/class/System/System.Net/HttpListenerPrefixCollection.cs
  14. 363 0
      mcs/class/System/System.Net/HttpListenerRequest.cs
  15. 499 0
      mcs/class/System/System.Net/HttpListenerResponse.cs
  16. 64 0
      mcs/class/System/System.Net/HttpRequestHeader.cs
  17. 64 0
      mcs/class/System/System.Net/HttpResponseHeader.cs
  18. 2 142
      mcs/class/System/System.Net/HttpStatusCode.cs
  19. 95 0
      mcs/class/System/System.Net/HttpStreamAsyncResult.cs
  20. 103 0
      mcs/class/System/System.Net/HttpUtility.cs
  21. 125 0
      mcs/class/System/System.Net/ListenerAsyncResult.cs
  22. 159 0
      mcs/class/System/System.Net/ListenerPrefix.cs
  23. 185 0
      mcs/class/System/System.Net/RequestStream.cs
  24. 190 0
      mcs/class/System/System.Net/ResponseStream.cs
  25. 300 299
      mcs/class/System/System.Net/WebHeaderCollection.cs

+ 33 - 0
mcs/class/System/System.Net/AuthenticationSchemeSelector.cs

@@ -0,0 +1,33 @@
+//
+// System.Net.AuthenticationSchemeSelector.cs
+//
+// Author:
+//	Gonzalo Paniagua Javier  <[email protected]>
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+namespace System.Net {
+	public delegate AuthenticationSchemes AuthenticationSchemeSelector (HttpListenerRequest httpRequest);
+}
+#endif
+

+ 34 - 0
mcs/class/System/System.Net/BindIPEndPoint.cs

@@ -0,0 +1,34 @@
+//
+// System.Net.BindIPEndPoint
+//
+// Author:
+//	Gonzalo Paniagua Javier  <[email protected]>
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+namespace System.Net {
+	public delegate IPEndPoint BindIPEndPoint (ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount);
+
+}
+#endif
+

+ 28 - 0
mcs/class/System/System.Net/ChangeLog

@@ -1,3 +1,31 @@
+2005-11-17 Gonzalo Paniagua Javier <[email protected]>
+
+	* HttpListenerPrefixCollection.cs:
+	* EndPointListener.cs:
+	* HttpStreamAsyncResult.cs:
+	* HttpListenerContext.cs:
+	* HttpListenerBasicIdentity.cs:
+	* AuthenticationSchemeSelector.cs:
+	* HttpListener.cs:
+	* WebHeaderCollection.cs:
+	* ListenerPrefix.cs:
+	* HttpListenerException.cs:
+	* HttpResponseHeader.cs:
+	* EndPointManager.cs:
+	* HttpListenerRequest.cs:
+	* HttpRequestHeader.cs:
+	* BindIPEndPoint.cs:
+	* ResponseStream.cs:
+	* ChunkedInputStream.cs:
+	* HttpStatusCode.cs:
+	* ListenerAsyncResult.cs:
+	* ChunkStream.cs:
+	* HttpConnection.cs:
+	* HttpUtility.cs:
+	* HttpListenerResponse.cs:
+	* RequestStream.cs: initial implementation of HttpListener. There are
+	a few TODOs left (cookies, HTTPS) but almost everything else is in.
+
 2005-11-15  Sebastien Pouliot  <[email protected]>
 
 	* IPv6Address.cs: Added on overload to ToString to get a the full 

+ 7 - 2
mcs/class/System/System.Net/ChunkStream.cs

@@ -62,7 +62,7 @@ namespace System.Net
 			}
 		}
 
-		WebHeaderCollection headers;
+		internal WebHeaderCollection headers;
 		int chunkSize;
 		int chunkRead;
 		State state;
@@ -73,12 +73,17 @@ namespace System.Net
 		ArrayList chunks;
 		
 		public ChunkStream (byte [] buffer, int offset, int size, WebHeaderCollection headers)
+					: this (headers)
+		{
+			Write (buffer, offset, size);
+		}
+
+		public ChunkStream (WebHeaderCollection headers)
 		{
 			this.headers = headers;
 			saved = new StringBuilder ();
 			chunks = new ArrayList ();
 			chunkSize = -1;
-			Write (buffer, offset, size);
 		}
 
 		public void ResetBuffer ()

+ 157 - 0
mcs/class/System/System.Net/ChunkedInputStream.cs

@@ -0,0 +1,157 @@
+//
+// System.Net.ChunkedInputStream
+//
+// Authors:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc (http://www.novell.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 NET_2_0
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+namespace System.Net {
+	class ChunkedInputStream : RequestStream {
+		bool disposed;
+		ChunkStream decoder;
+		HttpListenerContext context;
+		bool no_more_data;
+
+		class ReadBufferState {
+			public byte [] Buffer;
+			public int Offset;
+			public int Count;
+			public int InitialCount;
+			public HttpStreamAsyncResult Ares;
+			public ReadBufferState (byte [] buffer, int offset, int count,
+						HttpStreamAsyncResult ares)
+			{
+				Buffer = buffer;
+				Offset = offset;
+				Count = count;
+				InitialCount = count;
+				Ares = ares;
+			}
+		}
+
+		public ChunkedInputStream (HttpListenerContext context, Socket sock,
+						byte [] buffer, int offset, int length)
+					: base (sock, buffer, offset, length)
+		{
+			this.context = context;
+			WebHeaderCollection coll = (WebHeaderCollection) context.Request.Headers;
+			decoder = new ChunkStream (coll);
+		}
+
+		public ChunkStream Decoder {
+			get { return decoder; }
+			set { decoder = value; }
+		}
+
+		public override int Read ([In,Out] byte [] buffer, int offset, int count)
+		{
+			IAsyncResult ares = BeginRead (buffer, offset, count, null, null);
+			return EndRead (ares);
+		}
+
+		public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
+							AsyncCallback cback, object state)
+		{
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().ToString ());
+
+			if (buffer == null)
+				throw new ArgumentNullException ("buffer");
+
+			int len = buffer.Length;
+			if (offset < 0 || offset > len)
+				throw new ArgumentOutOfRangeException ("offset exceeds the size of buffer");
+
+			if (count < 0 || offset > len - count)
+				throw new ArgumentOutOfRangeException ("offset+size exceeds the size of buffer");
+
+			HttpStreamAsyncResult ares = new HttpStreamAsyncResult ();
+			ares.Callback = cback;
+			ares.State = state;
+			if (no_more_data) {
+				ares.Complete ();
+				return ares;
+			}
+			ares.Buffer = new byte [8192];
+			ares.Offset = 0;
+			ares.Count = 8192;
+			ReadBufferState rb = new ReadBufferState (buffer, offset, count, ares);
+			base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
+			return ares;
+		}
+
+		void OnRead (IAsyncResult base_ares)
+		{
+			ReadBufferState rb = (ReadBufferState) base_ares.AsyncState;
+			HttpStreamAsyncResult ares = rb.Ares;
+			try {
+				int nread = base.EndRead (base_ares);
+				decoder.Write (ares.Buffer, ares.Offset, nread);
+				nread = decoder.Read (rb.Buffer, rb.Offset, rb.Count);
+				rb.Offset += nread;
+				rb.Count -= nread;
+				if (rb.Count == 0 || !decoder.WantMore) {
+					no_more_data = !decoder.WantMore;
+					ares.Count = rb.InitialCount - rb.Count;
+					ares.Complete ();
+					return;
+				}
+				ares.Offset = 0;
+				ares.Count = Math.Min (8192, decoder.ChunkLeft + 6);
+				base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
+			} catch (Exception e) {
+				context.Connection.SendError (e.Message, 400);
+				ares.Complete (e);
+			}
+		}
+
+		public override int EndRead (IAsyncResult ares)
+		{
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().ToString ());
+
+			HttpStreamAsyncResult my_ares = ares as HttpStreamAsyncResult;
+			if (ares == null)
+				throw new ArgumentException ("Invalid IAsyncResult", "ares");
+
+			if (!ares.IsCompleted)
+				ares.AsyncWaitHandle.WaitOne ();
+
+			if (my_ares.Error != null)
+				throw new HttpListenerException (400, "I/O operation aborted.");
+
+			return my_ares.Count;
+		}
+
+		public override void Close ()
+		{
+			if (!disposed) {
+				disposed = true;
+				base.Close ();
+			}
+		}
+	}
+}
+#endif

+ 261 - 0
mcs/class/System/System.Net/EndPointListener.cs

@@ -0,0 +1,261 @@
+//
+// System.Net.EndPointListener
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.IO;
+using System.Net.Sockets;
+using System.Collections.Generic;
+namespace System.Net {
+	sealed class EndPointListener
+	{
+		IPEndPoint endpoint;
+		Socket sock;
+		Dictionary<ListenerPrefix, HttpListener> prefixes;
+		List<ListenerPrefix> unhandled; // host = '*'
+		List<ListenerPrefix> all; // host = '+'
+		bool secure; // Can a port have listeners for secure and not secure at the same time?
+
+		public EndPointListener (IPAddress addr, int port, bool secure)
+		{
+			endpoint = new IPEndPoint (addr, port);
+			sock = new Socket (addr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+			sock.Bind (endpoint);
+			sock.Listen (500);
+			sock.BeginAccept (OnAccept, this);
+			prefixes = new Dictionary<ListenerPrefix, HttpListener> ();
+			this.secure = secure;
+		}
+
+		static void OnAccept (IAsyncResult ares)
+		{
+			EndPointListener epl = (EndPointListener) ares.AsyncState;
+			Socket accepted = null;
+			try {
+				accepted = epl.sock.EndAccept (ares);
+			} catch {
+				// Anything to do here?
+			} finally {
+				epl.sock.BeginAccept (OnAccept, epl);
+			}
+
+			HttpConnection conn = new HttpConnection (accepted, epl, epl.secure);
+			conn.BeginReadRequest ();
+		}
+
+		public bool BindContext (HttpListenerContext context)
+		{
+			HttpListenerRequest req = context.Request;
+			ListenerPrefix prefix;
+			HttpListener listener = SearchListener (req.UserHostName, req.RawUrl, out prefix);
+			if (listener == null)
+				return false;
+
+			context.Listener = listener;
+			context.Connection.Prefix = prefix;
+			listener.RegisterContext (context);
+			return true;
+		}
+
+		public void UnbindContext (HttpListenerContext context)
+		{
+			if (context == null || context.Request == null)
+				return;
+
+			HttpListenerRequest req = context.Request;
+			ListenerPrefix prefix;
+			HttpListener listener = SearchListener (req.UserHostName, req.RawUrl, out prefix);
+			if (listener != null)
+				listener.UnregisterContext (context);
+		}
+
+		HttpListener SearchListener (string host, string raw_url, out ListenerPrefix prefix)
+		{
+			prefix = null;
+			if (raw_url == null)
+				return null;
+
+			//TODO: We should use a ReaderWriterLock between this and the add/remove operations.
+			int colon = host.IndexOf (':');
+			if (colon >= 0)
+				host = host.Substring (0, colon);
+
+			string path = HttpUtility.UrlDecode (raw_url);
+			HttpListener best_match = null;
+			int best_length = -1;
+
+			lock (prefixes) {
+				foreach (ListenerPrefix p in prefixes.Keys) {
+					string ppath = p.Path;
+					if (ppath.Length < best_length)
+						continue;
+
+					if (p.Host == host && path.StartsWith (ppath)) {
+						best_length = ppath.Length;
+						best_match = prefixes [p];
+						prefix = p;
+					}
+				}
+
+				if (best_length != -1)
+					return best_match;
+
+				best_match = MatchFromList (host, path, unhandled, out prefix);
+				if (best_match != null)
+					return best_match;
+
+				best_match = MatchFromList (host, path, all, out prefix);
+				if (best_match != null)
+					return best_match;
+			}
+			return null;
+		}
+
+		HttpListener MatchFromList (string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
+		{
+			prefix = null;
+			if (list == null)
+				return null;
+
+			HttpListener best_match = null;
+			int best_length = -1;
+			
+			foreach (ListenerPrefix p in list) {
+				string ppath = p.Path;
+				if (ppath.Length < best_length)
+					continue;
+
+				if (path.StartsWith (ppath)) {
+					best_length = ppath.Length;
+					best_match = p.Listener;
+					prefix = p;
+				}
+			}
+
+			return best_match;
+		}
+
+		void AddSpecial (List<ListenerPrefix> coll, ListenerPrefix prefix)
+		{
+			if (coll == null)
+				return;
+
+			foreach (ListenerPrefix p in coll) {
+				if (p.Path == prefix.Path) //TODO: code
+					throw new HttpListenerException (400, "Prefix already in use.");
+			}
+
+			coll.Add (prefix);
+		}
+
+		void RemoveSpecial (List<ListenerPrefix> coll, ListenerPrefix prefix)
+		{
+			if (coll == null)
+				return;
+
+			int c = coll.Count;
+			for (int i = 0; i < c; i++) {
+				ListenerPrefix p = coll [i];
+				if (p.Path == prefix.Path) {
+					coll.RemoveAt (i);
+					CheckIfRemove ();
+					return;
+				}
+			}
+		}
+
+		void CheckIfRemove ()
+		{
+			if (prefixes.Count > 0)
+				return;
+
+			if (unhandled != null && unhandled.Count > 0)
+				return;
+
+			if (all != null && all.Count > 0)
+				return;
+
+			EndPointManager.RemoveEndPoint (this, endpoint);
+		}
+
+		public void Close ()
+		{
+			sock.Close ();
+		}
+
+		public void AddPrefix (ListenerPrefix prefix, HttpListener listener)
+		{
+			lock (prefixes) {
+				if (prefix.Host == "*") {
+					if (unhandled == null)
+						unhandled = new List<ListenerPrefix> ();
+
+					prefix.Listener = listener;
+					AddSpecial (unhandled, prefix);
+					return;
+				}
+
+				if (prefix.Host == "+") {
+					if (all == null)
+						all = new List<ListenerPrefix> ();
+					prefix.Listener = listener;
+					AddSpecial (all, prefix);
+					return;
+				}
+
+				if (prefixes.ContainsKey (prefix)) {
+					HttpListener other = prefixes [prefix];
+					if (other != listener) // TODO: code.
+						throw new HttpListenerException (400, "There's another listener for " + prefix);
+					return;
+				}
+
+				prefixes [prefix] = listener;
+			}
+		}
+
+		public void RemovePrefix (ListenerPrefix prefix, HttpListener listener)
+		{
+			lock (prefixes) {
+				if (prefix.Host == "*") {
+					RemoveSpecial (unhandled, prefix);
+					return;
+				}
+
+				if (prefix.Host == "+") {
+					RemoveSpecial (all, prefix);
+					return;
+				}
+
+				if (prefixes.ContainsKey (prefix)) {
+					prefixes.Remove (prefix);
+				}
+			}
+		}
+	}
+}
+#endif
+

+ 142 - 0
mcs/class/System/System.Net/EndPointManager.cs

@@ -0,0 +1,142 @@
+//
+// System.Net.EndPointManager
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.Collections;
+using System.Collections.Generic;
+namespace System.Net {
+	sealed class EndPointManager
+	{
+		static Dictionary<IPAddress, Dictionary<int, EndPointListener>> ip_to_endpoints =
+				new Dictionary<IPAddress, Dictionary<int, EndPointListener>> ();
+		
+		private EndPointManager ()
+		{
+		}
+
+		public static void AddListener (HttpListener listener)
+		{
+			List<string> added = new List<string> ();
+			try {
+				lock (ip_to_endpoints) {
+					foreach (string prefix in listener.Prefixes) {
+						AddPrefixInternal (prefix, listener);
+						added.Add (prefix);
+					}
+				}
+			} catch {
+				foreach (string prefix in added) {
+					RemovePrefixInternal (prefix, listener);
+				}
+				throw;
+			}
+		}
+
+		public static void AddPrefix (string prefix, HttpListener listener)
+		{
+			lock (ip_to_endpoints) {
+				AddPrefixInternal (prefix, listener);
+			}
+		}
+
+		static void AddPrefixInternal (string p, HttpListener listener)
+		{
+			ListenerPrefix lp = new ListenerPrefix (p);
+			if (lp.Path.IndexOf ('%') != -1)
+				throw new HttpListenerException (400, "Invalid path.");
+
+			if (lp.Path.IndexOf ("//") != -1) // TODO: Code?
+				throw new HttpListenerException (400, "Invalid path.");
+
+			// Always listens on all the interfaces, no matter the host name/ip used.
+			EndPointListener epl = GetEPListener (IPAddress.Any, lp.Port, listener, lp.Secure);
+			epl.AddPrefix (lp, listener);
+		}
+
+		static EndPointListener GetEPListener (IPAddress addr, int port, HttpListener listener, bool secure)
+		{
+			Dictionary<int, EndPointListener> p = null;
+			if (ip_to_endpoints.ContainsKey (addr)) {
+				p = ip_to_endpoints [addr];
+			} else {
+				p = new Dictionary<int, EndPointListener> ();
+				ip_to_endpoints [addr] = p;
+			}
+
+			EndPointListener epl = null;
+			if (p.ContainsKey (port)) {
+				epl = p [port];
+			} else {
+				epl = new EndPointListener (addr, port, secure);
+				p [port] = epl;
+			}
+
+			return epl;
+		}
+
+		public static void RemoveEndPoint (EndPointListener epl, IPEndPoint ep)
+		{
+			lock (ip_to_endpoints) {
+				Dictionary<int, EndPointListener> p = null;
+				p = ip_to_endpoints [ep.Address];
+				p.Remove (ep.Port);
+				epl.Close ();
+			}
+		}
+
+		public static void RemoveListener (HttpListener listener)
+		{
+			lock (ip_to_endpoints) {
+				foreach (string prefix in listener.Prefixes) {
+					RemovePrefixInternal (prefix, listener);
+				}
+			}
+		}
+
+		public static void RemovePrefix (string prefix, HttpListener listener)
+		{
+			lock (ip_to_endpoints) {
+				RemovePrefixInternal (prefix, listener);
+			}
+		}
+
+		static void RemovePrefixInternal (string prefix, HttpListener listener)
+		{
+			ListenerPrefix lp = new ListenerPrefix (prefix);
+			if (lp.Path.IndexOf ('%') != -1)
+				return;
+
+			if (lp.Path.IndexOf ("//") != -1)
+				return;
+
+			EndPointListener epl = GetEPListener (IPAddress.Any, lp.Port, listener, lp.Secure);
+			epl.RemovePrefix (lp, listener);
+		}
+	}
+}
+#endif
+

+ 303 - 0
mcs/class/System/System.Net/HttpConnection.cs

@@ -0,0 +1,303 @@
+//
+// System.Net.HttpConnection
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+namespace System.Net {
+	sealed class HttpConnection
+	{
+		const int BufferSize = 8192;
+		Socket sock;
+		NetworkStream stream;
+		EndPointListener epl;
+		MemoryStream ms;
+		byte [] buffer;
+		HttpListenerContext context;
+		bool secure;
+		StringBuilder current_line;
+		ListenerPrefix prefix;
+		RequestStream i_stream;
+		ResponseStream o_stream;
+		bool chunked;
+		int chunked_uses;
+		bool context_bound;
+
+		public HttpConnection (Socket sock, EndPointListener epl, bool secure)
+		{
+			this.sock = sock;
+			stream = new NetworkStream (sock, false);
+			this.epl = epl;
+			this.secure = secure;
+			Init ();
+		}
+
+		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 int ChunkedUses {
+			get { return chunked_uses; }
+		}
+
+		public IPEndPoint LocalEndPoint {
+			get { return (IPEndPoint) sock.LocalEndPoint; }
+		}
+
+		public IPEndPoint RemoteEndPoint {
+			get { return (IPEndPoint) sock.RemoteEndPoint; }
+		}
+
+		public bool IsSecure {
+			get { return secure; }
+		}
+
+		public ListenerPrefix Prefix {
+			get { return prefix; }
+			set { prefix = value; }
+		}
+
+		public void BeginReadRequest ()
+		{
+			if (buffer == null)
+				buffer = new byte [BufferSize];
+			stream.BeginRead (buffer, 0, BufferSize, OnRead, this);
+		}
+
+		public RequestStream GetRequestStream (bool chunked)
+		{
+			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, sock, buffer, position, length);
+				} else {
+					i_stream = new RequestStream (sock, buffer, position, length);
+				}
+			}
+			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 (sock, context.Response, ign);
+			}
+			return o_stream;
+		}
+
+		void OnRead (IAsyncResult ares)
+		{
+			// TODO: set a limit on ms length.
+			HttpConnection cnc = (HttpConnection) ares.AsyncState;
+			int nread = -1;
+			try {
+				nread = stream.EndRead (ares);
+				ms.Write (buffer, 0, nread);
+			} catch {
+				if (ms.Length > 0)
+					SendError ();
+				sock.Close ();
+				return;
+			}
+
+			if (nread == 0) {
+				//if (ms.Length > 0)
+				//	SendError (); // Why bother?
+				sock.Close ();
+				return;
+			}
+
+			if (ProcessInput (ms)) {
+				if (!context.HaveError)
+					context.Request.FinishInitialization ();
+
+				if (context.HaveError) {
+					SendError ();
+					Close ();
+					return;
+				}
+
+				if (!epl.BindContext (context)) {
+					SendError ("Invalid host", 400);
+					Close ();
+				}
+				context_bound = true;
+				return;
+			}
+			stream.BeginRead (buffer, 0, BufferSize, OnRead, cnc);
+		}
+
+		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;
+			while ((line = ReadLine (buffer, position, len - position, ref used)) != null) {
+				position += used;
+				if (line == "") {
+					if (input_state == InputState.RequestLine)
+						continue;
+					current_line = null;
+					ms = null;
+					return true;
+				}
+
+				if (input_state == InputState.RequestLine) {
+					context.Request.SetRequestLine (line);
+					input_state = InputState.Headers;
+				} else {
+					context.Request.AddHeader (line);
+				}
+
+				if (context.HaveError)
+					return true;
+
+				if (position >= len)
+					break;
+			}
+
+			if (used == len) {
+				ms.SetLength (0);
+				position = 0;
+			}
+			return false;
+		}
+
+		string ReadLine (byte [] buffer, int offset, int len, ref int used)
+		{
+			if (current_line == null)
+				current_line = new StringBuilder ();
+			int last = offset + len;
+			used = 0;
+			for (int i = offset; i < last && line_state != LineState.LF; i++) {
+				used++;
+				byte b = buffer [i];
+				if (b == 13) {
+					line_state = LineState.CR;
+				} else if (b == 10) {
+					line_state = LineState.LF;
+				} else {
+					current_line.Append ((char) b);
+				}
+			}
+
+			string result = null;
+			if (line_state == LineState.LF) {
+				line_state = LineState.None;
+				result = current_line.ToString ();
+				current_line.Length = 0;
+			}
+
+			return result;
+		}
+
+		public void SendError (string msg, int status)
+		{
+			HttpListenerResponse response = context.Response;
+			response.StatusCode = status;
+			response.ContentType = "text/html";
+			string description = HttpListenerResponse.GetStatusDescription (status);
+			string str;
+			if (msg != null)
+				str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
+			else
+				str = String.Format ("<h1>{0}</h1>", description);
+
+			byte [] error = context.Response.ContentEncoding.GetBytes (str);
+			response.Close (error, false);
+		}
+
+		public void SendError ()
+		{
+			SendError (context.ErrorMessage, context.ErrorStatus);
+		}
+
+		public void Close ()
+		{
+			if (o_stream != null) {
+				Stream st = o_stream;
+				st.Close ();
+				o_stream = null;
+			}
+
+			if (sock != null) {
+				if (chunked && context.Response.ForceCloseChunked == false) {
+					// Don't close. Keep working.
+					chunked_uses++;
+					Init ();
+					BeginReadRequest ();
+					return;
+				}
+
+				Socket s = sock;
+				sock = null;
+				s.Shutdown (SocketShutdown.Both);
+				s.Close ();
+				if (context_bound)
+					epl.UnbindContext (context);
+			}
+		}
+	}
+}
+#endif
+

+ 310 - 0
mcs/class/System/System.Net/HttpListener.cs

@@ -0,0 +1,310 @@
+//
+// System.Net.HttpListener
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.Collections.Generic;
+using System.Threading;
+//TODO: logging
+namespace System.Net {
+	public sealed class HttpListener : IDisposable {
+		AuthenticationSchemes auth_schemes;
+		HttpListenerPrefixCollection prefixes;
+		AuthenticationSchemeSelector auth_selector; 
+		string realm;
+		bool ignore_write_exceptions;
+		bool unsafe_ntlm_auth;
+		bool listening;
+		bool disposed;
+
+		Dictionary<HttpListenerContext,HttpListenerContext> registry;
+		List<HttpListenerContext> ctx_queue;
+		List<ListenerAsyncResult> wait_queue;
+
+		public HttpListener ()
+		{
+			prefixes = new HttpListenerPrefixCollection (this);
+			registry = new Dictionary<HttpListenerContext,HttpListenerContext> ();
+			ctx_queue = new List<HttpListenerContext> ();
+			wait_queue = new List<ListenerAsyncResult> ();
+			auth_schemes = AuthenticationSchemes.Anonymous;
+		}
+
+		// TODO: Digest, NTLM and Negotiate require ControlPrincipal
+		public AuthenticationSchemes AuthenticationSchemes {
+			get { return auth_schemes; }
+			set {
+				CheckDisposed ();
+				auth_schemes = value;
+			}
+		}
+
+		//TODO: when is this called?
+		public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate {
+			get { return auth_selector; }
+			set {
+				CheckDisposed ();
+				auth_selector = value;
+			}
+		}
+
+		public bool IgnoreWriteExceptions {
+			get { return ignore_write_exceptions; }
+			set {
+				CheckDisposed ();
+				ignore_write_exceptions = value;
+			}
+		}
+
+		public bool IsListening {
+			get { return listening; }
+		}
+
+		public static bool IsSupported {
+			get { return true; }
+		}
+
+		public HttpListenerPrefixCollection Prefixes {
+			get {
+				CheckDisposed ();
+				return prefixes;
+			}
+		}
+
+		// TODO: use this
+		public string Realm {
+			get { return realm; }
+			set {
+				CheckDisposed ();
+				realm = value;
+			}
+		}
+
+		[MonoTODO ("Support for NTLM needs some loving.")]
+		public bool UnsafeConnectionNtlmAuthentication {
+			get { return unsafe_ntlm_auth; }
+			set {
+				CheckDisposed ();
+				unsafe_ntlm_auth = value;
+			}
+		}
+
+		public void Abort ()
+		{
+			if (disposed)
+				return;
+
+			if (!listening) {
+				disposed = true;
+				return;
+			}
+
+			Close (true);
+		}
+
+		public void Close ()
+		{
+			if (disposed)
+				return;
+
+			if (!listening) {
+				disposed = true;
+				return;
+			}
+
+			Close (false);
+		}
+
+		void Close (bool force)
+		{
+			CheckDisposed ();
+			EndPointManager.RemoveListener (this);
+			Cleanup (force);
+			disposed = true;
+		}
+
+		void Cleanup (bool close_existing)
+		{
+			lock (registry) {
+				if (close_existing) {
+					foreach (HttpListenerContext context in registry.Keys) {
+						context.Connection.Close ();
+					}
+					registry.Clear (); // Just in case.
+				}
+
+				lock (ctx_queue) {
+					foreach (HttpListenerContext context in ctx_queue)
+						context.Connection.Close ();
+
+					ctx_queue.Clear ();
+				}
+
+				lock (wait_queue) {
+					foreach (ListenerAsyncResult ares in wait_queue) {
+						ares.Complete ("Listener was closed.");
+					}
+					wait_queue.Clear ();
+				}
+			}
+		}
+
+		public IAsyncResult BeginGetContext (AsyncCallback callback, Object state)
+		{
+			CheckDisposed ();
+			if (!listening)
+				throw new InvalidOperationException ("Please, call Start before using this method.");
+
+			ListenerAsyncResult ares = new ListenerAsyncResult (callback, state);
+			lock (ctx_queue) {
+				HttpListenerContext ctx = GetContextFromQueue ();
+				if (ctx != null) {
+					ares.Complete (ctx, true);
+					return ares;
+				}
+			}
+
+			lock (wait_queue) {
+				wait_queue.Add (ares);
+			}
+
+			return ares;
+		}
+
+		public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
+		{
+			CheckDisposed ();
+			if (asyncResult == null)
+				throw new ArgumentNullException ("asyncResult");
+
+			ListenerAsyncResult ares = asyncResult as ListenerAsyncResult;
+			if (ares == null)
+				throw new ArgumentException ("Wrong IAsyncResult.", "asyncResult");
+
+			if (!ares.IsCompleted)
+				ares.AsyncWaitHandle.WaitOne ();
+
+			lock (wait_queue) {
+				int idx = wait_queue.IndexOf (ares);
+				if (idx >= 0)
+					wait_queue.RemoveAt (idx);
+			}
+
+			return ares.GetContext (); // This will throw on error.
+		}
+
+		public HttpListenerContext GetContext ()
+		{
+			// The prefixes are not checked when using the async interface!?
+			if (prefixes.Count == 0)
+				throw new InvalidOperationException ("Please, call AddPrefix before using this method.");
+
+			IAsyncResult ares = BeginGetContext (null, null);
+			return EndGetContext (ares);
+		}
+
+		public void Start ()
+		{
+			CheckDisposed ();
+			if (listening)
+				return;
+
+			EndPointManager.AddListener (this);
+			listening = true;
+		}
+
+		public void Stop ()
+		{
+			CheckDisposed ();
+			listening = false;
+			Close (false);
+		}
+
+		void IDisposable.Dispose ()
+		{
+			if (disposed)
+				return;
+
+			disposed = true;
+			Close (true); //TODO: Should we force here or not?
+		}
+
+		internal void CheckDisposed ()
+		{
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().ToString ());
+		}
+
+		// Must be called with a lock on ctx_queue
+		HttpListenerContext GetContextFromQueue ()
+		{
+			if (ctx_queue.Count == 0)
+				return null;
+
+			HttpListenerContext context = ctx_queue [0];
+			ctx_queue.RemoveAt (0);
+			return context;
+		}
+
+		internal void RegisterContext (HttpListenerContext context)
+		{
+			try {
+				Monitor.Enter (registry);
+				registry [context] = context;
+				Monitor.Enter (wait_queue);
+				Monitor.Enter (ctx_queue);
+				if (wait_queue.Count == 0) {
+					ctx_queue.Add (context);
+				} else {
+					ListenerAsyncResult ares = wait_queue [0];
+					wait_queue.RemoveAt (0);
+					ares.Complete (context);
+				}
+			} finally {
+				Monitor.Exit (ctx_queue);
+				Monitor.Exit (wait_queue);
+				Monitor.Exit (registry);
+			}
+		}
+
+		internal void UnregisterContext (HttpListenerContext context)
+		{
+			try {
+				Monitor.Enter (registry);
+				Monitor.Enter (ctx_queue);
+				int idx = ctx_queue.IndexOf (context);
+				if (idx >= 0)
+					ctx_queue.RemoveAt (idx);
+				registry.Remove (context);
+			} finally {
+				Monitor.Exit (ctx_queue);
+				Monitor.Exit (registry);
+			}
+		}
+	}
+}
+#endif
+

+ 45 - 0
mcs/class/System/System.Net/HttpListenerBasicIdentity.cs

@@ -0,0 +1,45 @@
+//
+// System.Net.HttpListenerBasicIdentity
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.Security.Principal;
+namespace System.Net {
+	public class HttpListenerBasicIdentity : GenericIdentity {
+		string password;
+
+		public HttpListenerBasicIdentity (string username, string password) : base (username, "Basic")
+		{
+			this.password = password;
+		}
+
+		public virtual string Password {
+			get { return password; }
+		}
+	}
+}
+#endif
+

+ 82 - 0
mcs/class/System/System.Net/HttpListenerContext.cs

@@ -0,0 +1,82 @@
+//
+// System.Net.HttpListenerContext
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.Collections.Specialized;
+using System.IO;
+using System.Security.Principal;
+using System.Text;
+namespace System.Net {
+	public sealed class HttpListenerContext {
+		HttpListenerRequest request;
+		HttpListenerResponse response;
+		IPrincipal user;
+		HttpConnection cnc;
+		string error;
+		int err_status = 400;
+		internal HttpListener Listener;
+
+		internal HttpListenerContext (HttpConnection cnc)
+		{
+			this.cnc = cnc;
+			request = new HttpListenerRequest (this);
+			response = new HttpListenerResponse (this);
+		}
+
+		internal int ErrorStatus {
+			get { return err_status; }
+			set { err_status = value; }
+		}
+
+		internal string ErrorMessage {
+			get { return error; }
+			set { error = value; }
+		}
+
+		internal bool HaveError {
+			get { return (error != null); }
+		}
+
+		internal HttpConnection Connection {
+			get { return cnc; }
+		}
+
+		public HttpListenerRequest Request {
+			get { return request; }
+		}
+
+		public HttpListenerResponse Response {
+			get { return response; }
+		}
+
+		public IPrincipal User {
+			get { return user; }
+		}
+	}
+}
+#endif
+

+ 57 - 0
mcs/class/System/System.Net/HttpListenerException.cs

@@ -0,0 +1,57 @@
+//
+// System.Net.HttpListenerException
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.ComponentModel;
+using System.Runtime.Serialization;
+namespace System.Net {
+	[Serializable]
+	public class HttpListenerException : Win32Exception
+	{
+		public HttpListenerException ()
+		{
+		}
+
+		public HttpListenerException (int errorCode) : base (errorCode)
+		{
+		}
+
+		public HttpListenerException (int errorCode, string message) : base (errorCode, message)
+		{
+		}
+
+		protected HttpListenerException (SerializationInfo serializationInfo, StreamingContext streamingContext) : base (serializationInfo, streamingContext)
+		{
+		}
+
+		public override int ErrorCode {
+			get { return base.ErrorCode; }
+		}
+	}
+}
+#endif
+

+ 117 - 0
mcs/class/System/System.Net/HttpListenerPrefixCollection.cs

@@ -0,0 +1,117 @@
+//
+// System.Net.HttpListenerPrefixCollection.cs
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.Collections;
+using System.Collections.Generic;
+namespace System.Net {
+	public class HttpListenerPrefixCollection : ICollection<string>, IEnumerable<string>, IEnumerable {
+		HttpListener listener;
+		List<string> prefixes;
+
+		internal HttpListenerPrefixCollection (HttpListener listener)
+		{
+			this.listener = listener;
+			prefixes = new List<string> ();
+		}
+
+		public int Count {
+			get { return prefixes.Count; }
+		}
+
+		public bool IsReadOnly {
+			get { return false; }
+		}
+
+		public bool IsSynchronized {
+			get { return false; }
+		}
+
+		public void Add (string uriPrefix)
+		{
+			listener.CheckDisposed ();
+			ListenerPrefix.CheckUri (uriPrefix);
+			if (prefixes.Contains (uriPrefix))
+				return;
+
+			prefixes.Add (uriPrefix);
+			if (listener.IsListening)
+				EndPointManager.AddPrefix (uriPrefix, listener);
+		}
+
+		public void Clear ()
+		{
+			listener.CheckDisposed ();
+			prefixes.Clear ();
+			if (listener.IsListening)
+				EndPointManager.RemoveListener (listener);
+		}
+
+		public bool Contains (string uriPrefix)
+		{
+			listener.CheckDisposed ();
+			return prefixes.Contains (uriPrefix);
+		}
+
+		public void CopyTo (string [] array, int offset)
+		{
+			listener.CheckDisposed ();
+			prefixes.CopyTo (array, offset);
+		}
+
+		public void CopyTo (Array array, int offset)
+		{
+			listener.CheckDisposed ();
+			((ICollection) prefixes).CopyTo (array, offset);
+		}
+
+		public IEnumerator GetEnumerator ()
+		{
+			return prefixes.GetEnumerator ();
+		}
+
+		IEnumerator<string> IEnumerable<string>.GetEnumerator ()
+		{
+			return prefixes.GetEnumerator ();
+		}
+
+		public bool Remove (string uriPrefix)
+		{
+			listener.CheckDisposed ();
+			if (uriPrefix == null)
+				throw new ArgumentNullException ("uriPrefix");
+
+			bool result = prefixes.Remove (uriPrefix);
+			if (result && listener.IsListening)
+				EndPointManager.RemovePrefix (uriPrefix, listener);
+
+			return result;
+		}
+	}
+}
+#endif
+

+ 363 - 0
mcs/class/System/System.Net/HttpListenerRequest.cs

@@ -0,0 +1,363 @@
+//
+// System.Net.HttpListenerRequest
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.Collections;
+using System.Collections.Specialized;
+using System.Globalization;
+using System.IO;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+namespace System.Net {
+	public sealed class HttpListenerRequest
+	{
+		string [] accept_types;
+		int client_cert_error;
+		Encoding content_encoding;
+		long content_length;
+		bool cl_set;
+		CookieCollection cookies;
+		WebHeaderCollection headers;
+		string method;
+		Stream input_stream;
+		bool is_authenticated;
+		Version version;
+		NameValueCollection query_string; // check if null is ok, check if read-only, check case-sensitiveness
+		string raw_url;
+		Guid identifier;
+		Uri url;
+		Uri referrer;
+		string [] user_languages;
+		bool no_get_certificate;
+		HttpListenerContext context;
+		bool is_chunked;
+		static byte [] _100continue = Encoding.ASCII.GetBytes ("HTTP/1.1 100 Continue\r\n\r\n");
+
+		internal HttpListenerRequest (HttpListenerContext context)
+		{
+			this.context = context;
+			headers = new WebHeaderCollection ();
+			input_stream = Stream.Null;
+		}
+
+		static char [] separators = new char [] { ' ' };
+		// From WebRequestMethods.Http
+		static readonly string [] methods = new string [] { "GET", "POST", "HEAD",
+								"PUT", "CONNECT", "MKCOL" };
+		internal void SetRequestLine (string req)
+		{
+			string [] parts = req.Split (separators, 3);
+			if (parts.Length != 3) {
+				context.ErrorMessage = "Invalid request line (parts).";
+				return;
+			}
+
+			method = parts [0];
+			if (Array.IndexOf (methods, method) == -1) {
+				context.ErrorMessage = "Invalid request line (verb).";
+				return;
+			}
+
+			raw_url = parts [1];
+			if (parts [2].Length != 8 || !parts [2].StartsWith ("HTTP/")) {
+				context.ErrorMessage = "Invalid request line (version).";
+				return;
+			}
+
+			try {
+				version = new Version (parts [2].Substring (5));
+				if (version.Major < 1)
+					throw new Exception ();
+			} catch {
+				context.ErrorMessage = "Invalid request line (version).";
+				return;
+			}
+		}
+
+		void CreateQueryString (string query)
+		{
+			query_string = new NameValueCollection ();
+			if (query == null || query.Length == 0)
+				return;
+
+			string [] components = query.Split ('&');
+			foreach (string kv in components) {
+				int pos = kv.IndexOf ('=');
+				if (pos == -1) {
+					query_string.Add (null, HttpUtility.UrlDecode (kv));
+				} else {
+					string key = HttpUtility.UrlDecode (kv.Substring (0, pos));
+					string val = HttpUtility.UrlDecode (kv.Substring (pos + 1));
+					
+					query_string.Add (key, val);
+				}
+			}
+		}
+
+		internal void FinishInitialization ()
+		{
+			string host = UserHostName;
+			if (host == null || host == "") {
+				context.ErrorMessage = "Invalid host name";
+				return;
+			}
+
+			int colon = host.IndexOf (':');
+			if (colon >= 0)
+				host = host.Substring (0, colon);
+
+			string base_uri = String.Format ("{0}://{1}:{2}",
+								(IsSecureConnection) ? "https" : "http",
+								host,
+								LocalEndPoint.Port);
+			try {
+				url = new Uri (base_uri + raw_url);
+			} catch {
+				context.ErrorMessage = "Invalid url";
+				return;
+			}
+
+			CreateQueryString (url.Query);
+
+			if (method == "GET" || method == "HEAD")
+				return;
+
+			string t_encoding = null;
+			if (version >= HttpVersion.Version11) {
+				t_encoding = Headers ["Transfer-Encoding"];
+				// 'identity' is not valid!
+				if (t_encoding != null && t_encoding != "chunked") {
+					context.Connection.SendError (null, 501);
+					return;
+				}
+			}
+
+			bool is_chunked = (t_encoding == "chunked");
+			if (!is_chunked && !cl_set) {
+				context.Connection.SendError (null, 411);
+				return;
+			}
+
+			input_stream = context.Connection.GetRequestStream (is_chunked);
+			if (Headers ["Expect"] == "100-continue") {
+				ResponseStream output = context.Connection.GetResponseStream ();
+				output.InternalWrite (_100continue, 0, _100continue.Length);
+			}
+		}
+
+		internal void AddHeader (string header)
+		{
+			int colon = header.IndexOf (':');
+			if (colon == -1 || colon == 0) {
+				context.ErrorMessage = "Bad Request";
+				return;
+			}
+
+			string name = header.Substring (0, colon).Trim ();
+			string val = header.Substring (colon + 1).Trim ();
+			string lower = name.ToLower (CultureInfo.InvariantCulture);
+			headers.SetInternal (name, val);
+			switch (lower) {
+				case "accept-language":
+					user_languages = val.Split (','); // yes, only split with a ','
+					break;
+				case "accept-types":
+					accept_types = val.Split (','); // yes, only split with a ','
+					break;
+				case "content-length":
+					try {
+						//TODO: max. content_length?
+						content_length = Int64.Parse (val.Trim ());
+						if (content_length < 0)
+							context.ErrorMessage = "Invalid Content-Length.";
+						cl_set = true;
+					} catch {
+						context.ErrorMessage = "Invalid Content-Length.";
+					}
+
+					break;
+				case "referer":
+					try {
+						referrer = new Uri (val);
+					} catch {
+						referrer = new Uri ("http://someone.is.screwing.with.the.headers.com/");
+					}
+					break;
+				//TODO: cookie headers
+			}
+		}
+
+		public string [] AcceptTypes {
+			get { return accept_types; }
+		}
+
+		public int ClientCertificateError {
+			get {
+				if (no_get_certificate)
+					throw new InvalidOperationException (
+						"Call GetClientCertificate() before calling this method.");
+				return client_cert_error;
+			}
+		}
+
+		public Encoding ContentEncoding {
+			get {
+				if (content_encoding == null)
+					content_encoding = Encoding.Default;
+				return content_encoding;
+			}
+		}
+
+		public long ContentLength64 {
+			get { return content_length; }
+		}
+
+		public string ContentType {
+			get { return headers ["content-type"]; }
+		}
+
+		public CookieCollection Cookies {
+			get {
+				// TODO: check if the collection is read-only
+				if (cookies == null)
+					cookies = new CookieCollection ();
+				return cookies;
+			}
+		}
+
+		public bool HasEntityBody {
+			get { return (method == "GET" || method == "HEAD" || content_length <= 0 || is_chunked); }
+		}
+
+		public NameValueCollection Headers {
+			get { return headers; }
+		}
+
+		public string HttpMethod {
+			get { return method; }
+		}
+
+		public Stream InputStream {
+			get { return input_stream; }
+		}
+
+		public bool IsAuthenticated {
+			get { return is_authenticated; }
+		}
+
+		public bool IsLocal {
+			get { return IPAddress.IsLoopback (RemoteEndPoint.Address); }
+		}
+
+		public bool IsSecureConnection {
+			get { return context.Connection.IsSecure; } 
+		}
+
+		public bool KeepAlive {
+			get { return false; }
+		}
+
+		public IPEndPoint LocalEndPoint {
+			get { return context.Connection.LocalEndPoint; }
+		}
+
+		public Version ProtocolVersion {
+			get { return version; }
+		}
+
+		public NameValueCollection QueryString {
+			get { return query_string; }
+		}
+
+		public string RawUrl {
+			get { return raw_url; }
+		}
+
+		public IPEndPoint RemoteEndPoint {
+			get { return context.Connection.RemoteEndPoint; }
+		}
+
+		public Guid RequestTraceIdentifier {
+			get { return identifier; }
+		}
+
+		public Uri Url {
+			get { return url; }
+		}
+
+		public Uri UrlReferrer {
+			get { return referrer; }
+		}
+
+		public string UserAgent {
+			get { return headers ["user-agent"]; }
+		}
+
+		public string UserHostAddress {
+			get { return LocalEndPoint.ToString (); }
+		}
+
+		public string UserHostName {
+			get { return headers ["host"]; }
+		}
+
+		public string [] UserLanguages {
+			get { return user_languages; }
+		}
+
+		public IAsyncResult BeginGetClientCertificate (AsyncCallback requestCallback, Object state)
+		{
+			return null;
+		}
+
+		public X509Certificate2 EndGetClientCertificate (IAsyncResult asyncResult)
+		{
+			return null;
+			// set no_client_certificate once done.
+		}
+
+		public X509Certificate2 GetClientCertificate ()
+		{
+			// set no_client_certificate once done.
+
+			// InvalidOp if call in progress.
+			return null;
+		}
+
+		public override bool Equals (object obj)
+		{
+			return false;
+		}
+
+		public override int GetHashCode ()
+		{
+			return base.GetHashCode (); // May be use the Guid?
+		}
+	}
+}
+#endif
+

+ 499 - 0
mcs/class/System/System.Net/HttpListenerResponse.cs

@@ -0,0 +1,499 @@
+//
+// System.Net.HttpListenerResponse
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.Globalization;
+using System.IO;
+using System.Text;
+namespace System.Net {
+	public sealed class HttpListenerResponse : IDisposable
+	{
+		bool disposed;
+		Encoding content_encoding;
+		long content_length;
+		bool cl_set;
+		string content_type;
+		CookieCollection cookies;
+		WebHeaderCollection headers = new WebHeaderCollection ();
+		bool keep_alive = true;
+		ResponseStream output_stream;
+		Version version = HttpVersion.Version11;
+		string location;
+		int status_code = 200;
+		string status_description = "OK";
+		bool chunked;
+		HttpListenerContext context;
+		internal bool HeadersSent;
+		bool force_close_chunked;
+
+		internal HttpListenerResponse (HttpListenerContext context)
+		{
+			this.context = context;
+		}
+
+		internal bool ForceCloseChunked {
+			get { return force_close_chunked; }
+		}
+
+		public Encoding ContentEncoding {
+			get {
+				if (content_encoding == null)
+					content_encoding = Encoding.Default;
+				return content_encoding;
+			}
+			set {
+				if (disposed)
+					throw new ObjectDisposedException (GetType ().ToString ());
+
+				//TODO: is null ok?
+				if (HeadersSent)
+					throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+					
+				content_encoding = value;
+			}
+		}
+
+		public long ContentLength64 {
+			get { return content_length; }
+			set {
+				if (disposed)
+					throw new ObjectDisposedException (GetType ().ToString ());
+
+				if (HeadersSent)
+					throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+
+				if (value < 0)
+					throw new ArgumentOutOfRangeException ("Must be >= 0", "value");
+
+				cl_set = true;
+				content_length = value;
+			}
+		}
+		
+		public string ContentType {
+			get { return content_type; }
+			set {
+				// TODO: is null ok?
+				if (disposed)
+					throw new ObjectDisposedException (GetType ().ToString ());
+
+				if (HeadersSent)
+					throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+
+				content_type = value;
+			}
+		}
+
+		// RFC 2109, 2965 + the netscape specification at http://wp.netscape.com/newsref/std/cookie_spec.html
+		public CookieCollection Cookies {
+			get {
+				if (cookies == null)
+					cookies = new CookieCollection ();
+				return cookies;
+			}
+			set { cookies = value; } // null allowed?
+		}
+
+		public WebHeaderCollection Headers {
+			get { return headers; }
+			set {
+		/**
+		 *	"If you attempt to set a Content-Length, Keep-Alive, Transfer-Encoding, or
+		 *	WWW-Authenticate header using the Headers property, an exception will be
+		 *	thrown. Use the KeepAlive or ContentLength64 properties to set these headers.
+		 *	You cannot set the Transfer-Encoding or WWW-Authenticate headers manually."
+		*/
+		// TODO: check if this is marked readonly after headers are sent.
+				headers = value;
+			}
+		}
+
+		public bool KeepAlive {
+			get { return keep_alive; }
+			set {
+				if (disposed)
+					throw new ObjectDisposedException (GetType ().ToString ());
+
+				if (HeadersSent)
+					throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+					
+				keep_alive = value;
+			}
+		}
+
+		public Stream OutputStream {
+			get {
+				if (disposed)
+					throw new ObjectDisposedException (GetType ().ToString ());
+
+				if (output_stream == null)
+					output_stream = context.Connection.GetResponseStream ();
+				return output_stream;
+			}
+		}
+		
+		public Version ProtocolVersion {
+			get { return version; }
+			set {
+				if (disposed)
+					throw new ObjectDisposedException (GetType ().ToString ());
+
+				if (HeadersSent)
+					throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+					
+				if (value == null)
+					throw new ArgumentNullException ("value");
+
+				if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1))
+					throw new ArgumentException ("Must be 1.0 or 1.1", "value");
+
+				if (disposed)
+					throw new ObjectDisposedException (GetType ().ToString ());
+
+				version = value;
+			}
+		}
+
+		public string RedirectLocation {
+			get { return location; }
+			set {
+				if (disposed)
+					throw new ObjectDisposedException (GetType ().ToString ());
+
+				if (HeadersSent)
+					throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+					
+				location = value;
+			}
+		}
+
+		public bool SendChunked {
+			get { return chunked; }
+			set {
+				if (disposed)
+					throw new ObjectDisposedException (GetType ().ToString ());
+
+				if (HeadersSent)
+					throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+					
+				chunked = value;
+			}
+		}
+
+		public int StatusCode {
+			get { return status_code; }
+			set {
+				if (disposed)
+					throw new ObjectDisposedException (GetType ().ToString ());
+
+				if (HeadersSent)
+					throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+					
+				if (value < 100 || value > 999)
+					throw new ProtocolViolationException ("StatusCode must be between 100 and 999.");
+				status_code = value;
+				status_description = GetStatusDescription (value);
+			}
+		}
+
+		internal static string GetStatusDescription (int code)
+		{
+			switch (code){
+			case 100: return "Continue";
+			case 101: return "Switching Protocols";
+			case 102: return "Processing";
+			case 200: return "OK";
+			case 201: return "Created";
+			case 202: return "Accepted";
+			case 203: return "Non-Authoritative Information";
+			case 204: return "No Content";
+			case 205: return "Reset Content";
+			case 206: return "Partial Content";
+			case 207: return "Multi-Status";
+			case 300: return "Multiple Choices";
+			case 301: return "Moved Permanently";
+			case 302: return "Found";
+			case 303: return "See Other";
+			case 304: return "Not Modified";
+			case 305: return "Use Proxy";
+			case 307: return "Temporary Redirect";
+			case 400: return "Bad Request";
+			case 401: return "Unauthorized";
+			case 402: return "Payment Required";
+			case 403: return "Forbidden";
+			case 404: return "Not Found";
+			case 405: return "Method Not Allowed";
+			case 406: return "Not Acceptable";
+			case 407: return "Proxy Authentication Required";
+			case 408: return "Request Timeout";
+			case 409: return "Conflict";
+			case 410: return "Gone";
+			case 411: return "Length Required";
+			case 412: return "Precondition Failed";
+			case 413: return "Request Entity Too Large";
+			case 414: return "Request-Uri Too Long";
+			case 415: return "Unsupported Media Type";
+			case 416: return "Requested Range Not Satisfiable";
+			case 417: return "Expectation Failed";
+			case 422: return "Unprocessable Entity";
+			case 423: return "Locked";
+			case 424: return "Failed Dependency";
+			case 500: return "Internal Server Error";
+			case 501: return "Not Implemented";
+			case 502: return "Bad Gateway";
+			case 503: return "Service Unavailable";
+			case 504: return "Gateway Timeout";
+			case 505: return "Http Version Not Supported";
+			case 507: return "Insufficient Storage";
+			}
+			return "";
+		}
+
+		public string StatusDescription {
+			get { return status_description; }
+			set {
+				status_description = value;
+			}
+		}
+
+		void IDisposable.Dispose ()
+		{
+			Close (true); //TODO: Abort or Close?
+		}
+
+		public void Abort ()
+		{
+			if (disposed)
+				return;
+
+			Close (true);
+		}
+
+		public void AddHeader (string name, string value)
+		{
+			if (name == null)
+				throw new ArgumentNullException ("name");
+
+			if (name == "")
+				throw new ArgumentException ("'name' cannot be empty", "name");
+			
+			//TODO: check for forbidden headers and invalid characters
+			if (value.Length > 65535)
+				throw new ArgumentOutOfRangeException ("value");
+
+			headers.Set (name, value);
+		}
+
+		public void AppendCookie (Cookie cookie)
+		{
+			if (cookie == null)
+				throw new ArgumentNullException ("cookie");
+			
+			cookies.Add (cookie);
+		}
+
+		public void AppendHeader (string name, string value)
+		{
+			if (name == null)
+				throw new ArgumentNullException ("name");
+
+			if (name == "")
+				throw new ArgumentException ("'name' cannot be empty", "name");
+			
+			if (value.Length > 65535)
+				throw new ArgumentOutOfRangeException ("value");
+
+			headers.Add (name, value);
+		}
+
+		void Close (bool force)
+		{
+			// TODO: use the 'force' argument
+			context.Connection.Close ();
+			disposed = true;
+		}
+
+		public void Close ()
+		{
+			if (disposed)
+				return;
+
+			Close (false);
+		}
+
+		public void Close (byte [] responseEntity, bool willBlock)
+		{
+			if (disposed)
+				return;
+
+			if (responseEntity == null)
+				throw new ArgumentNullException ("responseEntity");
+
+			//TODO: if willBlock -> BeginWrite + Close ?
+			ContentLength64 = responseEntity.Length;
+			OutputStream.Write (responseEntity, 0, (int) content_length);
+			Close (false);
+		}
+
+		public void CopyFrom (HttpListenerResponse templateResponse)
+		{
+			headers.Clear ();
+			headers.Add (templateResponse.headers);
+			content_length = templateResponse.content_length;
+			status_code = templateResponse.status_code;
+			status_description = templateResponse.status_description;
+			keep_alive = templateResponse.keep_alive;
+			version = templateResponse.version;
+		}
+
+		public override bool Equals (object obj)
+		{
+			return false;
+		}
+
+		public override int GetHashCode ()
+		{
+			return base.GetHashCode ();
+		}
+
+		public void Redirect (string url)
+		{
+			StatusCode = 302; // Found
+			location = url;
+		}
+
+		bool FindCookie (Cookie cookie)
+		{
+			string name = cookie.Name;
+			string domain = cookie.Domain;
+			string path = cookie.Path;
+			foreach (Cookie c in cookies) {
+				if (name != c.Name)
+					continue;
+				if (domain != c.Domain)
+					continue;
+				if (path == c.Path)
+					return true;
+			}
+
+			return false;
+		}
+
+		internal void SendHeaders ()
+		{
+			//TODO: When do we send KeepAlive?
+			//TODO: send cookies
+			MemoryStream ms = new MemoryStream ();
+			Encoding encoding = content_encoding;
+			if (encoding == null)
+				encoding = Encoding.Default;
+
+			if (content_type != null) {
+				if (content_encoding != null && content_type.IndexOf ("charset=") == -1) {
+					string enc_name = content_encoding.WebName;
+					headers.SetInternal ("Content-Type", content_type + "; charset=" + enc_name);
+				} else {
+					headers.SetInternal ("Content-Type", content_type);
+				}
+			}
+
+			if (headers ["Server"] == null)
+				headers.SetInternal ("Server", "Mono-HTTPAPI/1.0");
+
+			CultureInfo inv = CultureInfo.InvariantCulture;
+			if (headers ["Date"] == null)
+				headers.SetInternal ("Date", DateTime.UtcNow.ToString ("r", inv));
+
+			if (!chunked && cl_set)
+				headers.SetInternal ("Content-Length", content_length.ToString (inv));
+
+			Version v = context.Request.ProtocolVersion;
+			if (!cl_set && !chunked && v >= HttpVersion.Version11)
+				chunked = true;
+				
+			/* Apache forces closing the connection for these status codes:
+			 *	HttpStatusCode.BadRequest 		400
+			 *	HttpStatusCode.RequestTimeout 		408
+			 *	HttpStatusCode.LengthRequired 		411
+			 *	HttpStatusCode.RequestEntityTooLarge 	413
+			 *	HttpStatusCode.RequestUriTooLong 	414
+			 *	HttpStatusCode.InternalServerError 	500
+			 *	HttpStatusCode.ServiceUnavailable 	503
+			 */
+			bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 ||
+					status_code == 413 || status_code == 414 || status_code == 500 ||
+					status_code == 503);
+
+			if (conn_close == false)
+				conn_close = (context.Request.Headers ["connection"] == "close");
+
+			// They sent both KeepAlive: true and Connection: close!?
+			if (!chunked || conn_close)
+				headers.SetInternal ("Connection", "close");
+
+			if (chunked)
+				headers.SetInternal ("Transfer-Encoding", "chunked");
+
+			int chunked_uses = context.Connection.ChunkedUses;
+			if (chunked_uses >= 100) {
+				force_close_chunked = true;
+				if (!conn_close)
+					headers.SetInternal ("Connection", "close");
+			}
+
+			if (location != null)
+				headers.SetInternal ("Location", location);
+
+			StreamWriter writer = new StreamWriter (ms, encoding);
+			writer.Write ("HTTP/{0} {1} {2}\r\n", version, status_code, status_description);
+			string headers_str = headers.ToString ();
+			writer.Write (headers_str);
+			writer.Flush ();
+			// Perf.: use TCP_CORK if we're writing more?
+			int preamble = encoding.GetPreamble ().Length;
+			output_stream.InternalWrite (ms.GetBuffer (), 0 + preamble, (int) ms.Length - preamble);
+			HeadersSent = true;
+		}
+
+		public void SetCookie (Cookie cookie)
+		{
+			if (cookie == null)
+				throw new ArgumentNullException ("cookie");
+
+			if (cookies != null) {
+				if (FindCookie (cookie))
+					throw new ArgumentException ("The cookie already exists.");
+			} else {
+				cookies = new CookieCollection ();
+			}
+
+			cookies.Add (cookie);
+		}
+	}
+}
+#endif
+

+ 64 - 0
mcs/class/System/System.Net/HttpRequestHeader.cs

@@ -0,0 +1,64 @@
+//
+// System.Net.HttpRequestHeader
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+namespace System.Net {
+	public enum HttpRequestHeader {
+		CacheControl,
+		Connection,
+		Date,
+		KeepAlive,
+		Pragma,
+		Trailer,
+		TransferEncoding,
+		Upgrade,
+		Via,
+		Warning,
+		Allow,
+		ContentLength,
+		ContentType,
+		ContentEncoding,
+		ContentLanguage,
+		ContentLocation,
+		ContentMd5,
+		ContentRange,
+		Expires,
+		LastModified,
+		Accept,
+		AcceptCharset,
+		AcceptEncoding,
+		AcceptLanguage,
+		Authorization,
+		Cookie,
+		Expect,
+		From,
+		Host,
+		IfMatch
+	}
+}
+#endif
+

+ 64 - 0
mcs/class/System/System.Net/HttpResponseHeader.cs

@@ -0,0 +1,64 @@
+//
+// System.Net.HttpResponseHeader
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+namespace System.Net {
+	public enum HttpResponseHeader {
+		CacheControl,
+		Connection,
+		Date,
+		KeepAlive,
+		Pragma,
+		Trailer,
+		TransferEncoding,
+		Upgrade,
+		Via,
+		Warning,
+		Allow,
+		ContentLength,
+		ContentType,
+		ContentEncoding,
+		ContentLanguage,
+		ContentLocation,
+		ContentMd5,
+		ContentRange,
+		Expires,
+		LastModified,
+		AcceptRanges,
+		Age,
+		ETag,
+		Location,
+		ProxyAuthenticate,
+		RetryAfter,
+		Server,
+		SetCookie,
+		Vary,
+		WwwAuthenticate
+	}
+}
+#endif
+

+ 2 - 142
mcs/class/System/System.Net/HttpStatusCode.cs

@@ -32,195 +32,55 @@
 
 
 namespace System.Net {
-
-
-	/// <summary>
-	/// </summary>
 	public enum HttpStatusCode {
-
-		/// <summary>
-		/// </summary>
 		Continue = 100,
-
-		/// <summary>
-		/// </summary>
 		SwitchingProtocols = 101,
-
-		/// <summary>
-		/// </summary>
 		OK = 200,
-
-		/// <summary>
-		/// </summary>
 		Created = 201,
-
-		/// <summary>
-		/// </summary>
 		Accepted = 202,
-
-		/// <summary>
-		/// </summary>
 		NonAuthoritativeInformation = 203,
-
-		/// <summary>
-		/// </summary>
 		NoContent = 204,
-
-		/// <summary>
-		/// </summary>
 		ResetContent = 205,
-
-		/// <summary>
-		/// </summary>
 		PartialContent = 206,
-
-		/// <summary>
-		/// </summary>
 		MultipleChoices = 300,
-
-		/// <summary>
-		/// </summary>
 		Ambiguous = 300,
-
-		/// <summary>
-		/// </summary>
 		MovedPermanently = 301,
-
-		/// <summary>
-		/// </summary>
 		Moved = 301,
-
-		/// <summary>
-		/// </summary>
 		Found = 302,
-
-		/// <summary>
-		/// </summary>
 		Redirect = 302,
-
-		/// <summary>
-		/// </summary>
 		SeeOther = 303,
-
-		/// <summary>
-		/// </summary>
 		RedirectMethod = 303,
-
-		/// <summary>
-		/// </summary>
 		NotModified = 304,
-
-		/// <summary>
-		/// </summary>
 		UseProxy = 305,
-
-		/// <summary>
-		/// </summary>
 		Unused = 306,
-
-		/// <summary>
-		/// </summary>
 		TemporaryRedirect = 307,
-
-		/// <summary>
-		/// </summary>
 		RedirectKeepVerb = 307,
-
-		/// <summary>
-		/// </summary>
 		BadRequest = 400,
-
-		/// <summary>
-		/// </summary>
 		Unauthorized = 401,
-
-		/// <summary>
-		/// </summary>
 		PaymentRequired = 402,
-
-		/// <summary>
-		/// </summary>
 		Forbidden = 403,
-
-		/// <summary>
-		/// </summary>
 		NotFound = 404,
-
-		/// <summary>
-		/// </summary>
 		MethodNotAllowed = 405,
-
-		/// <summary>
-		/// </summary>
 		NotAcceptable = 406,
-
-		/// <summary>
-		/// </summary>
 		ProxyAuthenticationRequired = 407,
-
-		/// <summary>
-		/// </summary>
 		RequestTimeout = 408,
-
-		/// <summary>
-		/// </summary>
 		Conflict = 409,
-
-		/// <summary>
-		/// </summary>
 		Gone = 410,
-
-		/// <summary>
-		/// </summary>
 		LengthRequired = 411,
-
-		/// <summary>
-		/// </summary>
 		PreconditionFailed = 412,
-
-		/// <summary>
-		/// </summary>
 		RequestEntityTooLarge = 413,
-
-		/// <summary>
-		/// </summary>
 		RequestUriTooLong = 414,
-
-		/// <summary>
-		/// </summary>
 		UnsupportedMediaType = 415,
-
-		/// <summary>
-		/// </summary>
 		RequestedRangeNotSatisfiable = 416,
-
-		/// <summary>
-		/// </summary>
 		ExpectationFailed = 417,
-
-		/// <summary>
-		/// </summary>
 		InternalServerError = 500,
-
-		/// <summary>
-		/// </summary>
 		NotImplemented = 501,
-
-		/// <summary>
-		/// </summary>
 		BadGateway = 502,
-
-		/// <summary>
-		/// </summary>
 		ServiceUnavailable = 503,
-
-		/// <summary>
-		/// </summary>
 		GatewayTimeout = 504,
-
-		/// <summary>
-		/// </summary>
 		HttpVersionNotSupported = 505,
 	} // HttpStatusCode
 
 } // System.Net
+
+

+ 95 - 0
mcs/class/System/System.Net/HttpStreamAsyncResult.cs

@@ -0,0 +1,95 @@
+//
+// System.Net.HttpStreamAsyncResult
+//
+// Authors:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.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 NET_2_0
+using System.Threading;
+
+namespace System.Net {
+	class HttpStreamAsyncResult : IAsyncResult {
+		object locker = new object ();
+		ManualResetEvent handle;
+		bool completed;
+
+		internal byte [] Buffer;
+		internal int Offset;
+		internal int Count;
+		internal AsyncCallback Callback;
+		internal object State;
+		internal int SynchRead;
+		internal Exception Error;
+
+		public void Complete (Exception e)
+		{
+			Error = e;
+			Complete ();
+		}
+
+		public void Complete ()
+		{
+			lock (locker) {
+				if (completed)
+					return;
+
+				completed = true;
+				if (handle != null)
+					handle.Set ();
+
+				if (Callback != null)
+					Callback.BeginInvoke (this, null, null);
+			}
+		}
+		
+		public object AsyncState {
+			get { return State; }
+		}
+
+		public WaitHandle AsyncWaitHandle {
+			get {
+				lock (locker) {
+					if (handle == null)
+						handle = new ManualResetEvent (completed);
+				}
+				
+				return handle;
+			}
+		}
+
+		public bool CompletedSynchronously {
+			get { return (SynchRead == Count); }
+		}
+
+		public bool IsCompleted {
+			get {
+				lock (locker) {
+					return completed;
+				}
+			}
+		}
+	}
+}
+#endif
+

+ 103 - 0
mcs/class/System/System.Net/HttpUtility.cs

@@ -0,0 +1,103 @@
+//
+// System.Net.HttpUtility
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.Globalization;
+using System.IO;
+using System.Text;
+namespace System.Net {
+	sealed class HttpUtility
+	{
+		private HttpUtility ()
+		{
+		}
+
+		public static string UrlDecode (string s)
+		{
+			return UrlDecode (s, null);
+		}
+
+		static char [] GetChars (MemoryStream b, Encoding e)
+		{
+			return e.GetChars (b.GetBuffer (), 0, (int) b.Length);
+		}
+
+		public static string UrlDecode (string s, Encoding e)
+		{
+			if (null == s) 
+				return null;
+
+			if (s.IndexOf ('%') == -1 && s.IndexOf ('+') == -1)
+				return s;
+
+			if (e == null)
+				e = Encoding.GetEncoding (28591);
+	
+			StringBuilder output = new StringBuilder ();
+			long len = s.Length;
+			NumberStyles hexa = NumberStyles.HexNumber;
+			MemoryStream bytes = new MemoryStream ();
+	
+			for (int i = 0; i < len; i++) {
+				if (s [i] == '%' && i + 2 < len) {
+					if (s [i + 1] == 'u' && i + 5 < len) {
+						if (bytes.Length > 0) {
+							output.Append (GetChars (bytes, e));
+							bytes.SetLength (0);
+						}
+						output.Append ((char) Int32.Parse (s.Substring (i + 2, 4), hexa));
+						i += 5;
+					} else {
+						bytes.WriteByte ((byte) Int32.Parse (s.Substring (i + 1, 2), hexa));
+						i += 2;
+					}
+					continue;
+				}
+
+				if (bytes.Length > 0) {
+					output.Append (GetChars (bytes, e));
+					bytes.SetLength (0);
+				}
+
+				if (s [i] == '+') {
+					output.Append (' ');
+				} else {
+					output.Append (s [i]);
+				}
+	         	}
+	
+			if (bytes.Length > 0) {
+				output.Append (GetChars (bytes, e));
+			}
+
+			bytes = null;
+			return output.ToString ();
+		}
+	}
+}
+#endif
+

+ 125 - 0
mcs/class/System/System.Net/ListenerAsyncResult.cs

@@ -0,0 +1,125 @@
+//
+// System.Net.ListenerAsyncResult
+//
+// Authors:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Ximian, Inc (http://www.ximian.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 NET_2_0
+using System.Threading;
+namespace System.Net {
+	class ListenerAsyncResult : IAsyncResult {
+		ManualResetEvent handle;
+		bool synch;
+		bool completed;
+		AsyncCallback cb;
+		object state;
+		Exception exception;
+		HttpListenerContext context;
+		object locker = new object ();
+
+		public ListenerAsyncResult (AsyncCallback cb, object state)
+		{
+			this.cb = cb;
+			this.state = state;
+		}
+
+		internal void Complete (string error)
+		{
+			//FIXME: error_code?
+			exception = new HttpListenerException (0, error);
+			lock (locker) {
+				completed = true;
+				if (handle != null)
+					handle.Set ();
+
+				if (cb != null)
+					ThreadPool.QueueUserWorkItem (InvokeCallback, this);
+			}
+		}
+
+		static void InvokeCallback (object o)
+		{
+			ListenerAsyncResult ares = (ListenerAsyncResult) o;
+			ares.cb (ares);
+		}
+
+		internal void Complete (HttpListenerContext context)
+		{
+			Complete (context, false);
+		}
+
+		internal void Complete (HttpListenerContext context, bool synch)
+		{
+			this.synch = synch;
+			this.context = context;
+			lock (locker) {
+				completed = true;
+				if (handle != null)
+					handle.Set ();
+
+				if (cb != null)
+					ThreadPool.QueueUserWorkItem (InvokeCallback, this);
+			}
+		}
+
+		internal HttpListenerContext GetContext ()
+		{
+			if (exception != null)
+				throw exception;
+
+			return context;
+		}
+		
+		public object AsyncState {
+			get { return state; }
+		}
+
+		public WaitHandle AsyncWaitHandle {
+			get {
+				lock (locker) {
+					if (handle == null)
+						handle = new ManualResetEvent (completed);
+				}
+				
+				return handle;
+			}
+		}
+
+		public bool CompletedSynchronously {
+			get { return synch; }
+		}
+
+		public bool IsCompleted {
+			get {
+				lock (locker) {
+					return completed;
+				}
+			}
+		}
+	}
+}
+#endif
+

+ 159 - 0
mcs/class/System/System.Net/ListenerPrefix.cs

@@ -0,0 +1,159 @@
+//
+// System.Net.ListenerPrefix
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+namespace System.Net {
+	sealed class ListenerPrefix
+	{
+		string original;
+		string host;
+		ushort port;
+		string path;
+		bool secure;
+		IPAddress [] addresses;
+		public HttpListener Listener;
+
+		public ListenerPrefix (string prefix)
+		{
+			this.original = prefix;
+			Parse (prefix);
+		}
+
+		public override string ToString ()
+		{
+			return original;
+		}
+
+		public IPAddress [] Addresses {
+			get { return addresses; }
+			set { addresses = value; }
+		}
+		public bool Secure {
+			get { return secure; }
+		}
+
+		public string Host {
+			get { return host; }
+		}
+
+		public int Port {
+			get { return (int) port; }
+		}
+
+		public string Path {
+			get { return path; }
+		}
+
+		// Equals and GetHashCode are required to detect duplicates in HttpListenerPrefixCollection.
+		public override bool Equals (object o)
+		{
+			ListenerPrefix other = o as ListenerPrefix;
+			if (other == null)
+				return false;
+
+			return (original == other.original);
+		}
+
+		public override int GetHashCode ()
+		{
+			return original.GetHashCode ();
+		}
+
+		void Parse (string uri)
+		{
+			int default_port = (uri.StartsWith ("http://")) ? 80 : -1;
+			if (default_port == -1) {
+				default_port = (uri.StartsWith ("https://")) ? 443 : -1;
+				secure = true;
+				throw new NotSupportedException ("We don't support https yet.");
+			}
+
+			int length = uri.Length;
+			int start_host = uri.IndexOf (':') + 3;
+			if (start_host >= length)
+				throw new ArgumentException ("No host specified.");
+
+			int colon = uri.IndexOf (':', start_host, length - start_host);
+			int root;
+			if (colon > 0) {
+				host = uri.Substring (start_host, colon - start_host);
+				root = uri.IndexOf ('/', colon, length - colon);
+				port = (ushort) Int32.Parse (uri.Substring (colon + 1, root - colon - 1));
+				path = uri.Substring (root);
+			} else {
+				root = uri.IndexOf ('/', start_host, length - start_host);
+				host = uri.Substring (start_host, root - start_host);
+				path = uri.Substring (root);
+			}
+		}
+
+		public static void CheckUri (string uri)
+		{
+			if (uri == null)
+				throw new ArgumentNullException ("uriPrefix");
+
+			int default_port = (uri.StartsWith ("http://")) ? 80 : -1;
+			if (default_port == -1)
+				default_port = (uri.StartsWith ("https://")) ? 443 : -1;
+			if (default_port == -1)
+				throw new ArgumentException ("Only 'http' and 'https' schemes are supported.");
+
+			int length = uri.Length;
+			int start_host = uri.IndexOf (':') + 3;
+			if (start_host >= length)
+				throw new ArgumentException ("No host specified.");
+
+			int colon = uri.IndexOf (':', start_host, length - start_host);
+			if (start_host == colon)
+				throw new ArgumentException ("No host specified.");
+
+			int root;
+			if (colon > 0) {
+				root = uri.IndexOf ('/', colon, length - colon);
+				if (root == -1)
+					throw new ArgumentException ("No path specified.");
+
+				try {
+					int p = Int32.Parse (uri.Substring (colon + 1, root - colon - 1));
+					if (p <= 0 || p >= 65536)
+						throw new Exception ();
+				} catch {
+					throw new ArgumentException ("Invalid port.");
+				}
+			} else {
+				root = uri.IndexOf ('/', start_host, length - start_host);
+				if (root == -1)
+					throw new ArgumentException ("No path specified.");
+			}
+
+			if (uri [uri.Length - 1] != '/')
+				throw new ArgumentException ("The prefix must end with '/'");
+		}
+	}
+}
+#endif
+

+ 185 - 0
mcs/class/System/System.Net/RequestStream.cs

@@ -0,0 +1,185 @@
+//
+// System.Net.RequestStream
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.IO;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+namespace System.Net {
+	class RequestStream : NetworkStream
+	{
+		byte [] buffer;
+		int offset;
+		int length;
+		bool disposed;
+
+		internal RequestStream (Socket sock, byte [] buffer, int offset, int length) :
+					base (sock, false)
+		{
+			this.buffer = buffer;
+			this.offset = offset;
+			this.length = length;
+		}
+
+		public override bool CanRead {
+			get { return true; }
+		}
+
+		public override bool CanSeek {
+			get { return false; }
+		}
+
+		public override bool CanWrite {
+			get { return false; }
+		}
+
+		public override long Length {
+			get { throw new NotSupportedException (); }
+		}
+
+		public override long Position {
+			get { throw new NotSupportedException (); }
+			set { throw new NotSupportedException (); }
+		}
+
+
+		public override void Close ()
+		{
+			// TODO: What do we close?
+		}
+
+		public override void Flush ()
+		{
+		}
+
+		// true if the we have >= count bytes in this.buffer
+		int FillFromBuffer (byte [] buffer, int off, int count)
+		{
+			int size = this.length - this.offset;
+			if (size <= 0)
+				return 0;
+
+			if (buffer == null)
+				throw new ArgumentNullException ("buffer");
+			if (off < 0)
+				throw new ArgumentOutOfRangeException ("offset", "< 0");
+			if (count < 0)
+				throw new ArgumentOutOfRangeException ("count", "< 0");
+			int len = buffer.Length;
+			if (off > len)
+				throw new ArgumentException ("destination offset is beyond array size");
+			if (off > len - count)
+				throw new ArgumentException ("Reading would overrun buffer");
+
+			size = Math.Min (size, count);
+			Buffer.BlockCopy (this.buffer, this.offset, buffer, off, size);
+			this.offset += size;
+			return size;
+		}
+
+		public override int Read ([In,Out] byte[] buffer, int offset, int count)
+		{
+			if (disposed)
+				throw new ObjectDisposedException (typeof (RequestStream).ToString ());
+
+			int nread = FillFromBuffer (buffer, offset, count);
+			if (nread > 0)
+				return nread;
+
+			return base.Read (buffer, offset, count);
+		}
+
+		public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
+							AsyncCallback cback, object state)
+		{
+			if (disposed)
+				throw new ObjectDisposedException (typeof (RequestStream).ToString ());
+
+			int nread = FillFromBuffer (buffer, offset, count);
+			if (nread > 0) {
+				HttpStreamAsyncResult ares = new HttpStreamAsyncResult ();
+				ares.Buffer = buffer;
+				ares.Offset = offset;
+				ares.Count = count;
+				ares.Callback = cback;
+				ares.State = state;
+				ares.SynchRead = nread;
+				ares.Complete ();
+				return ares;
+			}
+
+			return base.BeginRead (buffer, offset, count, cback, state);
+		}
+
+		public override int EndRead (IAsyncResult ares)
+		{
+			if (disposed)
+				throw new ObjectDisposedException (typeof (RequestStream).ToString ());
+
+			if (ares == null)
+				throw new ArgumentNullException ("async_result");
+
+			if (ares is HttpStreamAsyncResult) {
+				HttpStreamAsyncResult r = (HttpStreamAsyncResult) ares;
+				if (!ares.IsCompleted)
+					ares.AsyncWaitHandle.WaitOne ();
+				return r.SynchRead;
+			}
+
+			// Close on exception?
+			return base.EndRead (ares);
+		}
+
+		public override long Seek (long offset, SeekOrigin origin)
+		{
+			throw new NotSupportedException ();
+		}
+
+		public override void SetLength (long value)
+		{
+			throw new NotSupportedException ();
+		}
+
+		public override void Write (byte[] buffer, int offset, int count)
+		{
+			throw new NotSupportedException ();
+		}
+
+		public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
+							AsyncCallback cback, object state)
+		{
+			throw new NotSupportedException ();
+		}
+
+		public override void EndWrite (IAsyncResult async_result)
+		{
+			throw new NotSupportedException ();
+		}
+	}
+}
+#endif
+

+ 190 - 0
mcs/class/System/System.Net/ResponseStream.cs

@@ -0,0 +1,190 @@
+//
+// System.Net.ResponseStream
+//
+// Author:
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.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 NET_2_0
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+using System.Runtime.InteropServices;
+namespace System.Net {
+	// FIXME: Does this buffer the response until Close?
+	// What happens when we set content-length to X and write X-1 bytes then close?
+	// what if we don't set content-length at all?
+	class ResponseStream : NetworkStream
+	{
+		HttpListenerResponse response;
+		bool ignore_errors;
+		bool disposed;
+		bool trailer_sent;
+
+		internal ResponseStream (Socket sock, HttpListenerResponse response, bool ignore_errors) :
+					base (sock, false)
+		{
+			this.response = response;
+			this.ignore_errors = ignore_errors;
+		}
+
+		public override bool CanRead {
+			get { return false; }
+		}
+
+		public override bool CanSeek {
+			get { return false; }
+		}
+
+		public override bool CanWrite {
+			get { return true; }
+		}
+
+		public override long Length {
+			get { throw new NotSupportedException (); }
+		}
+
+		public override long Position {
+			get { throw new NotSupportedException (); }
+			set { throw new NotSupportedException (); }
+		}
+
+
+		public override void Close ()
+		{
+			if (disposed == false) {
+				disposed = true;
+				if (response.SendChunked && !trailer_sent) {
+					WriteChunkSize (0, true);
+					trailer_sent = true;
+				}
+				response.Close ();
+			}
+		}
+
+		public override void Flush ()
+		{
+		}
+
+		static byte [] crlf = new byte [] { 13, 10 };
+		void WriteChunkSize (int size, bool final)
+		{
+			string str = String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : "");
+			byte [] b = Encoding.ASCII.GetBytes (str);
+			base.Write (b, 0, b.Length);
+		}
+
+		internal void InternalWrite (byte [] buffer, int offset, int count)
+		{
+			if (ignore_errors) {
+				try {
+					base.Write (buffer, offset, count);
+				} catch { }
+			} else {
+				base.Write (buffer, offset, count);
+			}
+		}
+
+		public override void Write (byte [] buffer, int offset, int count)
+		{
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().ToString ());
+
+			if (response.HeadersSent == false)
+				response.SendHeaders ();
+
+			bool chunked = response.SendChunked;
+			try {
+				if (chunked)
+					WriteChunkSize (count, false);
+			} catch { }
+			InternalWrite (buffer, offset, count);
+			try {
+				if (chunked)
+					base.Write (crlf, 0, 2);
+			} catch { }
+		}
+
+		public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
+							AsyncCallback cback, object state)
+		{
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().ToString ());
+
+			if (response.HeadersSent == false)
+				response.SendHeaders ();
+
+			try {
+				if (response.SendChunked)
+					WriteChunkSize (count, false);
+			} catch { }
+			return base.BeginWrite (buffer, offset, count, cback, state);
+		}
+
+		public override void EndWrite (IAsyncResult ares)
+		{
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().ToString ());
+
+			if (ignore_errors) {
+				try {
+					base.EndWrite (ares);
+					if (response.SendChunked)
+						base.Write (crlf, 0, 2);
+				} catch { }
+			} else {
+				base.EndWrite (ares);
+				if (response.SendChunked)
+					base.Write (crlf, 0, 2);
+			}
+		}
+
+		public override int Read ([In,Out] byte[] buffer, int offset, int count)
+		{
+			throw new NotSupportedException ();
+		}
+
+		public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
+							AsyncCallback cback, object state)
+		{
+			throw new NotSupportedException ();
+		}
+
+		public override int EndRead (IAsyncResult ares)
+		{
+			throw new NotSupportedException ();
+		}
+
+		public override long Seek (long offset, SeekOrigin origin)
+		{
+			throw new NotSupportedException ();
+		}
+
+		public override void SetLength (long value)
+		{
+			throw new NotSupportedException ();
+		}
+	}
+}
+#endif
+

+ 300 - 299
mcs/class/System/System.Net/WebHeaderCollection.cs

@@ -1,12 +1,12 @@
-//
-// System.Net.WebHeaderCollection
-//
-// Authors:
-// 	Lawrence Pit ([email protected])
-//	Gonzalo Paniagua Javier ([email protected])
-//
-// (c) 2003 Ximian, Inc. (http://www.ximian.com)
-//
+//
+// System.Net.WebHeaderCollection
+//
+// Authors:
+// 	Lawrence Pit ([email protected])
+//	Gonzalo Paniagua Javier ([email protected])
+//
+// (c) 2003 Ximian, Inc. (http://www.ximian.com)
+//
 
 //
 // Permission is hereby granted, free of charge, to any person obtaining
@@ -28,134 +28,134 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-
-using System;
-using System.Collections;
-using System.Collections.Specialized;
-using System.Runtime.InteropServices;
-using System.Runtime.Serialization;
-using System.Text;
-    
-// See RFC 2068 par 4.2 Message Headers
-    
-namespace System.Net 
-{
-	[Serializable]
-	[ComVisible(true)]
-	public class WebHeaderCollection : NameValueCollection, ISerializable
-	{
-		private static readonly Hashtable restricted;
-		private static readonly Hashtable multiValue;
-		private bool internallyCreated = false;
-		
-		// Static Initializer
-		
-		static WebHeaderCollection () 
-		{
-			// the list of restricted header names as defined 
-			// by the ms.net spec
-			restricted = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
-						    CaseInsensitiveComparer.Default);
-
-			restricted.Add ("accept", true);
-			restricted.Add ("connection", true);
-			restricted.Add ("content-length", true);
-			restricted.Add ("content-type", true);
-			restricted.Add ("date", true);
-			restricted.Add ("expect", true);
-			restricted.Add ("host", true);
-			restricted.Add ("if-modified-since", true);
-			restricted.Add ("range", true);
-			restricted.Add ("referer", true);
-			restricted.Add ("transfer-encoding", true);
-			restricted.Add ("user-agent", true);			
-			
-			// see par 14 of RFC 2068 to see which header names
-			// accept multiple values each separated by a comma
-			multiValue = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
-						    CaseInsensitiveComparer.Default);
-
-			multiValue.Add ("accept", true);
-			multiValue.Add ("accept-charset", true);
-			multiValue.Add ("accept-encoding", true);
-			multiValue.Add ("accept-language", true);
-			multiValue.Add ("accept-ranges", true);
-			multiValue.Add ("allow", true);
-			multiValue.Add ("authorization", true);
-			multiValue.Add ("cache-control", true);
-			multiValue.Add ("connection", true);
-			multiValue.Add ("content-encoding", true);
-			multiValue.Add ("content-language", true);			
-			multiValue.Add ("expect", true);		
-			multiValue.Add ("if-match", true);
-			multiValue.Add ("if-none-match", true);
-			multiValue.Add ("proxy-authenticate", true);
-			multiValue.Add ("public", true);			
-			multiValue.Add ("range", true);
-			multiValue.Add ("transfer-encoding", true);
-			multiValue.Add ("upgrade", true);
-			multiValue.Add ("vary", true);
-			multiValue.Add ("via", true);
-			multiValue.Add ("warning", true);
+
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Text;
+    
+// See RFC 2068 par 4.2 Message Headers
+    
+namespace System.Net 
+{
+	[Serializable]
+	[ComVisible(true)]
+	public class WebHeaderCollection : NameValueCollection, ISerializable
+	{
+		private static readonly Hashtable restricted;
+		private static readonly Hashtable multiValue;
+		private bool internallyCreated = false;
+		
+		// Static Initializer
+		
+		static WebHeaderCollection () 
+		{
+			// the list of restricted header names as defined 
+			// by the ms.net spec
+			restricted = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
+						    CaseInsensitiveComparer.Default);
+
+			restricted.Add ("accept", true);
+			restricted.Add ("connection", true);
+			restricted.Add ("content-length", true);
+			restricted.Add ("content-type", true);
+			restricted.Add ("date", true);
+			restricted.Add ("expect", true);
+			restricted.Add ("host", true);
+			restricted.Add ("if-modified-since", true);
+			restricted.Add ("range", true);
+			restricted.Add ("referer", true);
+			restricted.Add ("transfer-encoding", true);
+			restricted.Add ("user-agent", true);			
+			
+			// see par 14 of RFC 2068 to see which header names
+			// accept multiple values each separated by a comma
+			multiValue = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
+						    CaseInsensitiveComparer.Default);
+
+			multiValue.Add ("accept", true);
+			multiValue.Add ("accept-charset", true);
+			multiValue.Add ("accept-encoding", true);
+			multiValue.Add ("accept-language", true);
+			multiValue.Add ("accept-ranges", true);
+			multiValue.Add ("allow", true);
+			multiValue.Add ("authorization", true);
+			multiValue.Add ("cache-control", true);
+			multiValue.Add ("connection", true);
+			multiValue.Add ("content-encoding", true);
+			multiValue.Add ("content-language", true);			
+			multiValue.Add ("expect", true);		
+			multiValue.Add ("if-match", true);
+			multiValue.Add ("if-none-match", true);
+			multiValue.Add ("proxy-authenticate", true);
+			multiValue.Add ("public", true);			
+			multiValue.Add ("range", true);
+			multiValue.Add ("transfer-encoding", true);
+			multiValue.Add ("upgrade", true);
+			multiValue.Add ("vary", true);
+			multiValue.Add ("via", true);
+			multiValue.Add ("warning", true);
 
 			// Extra
-			multiValue.Add ("set-cookie", true);
-			multiValue.Add ("set-cookie2", true);
-		}
-		
-		// Constructors
-		
-		public WebHeaderCollection () {	}	
-		
-		protected WebHeaderCollection (SerializationInfo serializationInfo, 
-					       StreamingContext streamingContext)
-		{
-			// TODO: test for compatibility with ms.net
-			int count = serializationInfo.GetInt32("count");
-			for (int i = 0; i < count; i++) 
-				this.Add (serializationInfo.GetString ("k" + i),
-					  serializationInfo.GetString ("v" + i));
-		}
-		
-		internal WebHeaderCollection (bool internallyCreated)
-		{	
-			this.internallyCreated = internallyCreated;
-		}		
-		
-		// Methods
-		
-		public void Add (string header)
-		{
-			if (header == null)
-				throw new ArgumentNullException ("header");
-			int pos = header.IndexOf (':');
-			if (pos == -1)
-				throw new ArgumentException ("no colon found", "header");				
-			this.Add (header.Substring (0, pos), 
-				  header.Substring (pos + 1));
-		}
-		
-		public override void Add (string name, string value)
-		{
-			if (name == null)
-				throw new ArgumentNullException ("name");
-			if (internallyCreated && IsRestricted (name))
+			multiValue.Add ("set-cookie", true);
+			multiValue.Add ("set-cookie2", true);
+		}
+		
+		// Constructors
+		
+		public WebHeaderCollection () {	}	
+		
+		protected WebHeaderCollection (SerializationInfo serializationInfo, 
+					       StreamingContext streamingContext)
+		{
+			// TODO: test for compatibility with ms.net
+			int count = serializationInfo.GetInt32("count");
+			for (int i = 0; i < count; i++) 
+				this.Add (serializationInfo.GetString ("k" + i),
+					  serializationInfo.GetString ("v" + i));
+		}
+		
+		internal WebHeaderCollection (bool internallyCreated)
+		{	
+			this.internallyCreated = internallyCreated;
+		}		
+		
+		// Methods
+		
+		public void Add (string header)
+		{
+			if (header == null)
+				throw new ArgumentNullException ("header");
+			int pos = header.IndexOf (':');
+			if (pos == -1)
+				throw new ArgumentException ("no colon found", "header");				
+			this.Add (header.Substring (0, pos), 
+				  header.Substring (pos + 1));
+		}
+		
+		public override void Add (string name, string value)
+		{
+			if (name == null)
+				throw new ArgumentNullException ("name");
+			if (internallyCreated && IsRestricted (name))
 				throw new ArgumentException ("This header must be modified with the appropiate property.");
-			this.AddWithoutValidate (name, value);
-		}
-
-		protected void AddWithoutValidate (string headerName, string headerValue)
-		{
-			if (!IsHeaderName (headerName))
-				throw new ArgumentException ("invalid header name: " + headerName, "headerName");
-			if (headerValue == null)
-				headerValue = String.Empty;
-			else
-				headerValue = headerValue.Trim ();
-			if (!IsHeaderValue (headerValue))
-				throw new ArgumentException ("invalid header value: " + headerValue, "headerValue");
-			base.Add (headerName, headerValue);			
-		}
+			this.AddWithoutValidate (name, value);
+		}
+
+		protected void AddWithoutValidate (string headerName, string headerValue)
+		{
+			if (!IsHeaderName (headerName))
+				throw new ArgumentException ("invalid header name: " + headerName, "headerName");
+			if (headerValue == null)
+				headerValue = String.Empty;
+			else
+				headerValue = headerValue.Trim ();
+			if (!IsHeaderValue (headerValue))
+				throw new ArgumentException ("invalid header value: " + headerValue, "headerValue");
+			base.Add (headerName, headerValue);			
+		}
 
 		public override string [] GetValues (string header)
 		{
@@ -213,105 +213,105 @@ namespace System.Net
 		}
 		*/
 
-		public static bool IsRestricted (string headerName)
-		{
-			if (headerName == null)
-				throw new ArgumentNullException ("headerName");
-
-			if (headerName == "") // MS throw nullexception here!
-				throw new ArgumentException ("empty string", "headerName");
-
-			return restricted.ContainsKey (headerName);
-		}
-
-		public override void OnDeserialization (object sender)
-		{
-		}
-
-		public override void Remove (string name)
-		{
-			if (name == null)
-				throw new ArgumentNullException ("name");
-			if (internallyCreated && IsRestricted (name))
-				throw new ArgumentException ("restricted header");
-			base.Remove (name);
-		}
-
-		public override void Set (string name, string value)
-		{
-			if (name == null)
-				throw new ArgumentNullException ("name");
-			if (internallyCreated && IsRestricted (name))
-				throw new ArgumentException ("restricted header");
-			if (!IsHeaderName (name))
-				throw new ArgumentException ("invalid header name");
-			if (value == null)
-				value = String.Empty;
-			else
-				value = value.Trim ();
-			if (!IsHeaderValue (value))
-				throw new ArgumentException ("invalid header value");
-			base.Set (name, value);			
-		}
-
-		public byte[] ToByteArray ()
-		{
-			return Encoding.UTF8.GetBytes(ToString ());
-		}
-
-		public override string ToString ()
-		{
-			StringBuilder sb = new StringBuilder();
-
-			int count = base.Count;
-			for (int i = 0; i < count ; i++)
-				sb.Append (GetKey (i))
-				  .Append (": ")
-				  .Append (Get (i))
-				  .Append ("\r\n");
-				  
-			return sb.Append("\r\n").ToString();
-		}
-		
-		void ISerializable.GetObjectData (SerializationInfo serializationInfo,
-		   				  StreamingContext streamingContext)
-		{
-			int count = base.Count;
-			serializationInfo.AddValue ("count", count);
-			for (int i = 0; i < count ; i++) {
-				serializationInfo.AddValue ("k" + i, GetKey (i));
-				serializationInfo.AddValue ("v" + i, Get (i));
-			}
-		}
-		
-		// Internal Methods
-		
-		// With this we don't check for invalid characters in header. See bug #55994.
-		internal void SetInternal (string header)
-		{
-			int pos = header.IndexOf (':');
-			if (pos == -1)
-				throw new ArgumentException ("no colon found", "header");				
-
-			SetInternal (header.Substring (0, pos), header.Substring (pos + 1));
-		}
-
-		internal void SetInternal (string name, string value)
-		{
-			if (value == null)
-				value = String.Empty;
-			else
-				value = value.Trim ();
-			if (!IsHeaderValue (value))
-				throw new ArgumentException ("invalid header value");
-
+		public static bool IsRestricted (string headerName)
+		{
+			if (headerName == null)
+				throw new ArgumentNullException ("headerName");
+
+			if (headerName == "") // MS throw nullexception here!
+				throw new ArgumentException ("empty string", "headerName");
+
+			return restricted.ContainsKey (headerName);
+		}
+
+		public override void OnDeserialization (object sender)
+		{
+		}
+
+		public override void Remove (string name)
+		{
+			if (name == null)
+				throw new ArgumentNullException ("name");
+			if (internallyCreated && IsRestricted (name))
+				throw new ArgumentException ("restricted header");
+			base.Remove (name);
+		}
+
+		public override void Set (string name, string value)
+		{
+			if (name == null)
+				throw new ArgumentNullException ("name");
+			if (internallyCreated && IsRestricted (name))
+				throw new ArgumentException ("restricted header");
+			if (!IsHeaderName (name))
+				throw new ArgumentException ("invalid header name");
+			if (value == null)
+				value = String.Empty;
+			else
+				value = value.Trim ();
+			if (!IsHeaderValue (value))
+				throw new ArgumentException ("invalid header value");
+			base.Set (name, value);			
+		}
+
+		public byte[] ToByteArray ()
+		{
+			return Encoding.UTF8.GetBytes(ToString ());
+		}
+
+		public override string ToString ()
+		{
+			StringBuilder sb = new StringBuilder();
+
+			int count = base.Count;
+			for (int i = 0; i < count ; i++)
+				sb.Append (GetKey (i))
+				  .Append (": ")
+				  .Append (Get (i))
+				  .Append ("\r\n");
+				  
+			return sb.Append("\r\n").ToString();
+		}
+		
+		void ISerializable.GetObjectData (SerializationInfo serializationInfo,
+		   				  StreamingContext streamingContext)
+		{
+			int count = base.Count;
+			serializationInfo.AddValue ("count", count);
+			for (int i = 0; i < count ; i++) {
+				serializationInfo.AddValue ("k" + i, GetKey (i));
+				serializationInfo.AddValue ("v" + i, Get (i));
+			}
+		}
+		
+		// Internal Methods
+		
+		// With this we don't check for invalid characters in header. See bug #55994.
+		internal void SetInternal (string header)
+		{
+			int pos = header.IndexOf (':');
+			if (pos == -1)
+				throw new ArgumentException ("no colon found", "header");				
+
+			SetInternal (header.Substring (0, pos), header.Substring (pos + 1));
+		}
+
+		internal void SetInternal (string name, string value)
+		{
+			if (value == null)
+				value = String.Empty;
+			else
+				value = value.Trim ();
+			if (!IsHeaderValue (value))
+				throw new ArgumentException ("invalid header value");
+
 			if (IsMultiValue (name)) {
 				base.Add (name, value);
 			} else {
-				base.Remove (name);
-				base.Set (name, value);	
+				base.Remove (name);
+				base.Set (name, value);	
 			}
-		}
+		}
 
 		internal void RemoveAndAdd (string name, string value)
 		{
@@ -324,73 +324,74 @@ namespace System.Net
 			base.Set (name, value);
 		}
 
-		internal void RemoveInternal (string name)
-		{
-			if (name == null)
-				throw new ArgumentNullException ("name");
-			base.Remove (name);
-		}		
-		
-		// Private Methods
-		
-		internal static bool IsMultiValue (string headerName)
-		{
-			if (headerName == null || headerName == "")
-				return false;
-
-			return multiValue.ContainsKey (headerName);
-		}		
-		
-		internal static bool IsHeaderValue (string value)
-		{
-			// TEXT any 8 bit value except CTL's (0-31 and 127)
-			//      but including \r\n space and \t
-			//      after a newline at least one space or \t must follow
-			//      certain header fields allow comments ()
-				
-			int len = value.Length;
-			for (int i = 0; i < len; i++) {			
-				char c = value [i];
-				if (c == 127)
-					return false;
-				if (c < 0x20 && (c != '\r' && c != '\n' && c != '\t'))
-					return false;
-				if (c == '\n' && ++i < len) {
-					c = value [i];
-					if (c != ' ' && c != '\t')
-						return false;
-				}
-			}
-			
-			return true;
-		}
-		
-		internal static bool IsHeaderName (string name)
-		{
-			// token          = 1*<any CHAR except CTLs or tspecials>
-			// tspecials      = "(" | ")" | "<" | ">" | "@"
-			//                | "," | ";" | ":" | "\" | <">
-			//                | "/" | "[" | "]" | "?" | "="
-			//                | "{" | "}" | SP | HT
-			
-			if (name == null || name.Length == 0)
-				return false;
-
-			int len = name.Length;
-			for (int i = 0; i < len; i++) {			
-				char c = name [i];
-				if (c < 0x20 || c >= 0x7f)
-					return false;
-			}
-			
-			return name.IndexOfAny (tspecials) == -1;
-		}
-
-		private static char [] tspecials = 
-				new char [] {'(', ')', '<', '>', '@',
-					     ',', ';', ':', '\\', '"',
-					     '/', '[', ']', '?', '=',
-					     '{', '}', ' ', '\t'};
-							
-	}
-}
+		internal void RemoveInternal (string name)
+		{
+			if (name == null)
+				throw new ArgumentNullException ("name");
+			base.Remove (name);
+		}		
+		
+		// Private Methods
+		
+		internal static bool IsMultiValue (string headerName)
+		{
+			if (headerName == null || headerName == "")
+				return false;
+
+			return multiValue.ContainsKey (headerName);
+		}		
+		
+		internal static bool IsHeaderValue (string value)
+		{
+			// TEXT any 8 bit value except CTL's (0-31 and 127)
+			//      but including \r\n space and \t
+			//      after a newline at least one space or \t must follow
+			//      certain header fields allow comments ()
+				
+			int len = value.Length;
+			for (int i = 0; i < len; i++) {			
+				char c = value [i];
+				if (c == 127)
+					return false;
+				if (c < 0x20 && (c != '\r' && c != '\n' && c != '\t'))
+					return false;
+				if (c == '\n' && ++i < len) {
+					c = value [i];
+					if (c != ' ' && c != '\t')
+						return false;
+				}
+			}
+			
+			return true;
+		}
+		
+		internal static bool IsHeaderName (string name)
+		{
+			// token          = 1*<any CHAR except CTLs or tspecials>
+			// tspecials      = "(" | ")" | "<" | ">" | "@"
+			//                | "," | ";" | ":" | "\" | <">
+			//                | "/" | "[" | "]" | "?" | "="
+			//                | "{" | "}" | SP | HT
+			
+			if (name == null || name.Length == 0)
+				return false;
+
+			int len = name.Length;
+			for (int i = 0; i < len; i++) {			
+				char c = name [i];
+				if (c < 0x20 || c >= 0x7f)
+					return false;
+			}
+			
+			return name.IndexOfAny (tspecials) == -1;
+		}
+
+		private static char [] tspecials = 
+				new char [] {'(', ')', '<', '>', '@',
+					     ',', ';', ':', '\\', '"',
+					     '/', '[', ']', '?', '=',
+					     '{', '}', ' ', '\t'};
+							
+	}
+}
+