瀏覽代碼

replace external httpserver by embedded one (based on same code) - This may still be very bad; clean solution and runprebuild, or clone to clan folder

UbitUmarov 4 年之前
父節點
當前提交
67cd5efab3
共有 54 個文件被更改,包括 5666 次插入1627 次删除
  1. 3 1
      OpenSim/Framework/Serialization/ArchiveConstants.cs
  2. 39 49
      OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
  3. 4 5
      OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpResponse.cs
  4. 0 183
      OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs
  5. 0 145
      OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs
  6. 1 1
      OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs
  7. 0 298
      OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs
  8. 0 68
      OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs
  9. 1 14
      OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs
  10. 0 210
      OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs
  11. 33 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/BadRequestException.cs
  12. 55 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/BodyEventArgs.cs
  13. 50 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/ClientAcceptedEventArgs.cs
  14. 413 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/ContextTimeoutManager.cs
  15. 29 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/ExceptionEventArgs.cs
  16. 16 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/ExceptionHandler.cs
  17. 25 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/ForbiddenException.cs
  18. 38 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HeaderEventArgs.cs
  19. 786 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpClientContext.cs
  20. 195 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpContextFactory.cs
  21. 43 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpException.cs
  22. 118 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpHelper.cs
  23. 263 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpInput.cs
  24. 309 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpInputItem.cs
  25. 248 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpListener.cs
  26. 114 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpParam.cs
  27. 435 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpRequest.cs
  28. 417 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpRequestParser.cs
  29. 670 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpResponse.cs
  30. 146 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpClientContext.cs
  31. 165 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpRequest.cs
  32. 94 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpRequestParser.cs
  33. 180 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpResponse.cs
  34. 81 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/ILogWriter.cs
  35. 38 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/InternalServerException.cs
  36. 29 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/NotFoundException.cs
  37. 70 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestCookie.cs
  38. 164 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestCookies.cs
  39. 48 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestLineEventArgs.cs
  40. 33 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestParserFactory.cs
  41. 123 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/ResponseCookie.cs
  42. 108 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/ResponseCookies.cs
  43. 56 0
      OpenSim/Framework/Servers/HttpServer/OSHttpServer/UnauthorizedException.cs
  44. 0 180
      OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs
  45. 14 10
      OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs
  46. 1 1
      OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
  47. 0 439
      OpenSim/Framework/Servers/Tests/OSHttpTests.cs
  48. 2 1
      OpenSim/Region/Framework/Scenes/UuidGatherer.cs
  49. 1 1
      OpenSim/Tests/Common/Mock/TestHttpClientContext.cs
  50. 1 2
      OpenSim/Tests/Common/Mock/TestHttpRequest.cs
  51. 1 1
      OpenSim/Tests/Common/Mock/TestHttpResponse.cs
  52. 5 5
      OpenSim/Tests/Common/Mock/TestOSHttpResponse.cs
  53. 二進制
      bin/HttpServer_OpenSim.dll
  54. 1 13
      prebuild.xml

+ 3 - 1
OpenSim/Framework/Serialization/ArchiveConstants.cs

@@ -125,7 +125,8 @@ namespace OpenSim.Framework.Serialization
             ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV]            = ASSET_EXTENSION_SEPARATOR + "sound.wav";
             ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Texture]             = ASSET_EXTENSION_SEPARATOR + "texture.jp2";
             ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TextureTGA]          = ASSET_EXTENSION_SEPARATOR + "texture.tga";
-            ASSET_TYPE_TO_EXTENSION[(sbyte)OpenSimAssetType.Material]     = ASSET_EXTENSION_SEPARATOR + "material.xml";   // Not sure if we'll ever see this
+            ASSET_TYPE_TO_EXTENSION[(sbyte)OpenSimAssetType.Material]     = ASSET_EXTENSION_SEPARATOR + "material.xml";
+            ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Settings]            = ASSET_EXTENSION_SEPARATOR + "settings.bin";
 
             EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "animation.bvh"]            = (sbyte)AssetType.Animation;
             EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bodypart.txt"]             = (sbyte)AssetType.Bodypart;
@@ -147,6 +148,7 @@ namespace OpenSim.Framework.Serialization
             EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.jp2"]              = (sbyte)AssetType.Texture;
             EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.tga"]              = (sbyte)AssetType.TextureTGA;
             EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "material.xml"]             = (sbyte)OpenSimAssetType.Material;
+            EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "settings.bin"]             = (sbyte)AssetType.Settings;
         }
 
         public static string CreateOarLandDataPath(LandData ld)

+ 39 - 49
OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs

@@ -28,27 +28,24 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
-using System.Collections.Specialized;
+using System.Globalization;
 using System.IO;
+using System.IO.Compression;
 using System.Net;
-using System.Net.Sockets;
 using System.Net.Security;
-using System.Security.Cryptography.X509Certificates;
+using System.Net.Sockets;
 using System.Reflection;
-using System.Globalization;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
 using System.Text;
-using System.Threading;
 using System.Xml;
-using HttpServer;
+using OSHttpServer;
+using tinyHTTPListener = OSHttpServer.OSHttpListener;
 using log4net;
 using Nwc.XmlRpc;
-using OpenMetaverse.StructuredData;
-using CoolHTTPListener = HttpServer.HttpListener;
-using LogPrio = HttpServer.LogPrio;
 using OpenSim.Framework.Monitoring;
-using System.IO.Compression;
-using System.Security.Cryptography;
-using OpenSim.Framework.Servers;
+using OpenMetaverse.StructuredData;
+
 
 namespace OpenSim.Framework.Servers.HttpServer
 {
@@ -94,8 +91,7 @@ namespace OpenSim.Framework.Servers.HttpServer
         private volatile int NotSocketErrors = 0;
         public volatile bool HTTPDRunning = false;
 
-        // protected HttpListener m_httpListener;
-        protected CoolHTTPListener m_httpListener2;
+        protected tinyHTTPListener m_httpListener;
         protected Dictionary<string, XmlRpcMethod> m_rpcHandlers        = new Dictionary<string, XmlRpcMethod>();
         protected Dictionary<string, JsonRPCMethod> jsonRpcHandlers     = new Dictionary<string, JsonRPCMethod>();
         protected Dictionary<string, bool> m_rpcHandlersKeepAlive       = new Dictionary<string, bool>();
@@ -121,7 +117,6 @@ namespace OpenSim.Framework.Servers.HttpServer
 
         protected IPAddress m_listenIPAddress = IPAddress.Any;
 
-
         public string Protocol
         {
             get { return m_ssl ? "https://" : "http://"; }
@@ -1243,7 +1238,7 @@ namespace OpenSim.Framework.Servers.HttpServer
                             rcn = "SSLCN:" + rcn;
                             xmlRprcRequest.Params.Add(rcn); // Param[4] or Param[5]
                         }
-                
+
                         try
                         {
                             xmlRpcResponse = method(xmlRprcRequest, request.RemoteIPEndPoint);
@@ -1955,7 +1950,7 @@ namespace OpenSim.Framework.Servers.HttpServer
 
             if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently)
             {
-                response.RedirectLocation = (string)responsedata["str_redirect_location"];
+                response.AddHeader("Location:", (string)responsedata["str_redirect_location"]);
                 response.StatusCode = responsecode;
             }
 
@@ -2051,32 +2046,32 @@ namespace OpenSim.Framework.Servers.HttpServer
                 NotSocketErrors = 0;
                 if (!m_ssl)
                 {
-                    //m_httpListener.Prefixes.Add("http://+:" + m_port + "/");
-                    //m_httpListener.Prefixes.Add("http://10.1.1.5:" + m_port + "/");
-                    m_httpListener2 = CoolHTTPListener.Create(m_listenIPAddress, (int)m_port);
-                    m_httpListener2.ExceptionThrown += httpServerException;
-                    m_httpListener2.LogWriter = httpserverlog;
-
+                    m_httpListener = tinyHTTPListener.Create(m_listenIPAddress, (int)m_port);
+                    m_httpListener.ExceptionThrown += httpServerException;
+                    if (DebugLevel > 0)
+                    {
+                        m_httpListener.LogWriter = httpserverlog;
+                        httpserverlog.DebugLevel = 1;
+                    }
                     // Uncomment this line in addition to those in HttpServerLogWriter
                     // if you want more detailed trace information from the HttpServer
-                    //m_httpListener2.UseTraceLogs = true;
-
                     //m_httpListener2.DisconnectHandler = httpServerDisconnectMonitor;
                 }
                 else
                 {
-                    //m_httpListener.Prefixes.Add("https://+:" + (m_sslport) + "/");
-                    //m_httpListener.Prefixes.Add("http://+:" + m_port + "/");
-                    m_httpListener2 = CoolHTTPListener.Create(IPAddress.Any, (int)m_port, m_cert);
+                    m_httpListener = tinyHTTPListener.Create(IPAddress.Any, (int)m_port, m_cert);
                     if(m_certificateValidationCallback != null)
-                        m_httpListener2.CertificateValidationCallback = m_certificateValidationCallback;
-                    m_httpListener2.ExceptionThrown += httpServerException;
-                    m_httpListener2.LogWriter = httpserverlog;
+                        m_httpListener.CertificateValidationCallback = m_certificateValidationCallback;
+                    m_httpListener.ExceptionThrown += httpServerException;
+                    if (DebugLevel > 0)
+                    {
+                        m_httpListener.LogWriter = httpserverlog;
+                        httpserverlog.DebugLevel = 1;
+                    }
                 }
 
-                m_httpListener2.RequestReceived += OnRequest;
-                //m_httpListener.Start();
-                m_httpListener2.Start(64);
+                m_httpListener.RequestReceived += OnRequest;
+                m_httpListener.Start(64);
 
                 lock(m_generalLock)
                 {
@@ -2089,13 +2084,6 @@ namespace OpenSim.Framework.Servers.HttpServer
                 }
 
                 HTTPDRunning = true;
-
-                //HttpListenerContext context;
-                //while (true)
-                //{
-                //    context = m_httpListener.GetContext();
-                //    ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(HandleRequest), context);
-               // }
             }
             catch (Exception e)
             {
@@ -2155,12 +2143,12 @@ namespace OpenSim.Framework.Servers.HttpServer
                         m_pollServiceManager.Stop();
                 }
 
-                m_httpListener2.ExceptionThrown -= httpServerException;
+                m_httpListener.ExceptionThrown -= httpServerException;
                 //m_httpListener2.DisconnectHandler = null;
 
-                m_httpListener2.LogWriter = null;
-                m_httpListener2.RequestReceived -= OnRequest;
-                m_httpListener2.Stop();
+                m_httpListener.LogWriter = null;
+                m_httpListener.RequestReceived -= OnRequest;
+                m_httpListener.Stop();
             }
             catch (NullReferenceException)
             {
@@ -2305,13 +2293,17 @@ namespace OpenSim.Framework.Servers.HttpServer
     ///
     /// You may also be able to get additional trace information from HttpServer if you uncomment the UseTraceLogs
     /// property in StartHttp() for the HttpListener
+    /// 
     public class HttpServerLogWriter : ILogWriter
     {
-//        private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+        private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+        public int DebugLevel {get; set;} = (int)LogPrio.Error;
 
         public void Write(object source, LogPrio priority, string message)
         {
-            /*
+            if((int)priority < DebugLevel)
+                return;
+
             switch (priority)
             {
                 case LogPrio.Trace:
@@ -2335,8 +2327,6 @@ namespace OpenSim.Framework.Servers.HttpServer
                 default:
                     break;
             }
-            */
-
             return;
         }
     }

+ 4 - 5
OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpResponse.cs

@@ -92,17 +92,16 @@ namespace OpenSim.Framework.Servers.HttpServer
         Stream OutputStream { get; }
 
         string ProtocolVersion { get; set; }
+        int Priority { get; set; }
+        byte[] RawBuffer { get; set; }
+        int RawBufferStart { get; set; }
+        int RawBufferLen { get; set; }
 
         /// <summary>
         /// Return the output stream feeding the body.
         /// </summary>
         Stream Body { get; }
 
-        /// <summary>
-        /// Set a redirct location.
-        /// </summary>
-        string RedirectLocation { set; }
-
         /// <summary>
         /// Chunk transfers.
         /// </summary>

+ 0 - 183
OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs

@@ -1,183 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the OpenSimulator Project nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text.RegularExpressions;
-
-namespace OpenSim.Framework.Servers.HttpServer
-{
-    /// <sumary>
-    /// Any OSHttpHandler must return one of the following results:
-    /// <list type = "table">
-    ///   <listheader>
-    ///     <term>result code</term>
-    ///     <description>meaning</description>
-    ///   </listheader>
-    ///   <item>
-    ///     <term>Pass</term>
-    ///     <description>handler did not process the request</request>
-    ///   </item>
-    ///   <item>
-    ///     <term>Done</term>
-    ///     <description>handler did process the request, OSHttpServer
-    ///       can clean up and close the request</request>
-    ///   </item>
-    /// </list>
-    /// </summary>
-    public enum OSHttpHandlerResult
-    {
-        Unprocessed,
-        Pass,
-        Done,
-    }
-
-    /// <summary>
-    /// An OSHttpHandler that matches on the "content-type" header can
-    /// supply an OSHttpContentTypeChecker delegate which will be
-    /// invoked by the request matcher in OSHttpRequestPump.
-    /// </summary>
-    /// <returns>true if the handler is interested in the content;
-    /// false otherwise</returns>
-    public delegate bool OSHttpContentTypeChecker(OSHttpRequest req);
-
-    public abstract class OSHttpHandler
-    {
-        /// <summary>
-        /// Regular expression used to match against method of
-        /// the incoming HTTP request. If you want to match any string
-        /// either use '.*' or null. To match on the empty string use
-        /// '^$'.
-        /// </summary>
-        public virtual Regex Method
-        {
-            get { return _method; }
-        }
-        protected Regex _method;
-
-        /// <summary>
-        /// Regular expression used to match against path of the
-        /// incoming HTTP request. If you want to match any string
-        /// either use '.*' or null. To match on the empty string use
-        /// '^$'.
-        /// </summary>
-        public virtual Regex Path
-        {
-            get { return _path; }
-        }
-        protected Regex _path;
-
-        /// <summary>
-        /// Dictionary of (query name, regular expression) tuples,
-        /// allowing us to match on URI query fields.
-        /// </summary>
-        public virtual Dictionary<string, Regex> Query
-        {
-            get { return _query; }
-        }
-        protected Dictionary<string, Regex> _query;
-
-        /// <summary>
-        /// Dictionary of (header name, regular expression) tuples,
-        /// allowing us to match on HTTP header fields.
-        /// </summary>
-        public virtual Dictionary<string, Regex> Headers
-        {
-            get { return _headers; }
-        }
-        protected Dictionary<string, Regex> _headers;
-
-        /// <summary>
-        /// Dictionary of (header name, regular expression) tuples,
-        /// allowing us to match on HTTP header fields.
-        /// </summary>
-        /// <remarks>
-        /// This feature is currently not implemented as it requires
-        /// (trivial) changes to HttpServer.HttpListener that have not
-        /// been implemented.
-        /// </remarks>
-        public virtual Regex IPEndPointWhitelist
-        {
-            get { return _ipEndPointRegex; }
-        }
-        protected Regex _ipEndPointRegex;
-
-
-        /// <summary>
-        /// Base class constructor.
-        /// </summary>
-        /// <param name="path">null or path regex</param>
-        /// <param name="headers">null or dictionary of header
-        /// regexs</param>
-        /// <param name="contentType">null or content type
-        /// regex</param>
-        /// <param name="whitelist">null or IP address regex</param>
-        public OSHttpHandler(Regex method, Regex path, Dictionary<string, Regex> query,
-                             Dictionary<string, Regex> headers, Regex contentType, Regex whitelist)
-        {
-            _method = method;
-            _path = path;
-            _query = query;
-            _ipEndPointRegex = whitelist;
-
-            if (null == _headers && null != contentType)
-            {
-                _headers = new Dictionary<string, Regex>();
-                _headers.Add("content-type", contentType);
-            }
-        }
-
-
-        /// <summary>
-        /// Process an incoming OSHttpRequest that matched our
-        /// requirements.
-        /// </summary>
-        /// <returns>
-        /// OSHttpHandlerResult.Pass if we are after all not
-        /// interested in the request; OSHttpHandlerResult.Done if we
-        /// did process the request.
-        /// </returns>
-        public abstract OSHttpHandlerResult Process(OSHttpRequest request);
-
-        public override string ToString()
-        {
-            StringWriter sw = new StringWriter();
-            sw.WriteLine("{0}", base.ToString());
-            sw.WriteLine("    method regex     {0}", null == Method ? "null" : Method.ToString());
-            sw.WriteLine("    path regex       {0}", null == Path ? "null": Path.ToString());
-            foreach (string tag in Headers.Keys)
-            {
-                sw.WriteLine("    header           {0} : {1}", tag, Headers[tag].ToString());
-            }
-            sw.WriteLine("    IP whitelist     {0}", null == IPEndPointWhitelist ? "null" : IPEndPointWhitelist.ToString());
-            sw.WriteLine();
-            sw.Close();
-            return sw.ToString();
-        }
-    }
-}

+ 0 - 145
OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs

@@ -1,145 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the OpenSimulator Project nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-using System.Reflection;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Xml;
-using log4net;
-using Nwc.XmlRpc;
-
-namespace OpenSim.Framework.Servers.HttpServer
-{
-    public delegate XmlRpcResponse OSHttpHttpProcessor(XmlRpcRequest request);
-
-    public class OSHttpHttpHandler: OSHttpHandler
-    {
-        private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
-        // contains handler for processing HTTP Request
-        private GenericHTTPMethod _handler;
-
-        /// <summary>
-        /// Instantiate an HTTP handler.
-        /// </summary>
-        /// <param name="handler">a GenericHTTPMethod</param>
-        /// <param name="method">null or HTTP method regex</param>
-        /// <param name="path">null or path regex</param>
-        /// <param name="query">null or dictionary with query regexs</param>
-        /// <param name="headers">null or dictionary with header
-        /// regexs</param>
-        /// <param name="whitelist">null or IP address whitelist</param>
-        public OSHttpHttpHandler(GenericHTTPMethod handler, Regex method, Regex path,
-                                 Dictionary<string, Regex> query,
-                                 Dictionary<string, Regex> headers, Regex whitelist)
-            : base(method, path, query, headers, new Regex(@"^text/html", RegexOptions.IgnoreCase | RegexOptions.Compiled),
-                   whitelist)
-        {
-            _handler = handler;
-        }
-
-        /// <summary>
-        /// Instantiate an HTTP handler.
-        /// </summary>
-        /// <param name="handler">a GenericHTTPMethod</param>
-        public OSHttpHttpHandler(GenericHTTPMethod handler)
-            : this(handler, new Regex(@"^GET$", RegexOptions.IgnoreCase | RegexOptions.Compiled), null, null, null, null)
-        {
-        }
-
-        /// <summary>
-        /// Invoked by OSHttpRequestPump.
-        /// </summary>
-        public override OSHttpHandlerResult Process(OSHttpRequest request)
-        {
-            // call handler method
-            Hashtable responseData = _handler(request.Query);
-
-            int responseCode = (int)responseData["int_response_code"];
-            string responseString = (string)responseData["str_response_string"];
-            string contentType = (string)responseData["content_type"];
-
-            //Even though only one other part of the entire code uses HTTPHandlers, we shouldn't expect this
-            //and should check for NullReferenceExceptions
-
-            if (string.IsNullOrEmpty(contentType))
-            {
-                contentType = "text/html";
-            }
-
-            OSHttpResponse response = new OSHttpResponse(request);
-
-            // We're forgoing the usual error status codes here because the client
-            // ignores anything but 200 and 301
-
-            response.StatusCode = (int)OSHttpStatusCode.SuccessOk;
-
-            if (responseCode == (int)OSHttpStatusCode.RedirectMovedPermanently)
-            {
-                response.RedirectLocation = (string)responseData["str_redirect_location"];
-                response.StatusCode = responseCode;
-            }
-
-            response.AddHeader("Content-type", contentType);
-
-            byte[] buffer;
-
-            if (!contentType.Contains("image"))
-            {
-                buffer = Encoding.UTF8.GetBytes(responseString);
-            }
-            else
-            {
-                buffer = Convert.FromBase64String(responseString);
-            }
-
-            response.SendChunked = false;
-            response.ContentLength64 = buffer.Length;
-            response.ContentEncoding = Encoding.UTF8;
-
-            try
-            {
-                response.Body.Write(buffer, 0, buffer.Length);
-            }
-            catch (Exception ex)
-            {
-                _log.ErrorFormat("[OSHttpHttpHandler]: Error:  {0}", ex.Message);
-            }
-            finally
-            {
-                response.Send();
-            }
-
-            return OSHttpHandlerResult.Done;
-        }
-    }
-}

+ 1 - 1
OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs

@@ -34,7 +34,7 @@ using System.Net;
 using System.Reflection;
 using System.Text;
 using System.Web;
-using HttpServer;
+using OSHttpServer;
 using log4net;
 
 namespace OpenSim.Framework.Servers.HttpServer

+ 0 - 298
OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs

@@ -1,298 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the OpenSimulator Project nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-// #define DEBUGGING
-
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Diagnostics;
-using System.IO;
-using System.Net;
-using System.Reflection;
-using System.Text.RegularExpressions;
-using System.Threading;
-using log4net;
-using HttpServer;
-
-namespace OpenSim.Framework.Servers.HttpServer
-{
-    /// <summary>
-    /// An OSHttpRequestPump fetches incoming OSHttpRequest objects
-    /// from the OSHttpRequestQueue and feeds them to all subscribed
-    /// parties. Each OSHttpRequestPump encapsulates one thread to do
-    /// the work and there is a fixed number of pumps for each
-    /// OSHttpServer object.
-    /// </summary>
-    public class OSHttpRequestPump
-    {
-        private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
-        protected OSHttpServer _server;
-        protected OSHttpRequestQueue _queue;
-        protected Thread _engine;
-
-        private int _id;
-
-        public string EngineID
-        {
-            get { return String.Format("{0} pump {1}", _server.EngineID, _id); }
-        }
-
-        public OSHttpRequestPump(OSHttpServer server, OSHttpRequestQueue queue, int id)
-        {
-            _server = server;
-            _queue = queue;
-            _id = id;
-
-            _engine = new Thread(new ThreadStart(Engine));
-            _engine.IsBackground = true;
-            _engine.Start();
-            _engine.Name = string.Format ("Engine:{0}",EngineID);
-
-            ThreadTracker.Add(_engine);
-        }
-
-        public static OSHttpRequestPump[] Pumps(OSHttpServer server, OSHttpRequestQueue queue, int poolSize)
-        {
-            OSHttpRequestPump[] pumps = new OSHttpRequestPump[poolSize];
-            for (int i = 0; i < pumps.Length; i++)
-            {
-                pumps[i] = new OSHttpRequestPump(server, queue, i);
-            }
-
-            return pumps;
-        }
-
-        public void Start()
-        {
-            _engine = new Thread(new ThreadStart(Engine));
-            _engine.IsBackground = true;
-            _engine.Start();
-            _engine.Name = string.Format ("Engine:{0}",EngineID);
-
-            ThreadTracker.Add(_engine);
-        }
-
-        public void Engine()
-        {
-            OSHttpRequest req = null;
-
-            while (true)
-            {
-                try
-                {
-                    // dequeue an OSHttpRequest from OSHttpServer's
-                    // request queue
-                    req = _queue.Dequeue();
-
-                    // get a copy of the list of registered handlers
-                    List<OSHttpHandler> handlers = _server.OSHttpHandlers;
-
-                    // prune list and have it sorted from most
-                    // specific to least specific
-                    handlers = MatchHandlers(req, handlers);
-
-                    // process req: we try each handler in turn until
-                    // we are either out of handlers or get back a
-                    // Pass or Done
-                    OSHttpHandlerResult rc = OSHttpHandlerResult.Unprocessed;
-                    foreach (OSHttpHandler h in handlers)
-                    {
-                        rc = h.Process(req);
-
-                        // Pass: handler did not process the request,
-                        // try next handler
-                        if (OSHttpHandlerResult.Pass == rc) continue;
-
-                        // Handled: handler has processed the request
-                        if (OSHttpHandlerResult.Done == rc) break;
-
-                        // hmm, something went wrong
-                        throw new Exception(String.Format("[{0}] got unexpected OSHttpHandlerResult {1}", EngineID, rc));
-                    }
-
-                    if (OSHttpHandlerResult.Unprocessed == rc)
-                    {
-                        _log.InfoFormat("[{0}] OSHttpHandler: no handler registered for {1}", EngineID, req);
-
-                        // set up response header
-                        OSHttpResponse resp = new OSHttpResponse(req);
-                        resp.StatusCode = (int)OSHttpStatusCode.ClientErrorNotFound;
-                        resp.StatusDescription = String.Format("no handler on call for {0}", req);
-                        resp.ContentType = "text/html";
-
-                        // add explanatory message
-                        StreamWriter body = new StreamWriter(resp.Body);
-                        body.WriteLine("<html>");
-                        body.WriteLine("<header><title>Ooops...</title><header>");
-                        body.WriteLine(String.Format("<body><p>{0}</p></body>", resp.StatusDescription));
-                        body.WriteLine("</html>");
-                        body.Flush();
-
-                        // and ship it back
-                        resp.Send();
-                    }
-                }
-                catch (Exception e)
-                {
-                    _log.DebugFormat("[{0}] OSHttpHandler problem: {1}", EngineID, e.ToString());
-                    _log.ErrorFormat("[{0}] OSHttpHandler problem: {1}", EngineID, e.Message);
-                }
-            }
-        }
-
-        protected List<OSHttpHandler> MatchHandlers(OSHttpRequest req, List<OSHttpHandler> handlers)
-        {
-            Dictionary<OSHttpHandler, int> scoredHandlers = new Dictionary<OSHttpHandler, int>();
-
-            _log.DebugFormat("[{0}] MatchHandlers for {1}", EngineID, req);
-            foreach (OSHttpHandler h in handlers)
-            {
-                // initial anchor
-                scoredHandlers[h] = 0;
-
-                // first, check whether IPEndPointWhitelist applies
-                // and, if it does, whether client is on that white
-                // list.
-                if (null != h.IPEndPointWhitelist)
-                {
-                    // TODO: following code requires code changes to
-                    // HttpServer.HttpRequest to become functional
-
-                    IPEndPoint remote = req.RemoteIPEndPoint;
-                    if (null != remote)
-                    {
-                        Match epm = h.IPEndPointWhitelist.Match(remote.ToString());
-                        if (!epm.Success)
-                        {
-                            scoredHandlers.Remove(h);
-                            continue;
-                        }
-                    }
-                }
-
-                if (null != h.Method)
-                {
-                    Match m = h.Method.Match(req.HttpMethod);
-                    if (!m.Success)
-                    {
-                        scoredHandlers.Remove(h);
-                        continue;
-                    }
-                    scoredHandlers[h]++;
-                }
-
-                // whitelist ok, now check path
-                if (null != h.Path)
-                {
-                    Match m = h.Path.Match(req.RawUrl);
-                    if (!m.Success)
-                    {
-                        scoredHandlers.Remove(h);
-                        continue;
-                    }
-                    scoredHandlers[h] += m.ToString().Length;
-                }
-
-                // whitelist & path ok, now check query string
-                if (null != h.Query)
-                {
-                    int queriesMatch = MatchOnNameValueCollection(req.QueryString, h.Query);
-                    if (0 == queriesMatch)
-                    {
-                        _log.DebugFormat("[{0}] request {1}", EngineID, req);
-                        _log.DebugFormat("[{0}] dropping handler {1}", EngineID, h);
-
-                        scoredHandlers.Remove(h);
-                        continue;
-                    }
-                    scoredHandlers[h] +=  queriesMatch;
-                }
-
-                // whitelist, path, query string ok, now check headers
-                if (null != h.Headers)
-                {
-                    int headersMatch = MatchOnNameValueCollection(req.Headers, h.Headers);
-                    if (0 == headersMatch)
-                    {
-                        _log.DebugFormat("[{0}] request {1}", EngineID, req);
-                        _log.DebugFormat("[{0}] dropping handler {1}", EngineID, h);
-
-                        scoredHandlers.Remove(h);
-                        continue;
-                    }
-                    scoredHandlers[h] +=  headersMatch;
-                }
-            }
-
-            List<OSHttpHandler> matchingHandlers = new List<OSHttpHandler>(scoredHandlers.Keys);
-            matchingHandlers.Sort(delegate(OSHttpHandler x, OSHttpHandler y)
-                                  {
-                                      return scoredHandlers[x] - scoredHandlers[y];
-                                  });
-            LogDumpHandlerList(matchingHandlers);
-            return matchingHandlers;
-        }
-
-        protected int MatchOnNameValueCollection(NameValueCollection collection, Dictionary<string, Regex> regexs)
-        {
-            int matched = 0;
-
-            foreach (string tag in regexs.Keys)
-            {
-                // do we have a header "tag"?
-                if (null == collection[tag])
-                {
-                    return 0;
-                }
-
-                // does the content of collection[tag] match
-                // the supplied regex?
-                Match cm = regexs[tag].Match(collection[tag]);
-                if (!cm.Success)
-                {
-                    return 0;
-                }
-
-                // ok: matches
-                matched++;
-                continue;
-            }
-
-            return matched;
-        }
-
-        [ConditionalAttribute("DEBUGGING")]
-        private void LogDumpHandlerList(List<OSHttpHandler> l)
-        {
-            _log.DebugFormat("[{0}] OSHttpHandlerList dump:", EngineID);
-            foreach (OSHttpHandler h in l)
-                _log.DebugFormat("    ", h.ToString());
-        }
-    }
-}

+ 0 - 68
OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs

@@ -1,68 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the OpenSimulator Project nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using HttpServer;
-
-namespace OpenSim.Framework.Servers.HttpServer
-{
-    /// <summary>
-    /// OSHttpRequestQueues are used to hand over incoming HTTP
-    /// requests to OSHttpRequestPump objects.
-    /// </summary>
-    public class OSHttpRequestQueue : Queue<OSHttpRequest>
-    {
-        private object _syncObject = new object();
-
-        new public void Enqueue(OSHttpRequest req)
-        {
-            lock (_syncObject)
-            {
-                base.Enqueue(req);
-                Monitor.Pulse(_syncObject);
-            }
-        }
-
-        new public OSHttpRequest Dequeue()
-        {
-            OSHttpRequest req = null;
-
-            lock (_syncObject)
-            {
-                while (null == req)
-                {
-                    Monitor.Wait(_syncObject);
-                    if (0 != this.Count) req = base.Dequeue();
-                }
-            }
-
-            return req;
-        }
-    }
-}

+ 1 - 14
OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs

@@ -28,7 +28,7 @@
 using System.IO;
 using System.Net;
 using System.Text;
-using HttpServer;
+using OSHttpServer;
 
 namespace OpenSim.Framework.Servers.HttpServer
 {
@@ -243,19 +243,6 @@ namespace OpenSim.Framework.Servers.HttpServer
             }
         }
 
-        /// <summary>
-        /// Set a redirct location.
-        /// </summary>
-        public string RedirectLocation
-        {
-            // get { return _redirectLocation; }
-            set
-            {
-                _httpResponse.Redirect(value);
-            }
-        }
-
-
         /// <summary>
         /// Chunk transfers.
         /// </summary>

+ 0 - 210
OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs

@@ -1,210 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the OpenSimulator Project nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Net.Sockets;
-using System.Reflection;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Security.Cryptography.X509Certificates;
-using log4net;
-using HttpServer;
-
-using HttpListener = HttpServer.HttpListener;
-
-namespace OpenSim.Framework.Servers.HttpServer
-{
-    /// <summary>
-    /// OSHttpServer provides an HTTP server bound to a specific
-    /// port. When instantiated with just address and port it uses
-    /// normal HTTP, when instantiated with address, port, and X509
-    /// certificate, it uses HTTPS.
-    /// </summary>
-    public class OSHttpServer
-    {
-        private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
-        private object _syncObject = new object();
-
-        // underlying HttpServer.HttpListener
-        protected HttpListener _listener;
-        // underlying core/engine thread
-        protected Thread _engine;
-
-        // Queue containing (OS)HttpRequests
-        protected OSHttpRequestQueue _queue;
-
-        // OSHttpRequestPumps "pumping" incoming OSHttpRequests
-        // upwards
-        protected OSHttpRequestPump[] _pumps;
-
-        // thread identifier
-        protected string _engineId;
-        public string EngineID
-        {
-            get { return _engineId; }
-        }
-
-        /// <summary>
-        /// True if this is an HTTPS connection; false otherwise.
-        /// </summary>
-        protected bool _isSecure;
-        public bool IsSecure
-        {
-            get { return _isSecure; }
-        }
-
-        public int QueueSize
-        {
-            get { return _pumps.Length; }
-        }
-
-        /// <summary>
-        /// List of registered OSHttpHandlers for this OSHttpServer instance.
-        /// </summary>
-        protected List<OSHttpHandler> _httpHandlers = new List<OSHttpHandler>();
-        public List<OSHttpHandler> OSHttpHandlers
-        {
-            get
-            {
-                lock (_httpHandlers)
-                {
-                    return new List<OSHttpHandler>(_httpHandlers);
-                }
-            }
-        }
-
-
-        /// <summary>
-        /// Instantiate an HTTP server.
-        /// </summary>
-        public OSHttpServer(IPAddress address, int port, int poolSize)
-        {
-            _engineId = String.Format("OSHttpServer (HTTP:{0})", port);
-            _isSecure = false;
-            _log.DebugFormat("[{0}] HTTP server instantiated", EngineID);
-
-            _listener = new HttpListener(address, port);
-            _queue = new OSHttpRequestQueue();
-            _pumps = OSHttpRequestPump.Pumps(this, _queue, poolSize);
-        }
-
-        /// <summary>
-        /// Instantiate an HTTPS server.
-        /// </summary>
-        public OSHttpServer(IPAddress address, int port, X509Certificate certificate, int poolSize)
-        {
-            _engineId = String.Format("OSHttpServer [HTTPS:{0}/ps:{1}]", port, poolSize);
-            _isSecure = true;
-            _log.DebugFormat("[{0}] HTTPS server instantiated", EngineID);
-
-            _listener = new HttpListener(address, port, certificate);
-            _queue = new OSHttpRequestQueue();
-            _pumps = OSHttpRequestPump.Pumps(this, _queue, poolSize);
-        }
-
-        /// <summary>
-        /// Turn an HttpRequest into an OSHttpRequestItem and place it
-        /// in the queue. The OSHttpRequestQueue object will pulse the
-        /// next available idle pump.
-        /// </summary>
-        protected void OnHttpRequest(HttpClientContext client, HttpRequest request)
-        {
-            // turn request into OSHttpRequest
-            OSHttpRequest req = new OSHttpRequest(client, request);
-
-            // place OSHttpRequest into _httpRequestQueue, will
-            // trigger Pulse to idle waiting pumps
-            _queue.Enqueue(req);
-        }
-
-        /// <summary>
-        /// Start the HTTP server engine.
-        /// </summary>
-        public void Start()
-        {
-            _engine = new Thread(new ThreadStart(Engine));
-            _engine.IsBackground = true;
-            _engine.Start();
-            _engine.Name = string.Format ("Engine:{0}",_engineId);
-
-            ThreadTracker.Add(_engine);
-
-            // start the pumps...
-            for (int i = 0; i < _pumps.Length; i++)
-                _pumps[i].Start();
-        }
-
-        public void Stop()
-        {
-            lock (_syncObject) Monitor.Pulse(_syncObject);
-        }
-
-        /// <summary>
-        /// Engine keeps the HTTP server running.
-        /// </summary>
-        private void Engine()
-        {
-            try {
-                _listener.RequestHandler += OnHttpRequest;
-                _listener.Start(QueueSize);
-                _log.InfoFormat("[{0}] HTTP server started", EngineID);
-
-                lock (_syncObject) Monitor.Wait(_syncObject);
-            }
-            catch (Exception ex)
-            {
-                _log.DebugFormat("[{0}] HTTP server startup failed: {1}", EngineID, ex.ToString());
-            }
-
-            _log.InfoFormat("[{0}] HTTP server terminated", EngineID);
-        }
-
-
-        /// <summary>
-        /// Add an HTTP request handler.
-        /// </summary>
-        /// <param name="handler">OSHttpHandler delegate</param>
-        /// <param name="path">regex object for path matching</parm>
-        /// <param name="headers">dictionary containing header names
-        /// and regular expressions to match against header values</param>
-        public void AddHandler(OSHttpHandler handler)
-        {
-            lock (_httpHandlers)
-            {
-                if (_httpHandlers.Contains(handler))
-                {
-                    _log.DebugFormat("[OSHttpServer] attempt to add already existing handler ignored");
-                    return;
-                }
-                _httpHandlers.Add(handler);
-            }
-        }
-    }
-}

+ 33 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/BadRequestException.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Net;
+
+namespace OSHttpServer.Exceptions
+{
+    /// <summary>
+    /// The request could not be understood by the server due to malformed syntax. 
+    /// The client SHOULD NOT repeat the request without modifications.
+    /// 
+    /// Text taken from: http://www.submissionchamber.com/help-guides/error-codes.php
+    /// </summary>
+    public class BadRequestException : HttpException
+    {
+        /// <summary>
+        /// Create a new bad request exception.
+        /// </summary>
+        /// <param name="errMsg">reason to why the request was bad.</param>
+        public BadRequestException(string errMsg)
+            : base(HttpStatusCode.BadRequest, errMsg)
+        {
+        }
+
+        /// <summary>
+        /// Create a new bad request exception.
+        /// </summary>
+        /// <param name="errMsg">reason to why the request was bad.</param>
+        /// <param name="inner">inner exception</param>
+        public BadRequestException(string errMsg, Exception inner)
+            : base(HttpStatusCode.BadRequest, errMsg, inner)
+        {
+        }
+    }
+}

+ 55 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/BodyEventArgs.cs

@@ -0,0 +1,55 @@
+using System;
+
+namespace OSHttpServer.Parser
+{
+    /// <summary>
+    /// Arguments used when more body bytes have come.
+    /// </summary>
+    public class BodyEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BodyEventArgs"/> class.
+        /// </summary>
+        /// <param name="buffer">buffer that contains the received bytes.</param>
+        /// <param name="offset">offset in buffer where to start processing.</param>
+        /// <param name="count">number of bytes from <paramref name="offset"/> that should be parsed.</param>
+        public BodyEventArgs(byte[] buffer, int offset, int count)
+        {
+            Buffer = buffer;
+            Offset = offset;
+            Count = count;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BodyEventArgs"/> class.
+        /// </summary>
+        public BodyEventArgs()
+        {
+        }
+
+        /// <summary>
+        /// Gets or sets buffer that contains the received bytes.
+        /// </summary>
+        public byte[] Buffer { get; set; }
+        /*
+        /// <summary>
+        /// Gets or sets number of bytes used by the request.
+        /// </summary>
+        public int BytesUsed { get; set; }
+        */
+        /// <summary>
+        /// Gets or sets number of bytes from <see cref="Offset"/> that should be parsed.
+        /// </summary>
+        public int Count { get; set; }
+        /*
+        /// <summary>
+        /// Gets or sets whether the body is complete.
+        /// </summary>
+        public bool IsBodyComplete { get; set; }
+        */
+        /// <summary>
+        /// Gets or sets offset in buffer where to start processing.
+        /// </summary>
+        public int Offset { get; set; }
+    }
+}

+ 50 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/ClientAcceptedEventArgs.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Net.Sockets;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Invoked when a client have been accepted by the <see cref="OSHttpListener"/>
+    /// </summary>
+    /// <remarks>
+    /// Can be used to revoke incoming connections
+    /// </remarks>
+    public class ClientAcceptedEventArgs : EventArgs
+    {
+        private readonly Socket _socket;
+        private bool _revoke;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ClientAcceptedEventArgs"/> class.
+        /// </summary>
+        /// <param name="socket">The socket.</param>
+        public ClientAcceptedEventArgs(Socket socket)
+        {
+            _socket = socket;
+        }
+
+        /// <summary>
+        /// Accepted socket.
+        /// </summary>
+        public Socket Socket
+        {
+            get { return _socket; }
+        }
+
+        /// <summary>
+        /// Client should be revoked.
+        /// </summary>
+        public bool Revoked
+        {
+            get { return _revoke; }
+        }
+
+        /// <summary>
+        /// Client may not be handled.
+        /// </summary>
+        public void Revoke()
+        {
+            _revoke = true;
+        }
+    }
+}

+ 413 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/ContextTimeoutManager.cs

@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the OpenSimulator Project nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.Globalization;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Timeout Manager.   Checks for dead clients.  Clients with open connections that are not doing anything.   Closes sessions opened with keepalive.
+    /// </summary>
+    public static class ContextTimeoutManager
+    {
+        /// <summary>
+        /// Use a Thread or a Timer to monitor the ugly
+        /// </summary>
+        private static Thread m_internalThread = null;
+        private static object m_threadLock = new object();
+        private static ConcurrentQueue<HttpClientContext> m_contexts = new ConcurrentQueue<HttpClientContext>();
+        private static ConcurrentQueue<HttpClientContext> m_highPrio = new ConcurrentQueue<HttpClientContext>();
+        private static ConcurrentQueue<HttpClientContext> m_midPrio = new ConcurrentQueue<HttpClientContext>();
+        private static ConcurrentQueue<HttpClientContext> m_lowPrio = new ConcurrentQueue<HttpClientContext>();
+        private static AutoResetEvent m_processWaitEven = new AutoResetEvent(false);
+        private static bool m_shuttingDown;
+
+        private static int m_ActiveSendingCount;
+        private static double m_lastTimeOutCheckTime = 0;
+        private static double m_lastSendCheckTime = 0;
+
+        const int m_maxBandWidth = 10485760; //80Mbps
+        const int m_maxConcurrenSend = 32;
+
+        static ContextTimeoutManager()
+        {
+            TimeStampClockPeriod = 1.0 / (double)Stopwatch.Frequency;
+            TimeStampClockPeriodMS = 1e3 / (double)Stopwatch.Frequency;
+        }
+
+        public static void Start()
+        {
+            lock (m_threadLock)
+            {
+                if (m_internalThread != null)
+                    return;
+
+                m_lastTimeOutCheckTime = GetTimeStampMS();
+                m_internalThread = new Thread(ThreadRunProcess);
+                m_internalThread.Priority = ThreadPriority.Normal;
+                m_internalThread.IsBackground = true;
+                m_internalThread.CurrentCulture = new CultureInfo("en-US", false);
+                m_internalThread.Name = "HttpServerMain";
+                m_internalThread.Start();
+            }
+        }
+
+        public static void Stop()
+        {
+            m_shuttingDown = true;
+            m_internalThread.Join();
+            ProcessShutDown();
+        }
+
+        private static void ThreadRunProcess()
+        {
+            while (!m_shuttingDown)
+            {
+                m_processWaitEven.WaitOne(100);
+
+                if(m_shuttingDown)
+                    return;
+
+                double now = GetTimeStampMS();
+                if(m_contexts.Count > 0)
+                {
+                    ProcessSendQueues(now);
+
+                    if (now - m_lastTimeOutCheckTime > 1000)
+                    {
+                        ProcessContextTimeouts();
+                        m_lastTimeOutCheckTime = now;
+                    }
+                }
+                else
+                    m_lastTimeOutCheckTime = now;
+            }
+        }
+
+        public static void ProcessShutDown()
+        {
+            try
+            {
+                SocketError disconnectError = SocketError.HostDown;
+                for (int i = 0; i < m_contexts.Count; i++)
+                {
+                    HttpClientContext context = null;
+                    if (m_contexts.TryDequeue(out context))
+                    {
+                        try
+                        {
+                            context.Disconnect(disconnectError);
+                        }
+                        catch { }
+                    }
+                }
+                m_processWaitEven.Dispose();
+                m_processWaitEven = null;
+            }
+            catch
+            {
+                // We can't let this crash.
+            }
+        }
+
+        public static void ProcessSendQueues(double now)
+        {
+            int inqueues = m_highPrio.Count + m_midPrio.Count + m_lowPrio.Count;
+            if(inqueues == 0)
+                return;
+
+            double dt = now - m_lastSendCheckTime;
+            m_lastSendCheckTime = now;
+
+            int totalSending = m_ActiveSendingCount;
+
+            int curConcurrentLimit = m_maxConcurrenSend - totalSending;
+            if(curConcurrentLimit <= 0)
+                return;
+
+            if(curConcurrentLimit > inqueues)
+                curConcurrentLimit = inqueues;
+
+            if (dt > 0.1)
+                dt = 0.1;
+
+            dt /= curConcurrentLimit;
+            int curbytesLimit = (int)(m_maxBandWidth * dt);
+            if(curbytesLimit < 8192)
+                curbytesLimit = 8192;
+
+            HttpClientContext ctx;
+            int sent;
+            while (curConcurrentLimit > 0)
+            {
+                sent = 0;
+                while (m_highPrio.TryDequeue(out ctx))
+                {
+                    if(TrySend(ctx, curbytesLimit))
+                        m_highPrio.Enqueue(ctx);
+
+                    if (m_shuttingDown)
+                        return;
+                    --curConcurrentLimit;
+                    if (++sent == 4)
+                        break;
+                }
+
+                sent = 0;
+                while(m_midPrio.TryDequeue(out ctx))
+                {
+                    if(TrySend(ctx, curbytesLimit))
+                        m_midPrio.Enqueue(ctx);
+
+                    if (m_shuttingDown)
+                        return;
+                    --curConcurrentLimit;
+                    if (++sent >= 2)
+                        break;
+                }
+
+                if (m_lowPrio.TryDequeue(out ctx))
+                {
+                    --curConcurrentLimit;
+                    if(TrySend(ctx, curbytesLimit))
+                        m_lowPrio.Enqueue(ctx);
+                }
+
+                if (m_shuttingDown)
+                    return;
+            }
+        }
+
+        private static bool TrySend(HttpClientContext ctx, int bytesLimit)
+        {
+            if(!ctx.CanSend())
+                return false;
+
+            return ctx.TrySendResponse(bytesLimit);
+        }
+
+        /// <summary>
+        /// Causes the watcher to immediately check the connections. 
+        /// </summary>
+        public static void ProcessContextTimeouts()
+        {
+            try
+            {
+                for (int i = 0; i < m_contexts.Count; i++)
+                {
+                    if (m_shuttingDown)
+                        return;
+                    if (m_contexts.TryDequeue(out HttpClientContext context))
+                    {
+                        if (!ContextTimedOut(context, out SocketError disconnectError))
+                            m_contexts.Enqueue(context);
+                        else if(disconnectError != SocketError.InProgress)
+                            context.Disconnect(disconnectError);
+                    }
+                }
+            }
+            catch
+            {
+                // We can't let this crash.
+            }
+        }
+
+        private static bool ContextTimedOut(HttpClientContext context, out SocketError disconnectError)
+        {
+            disconnectError = SocketError.InProgress;
+
+            // First our error conditions
+            if (context.contextID < 0 || context.StopMonitoring || context.StreamPassedOff)
+                return true;
+
+            // Now we start checking for actual timeouts
+
+            // First we check that we got at least one line within context.TimeoutFirstLine milliseconds
+            if (!context.FirstRequestLineReceived)
+            {
+                if (EnvironmentTickCountAdd(context.TimeoutFirstLine, context.MonitorStartMS) <= EnvironmentTickCount())
+                {
+                    disconnectError = SocketError.TimedOut;
+                    context.MonitorStartMS = 0;
+                    return true;
+                }
+            }
+
+            if (!context.FullRequestReceived)
+            {
+                if (EnvironmentTickCountAdd(context.TimeoutRequestReceived, context.MonitorStartMS) <= EnvironmentTickCount())
+                {
+                    disconnectError = SocketError.TimedOut;
+                    context.MonitorStartMS = 0;
+                    return true;
+                }
+            }
+
+            // 
+            if (!context.FullRequestProcessed)
+            {
+                if (EnvironmentTickCountAdd(context.TimeoutFullRequestProcessed, context.MonitorStartMS) <= EnvironmentTickCount())
+                {
+                    disconnectError = SocketError.TimedOut;
+                    context.MonitorStartMS = 0;
+                    return true;
+                }
+            }
+
+            if (context.TriggerKeepalive)
+            {
+                context.TriggerKeepalive = false;
+                context.MonitorKeepaliveMS = EnvironmentTickCount();
+            }
+
+            if (context.FullRequestProcessed && context.MonitorKeepaliveMS == 0)
+                return true;
+
+            if (context.MonitorKeepaliveMS != 0 &&
+                EnvironmentTickCountAdd(context.TimeoutKeepAlive, context.MonitorKeepaliveMS) <= EnvironmentTickCount())
+            {
+                disconnectError = SocketError.TimedOut;
+                context.MonitorStartMS = 0;
+                context.MonitorKeepaliveMS = 0;
+                return true;
+            }
+
+            return false;
+        }
+
+        public static void StartMonitoringContext(HttpClientContext context)
+        {
+            context.MonitorStartMS = EnvironmentTickCount();
+            m_contexts.Enqueue(context);
+        }
+
+        public static void EnqueueSend(HttpClientContext context, int priority)
+        {
+            switch(priority)
+            {
+                case 0:
+                    m_highPrio.Enqueue(context);
+                    break;
+                case 1:
+                    m_midPrio.Enqueue(context);
+                    break;
+                case 2:
+                    m_lowPrio.Enqueue(context);
+                    break;
+                default:
+                    return;
+            }
+            m_processWaitEven.Set();
+        }
+
+        public static void ContextEnterActiveSend()
+        {
+            Interlocked.Increment(ref m_ActiveSendingCount);
+        }
+
+        public static void ContextLeaveActiveSend()
+        {
+            Interlocked.Decrement(ref m_ActiveSendingCount);
+        }
+
+        /// <summary>
+        /// Environment.TickCount is an int but it counts all 32 bits so it goes positive
+        /// and negative every 24.9 days. This trims down TickCount so it doesn't wrap
+        /// for the callers. 
+        /// This trims it to a 12 day interval so don't let your frame time get too long.
+        /// </summary>
+        /// <returns></returns>
+        public static int EnvironmentTickCount()
+        {
+            return Environment.TickCount & EnvironmentTickCountMask;
+        }
+        const int EnvironmentTickCountMask = 0x3fffffff;
+
+        /// <summary>
+        /// Environment.TickCount is an int but it counts all 32 bits so it goes positive
+        /// and negative every 24.9 days. Subtracts the passed value (previously fetched by
+        /// 'EnvironmentTickCount()') and accounts for any wrapping.
+        /// </summary>
+        /// <param name="newValue"></param>
+        /// <param name="prevValue"></param>
+        /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns>
+        public static int EnvironmentTickCountSubtract(Int32 newValue, Int32 prevValue)
+        {
+            int diff = newValue - prevValue;
+            return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1);
+        }
+
+        /// <summary>
+        /// Environment.TickCount is an int but it counts all 32 bits so it goes positive
+        /// and negative every 24.9 days. Subtracts the passed value (previously fetched by
+        /// 'EnvironmentTickCount()') and accounts for any wrapping.
+        /// </summary>
+        /// <param name="newValue"></param>
+        /// <param name="prevValue"></param>
+        /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns>
+        public static int EnvironmentTickCountAdd(Int32 newValue, Int32 prevValue)
+        {
+            int ret = newValue + prevValue;
+            return (ret >= 0) ? ret : (ret + EnvironmentTickCountMask + 1);
+        }
+
+        public static double TimeStampClockPeriodMS;
+        public static double TimeStampClockPeriod;
+
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+        public static double GetTimeStamp()
+        {
+            return Stopwatch.GetTimestamp() * TimeStampClockPeriod;
+        }
+
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+        public static double GetTimeStampMS()
+        {
+            return Stopwatch.GetTimestamp() * TimeStampClockPeriodMS;
+        }
+
+        // doing math in ticks is usefull to avoid loss of resolution
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+        public static long GetTimeStampTicks()
+        {
+            return Stopwatch.GetTimestamp();
+        }
+
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+        public static double TimeStampTicksToMS(long ticks)
+        {
+            return ticks * TimeStampClockPeriodMS;
+        }
+
+    }
+}

+ 29 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/ExceptionEventArgs.cs

@@ -0,0 +1,29 @@
+using System;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// An unhandled exception have been caught by the system.
+    /// </summary>
+    public class ExceptionEventArgs : EventArgs
+    {
+        private readonly Exception _exception;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ExceptionEventArgs"/> class.
+        /// </summary>
+        /// <param name="exception">Caught exception.</param>
+        public ExceptionEventArgs(Exception exception)
+        {
+            _exception = exception;
+        }
+
+        /// <summary>
+        /// caught exception
+        /// </summary>
+        public Exception Exception
+        {
+            get { return _exception; }
+        }
+    }
+}

+ 16 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/ExceptionHandler.cs

@@ -0,0 +1,16 @@
+using System;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// We dont want to let the server to die due to exceptions thrown in worker threads.
+    /// therefore we use this delegate to give you a change to handle uncaught exceptions.
+    /// </summary>
+    /// <param name="source">Class that the exception was thrown in.</param>
+    /// <param name="exception">Exception</param>
+    /// <remarks>
+    /// Server will throw a InternalServerException in release version if you dont
+    /// handle this delegate.
+    /// </remarks>
+    public delegate void ExceptionHandler(object source, Exception exception);
+}

+ 25 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/ForbiddenException.cs

@@ -0,0 +1,25 @@
+using System.Net;
+
+namespace OSHttpServer.Exceptions
+{
+    /// <summary>
+    /// The server understood the request, but is refusing to fulfill it. 
+    /// Authorization will not help and the request SHOULD NOT be repeated. 
+    /// If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, 
+    /// it SHOULD describe the reason for the refusal in the entity. If the server does not wish to make this information 
+    /// available to the client, the status code 404 (Not Found) can be used instead.
+    /// 
+    /// Text taken from: http://www.submissionchamber.com/help-guides/error-codes.php
+    /// </summary>
+    public class ForbiddenException : HttpException
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ForbiddenException"/> class.
+        /// </summary>
+        /// <param name="errorMsg">error message</param>
+        public ForbiddenException(string errorMsg)
+            : base(HttpStatusCode.Forbidden, errorMsg)
+        {
+        }
+    }
+}

+ 38 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HeaderEventArgs.cs

@@ -0,0 +1,38 @@
+using System;
+
+namespace OSHttpServer.Parser
+{
+    /// <summary>
+    /// Event arguments used when a new header have been parsed.
+    /// </summary>
+    public class HeaderEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="HeaderEventArgs"/> class.
+        /// </summary>
+        /// <param name="name">Name of header.</param>
+        /// <param name="value">Header value.</param>
+        public HeaderEventArgs(string name, string value)
+        {
+            Name = name;
+            Value = value;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="HeaderEventArgs"/> class.
+        /// </summary>
+        public HeaderEventArgs()
+        {
+        }
+
+        /// <summary>
+        /// Gets or sets header name.
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Gets or sets header value.
+        /// </summary>
+        public string Value { get; set; }
+    }
+}

+ 786 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpClientContext.cs

@@ -0,0 +1,786 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+using OSHttpServer.Exceptions;
+using OSHttpServer.Parser;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Contains a connection to a browser/client.
+    /// </summary>
+    /// <remarks>
+    /// Remember to <see cref="Start"/> after you have hooked the <see cref="RequestReceived"/> event.
+    /// </remarks>
+    public class HttpClientContext : IHttpClientContext, IDisposable
+    {
+        const int MAXREQUESTS = 20;
+        const int MAXKEEPALIVE = 60000;
+
+        static private int basecontextID;
+
+        private readonly byte[] m_ReceiveBuffer;
+        private int m_ReceiveBytesLeft;
+        private ILogWriter _log;
+        private readonly IHttpRequestParser m_parser;
+        private readonly int m_bufferSize;
+        private HashSet<uint> requestsInServiceIDs;
+        private Socket m_sock;
+
+        public bool Available = true;
+        public bool StreamPassedOff = false;
+
+        public int MonitorStartMS = 0;
+        public int MonitorKeepaliveMS = 0;
+        public bool TriggerKeepalive = false;
+        public int TimeoutFirstLine = 70000; // 70 seconds
+        public int TimeoutRequestReceived = 180000; // 180 seconds
+
+        // The difference between this and request received is on POST more time is needed before we get the full request.
+        public int TimeoutFullRequestProcessed = 600000; // 10 minutes
+        public int m_TimeoutKeepAlive = MAXKEEPALIVE; // 400 seconds before keepalive timeout
+        // public int TimeoutKeepAlive = 120000; // 400 seconds before keepalive timeout
+
+        public int m_maxRequests = MAXREQUESTS;
+
+        public bool FirstRequestLineReceived;
+        public bool FullRequestReceived;
+        public bool FullRequestProcessed;
+
+        private bool isSendingResponse = false;
+
+        private HttpRequest m_currentRequest;
+        private HttpResponse m_currentResponse;
+
+        public int contextID { get; private set; }
+        public int TimeoutKeepAlive
+        {
+            get { return m_TimeoutKeepAlive; }
+            set
+            {
+                m_TimeoutKeepAlive = (value > MAXKEEPALIVE) ? MAXKEEPALIVE : value;
+            }
+        }
+
+        public int MAXRequests
+        {
+            get { return m_maxRequests; }
+            set
+            {
+                m_maxRequests = value > MAXREQUESTS ? MAXREQUESTS : value;
+            }
+        }
+
+        public bool IsSending()
+        {
+            return isSendingResponse;
+        }
+
+        public bool StopMonitoring;
+
+        /// <summary>
+        /// Context have been started (a new client have connected)
+        /// </summary>
+        public event EventHandler Started;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="HttpClientContext"/> class.
+        /// </summary>
+        /// <param name="secured">true if the connection is secured (SSL/TLS)</param>
+        /// <param name="remoteEndPoint">client that connected.</param>
+        /// <param name="stream">Stream used for communication</param>
+        /// <param name="parserFactory">Used to create a <see cref="IHttpRequestParser"/>.</param>
+        /// <param name="bufferSize">Size of buffer to use when reading data. Must be at least 4096 bytes.</param>
+        /// <exception cref="SocketException">If <see cref="Socket.BeginReceive(byte[],int,int,SocketFlags,AsyncCallback,object)"/> fails</exception>
+        /// <exception cref="ArgumentException">Stream must be writable and readable.</exception>
+        public HttpClientContext(bool secured, IPEndPoint remoteEndPoint,
+                                    Stream stream, IRequestParserFactory parserFactory, Socket sock)
+        {
+            if (!stream.CanWrite || !stream.CanRead)
+                throw new ArgumentException("Stream must be writable and readable.");
+
+            RemoteAddress = remoteEndPoint.Address.ToString();
+            RemotePort = remoteEndPoint.Port.ToString();
+            _log = NullLogWriter.Instance;
+            m_parser = parserFactory.CreateParser(_log);
+            m_parser.RequestCompleted += OnRequestCompleted;
+            m_parser.RequestLineReceived += OnRequestLine;
+            m_parser.HeaderReceived += OnHeaderReceived;
+            m_parser.BodyBytesReceived += OnBodyBytesReceived;
+            m_currentRequest = new HttpRequest(this);
+            IsSecured = secured;
+            _stream = stream;
+            m_sock = sock;
+
+            m_bufferSize = 8196;
+            m_ReceiveBuffer = new byte[m_bufferSize];
+            requestsInServiceIDs = new HashSet<uint>();
+
+            SSLCommonName = "";
+            if (secured)
+            {
+                SslStream _ssl = (SslStream)_stream;
+                X509Certificate _cert1 = _ssl.RemoteCertificate;
+                if (_cert1 != null)
+                {
+                    X509Certificate2 _cert2 = new X509Certificate2(_cert1);
+                    if (_cert2 != null)
+                        SSLCommonName = _cert2.GetNameInfo(X509NameType.SimpleName, false);
+                }
+            }
+
+            ++basecontextID;
+            if (basecontextID <= 0)
+                basecontextID = 1;
+
+            contextID = basecontextID;
+        }
+
+        public bool CanSend()
+        {
+            if (contextID < 0)
+                return false;
+
+            if (Stream == null || m_sock == null || !m_sock.Connected)
+                return false;
+
+            return true;
+        }
+
+        /// <summary>
+        /// Process incoming body bytes.
+        /// </summary>
+        /// <param name="sender"><see cref="IHttpRequestParser"/></param>
+        /// <param name="e">Bytes</param>
+        protected virtual void OnBodyBytesReceived(object sender, BodyEventArgs e)
+        {
+            m_currentRequest.AddToBody(e.Buffer, e.Offset, e.Count);
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        protected virtual void OnHeaderReceived(object sender, HeaderEventArgs e)
+        {
+            if (string.Compare(e.Name, "expect", true) == 0 && e.Value.Contains("100-continue"))
+            {
+                lock (requestsInServiceIDs)
+                {
+                    if (requestsInServiceIDs.Count == 0)
+                        Respond("HTTP/1.1", HttpStatusCode.Continue, "Please continue.");
+                }
+            }
+            m_currentRequest.AddHeader(e.Name, e.Value);
+        }
+
+        private void OnRequestLine(object sender, RequestLineEventArgs e)
+        {
+            m_currentRequest.Method = e.HttpMethod;
+            m_currentRequest.HttpVersion = e.HttpVersion;
+            m_currentRequest.UriPath = e.UriPath;
+            m_currentRequest.AddHeader("remote_addr", RemoteAddress);
+            m_currentRequest.AddHeader("remote_port", RemotePort);
+            FirstRequestLineReceived = true;
+            TriggerKeepalive = false;
+            MonitorKeepaliveMS = 0;
+        }
+
+        /// <summary>
+        /// Start reading content.
+        /// </summary>
+        /// <remarks>
+        /// Make sure to call base.Start() if you override this method.
+        /// </remarks>
+        public virtual void Start()
+        {
+            ReceiveLoop();
+            Started?.Invoke(this, EventArgs.Empty);
+        }
+
+        /// <summary>
+        /// Clean up context.
+        /// </summary>
+        /// <remarks>
+        /// </remarks>
+        public virtual void Cleanup()
+        {
+            if (StreamPassedOff)
+                return;
+
+            if (Stream != null)
+            {
+                Stream.Close();
+                Stream = null;
+                m_sock = null;
+            }
+
+            m_currentRequest?.Clear();
+            m_currentRequest = null;
+            m_currentResponse?.Clear();
+            m_currentResponse = null;
+            requestsInServiceIDs.Clear();
+
+            FirstRequestLineReceived = false;
+            FullRequestReceived = false;
+            FullRequestProcessed = false;
+            MonitorStartMS = 0;
+            StopMonitoring = true;
+            MonitorKeepaliveMS = 0;
+            TriggerKeepalive = false;
+
+            isSendingResponse = false;
+
+            m_ReceiveBytesLeft = 0;
+
+            contextID = -100;
+            m_parser.Clear();
+        }
+
+        public void Close()
+        {
+            Dispose();
+        }
+
+        /// <summary>
+        /// Using SSL or other encryption method.
+        /// </summary>
+        [Obsolete("Use IsSecured instead.")]
+        public bool Secured
+        {
+            get { return IsSecured; }
+        }
+
+        /// <summary>
+        /// Using SSL or other encryption method.
+        /// </summary>
+        public bool IsSecured { get; internal set; }
+
+
+        // returns the SSL commonName of remote Certificate
+        public string SSLCommonName { get; internal set; }
+
+        /// <summary>
+        /// Specify which logger to use.
+        /// </summary>
+        public ILogWriter LogWriter
+        {
+            get { return _log; }
+            set
+            {
+                _log = value ?? NullLogWriter.Instance;
+                m_parser.LogWriter = _log;
+            }
+        }
+
+        private Stream _stream;
+
+        /// <summary>
+        /// Gets or sets the network stream.
+        /// </summary>
+        internal Stream Stream
+        {
+            get { return _stream; }
+            set { _stream = value; }
+        }
+
+        /// <summary>
+        /// Gets or sets IP address that the client connected from.
+        /// </summary>
+        internal string RemoteAddress { get; set; }
+
+        /// <summary>
+        /// Gets or sets port that the client connected from.
+        /// </summary>
+        internal string RemotePort { get; set; }
+
+        /// <summary>
+        /// Disconnect from client
+        /// </summary>
+        /// <param name="error">error to report in the <see cref="Disconnected"/> event.</param>
+        public void Disconnect(SocketError error)
+        {
+            // disconnect may not throw any exceptions
+            try
+            {
+                try
+                {
+                    if (Stream != null)
+                    {
+                        if (error == SocketError.Success)
+                            Stream.Flush();
+                        Stream.Close();
+                        Stream = null;
+                    }
+                    m_sock = null;
+                }
+                catch { }
+
+                Disconnected?.Invoke(this, new DisconnectedEventArgs(error));
+            }
+            catch (Exception err)
+            {
+                LogWriter.Write(this, LogPrio.Error, "Disconnect threw an exception: " + err);
+            }
+        }
+
+        private void OnReceive(IAsyncResult ar)
+        {
+            try
+            {
+                int bytesRead = 0;
+                if (Stream == null)
+                    return;
+                try
+                {
+                    bytesRead = Stream.EndRead(ar);
+                }
+                catch (NullReferenceException)
+                {
+                    Disconnect(SocketError.ConnectionReset);
+                    return;
+                }
+
+                if (bytesRead == 0)
+                {
+                    Disconnect(SocketError.ConnectionReset);
+                    return;
+                }
+
+                m_ReceiveBytesLeft += bytesRead;
+                if (m_ReceiveBytesLeft > m_ReceiveBuffer.Length)
+                {
+                    throw new BadRequestException("HTTP header Too large: " + m_ReceiveBytesLeft);
+                }
+
+                int offset = m_parser.Parse(m_ReceiveBuffer, 0, m_ReceiveBytesLeft);
+                if (Stream == null)
+                    return; // "Connection: Close" in effect.
+
+                // try again to see if we can parse another message (check parser to see if it is looking for a new message)
+                int nextOffset;
+                int nextBytesleft = m_ReceiveBytesLeft - offset;
+
+                while (offset != 0 && nextBytesleft > 0)
+                {
+                    nextOffset = m_parser.Parse(m_ReceiveBuffer, offset, nextBytesleft);
+
+                    if (Stream == null)
+                        return; // "Connection: Close" in effect.
+
+                    if (nextOffset == 0)
+                        break;
+
+                    offset = nextOffset;
+                    nextBytesleft = m_ReceiveBytesLeft - offset;
+                }
+
+                // copy unused bytes to the beginning of the array
+                if (offset > 0 && m_ReceiveBytesLeft > offset)
+                    Buffer.BlockCopy(m_ReceiveBuffer, offset, m_ReceiveBuffer, 0, m_ReceiveBytesLeft - offset);
+
+                m_ReceiveBytesLeft -= offset;
+                if (Stream != null && Stream.CanRead)
+                {
+                    if (!StreamPassedOff)
+                        Stream.BeginRead(m_ReceiveBuffer, m_ReceiveBytesLeft, m_ReceiveBuffer.Length - m_ReceiveBytesLeft, OnReceive, null);
+                    else
+                    {
+                        _log.Write(this, LogPrio.Warning, "Could not read any more from the socket.");
+                        Disconnect(SocketError.Success);
+                    }
+                }
+            }
+            catch (BadRequestException err)
+            {
+                LogWriter.Write(this, LogPrio.Warning, "Bad request, responding with it. Error: " + err);
+                try
+                {
+                    Respond("HTTP/1.0", HttpStatusCode.BadRequest, err.Message);
+                }
+                catch (Exception err2)
+                {
+                    LogWriter.Write(this, LogPrio.Fatal, "Failed to reply to a bad request. " + err2);
+                }
+                Disconnect(SocketError.NoRecovery);
+            }
+            catch (IOException err)
+            {
+                LogWriter.Write(this, LogPrio.Debug, "Failed to end receive: " + err.Message);
+                if (err.InnerException is SocketException)
+                    Disconnect((SocketError)((SocketException)err.InnerException).ErrorCode);
+                else
+                    Disconnect(SocketError.ConnectionReset);
+            }
+            catch (ObjectDisposedException err)
+            {
+                LogWriter.Write(this, LogPrio.Debug, "Failed to end receive : " + err.Message);
+                Disconnect(SocketError.NotSocket);
+            }
+            catch (NullReferenceException err)
+            {
+                LogWriter.Write(this, LogPrio.Debug, "Failed to end receive : NullRef: " + err.Message);
+                Disconnect(SocketError.NoRecovery);
+            }
+            catch (Exception err)
+            {
+                LogWriter.Write(this, LogPrio.Debug, "Failed to end receive: " + err.Message);
+                Disconnect(SocketError.NoRecovery);
+            }
+        }
+
+        private async void ReceiveLoop()
+        {
+            m_ReceiveBytesLeft = 0;
+            try
+            {
+                while(true)
+                {
+                    if (_stream == null || !_stream.CanRead)
+                        return;
+
+                    int bytesRead = await _stream.ReadAsync(m_ReceiveBuffer, m_ReceiveBytesLeft, m_ReceiveBuffer.Length - m_ReceiveBytesLeft).ConfigureAwait(false);
+
+                    if (bytesRead == 0)
+                    {
+                        Disconnect(SocketError.ConnectionReset);
+                        return;
+                    }
+
+                    m_ReceiveBytesLeft += bytesRead;
+                    if (m_ReceiveBytesLeft > m_ReceiveBuffer.Length)
+                        throw new BadRequestException("HTTP header Too large: " + m_ReceiveBytesLeft);
+
+                    int offset = m_parser.Parse(m_ReceiveBuffer, 0, m_ReceiveBytesLeft);
+                    if (Stream == null)
+                        return; // "Connection: Close" in effect.
+
+                    // try again to see if we can parse another message (check parser to see if it is looking for a new message)
+                    int nextOffset;
+                    int nextBytesleft = m_ReceiveBytesLeft - offset;
+
+                    while (offset != 0 && nextBytesleft > 0)
+                    {
+                        nextOffset = m_parser.Parse(m_ReceiveBuffer, offset, nextBytesleft);
+
+                        if (Stream == null)
+                            return; // "Connection: Close" in effect.
+
+                        if (nextOffset == 0)
+                            break;
+
+                        offset = nextOffset;
+                        nextBytesleft = m_ReceiveBytesLeft - offset;
+                    }
+
+                    // copy unused bytes to the beginning of the array
+                    if (offset > 0 && m_ReceiveBytesLeft > offset)
+                        Buffer.BlockCopy(m_ReceiveBuffer, offset, m_ReceiveBuffer, 0, m_ReceiveBytesLeft - offset);
+
+                    m_ReceiveBytesLeft -= offset;
+                    if (StreamPassedOff)
+                        return; //?
+                }
+            }
+            catch (BadRequestException err)
+            {
+                LogWriter.Write(this, LogPrio.Warning, "Bad request, responding with it. Error: " + err);
+                try
+                {
+                    Respond("HTTP/1.0", HttpStatusCode.BadRequest, err.Message);
+                }
+                catch (Exception err2)
+                {
+                    LogWriter.Write(this, LogPrio.Fatal, "Failed to reply to a bad request. " + err2);
+                }
+                Disconnect(SocketError.NoRecovery);
+            }
+            catch (IOException err)
+            {
+                LogWriter.Write(this, LogPrio.Debug, "Failed to end receive: " + err.Message);
+                if (err.InnerException is SocketException)
+                    Disconnect((SocketError)((SocketException)err.InnerException).ErrorCode);
+                else
+                    Disconnect(SocketError.ConnectionReset);
+            }
+            catch (ObjectDisposedException err)
+            {
+                LogWriter.Write(this, LogPrio.Debug, "Failed to end receive : " + err.Message);
+                Disconnect(SocketError.NotSocket);
+            }
+            catch (NullReferenceException err)
+            {
+                LogWriter.Write(this, LogPrio.Debug, "Failed to end receive : NullRef: " + err.Message);
+                Disconnect(SocketError.NoRecovery);
+            }
+            catch (Exception err)
+            {
+                LogWriter.Write(this, LogPrio.Debug, "Failed to end receive: " + err.Message);
+                Disconnect(SocketError.NoRecovery);
+            }
+        }
+
+        private void OnRequestCompleted(object source, EventArgs args)
+        {
+            TriggerKeepalive = false;
+            MonitorKeepaliveMS = 0;
+
+            // load cookies if they exist
+
+            RequestCookies cookies = m_currentRequest.Headers["cookie"] != null
+                ? new RequestCookies(m_currentRequest.Headers["cookie"]) : new RequestCookies(String.Empty);
+            m_currentRequest.SetCookies(cookies);
+
+            m_currentRequest.Body.Seek(0, SeekOrigin.Begin);
+
+            FullRequestReceived = true;
+
+            int nreqs;
+            lock (requestsInServiceIDs)
+            {
+                nreqs = requestsInServiceIDs.Count;
+                requestsInServiceIDs.Add(m_currentRequest.ID);
+                if (m_maxRequests > 0)
+                    m_maxRequests--;
+            }
+
+            // for now pipeline requests need to be serialized by opensim
+            RequestReceived(this, new RequestEventArgs(m_currentRequest));
+
+            m_currentRequest = new HttpRequest(this);
+
+            int nreqsnow;
+            lock (requestsInServiceIDs)
+            {
+                nreqsnow = requestsInServiceIDs.Count;
+            }
+            if (nreqs != nreqsnow)
+            {
+                // request was not done by us
+            }
+        }
+
+        public void ReqResponseAboutToSend(uint requestID)
+        {
+            isSendingResponse = true;
+        }
+
+        public void StartSendResponse(HttpResponse response)
+        {
+            isSendingResponse = true;
+            m_currentResponse = response;
+            ContextTimeoutManager.EnqueueSend(this, response.Priority);
+        }
+
+        public bool TrySendResponse(int bytesLimit)
+        {
+            if(m_currentResponse == null)
+                return false;
+            if (m_currentResponse.Sent)
+                return false;
+
+            if(!CanSend())
+                return false;
+
+            m_currentResponse?.SendNextAsync(bytesLimit);
+            return false;
+        }
+
+        public void ContinueSendResponse()
+        {
+            if(m_currentResponse == null)
+                return;
+            ContextTimeoutManager.EnqueueSend(this, m_currentResponse.Priority);
+        }
+
+        public void ReqResponseSent(uint requestID, ConnectionType ctype)
+        {
+            isSendingResponse = false;
+            m_currentResponse?.Clear();
+            m_currentResponse = null;
+
+            bool doclose = ctype == ConnectionType.Close;
+            lock (requestsInServiceIDs)
+            {
+                requestsInServiceIDs.Remove(requestID);
+//                doclose = doclose && requestsInServiceIDs.Count == 0;
+                if (requestsInServiceIDs.Count > 1)
+                {
+
+                }
+            }
+
+            if (doclose)
+                Disconnect(SocketError.Success);
+            else
+            {
+                lock (requestsInServiceIDs)
+                {
+                    if (requestsInServiceIDs.Count == 0)
+                        TriggerKeepalive = true;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Send a response.
+        /// </summary>
+        /// <param name="httpVersion">Either <see cref="HttpHelper.HTTP10"/> or <see cref="HttpHelper.HTTP11"/></param>
+        /// <param name="statusCode">HTTP status code</param>
+        /// <param name="reason">reason for the status code.</param>
+        /// <param name="body">HTML body contents, can be null or empty.</param>
+        /// <param name="contentType">A content type to return the body as, i.e. 'text/html' or 'text/plain', defaults to 'text/html' if null or empty</param>
+        /// <exception cref="ArgumentException">If <paramref name="httpVersion"/> is invalid.</exception>
+        public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body, string contentType)
+        {
+            if (string.IsNullOrEmpty(contentType))
+                contentType = "text/html";
+
+            if (string.IsNullOrEmpty(reason))
+                reason = statusCode.ToString();
+
+            string response = string.IsNullOrEmpty(body)
+                                  ? httpVersion + " " + (int)statusCode + " " + reason + "\r\n\r\n"
+                                  : string.Format("{0} {1} {2}\r\nContent-Type: {5}\r\nContent-Length: {3}\r\n\r\n{4}",
+                                                  httpVersion, (int)statusCode, reason ?? statusCode.ToString(),
+                                                  body.Length, body, contentType);
+            byte[] buffer = Encoding.ASCII.GetBytes(response);
+
+            Send(buffer);
+            if (m_currentRequest.Connection == ConnectionType.Close)
+                FullRequestProcessed = true;
+        }
+
+        /// <summary>
+        /// Send a response.
+        /// </summary>
+        /// <param name="httpVersion">Either <see cref="HttpHelper.HTTP10"/> or <see cref="HttpHelper.HTTP11"/></param>
+        /// <param name="statusCode">HTTP status code</param>
+        /// <param name="reason">reason for the status code.</param>
+        public void Respond(string httpVersion, HttpStatusCode statusCode, string reason)
+        {
+            Respond(httpVersion, statusCode, reason, null, null);
+        }
+
+        /// <summary>
+        /// send a whole buffer
+        /// </summary>
+        /// <param name="buffer">buffer to send</param>
+        /// <exception cref="ArgumentNullException"></exception>
+        public bool Send(byte[] buffer)
+        {
+            if (buffer == null)
+                throw new ArgumentNullException("buffer");
+            return Send(buffer, 0, buffer.Length);
+        }
+
+        /// <summary>
+        /// Send data using the stream
+        /// </summary>
+        /// <param name="buffer">Contains data to send</param>
+        /// <param name="offset">Start position in buffer</param>
+        /// <param name="size">number of bytes to send</param>
+        /// <exception cref="ArgumentNullException"></exception>
+        /// <exception cref="ArgumentOutOfRangeException"></exception>
+
+        private object sendLock = new object();
+
+        public bool Send(byte[] buffer, int offset, int size)
+        {
+            if (Stream == null || m_sock == null || !m_sock.Connected)
+                return false;
+
+            if (offset + size > buffer.Length)
+                throw new ArgumentOutOfRangeException("offset", offset, "offset + size is beyond end of buffer.");
+
+            bool ok = true;
+            lock (sendLock) // can't have overlaps here
+            {
+                try
+                {
+                    Stream.Write(buffer, offset, size);
+                }
+                catch
+                {
+                    ok = false;
+                }
+
+                if (!ok && Stream != null)
+                    Disconnect(SocketError.NoRecovery);
+                return ok;
+            }
+        }
+
+        public async Task<bool> SendAsync(byte[] buffer, int offset, int size)
+        {
+            if (Stream == null || m_sock == null || !m_sock.Connected)
+                return false;
+
+            if (offset + size > buffer.Length)
+                throw new ArgumentOutOfRangeException("offset", offset, "offset + size is beyond end of buffer.");
+
+            bool ok = true;
+            ContextTimeoutManager.ContextEnterActiveSend();
+            try
+            {
+                await Stream.WriteAsync(buffer, offset, size).ConfigureAwait(false);
+            }
+            catch
+            {
+                ok = false;
+            }
+
+            ContextTimeoutManager.ContextLeaveActiveSend();
+
+            if (!ok && Stream != null)
+                Disconnect(SocketError.NoRecovery);
+            return ok;
+        }
+
+        /// <summary>
+        /// The context have been disconnected.
+        /// </summary>
+        /// <remarks>
+        /// Event can be used to clean up a context, or to reuse it.
+        /// </remarks>
+        public event EventHandler<DisconnectedEventArgs> Disconnected = delegate { };
+        /// <summary>
+        /// A request have been received in the context.
+        /// </summary>
+        public event EventHandler<RequestEventArgs> RequestReceived = delegate { };
+
+        public HTTPNetworkContext GiveMeTheNetworkStreamIKnowWhatImDoing()
+        {
+            StreamPassedOff = true;
+            m_parser.RequestCompleted -= OnRequestCompleted;
+            m_parser.RequestLineReceived -= OnRequestLine;
+            m_parser.HeaderReceived -= OnHeaderReceived;
+            m_parser.BodyBytesReceived -= OnBodyBytesReceived;
+            m_parser.Clear();
+
+            return new HTTPNetworkContext() { Socket = m_sock, Stream = _stream as NetworkStream };
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        public void Dispose(bool disposing)
+        {
+            if (contextID >= 0)
+            {
+                StreamPassedOff = false;
+                Cleanup();
+            }
+        }
+    }
+}

+ 195 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpContextFactory.cs

@@ -0,0 +1,195 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.IO;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Used to create and reuse contexts.
+    /// </summary>
+    public class HttpContextFactory : IHttpContextFactory
+    {
+        private readonly ConcurrentDictionary<int, HttpClientContext> m_activeContexts = new ConcurrentDictionary<int, HttpClientContext>();
+        private readonly IRequestParserFactory m_parserfactory;
+        private readonly ILogWriter m_logWriter;
+
+        /// <summary>
+        /// A request have been received from one of the contexts.
+        /// </summary>
+        public event EventHandler<RequestEventArgs> RequestReceived;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="HttpContextFactory"/> class.
+        /// </summary>
+        /// <param name="writer">The writer.</param>
+        /// <param name="bufferSize">Amount of bytes to read from the incoming socket stream.</param>
+        /// <param name="factory">Used to create a request parser.</param>
+        public HttpContextFactory(ILogWriter writer, IRequestParserFactory factory)
+        {
+            m_logWriter = writer;
+            m_parserfactory = factory;
+            ContextTimeoutManager.Start();
+        }
+
+        /// <summary>
+        /// Create a new context.
+        /// </summary>
+        /// <param name="isSecured">true if socket is running HTTPS.</param>
+        /// <param name="endPoint">Client that connected</param>
+        /// <param name="stream">Network/SSL stream.</param>
+        /// <returns>A context.</returns>
+        protected HttpClientContext CreateContext(bool isSecured, IPEndPoint endPoint, Stream stream, Socket sock)
+        {
+            HttpClientContext context;
+
+            context = CreateNewContext(isSecured, endPoint, stream, sock);
+            context.Disconnected += OnFreeContext;
+            context.RequestReceived += OnRequestReceived;
+
+            context.Stream = stream;
+            context.IsSecured = isSecured;
+            context.RemotePort = endPoint.Port.ToString();
+            context.RemoteAddress = endPoint.Address.ToString();
+            ContextTimeoutManager.StartMonitoringContext(context);
+            m_activeContexts[context.contextID] = context;
+            context.Start();
+            return context;
+        }
+
+        /// <summary>
+        /// Create a new context.
+        /// </summary>
+        /// <param name="isSecured">true if HTTPS is used.</param>
+        /// <param name="endPoint">Remote client</param>
+        /// <param name="stream">Network stream, <see cref="HttpClientContext"/></param>
+        /// <returns>A new context (always).</returns>
+        protected virtual HttpClientContext CreateNewContext(bool isSecured, IPEndPoint endPoint, Stream stream, Socket sock)
+        {
+            return new HttpClientContext(isSecured, endPoint, stream, m_parserfactory, sock);
+        }
+
+        private void OnRequestReceived(object sender, RequestEventArgs e)
+        {
+            RequestReceived?.Invoke(sender, e);
+        }
+
+        private void OnFreeContext(object sender, DisconnectedEventArgs e)
+        {
+            var imp = sender as HttpClientContext;
+            if (imp == null || imp.contextID < 0)
+                return;
+
+            m_activeContexts.TryRemove(imp.contextID, out HttpClientContext dummy);
+
+            imp.Close();
+        }
+
+
+        #region IHttpContextFactory Members
+
+        /// <summary>
+        /// Create a secure <see cref="IHttpClientContext"/>.
+        /// </summary>
+        /// <param name="socket">Client socket (accepted by the <see cref="OSHttpListener"/>).</param>
+        /// <param name="certificate">HTTPS certificate to use.</param>
+        /// <param name="protocol">Kind of HTTPS protocol. Usually TLS or SSL.</param>
+        /// <returns>
+        /// A created <see cref="IHttpClientContext"/>.
+        /// </returns>
+        public IHttpClientContext CreateSecureContext(Socket socket, X509Certificate certificate,
+             SslProtocols protocol, RemoteCertificateValidationCallback _clientCallback = null)
+        {
+            socket.NoDelay = true;
+            var networkStream = new NetworkStream(socket, true);
+            var remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint;
+
+            SslStream sslStream = null;
+            try
+            {
+                if (_clientCallback == null)
+                {
+                    sslStream = new SslStream(networkStream, false);
+                    sslStream.AuthenticateAsServer(certificate, false, protocol, false);
+                }
+                else
+                {
+                    sslStream = new SslStream(networkStream, false,
+                            new RemoteCertificateValidationCallback(_clientCallback));
+                    sslStream.AuthenticateAsServer(certificate, true, protocol, false);
+                }
+            }
+            catch (Exception e)
+            {
+                m_logWriter.Write(this, LogPrio.Error, e.Message);
+                sslStream.Close();
+                return null;
+            }
+
+            return CreateContext(true, remoteEndPoint, sslStream, socket);
+        }
+
+        /// <summary>
+        /// Creates a <see cref="IHttpClientContext"/> that handles a connected client.
+        /// </summary>
+        /// <param name="socket">Client socket (accepted by the <see cref="OSHttpListener"/>).</param>
+        /// <returns>
+        /// A creates <see cref="IHttpClientContext"/>.
+        /// </returns>
+        public IHttpClientContext CreateContext(Socket socket)
+        {
+            socket.NoDelay = true;
+            var networkStream = new NetworkStream(socket, true);
+            var remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint;
+            return CreateContext(false, remoteEndPoint, networkStream, socket);
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Server is shutting down so shut down the factory
+        /// </summary>
+        public void Shutdown()
+        {
+            ContextTimeoutManager.Stop();
+        }
+    }
+
+    /// <summary>
+    /// Used to create <see cref="IHttpClientContext"/>es.
+    /// </summary>
+    public interface IHttpContextFactory
+    {
+        /// <summary>
+        /// Creates a <see cref="IHttpClientContext"/> that handles a connected client.
+        /// </summary>
+        /// <param name="socket">Client socket (accepted by the <see cref="OSHttpListener"/>).</param>
+        /// <returns>A creates <see cref="IHttpClientContext"/>.</returns>
+        IHttpClientContext CreateContext(Socket socket);
+
+        /// <summary>
+        /// Create a secure <see cref="IHttpClientContext"/>.
+        /// </summary>
+        /// <param name="socket">Client socket (accepted by the <see cref="OSHttpListener"/>).</param>
+        /// <param name="certificate">HTTPS certificate to use.</param>
+        /// <param name="protocol">Kind of HTTPS protocol. Usually TLS or SSL.</param>
+        /// <returns>A created <see cref="IHttpClientContext"/>.</returns>
+        IHttpClientContext CreateSecureContext(Socket socket, X509Certificate certificate,
+             SslProtocols protocol, RemoteCertificateValidationCallback _clientCallback = null);
+
+        /// <summary>
+        /// A request have been received from one of the contexts.
+        /// </summary>
+        event EventHandler<RequestEventArgs> RequestReceived;
+
+        /// <summary>
+        /// Server is shutting down so shut down the factory
+        /// </summary>
+        void Shutdown();
+    }
+}

+ 43 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpException.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Net;
+
+namespace OSHttpServer.Exceptions
+{
+    /// <summary>
+    /// All HTTP based exceptions will derive this class.
+    /// </summary>
+    public class HttpException : Exception
+    {
+        private readonly HttpStatusCode _code;
+
+        /// <summary>
+        /// Create a new HttpException
+        /// </summary>
+        /// <param name="code">http status code (sent in the response)</param>
+        /// <param name="message">error description</param>
+        public HttpException(HttpStatusCode code, string message) : base(code + ": " + message)
+        {
+            _code = code;
+        }
+
+        /// <summary>
+        /// Create a new HttpException
+        /// </summary>
+        /// <param name="code">http status code (sent in the response)</param>
+        /// <param name="message">error description</param>
+        /// <param name="inner">inner exception</param>
+        public HttpException(HttpStatusCode code, string message, Exception inner)
+            : base(code + ": " + message, inner)
+        {
+            _code = code;
+        }
+
+        /// <summary>
+        /// status code to use in the response.
+        /// </summary>
+        public HttpStatusCode HttpStatusCode
+        {
+            get { return _code; }
+        }
+    }
+}

+ 118 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpHelper.cs

@@ -0,0 +1,118 @@
+using System;
+using System.Web;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Generic helper functions for HTTP
+    /// </summary>
+    public static class HttpHelper
+    {
+        /// <summary>
+        /// An empty URI
+        /// </summary>
+        public static readonly Uri EmptyUri = new Uri("http://localhost/");
+
+        /// <summary>
+        /// Parses a query string.
+        /// </summary>
+        /// <param name="queryString">Query string (URI encoded)</param>
+        /// <returns>A <see cref="HttpInput"/> object if successful; otherwise <see cref="HttpInput.Empty"/></returns>
+        /// <exception cref="ArgumentNullException"><c>queryString</c> is null.</exception>
+		/// <exception cref="FormatException">If string cannot be parsed.</exception>
+        public static HttpInput ParseQueryString(string queryString)
+        {
+            if (queryString == null)
+                throw new ArgumentNullException("queryString");
+            if (queryString == string.Empty)
+                return HttpInput.Empty;
+
+            HttpInput input = new HttpInput("QueryString");
+
+            queryString = queryString.TrimStart('?', '&');
+
+            // a simple value.
+            if (queryString.IndexOf("&") == -1 && !queryString.Contains("%3d") && !queryString.Contains("%3D") && !queryString.Contains("="))
+            {
+                input.Add(string.Empty, queryString);
+                return input;
+            }
+
+            int state = 0;
+            int startpos = 0;
+            string name = null;
+            for (int i = 0; i < queryString.Length; ++i)
+            {
+                int newIndexPos;
+                if (state == 0 && IsEqual(queryString, ref i, out newIndexPos))
+                {
+                    name = queryString.Substring(startpos, i - startpos);
+                    i = newIndexPos;
+                    startpos = i + 1;
+                    ++state;
+                }
+                else if (state == 1 && IsAmp(queryString, ref i, out newIndexPos))
+                {
+                    Add(input, name, queryString.Substring(startpos, i - startpos));
+                    i = newIndexPos;
+                    startpos = i + 1;
+                    state = 0;
+                    name = null;
+                }
+            }
+
+            if (state == 0 && !input.GetEnumerator().MoveNext())
+                throw new FormatException("Not a valid query string: " + queryString);
+
+            if (startpos <= queryString.Length)
+            {
+                if (name != null)
+                    Add(input, name, queryString.Substring(startpos, queryString.Length - startpos));
+                else
+                    Add(input, string.Empty, queryString.Substring(startpos, queryString.Length - startpos));
+            }
+
+            return input;
+        }
+
+        private static bool IsEqual(string queryStr, ref int index, out int outIndex)
+        {
+            outIndex = index;
+            if (queryStr[index] == '=')
+                return true;
+            if (queryStr[index] == '%' && queryStr.Length > index + 2 && queryStr[index + 1] == '3'
+                && (queryStr[index + 2] == 'd' || queryStr[index + 2] == 'D'))
+            {
+                outIndex += 2;
+                return true;
+            }
+            return false;
+        }
+
+        private static bool IsAmp(string queryStr, ref int index, out int outIndex)
+        {
+            outIndex = index;
+            if (queryStr[index] == '%' && queryStr.Length > index + 2 && queryStr[index + 1] == '2' &&
+                queryStr[index + 2] == '6')
+                outIndex += 2;
+            else if (queryStr[index] == '&')
+            {
+                if (queryStr.Length > index + 4
+                    && (queryStr[index + 1] == 'a' || queryStr[index + 1] == 'A')
+                    && (queryStr[index + 2] == 'm' || queryStr[index + 2] == 'M')
+                    && (queryStr[index + 3] == 'p' || queryStr[index + 3] == 'P')
+                    && queryStr[index + 4] == ';')
+                    outIndex += 4;
+            }
+            else
+                return false;
+
+            return true;
+        }
+
+        private static void Add(IHttpInput input, string name, string value)
+        {
+            input.Add(HttpUtility.UrlDecode(name), HttpUtility.UrlDecode(value));
+        }
+    }
+}

+ 263 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpInput.cs

@@ -0,0 +1,263 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Contains some kind of input from the browser/client.
+    /// can be QueryString, form data or any other request body content.
+    /// </summary>
+    public class HttpInput : IHttpInput
+    {
+        /// <summary> Representation of a non-initialized class instance </summary>
+        public static readonly HttpInput Empty = new HttpInput("Empty", true);
+        private readonly IDictionary<string, HttpInputItem> _items = new Dictionary<string, HttpInputItem>();
+        private string _name;
+
+        /// <summary> Variable telling the class that it is non-initialized <see cref="Empty"/> </summary>
+        protected readonly bool _ignoreChanges;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="HttpInput"/> class.
+        /// </summary>
+        /// <param name="name">form name.</param>
+        public HttpInput(string name)
+        {
+            Name = name;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="HttpInput"/> class.
+        /// </summary>
+        /// <param name="name">form name.</param>
+        /// <param name="ignoreChanges">if set to <c>true</c> all changes will be ignored. </param>
+        /// <remarks>this constructor should only be used by Empty</remarks>
+		protected HttpInput(string name, bool ignoreChanges)
+        {
+            _name = name;
+            _ignoreChanges = ignoreChanges;
+        }
+
+        /// <summary>Creates a deep copy of the HttpInput class</summary>
+        /// <param name="input">The object to copy</param>
+        /// <remarks>The function makes a deep copy of quite a lot which can be slow</remarks>
+        protected HttpInput(HttpInput input)
+        {
+            foreach (HttpInputItem item in input)
+                _items.Add(item.Name, new HttpInputItem(item));
+
+            _name = input._name;
+            _ignoreChanges = input._ignoreChanges;
+        }
+
+        /// <summary>
+        /// Form name as lower case
+        /// </summary>
+        public string Name
+        {
+            get { return _name; }
+            set { _name = value; }
+        }
+
+        /// <summary>
+        /// Add a new element. Form array elements are parsed
+        /// and added in a correct hierarchy.
+        /// </summary>
+        /// <param name="name">Name is converted to lower case.</param>
+        /// <param name="value"></param>
+        /// <exception cref="ArgumentNullException"><c>name</c> is null.</exception>
+        /// <exception cref="InvalidOperationException">Cannot add stuff to <see cref="HttpInput.Empty"/>.</exception>
+        public void Add(string name, string value)
+        {
+            if (name == null)
+                throw new ArgumentNullException("name");
+            if (_ignoreChanges)
+                throw new InvalidOperationException("Cannot add stuff to HttpInput.Empty.");
+
+            // Check if it's a sub item.
+            // we can have multiple levels of sub items as in user[extension[id]] => user -> extension -> id
+            int pos = name.IndexOf('[');
+            if (pos != -1)
+            {
+                string name1 = name.Substring(0, pos);
+                string name2 = ExtractOne(name);
+                if (!_items.ContainsKey(name1))
+                    _items.Add(name1, new HttpInputItem(name1, null));
+                _items[name1].Add(name2, value);
+            }
+            else
+            {
+                if (_items.ContainsKey(name))
+                    _items[name].Add(value);
+                else
+                    _items.Add(name, new HttpInputItem(name, value));
+            }
+        }
+
+        /// <summary>
+        /// Get a form item.
+        /// </summary>
+        /// <param name="name"></param>
+        /// <returns>Returns <see cref="HttpInputItem.Empty"/> if item was not found.</returns>
+        public HttpInputItem this[string name]
+        {
+            get
+            {
+                return _items.ContainsKey(name) ? _items[name] : HttpInputItem.Empty;
+            }
+        }
+
+        /// <summary>
+        /// Returns true if the class contains a <see cref="HttpInput"/> with the corresponding name.
+        /// </summary>
+        /// <param name="name">The field/query string name</param>
+        /// <returns>True if the value exists</returns>
+        public bool Contains(string name)
+        {
+            return _items.ContainsKey(name) && _items[name].Value != null;
+        }
+
+        /// <summary>
+        /// Parses an item and returns it.
+        /// This function is primarily used to parse array items as in user[name].
+        /// </summary>
+        /// <param name="name"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public static HttpInputItem ParseItem(string name, string value)
+        {
+            HttpInputItem item;
+
+            // Check if it's a sub item.
+            // we can have multiple levels of sub items as in user[extension[id]]] => user -> extension -> id
+            int pos = name.IndexOf('[');
+            if (pos != -1)
+            {
+                string name1 = name.Substring(0, pos);
+                string name2 = ExtractOne(name);
+                item = new HttpInputItem(name1, null);
+                item.Add(name2, value);
+            }
+            else
+                item = new HttpInputItem(name, value);
+
+            return item;
+        }
+
+        /// <summary> Outputs the instance representing all its values joined together </summary>
+        /// <returns></returns>
+        public override string ToString()
+        {
+            string temp = string.Empty;
+            foreach (KeyValuePair<string, HttpInputItem> item in _items)
+                temp += item.Value.ToString(Name);
+            return temp;
+        }
+
+        /// <summary>Returns all items as an unescaped query string.</summary>
+        /// <returns></returns>
+        public string ToString(bool asQueryString)
+        {
+            if (!asQueryString)
+                return ToString();
+
+            string temp = string.Empty;
+            foreach (KeyValuePair<string, HttpInputItem> item in _items)
+                temp += item.Value.ToString(null, true) + '&';
+
+            return temp.Length > 0 ? temp.Substring(0, temp.Length - 1) : string.Empty;
+        }
+
+        /// <summary>
+        /// Extracts one parameter from an array
+        /// </summary>
+        /// <param name="value">Containing the string array</param>
+        /// <returns>All but the first value</returns>
+        /// <example>
+        /// string test1 = ExtractOne("system[user][extension][id]");
+        /// string test2 = ExtractOne(test1);
+        /// string test3 = ExtractOne(test2);
+        /// // test1 = user[extension][id]
+        /// // test2 = extension[id]
+        /// // test3 = id
+        /// </example>
+        public static string ExtractOne(string value)
+        {
+            int pos = value.IndexOf('[');
+            if (pos != -1)
+            {
+                ++pos;
+                int gotMore = value.IndexOf('[', pos + 1);
+                if (gotMore != -1)
+                    value = value.Substring(pos, gotMore - pos - 1) + value.Substring(gotMore);
+                else
+                    value = value.Substring(pos, value.Length - pos - 1);
+            }
+            return value;
+        }
+
+        /// <summary>Resets all data contained by class</summary>
+        virtual public void Clear()
+        {
+            _name = string.Empty;
+            _items.Clear();
+        }
+
+        ///<summary>
+        ///Returns an enumerator that iterates through the collection.
+        ///</summary>
+        ///
+        ///<returns>
+        ///A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
+        ///</returns>
+        ///<filterpriority>1</filterpriority>
+        IEnumerator<HttpInputItem> IEnumerable<HttpInputItem>.GetEnumerator()
+        {
+            return _items.Values.GetEnumerator();
+        }
+
+
+        ///<summary>
+        ///Returns an enumerator that iterates through a collection.
+        ///</summary>
+        ///
+        ///<returns>
+        ///An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
+        ///</returns>
+        ///<filterpriority>2</filterpriority>
+        public IEnumerator GetEnumerator()
+        {
+            return _items.Values.GetEnumerator();
+        }
+
+    }
+
+    /// <summary>
+    /// Base class for request data containers
+    /// </summary>
+    public interface IHttpInput : IEnumerable<HttpInputItem>
+    {
+        /// <summary>
+        /// Adds a parameter mapped to the presented name
+        /// </summary>
+        /// <param name="name">The name to map the parameter to</param>
+        /// <param name="value">The parameter value</param>
+        void Add(string name, string value);
+
+        /// <summary>
+        /// Returns a request parameter
+        /// </summary>
+        /// <param name="name">The name associated with the parameter</param>
+        /// <returns></returns>
+        HttpInputItem this[string name]
+        { get; }
+
+        /// <summary>
+        /// Returns true if the container contains the requested parameter
+        /// </summary>
+        /// <param name="name">Parameter id</param>
+        /// <returns>True if parameter exists</returns>
+        bool Contains(string name);
+    }
+}

+ 309 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpInputItem.cs

@@ -0,0 +1,309 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// represents a HTTP input item. Each item can have multiple sub items, a sub item
+    /// is made in a HTML form by using square brackets
+    /// </summary>
+    /// <example>
+    ///   // <input type="text" name="user[FirstName]" value="jonas" /> becomes:
+    ///   Console.WriteLine("Value: {0}", form["user"]["FirstName"].Value);
+    /// </example>
+    /// <remarks>
+    /// All names in a form SHOULD be in lowercase.
+    /// </remarks>
+    public class HttpInputItem : IHttpInput
+    {
+		/// <summary> Representation of a non-initialized <see cref="HttpInputItem"/>.</summary>
+        public static readonly HttpInputItem Empty = new HttpInputItem(string.Empty, true);
+        private readonly IDictionary<string, HttpInputItem> _items = new Dictionary<string, HttpInputItem>();
+        private readonly List<string> _values = new List<string>();
+        private string _name;
+        private readonly bool _ignoreChanges;
+
+		/// <summary>
+		/// Initializes an input item setting its name/identifier and value
+		/// </summary>
+		/// <param name="name">Parameter name/id</param>
+		/// <param name="value">Parameter value</param>
+        public HttpInputItem(string name, string value)
+        {
+            Name = name;
+            Add(value);
+        }
+
+        private HttpInputItem(string name, bool ignore)
+        {
+            Name = name;
+            _ignoreChanges = ignore;
+        }
+
+		/// <summary>Creates a deep copy of the item specified</summary>
+		/// <param name="item">The item to copy</param>
+		/// <remarks>The function makes a deep copy of quite a lot which can be slow</remarks>
+		public HttpInputItem(HttpInputItem item)
+		{
+			foreach (KeyValuePair<string, HttpInputItem> pair in item._items)
+				_items.Add(pair.Key, pair.Value);
+
+			foreach (string value in item._values)
+				_values.Add(value);
+
+			_ignoreChanges = item._ignoreChanges;
+			_name = item.Name;
+		}
+
+        /// <summary>
+        /// Number of values
+        /// </summary>
+        public int Count
+        {
+            get { return _values.Count; }
+        }
+
+        /// <summary>
+        /// Get a sub item
+        /// </summary>
+        /// <param name="name">name in lower case.</param>
+        /// <returns><see cref="HttpInputItem.Empty"/> if no item was found.</returns>
+        public HttpInputItem this[string name]
+        {
+            get {
+                return _items.ContainsKey(name) ? _items[name] : Empty;
+            }
+        }
+
+        /// <summary>
+        /// Name of item (in lower case).
+        /// </summary>
+        public string Name
+        {
+            get { return _name; }
+            set { _name = value; }
+        }
+
+        /// <summary>
+        /// Returns the first value, or null if no value exist.
+        /// </summary>
+        public string Value
+        {
+            get {
+                return _values.Count == 0 ? null : _values[0];
+            }
+            set
+            {
+                if (_values.Count == 0)
+                    _values.Add(value);
+                else
+                    _values[0] = value;
+            }
+        }
+
+        /// <summary>
+        /// Returns the last value, or null if no value exist.
+        /// </summary>
+        public string LastValue
+        {
+            get
+            {
+                return _values.Count == 0 ? null : _values[_values.Count - 1];
+            }
+        }
+
+        /// <summary>
+        /// Returns the list with values.
+        /// </summary>
+        public IList<string> Values
+        {
+            get { return _values.AsReadOnly(); }
+        }
+
+
+        /// <summary>
+        /// Add another value to this item
+        /// </summary>
+        /// <param name="value">Value to add.</param>
+        /// <exception cref="InvalidOperationException">Cannot add stuff to <see cref="HttpInput.Empty"/>.</exception>
+        public void Add(string value)
+        {
+            if (value == null)
+                return;
+            if (_ignoreChanges)
+                throw new InvalidOperationException("Cannot add stuff to HttpInput.Empty.");
+
+            _values.Add(value);
+        }
+
+        /// <summary>
+        /// checks if a sub-item exists (and has a value).
+        /// </summary>
+        /// <param name="name">name in lower case</param>
+        /// <returns>true if the sub-item exists and has a value; otherwise false.</returns>
+        public bool Contains(string name)
+        {
+            return _items.ContainsKey(name) && _items[name].Value != null;
+        }
+
+		/// <summary> Returns a formatted representation of the instance with the values of all contained parameters </summary>
+        public override string ToString()
+        {
+            return ToString(string.Empty);
+        }
+
+		/// <summary>
+		/// Outputs the string in a formatted manner
+		/// </summary>
+		/// <param name="prefix">A prefix to append, used internally</param>
+        /// <param name="asQuerySting">produce a query string</param>
+        public string ToString(string prefix, bool asQuerySting)
+        {
+            string name;
+            if (string.IsNullOrEmpty(prefix))
+                name = Name;
+            else
+                name = prefix + "[" + Name + "]";
+
+            if (asQuerySting)
+            {
+                string temp;
+                if (_values.Count == 0 && _items.Count > 0)
+                    temp = string.Empty;
+                else
+                    temp = name;
+
+                if (_values.Count > 0)
+                {
+                    temp += '=';
+                    foreach (string value in _values)
+                        temp += value + ',';
+                    temp = temp.Remove(temp.Length - 1, 1);
+                }
+
+                foreach (KeyValuePair<string, HttpInputItem> item in _items)
+                    temp += item.Value.ToString(name, true) + '&';
+
+                return _items.Count > 0 ? temp.Substring(0, temp.Length - 1) : temp;
+            }
+            else
+            {
+                string temp = name;
+                if (_values.Count > 0)
+                {
+                    temp += " = ";
+                    foreach (string value in _values)
+                        temp += value + ", ";
+                    temp = temp.Remove(temp.Length - 2, 2);
+                }
+                temp += Environment.NewLine;
+
+                foreach (KeyValuePair<string, HttpInputItem> item in _items)
+                    temp += item.Value.ToString(name, false);
+                return temp;
+            }
+        }
+
+        #region IHttpInput Members
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="name">name in lower case</param>
+        /// <returns></returns>
+        HttpInputItem IHttpInput.this[string name]
+        {
+            get 
+            {
+                return _items.ContainsKey(name) ? _items[name] : Empty;
+            }
+        }
+
+        /// <summary>
+        /// Add a sub item.
+        /// </summary>
+        /// <param name="name">Can contain array formatting, the item is then parsed and added in multiple levels</param>
+        /// <param name="value">Value to add.</param>
+        /// <exception cref="ArgumentNullException">Argument is null.</exception>
+        /// <exception cref="InvalidOperationException">Cannot add stuff to <see cref="HttpInput.Empty"/>.</exception>
+        public void Add(string name, string value)
+        {
+            if (name == null && value != null)
+                throw new ArgumentNullException("name");
+            if (name == null)
+                return;
+            if (_ignoreChanges)
+                throw new InvalidOperationException("Cannot add stuff to HttpInput.Empty.");
+
+            int pos = name.IndexOf('[');
+            if (pos != -1)
+            {
+                string name1 = name.Substring(0, pos);
+                string name2 = HttpInput.ExtractOne(name);
+                if (!_items.ContainsKey(name1))
+                    _items.Add(name1, new HttpInputItem(name1, null));
+                _items[name1].Add(name2, value);
+                /*
+                HttpInputItem item = HttpInput.ParseItem(name, value);
+
+                // Add the value to an existing sub item
+                if (_items.ContainsKey(item.Name))
+                    _items[item.Name].Add(item.Value);
+                else
+                    _items.Add(item.Name, item);
+                 */
+            }
+            else
+            {
+                if (_items.ContainsKey(name))
+                    _items[name].Add(value);
+                else
+                    _items.Add(name, new HttpInputItem(name, value));
+            }
+        }
+
+        #endregion
+
+        ///<summary>
+        ///Returns an enumerator that iterates through the collection.
+        ///</summary>
+        ///
+        ///<returns>
+        ///A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
+        ///</returns>
+        ///<filterpriority>1</filterpriority>
+        IEnumerator<HttpInputItem> IEnumerable<HttpInputItem>.GetEnumerator()
+        {
+            return _items.Values.GetEnumerator();
+        }
+
+
+        #region IEnumerable Members
+
+        ///<summary>
+        ///Returns an enumerator that iterates through a collection.
+        ///</summary>
+        ///
+        ///<returns>
+        ///An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
+        ///</returns>
+        ///<filterpriority>2</filterpriority>
+        public IEnumerator GetEnumerator()
+        {
+            return _items.Values.GetEnumerator();
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Outputs the string in a formatted manner
+        /// </summary>
+        /// <param name="prefix">A prefix to append, used internally</param>
+        /// <returns></returns>
+        public string ToString(string prefix)
+        {
+            return ToString(prefix, false);
+        }
+    }
+}

+ 248 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpListener.cs

@@ -0,0 +1,248 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Net.Security;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+
+namespace OSHttpServer
+{
+    public class OSHttpListener: IDisposable
+    {
+        private readonly IPAddress m_address;
+        private readonly X509Certificate m_certificate;
+        private readonly IHttpContextFactory m_contextFactory;
+        private readonly int m_port;
+        private readonly ManualResetEvent m_shutdownEvent = new ManualResetEvent(false);
+        private readonly SslProtocols m_sslProtocol = SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2;
+        private TcpListener m_listener;
+        private ILogWriter m_logWriter = NullLogWriter.Instance;
+        private int m_pendingAccepts;
+        private bool m_shutdown;
+        protected RemoteCertificateValidationCallback m_clientCertValCallback = null;
+
+        public event EventHandler<ClientAcceptedEventArgs> Accepted;
+        public event ExceptionHandler ExceptionThrown;
+        public event EventHandler<RequestEventArgs> RequestReceived;
+
+        /// <summary>
+        /// Listen for regular HTTP connections
+        /// </summary>
+        /// <param name="address">IP Address to accept connections on</param>
+        /// <param name="port">TCP Port to listen on, default HTTP port is 80.</param>
+        /// <param name="factory">Factory used to create <see cref="IHttpClientContext"/>es.</param>
+        /// <exception cref="ArgumentNullException"><c>address</c> is null.</exception>
+        /// <exception cref="ArgumentException">Port must be a positive number.</exception>
+        protected OSHttpListener(IPAddress address, int port, IHttpContextFactory factory)
+        {
+            m_address = address;
+            m_port = port;
+            m_contextFactory = factory;
+            m_contextFactory.RequestReceived += OnRequestReceived;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OSHttpListener"/> class.
+        /// </summary>
+        /// <param name="address">IP Address to accept connections on</param>
+        /// <param name="port">TCP Port to listen on, default HTTPS port is 443</param>
+        /// <param name="factory">Factory used to create <see cref="IHttpClientContext"/>es.</param>
+        /// <param name="certificate">Certificate to use</param>
+        /// <param name="protocol">which HTTPS protocol to use, default is TLS.</param>
+        protected OSHttpListener(IPAddress address, int port, IHttpContextFactory factory, X509Certificate certificate,
+                                   SslProtocols protocol)
+            : this(address, port, factory, certificate)
+        {
+            m_sslProtocol = protocol;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OSHttpListener"/> class.
+        /// </summary>
+        /// <param name="address">IP Address to accept connections on</param>
+        /// <param name="port">TCP Port to listen on, default HTTPS port is 443</param>
+        /// <param name="factory">Factory used to create <see cref="IHttpClientContext"/>es.</param>
+        /// <param name="certificate">Certificate to use</param>
+        protected OSHttpListener(IPAddress address, int port, IHttpContextFactory factory, X509Certificate certificate)
+            : this(address, port, factory)
+        {
+            m_certificate = certificate;
+        }
+
+        public static OSHttpListener Create(IPAddress address, int port)
+        {
+            RequestParserFactory requestFactory = new RequestParserFactory();
+            HttpContextFactory factory = new HttpContextFactory(NullLogWriter.Instance, requestFactory);
+            return new OSHttpListener(address, port, factory);
+        }
+
+        public static OSHttpListener Create(IPAddress address, int port, X509Certificate certificate)
+        {
+            RequestParserFactory requestFactory = new RequestParserFactory();
+            HttpContextFactory factory = new HttpContextFactory(NullLogWriter.Instance, requestFactory);
+            return new OSHttpListener(address, port, factory, certificate);
+        }
+
+        public static OSHttpListener Create(IPAddress address, int port, X509Certificate certificate, SslProtocols protocol)
+        {
+            RequestParserFactory requestFactory = new RequestParserFactory();
+            HttpContextFactory factory = new HttpContextFactory(NullLogWriter.Instance, requestFactory);
+            return new OSHttpListener(address, port, factory, certificate, protocol);
+        }
+
+        private void OnRequestReceived(object sender, RequestEventArgs e)
+        {
+            RequestReceived?.Invoke(sender, e);
+        }
+
+
+        public RemoteCertificateValidationCallback CertificateValidationCallback
+        {
+            set { m_clientCertValCallback = value; }
+        }
+
+        /// <summary>
+        /// Gives you a change to receive log entries for all internals of the HTTP library.
+        /// </summary>
+        /// <remarks>
+        /// You may not switch log writer after starting the listener.
+        /// </remarks>
+        public ILogWriter LogWriter
+        {
+            get { return m_logWriter; }
+            set
+            {
+                m_logWriter = value ?? NullLogWriter.Instance;
+                if (m_certificate != null)
+                    m_logWriter.Write(this, LogPrio.Info,
+                                     "HTTPS(" + m_sslProtocol + ") listening on " + m_address + ":" + m_port);
+                else
+                    m_logWriter.Write(this, LogPrio.Info, "HTTP listening on " + m_address + ":" + m_port);
+            }
+        }
+
+        /// <summary>
+        /// True if we should turn on trace logs.
+        /// </summary>
+        public bool UseTraceLogs { get; set; }
+
+
+        /// <exception cref="Exception"><c>Exception</c>.</exception>
+        private void OnAccept(IAsyncResult ar)
+        {
+            bool beginAcceptCalled = false;
+            try
+            {
+                int count = Interlocked.Decrement(ref m_pendingAccepts);
+                if (m_shutdown)
+                {
+                    if (count == 0)
+                        m_shutdownEvent.Set();
+                    return;
+                }
+
+                Interlocked.Increment(ref m_pendingAccepts);
+                m_listener.BeginAcceptSocket(OnAccept, null);
+                beginAcceptCalled = true;
+                Socket socket = m_listener.EndAcceptSocket(ar);
+
+                if (!OnAcceptingSocket(socket))
+                {
+                    socket.Disconnect(true);
+                    return;
+                }
+
+                m_logWriter.Write(this, LogPrio.Debug, "Accepted connection from: " + socket.RemoteEndPoint);
+
+                if (m_certificate != null)
+                    m_contextFactory.CreateSecureContext(socket, m_certificate, m_sslProtocol, m_clientCertValCallback);
+                else
+                    m_contextFactory.CreateContext(socket);
+            }
+            catch (Exception err)
+            {
+                m_logWriter.Write(this, LogPrio.Debug, err.Message);
+                ExceptionThrown?.Invoke(this, err);
+
+                if (!beginAcceptCalled)
+                    RetryBeginAccept();
+            }
+        }
+
+        /// <summary>
+        /// Will try to accept connections one more time.
+        /// </summary>
+        /// <exception cref="Exception">If any exceptions is thrown.</exception>
+        private void RetryBeginAccept()
+        {
+            try
+            {
+                m_logWriter.Write(this, LogPrio.Error, "Trying to accept connections again.");
+                m_listener.BeginAcceptSocket(OnAccept, null);
+            }
+            catch (Exception err)
+            {
+                m_logWriter.Write(this, LogPrio.Fatal, err.Message);
+                 ExceptionThrown?.Invoke(this, err);
+            }
+        }
+
+        /// <summary>
+        /// Can be used to create filtering of new connections.
+        /// </summary>
+        /// <param name="socket">Accepted socket</param>
+        /// <returns>true if connection can be accepted; otherwise false.</returns>
+        protected bool OnAcceptingSocket(Socket socket)
+        {
+            ClientAcceptedEventArgs args = new ClientAcceptedEventArgs(socket);
+            Accepted?.Invoke(this, args);
+            return !args.Revoked;
+        }
+
+        /// <summary>
+        /// Start listen for new connections
+        /// </summary>
+        /// <param name="backlog">Number of connections that can stand in a queue to be accepted.</param>
+        /// <exception cref="InvalidOperationException">Listener have already been started.</exception>
+        public void Start(int backlog)
+        {
+            if (m_listener != null)
+                throw new InvalidOperationException("Listener have already been started.");
+
+            m_listener = new TcpListener(m_address, m_port);
+            m_listener.Start(backlog);
+            Interlocked.Increment(ref m_pendingAccepts);
+            m_listener.BeginAcceptSocket(OnAccept, null);
+        }
+
+        /// <summary>
+        /// Stop the listener
+        /// </summary>
+        /// <exception cref="SocketException"></exception>
+        public void Stop()
+        {
+            m_shutdown = true;
+            m_contextFactory.Shutdown();
+            m_listener.Stop();
+            if (!m_shutdownEvent.WaitOne())
+                m_logWriter.Write(this, LogPrio.Error, "Failed to shutdown listener properly.");
+            m_listener = null;
+            Dispose();
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        public void Dispose(bool disposing)
+        {
+            if (m_shutdownEvent != null)
+            {
+                m_shutdownEvent.Dispose();
+            }
+        }
+    }
+}

+ 114 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpParam.cs

@@ -0,0 +1,114 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Returns item either from a form or a query string (checks them in that order)
+    /// </summary>
+    public class HttpParam : IHttpInput
+    {
+        /// <summary> Representation of a non-initialized HttpParam </summary>
+        public static readonly HttpParam Empty = new HttpParam(HttpInput.Empty, HttpInput.Empty);
+
+        private IHttpInput m_form;
+        private IHttpInput m_query;
+
+        private List<HttpInputItem> _items = new List<HttpInputItem>();
+
+        /// <summary>Initialises the class to hold a value either from a post request or a querystring request</summary>		
+        public HttpParam(IHttpInput form, IHttpInput query)
+        {
+            m_form = form;
+            m_query = query;
+        }
+
+        #region IHttpInput Members
+
+        /// <summary>
+        /// The add method is not availible for HttpParam
+        /// since HttpParam checks both Request.Form and Request.QueryString
+        /// </summary>
+        /// <param name="name">name identifying the value</param>
+        /// <param name="value">value to add</param>
+        /// <exception cref="NotImplementedException"></exception>
+        [Obsolete("Not implemented for HttpParam")]
+        public void Add(string name, string value)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// Checks whether the form or querystring has the specified value
+        /// </summary>
+        /// <param name="name">Name, case sensitive</param>
+        /// <returns>true if found; otherwise false.</returns>
+        public bool Contains(string name)
+        {
+            return m_form.Contains(name) || m_query.Contains(name);
+        }
+
+        /// <summary>
+        /// Fetch an item from the form or querystring (in that order).
+        /// </summary>
+        /// <param name="name"></param>
+        /// <returns>Item if found; otherwise HttpInputItem.EmptyLanguageNode</returns>
+        public HttpInputItem this[string name]
+        {
+            get
+            {
+                if (m_form[name] != HttpInputItem.Empty)
+                    return m_form[name];
+                else
+                    return m_query[name];
+            }
+        }
+
+        #endregion
+
+        internal void SetQueryString(HttpInput query)
+        {
+            m_query = query;
+        }
+
+        internal void SetForm(HttpInput form)
+        {
+            m_form = form;
+        }
+
+        ///<summary>
+        ///Returns an enumerator that iterates through the collection.
+        ///</summary>
+        ///
+        ///<returns>
+        ///A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
+        ///</returns>
+        ///<filterpriority>1</filterpriority>
+        IEnumerator<HttpInputItem> IEnumerable<HttpInputItem>.GetEnumerator()
+        {
+            List<HttpInputItem> items = new List<HttpInputItem>(m_query);
+            items.AddRange(m_form);
+            return items.GetEnumerator();
+        }
+
+        #region IEnumerable Members
+
+        ///<summary>
+        ///Returns an enumerator that iterates through a collection.
+        ///</summary>
+        ///
+        ///<returns>
+        ///An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
+        ///</returns>
+        ///<filterpriority>2</filterpriority>
+        public IEnumerator GetEnumerator()
+        {
+            List<HttpInputItem> items = new List<HttpInputItem>(m_query);
+            items.AddRange(m_form);
+            return items.GetEnumerator();
+        }
+
+        #endregion
+    }
+}

+ 435 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpRequest.cs

@@ -0,0 +1,435 @@
+using System;
+using System.Collections.Specialized;
+using System.IO;
+using System.Text;
+using OSHttpServer.Exceptions;
+
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Contains server side HTTP request information.
+    /// </summary>
+    public class HttpRequest : IHttpRequest
+    {
+        /// <summary>
+        /// Chars used to split an URL path into multiple parts.
+        /// </summary>
+        public static readonly char[] UriSplitters = new[] { '/' };
+        public static uint baseID = 0;
+
+        private readonly NameValueCollection m_headers = new NameValueCollection();
+        private readonly HttpParam m_param = new HttpParam(HttpInput.Empty, HttpInput.Empty);
+        private Stream m_body = new MemoryStream();
+        private int m_bodyBytesLeft;
+        private ConnectionType m_connection = ConnectionType.KeepAlive;
+        private int m_contentLength;
+        private string m_httpVersion = string.Empty;
+        private string m_method = string.Empty;
+        private HttpInput m_queryString = HttpInput.Empty;
+        private Uri m_uri = HttpHelper.EmptyUri;
+        private string m_uriPath;
+        public readonly IHttpClientContext m_context;
+
+        public HttpRequest(IHttpClientContext pContext)
+        {
+            ID = ++baseID;
+            m_context = pContext;
+        }
+
+        public uint ID { get; private set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this <see cref="HttpRequest"/> is secure.
+        /// </summary>
+        public bool Secure { get; internal set; }
+
+        public IHttpClientContext Context { get { return m_context; } }
+        /// <summary>
+        /// Path and query (will be merged with the host header) and put in Uri
+        /// </summary>
+        /// <see cref="Uri"/>
+        public string UriPath
+        {
+            get { return m_uriPath; }
+            set
+            {
+                m_uriPath = value;
+                int pos = m_uriPath.IndexOf('?');
+                if (pos != -1)
+                {
+                    m_queryString = HttpHelper.ParseQueryString(m_uriPath.Substring(pos + 1));
+                    m_param.SetQueryString(m_queryString);
+                    string path = m_uriPath.Substring(0, pos);
+                    m_uriPath = System.Web.HttpUtility.UrlDecode(path) + "?" + m_uriPath.Substring(pos + 1);
+                    UriParts = value.Substring(0, pos).Split(UriSplitters, StringSplitOptions.RemoveEmptyEntries);
+                }
+                else
+                {
+                    m_uriPath = System.Web.HttpUtility.UrlDecode(m_uriPath);
+                    UriParts = value.Split(UriSplitters, StringSplitOptions.RemoveEmptyEntries);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Assign a form.
+        /// </summary>
+        /// <param name="form"></param>
+        /*
+        internal void AssignForm(HttpForm form)
+        {
+            _form = form;
+        }
+        */
+
+        #region IHttpRequest Members
+
+        /// <summary>
+        /// Gets whether the body is complete.
+        /// </summary>
+        public bool BodyIsComplete
+        {
+            get { return m_bodyBytesLeft == 0; }
+        }
+
+        /// <summary>
+        /// Gets kind of types accepted by the client.
+        /// </summary>
+        public string[] AcceptTypes { get; private set; }
+
+        /// <summary>
+        /// Gets or sets body stream.
+        /// </summary>
+        public Stream Body
+        {
+            get { return m_body; }
+            set { m_body = value; }
+        }
+
+        /// <summary>
+        /// Gets or sets kind of connection used for the session.
+        /// </summary>
+        public ConnectionType Connection
+        {
+            get { return m_connection; }
+            set { m_connection = value; }
+        }
+
+        /// <summary>
+        /// Gets or sets number of bytes in the body.
+        /// </summary>
+        public int ContentLength
+        {
+            get { return m_contentLength; }
+            set
+            {
+                m_contentLength = value;
+                m_bodyBytesLeft = value;
+            }
+        }
+
+        /// <summary>
+        /// Gets headers sent by the client.
+        /// </summary>
+        public NameValueCollection Headers
+        {
+            get { return m_headers; }
+        }
+
+        /// <summary>
+        /// Gets or sets version of HTTP protocol that's used.
+        /// </summary>
+        /// <remarks>
+        /// Probably <see cref="HttpHelper.HTTP10"/> or <see cref="HttpHelper.HTTP11"/>.
+        /// </remarks>
+        /// <seealso cref="HttpHelper"/>
+        public string HttpVersion
+        {
+            get { return m_httpVersion; }
+            set { m_httpVersion = value; }
+        }
+
+        /// <summary>
+        /// Gets or sets requested method.
+        /// </summary>
+        /// <value></value>
+        /// <remarks>
+        /// Will always be in upper case.
+        /// </remarks>
+        /// <see cref="OSHttpServer.Method"/>
+        public string Method
+        {
+            get { return m_method; }
+            set { m_method = value; }
+        }
+
+        /// <summary>
+        /// Gets variables sent in the query string
+        /// </summary>
+        public HttpInput QueryString
+        {
+            get { return m_queryString; }
+        }
+
+
+        /// <summary>
+        /// Gets or sets requested URI.
+        /// </summary>
+        public Uri Uri
+        {
+            get { return m_uri; }
+            set
+            {
+                m_uri = value ?? HttpHelper.EmptyUri;
+                UriParts = m_uri.AbsolutePath.Split(UriSplitters, StringSplitOptions.RemoveEmptyEntries);
+            }
+        }
+
+        /// <summary>
+        /// Uri absolute path splitted into parts.
+        /// </summary>
+        /// <example>
+        /// // uri is: http://gauffin.com/code/tiny/
+        /// Console.WriteLine(request.UriParts[0]); // result: code
+        /// Console.WriteLine(request.UriParts[1]); // result: tiny
+        /// </example>
+        /// <remarks>
+        /// If you're using controllers than the first part is controller name,
+        /// the second part is method name and the third part is Id property.
+        /// </remarks>
+        /// <seealso cref="Uri"/>
+        public string[] UriParts { get; private set; }
+
+        /// <summary>
+        /// Gets parameter from <see cref="QueryString"/> or <see cref="Form"/>.
+        /// </summary>
+        public HttpParam Param
+        {
+            get { return m_param; }
+        }
+
+        /// <summary>
+        /// Gets form parameters.
+        /// </summary>
+        /*
+        public HttpForm Form
+        {
+            get { return _form; }
+        }
+        */
+        /// <summary>
+        /// Gets whether the request was made by Ajax (Asynchronous JavaScript)
+        /// </summary>
+        public bool IsAjax { get; private set; }
+
+        /// <summary>
+        /// Gets cookies that was sent with the request.
+        /// </summary>
+        public RequestCookies Cookies { get; private set; }
+
+        ///<summary>
+        ///Creates a new object that is a copy of the current instance.
+        ///</summary>
+        ///
+        ///<returns>
+        ///A new object that is a copy of this instance.
+        ///</returns>
+        ///<filterpriority>2</filterpriority>
+        public object Clone()
+        {
+            // this method was mainly created for testing.
+            // dont use it that much...
+            var request = new HttpRequest(Context);
+            request.Method = m_method;
+            if (AcceptTypes != null)
+            {
+                request.AcceptTypes = new string[AcceptTypes.Length];
+                AcceptTypes.CopyTo(request.AcceptTypes, 0);
+            }
+            request.m_httpVersion = m_httpVersion;
+            request.m_queryString = m_queryString;
+            request.Uri = m_uri;
+
+            var buffer = new byte[m_body.Length];
+            m_body.Read(buffer, 0, (int)m_body.Length);
+            request.Body = new MemoryStream();
+            request.Body.Write(buffer, 0, buffer.Length);
+            request.Body.Seek(0, SeekOrigin.Begin);
+            request.Body.Flush();
+
+            request.m_headers.Clear();
+            foreach (string key in m_headers)
+            {
+                string[] values = m_headers.GetValues(key);
+                if (values != null)
+                    foreach (string value in values)
+                        request.AddHeader(key, value);
+            }
+            return request;
+        }
+
+        /// <summary>
+        /// Decode body into a form.
+        /// </summary>
+        /// <param name="providers">A list with form decoders.</param>
+        /// <exception cref="InvalidDataException">If body contents is not valid for the chosen decoder.</exception>
+        /// <exception cref="InvalidOperationException">If body is still being transferred.</exception>
+        /*
+        public void DecodeBody(FormDecoderProvider providers)
+        {
+            if (_bodyBytesLeft > 0)
+                throw new InvalidOperationException("Body have not yet been completed.");
+
+            _form = providers.Decode(_headers["content-type"], _body, Encoding.UTF8);
+            if (_form != HttpInput.Empty)
+                _param.SetForm(_form);
+        }
+        */
+        ///<summary>
+        /// Cookies
+        ///</summary>
+        ///<param name="cookies">the cookies</param>
+        public void SetCookies(RequestCookies cookies)
+        {
+            Cookies = cookies;
+        }
+        /*
+        /// <summary>
+        /// Create a response object.
+        /// </summary>
+        /// <returns>A new <see cref="IHttpResponse"/>.</returns>
+        public IHttpResponse CreateResponse(IHttpClientContext context)
+        {
+            return new HttpResponse(context, this);
+        }
+        */
+        /// <summary>
+        /// Called during parsing of a <see cref="IHttpRequest"/>.
+        /// </summary>
+        /// <param name="name">Name of the header, should not be URL encoded</param>
+        /// <param name="value">Value of the header, should not be URL encoded</param>
+        /// <exception cref="BadRequestException">If a header is incorrect.</exception>
+        public void AddHeader(string name, string value)
+        {
+            if (string.IsNullOrEmpty(name))
+                throw new BadRequestException("Invalid header name: " + name ?? "<null>");
+            if (string.IsNullOrEmpty(value))
+                throw new BadRequestException("Header '" + name + "' do not contain a value.");
+
+            name = name.ToLowerInvariant();
+
+            switch (name)
+            {
+                case "http_x_requested_with":
+                case "x-requested-with":
+                    if (string.Compare(value, "XMLHttpRequest", true) == 0)
+                        IsAjax = true;
+                    break;
+                case "accept":
+                    AcceptTypes = value.Split(',');
+                    for (int i = 0; i < AcceptTypes.Length; ++i)
+                        AcceptTypes[i] = AcceptTypes[i].Trim();
+                    break;
+                case "content-length":
+                    int t;
+                    if (!int.TryParse(value, out t))
+                        throw new BadRequestException("Invalid content length.");
+                    ContentLength = t;
+                    break; //todo: maybe throw an exception
+                case "host":
+                    try
+                    {
+                        m_uri = new Uri(Secure ? "https://" : "http://" + value + m_uriPath);
+                        UriParts = m_uri.AbsolutePath.Split(UriSplitters, StringSplitOptions.RemoveEmptyEntries);
+                    }
+                    catch (UriFormatException err)
+                    {
+                        throw new BadRequestException("Failed to parse uri: " + value + m_uriPath, err);
+                    }
+                    break;
+                case "remote_addr":
+                    // to prevent hacking (since it's added by IHttpClientContext before parsing).
+                    if (m_headers[name] == null)
+                        m_headers.Add(name, value);
+                    break;
+
+                case "connection":
+                    if (string.Compare(value, "close", true) == 0)
+                        Connection = ConnectionType.Close;
+                    else if (value.StartsWith("keep-alive", StringComparison.CurrentCultureIgnoreCase))
+                        Connection = ConnectionType.KeepAlive;
+                    else if (value.StartsWith("Upgrade", StringComparison.CurrentCultureIgnoreCase))
+                        Connection = ConnectionType.KeepAlive;
+                    else
+                        throw new BadRequestException("Unknown 'Connection' header type.");
+                    break;
+
+                case "expect":
+                    if (value.Contains("100-continue"))
+                    {
+
+                    }
+                    m_headers.Add(name, value);
+                    break;
+
+                default:
+                    m_headers.Add(name, value);
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Add bytes to the body
+        /// </summary>
+        /// <param name="bytes">buffer to read bytes from</param>
+        /// <param name="offset">where to start read</param>
+        /// <param name="length">number of bytes to read</param>
+        /// <returns>Number of bytes actually read (same as length unless we got all body bytes).</returns>
+        /// <exception cref="InvalidOperationException">If body is not writable</exception>
+        /// <exception cref="ArgumentNullException"><c>bytes</c> is null.</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><c>offset</c> is out of range.</exception>
+        public int AddToBody(byte[] bytes, int offset, int length)
+        {
+            if (bytes == null)
+                throw new ArgumentNullException("bytes");
+            if (offset + length > bytes.Length)
+                throw new ArgumentOutOfRangeException("offset");
+            if (length == 0)
+                return 0;
+            if (!m_body.CanWrite)
+                throw new InvalidOperationException("Body is not writable.");
+
+            if (length > m_bodyBytesLeft)
+            {
+                length = m_bodyBytesLeft;
+            }
+
+            m_body.Write(bytes, offset, length);
+            m_bodyBytesLeft -= length;
+
+            return length;
+        }
+
+        /// <summary>
+        /// Clear everything in the request
+        /// </summary>
+        public void Clear()
+        {
+            if (m_body != null && m_body.CanRead)
+                m_body.Dispose();
+            m_body = null;
+            m_contentLength = 0;
+            m_method = string.Empty;
+            m_uri = HttpHelper.EmptyUri;
+            m_queryString = HttpInput.Empty;
+            m_bodyBytesLeft = 0;
+            m_headers.Clear();
+            m_connection = ConnectionType.KeepAlive;
+            IsAjax = false;
+            //_form.Clear();
+        }
+
+        #endregion
+    }
+}

+ 417 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpRequestParser.cs

@@ -0,0 +1,417 @@
+using System;
+using System.Text;
+using OSHttpServer.Exceptions;
+
+namespace OSHttpServer.Parser
+{
+    /// <summary>
+    /// Parses a HTTP request directly from a stream
+    /// </summary>
+    public class HttpRequestParser : IHttpRequestParser
+    {
+        private ILogWriter m_log;
+        private readonly BodyEventArgs m_bodyArgs = new BodyEventArgs();
+        private readonly HeaderEventArgs m_headerArgs = new HeaderEventArgs();
+        private readonly RequestLineEventArgs m_requestLineArgs = new RequestLineEventArgs();
+        private string m_curHeaderName = string.Empty;
+        private string m_curHeaderValue = string.Empty;
+        private int m_bodyBytesLeft;
+
+        /// <summary>
+        /// More body bytes have been received.
+        /// </summary>
+        public event EventHandler<BodyEventArgs> BodyBytesReceived;
+
+        /// <summary>
+        /// Request line have been received.
+        /// </summary>
+        public event EventHandler<RequestLineEventArgs> RequestLineReceived;
+
+        /// <summary>
+        /// A header have been received.
+        /// </summary>
+        public event EventHandler<HeaderEventArgs> HeaderReceived;
+
+
+        /// <summary>
+        /// Create a new request parser
+        /// </summary>
+        /// <param name="logWriter">delegate receiving log entries.</param>
+        public HttpRequestParser(ILogWriter logWriter)
+        {
+            m_log = logWriter ?? NullLogWriter.Instance;
+        }
+
+        /// <summary>
+        /// Add a number of bytes to the body
+        /// </summary>
+        /// <param name="buffer">buffer containing more body bytes.</param>
+        /// <param name="offset">starting offset in buffer</param>
+        /// <param name="count">number of bytes, from offset, to read.</param>
+        /// <returns>offset to continue from.</returns>
+        private int AddToBody(byte[] buffer, int offset, int count)
+        {
+            // got all bytes we need, or just a few of them?
+            int bytesUsed = count > m_bodyBytesLeft ? m_bodyBytesLeft : count;
+            m_bodyArgs.Buffer = buffer;
+            m_bodyArgs.Offset = offset;
+            m_bodyArgs.Count = bytesUsed;
+            BodyBytesReceived?.Invoke(this, m_bodyArgs);
+
+            m_bodyBytesLeft -= bytesUsed;
+            if (m_bodyBytesLeft == 0)
+            {
+                // got a complete request.
+                m_log.Write(this, LogPrio.Trace, "Request parsed successfully.");
+                OnRequestCompleted();
+                Clear();
+            }
+
+            return offset + bytesUsed;
+        }
+
+        /// <summary>
+        /// Remove all state information for the request.
+        /// </summary>
+        public void Clear()
+        {
+            m_bodyBytesLeft = 0;
+            m_curHeaderName = string.Empty;
+            m_curHeaderValue = string.Empty;
+            CurrentState = RequestParserState.FirstLine;
+        }
+
+        /// <summary>
+        /// Gets or sets the log writer.
+        /// </summary>
+        public ILogWriter LogWriter
+        {
+            get { return m_log; }
+            set { m_log = value ?? NullLogWriter.Instance; }
+        }
+
+        /// <summary>
+        /// Parse request line
+        /// </summary>
+        /// <param name="value"></param>
+        /// <exception cref="BadRequestException">If line is incorrect</exception>
+        /// <remarks>Expects the following format: "Method SP Request-URI SP HTTP-Version CRLF"</remarks>
+        protected void OnFirstLine(string value)
+        {
+            //
+            //todo: In the interest of robustness, servers SHOULD ignore any empty line(s) received where a Request-Line is expected. 
+            // In other words, if the server is reading the protocol stream at the beginning of a message and receives a CRLF first, it should ignore the CRLF.
+            //
+            m_log.Write(this, LogPrio.Debug, "Got request: " + value);
+
+            //Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
+            int pos = value.IndexOf(' ');
+            if (pos == -1 || pos + 1 >= value.Length)
+            {
+                m_log.Write(this, LogPrio.Warning, "Invalid request line, missing Method. Line: " + value);
+                throw new BadRequestException("Invalid request line, missing Method. Line: " + value);
+            }
+
+            string method = value.Substring(0, pos).ToUpper();
+            int oldPos = pos + 1;
+            pos = value.IndexOf(' ', oldPos);
+            if (pos == -1)
+            {
+                m_log.Write(this, LogPrio.Warning, "Invalid request line, missing URI. Line: " + value);
+                throw new BadRequestException("Invalid request line, missing URI. Line: " + value);
+            }
+            string path = value.Substring(oldPos, pos - oldPos);
+            if (path.Length > 4196)
+                throw new BadRequestException("Too long URI.");
+
+            if (pos + 1 >= value.Length)
+            {
+                m_log.Write(this, LogPrio.Warning, "Invalid request line, missing HTTP-Version. Line: " + value);
+                throw new BadRequestException("Invalid request line, missing HTTP-Version. Line: " + value);
+            }
+            string version = value.Substring(pos + 1);
+            if (version.Length < 4 || string.Compare(version.Substring(0, 4), "HTTP", true) != 0)
+            {
+                m_log.Write(this, LogPrio.Warning, "Invalid HTTP version in request line. Line: " + value);
+                throw new BadRequestException("Invalid HTTP version in Request line. Line: " + value);
+            }
+
+            m_requestLineArgs.HttpMethod = method;
+            m_requestLineArgs.HttpVersion = version;
+            m_requestLineArgs.UriPath = path;
+            RequestLineReceived(this, m_requestLineArgs);
+        }
+
+        /// <summary>
+        /// We've parsed a new header.
+        /// </summary>
+        /// <param name="name">Name in lower case</param>
+        /// <param name="value">Value, unmodified.</param>
+        /// <exception cref="BadRequestException">If content length cannot be parsed.</exception>
+        protected void OnHeader(string name, string value)
+        {
+            m_headerArgs.Name = name;
+            m_headerArgs.Value = value;
+            if (string.Compare(name, "content-length", true) == 0)
+            {
+                if (!int.TryParse(value, out m_bodyBytesLeft))
+                    throw new BadRequestException("Content length is not a number.");
+            }
+
+            HeaderReceived?.Invoke(this, m_headerArgs);
+        }
+
+        private void OnRequestCompleted()
+        {
+            RequestCompleted?.Invoke(this, EventArgs.Empty);
+        }
+
+        #region IHttpRequestParser Members
+
+        /// <summary>
+        /// Current state in parser.
+        /// </summary>
+        public RequestParserState CurrentState { get; private set; }
+
+        /// <summary>
+        /// Parse a message
+        /// </summary>
+        /// <param name="buffer">bytes to parse.</param>
+        /// <param name="offset">where in buffer that parsing should start</param>
+        /// <param name="count">number of bytes to parse, starting on <paramref name="offset"/>.</param>
+        /// <returns>offset (where to start parsing next).</returns>
+        /// <exception cref="BadRequestException"><c>BadRequestException</c>.</exception>
+        public int Parse(byte[] buffer, int offset, int count)
+        {
+            // add body bytes
+            if (CurrentState == RequestParserState.Body)
+            {
+                return AddToBody(buffer, offset, count);
+            }
+
+#if DEBUG
+            string temp = Encoding.ASCII.GetString(buffer, offset, count);
+            _log.Write(this, LogPrio.Trace, "\r\n\r\n HTTP MESSAGE: " + temp + "\r\n");
+#endif
+
+            int currentLine = 1;
+            int startPos = -1;
+
+            // set start pos since this is from an partial request
+            if (CurrentState == RequestParserState.HeaderValue)
+                startPos = 0;
+
+            int endOfBufferPos = offset + count;
+
+            //<summary>
+            // Handled bytes are used to keep track of the number of bytes processed.
+            // We do this since we can handle partial requests (to be able to check headers and abort
+            // invalid requests directly without having to process the whole header / body).
+            // </summary>
+            int handledBytes = 0;
+
+            for (int currentPos = offset; currentPos < endOfBufferPos; ++currentPos)
+            {
+                var ch = (char)buffer[currentPos];
+                char nextCh = endOfBufferPos > currentPos + 1 ? (char)buffer[currentPos + 1] : char.MinValue;
+
+                if (ch == '\r')
+                    ++currentLine;
+
+                switch (CurrentState)
+                {
+                    case RequestParserState.FirstLine:
+                        if (currentPos == 8191)
+                        {
+                            m_log.Write(this, LogPrio.Warning, "HTTP Request is too large.");
+                            throw new BadRequestException("Too large request line.");
+                        }
+                        if (char.IsLetterOrDigit(ch) && startPos == -1)
+                            startPos = currentPos;
+                        if (startPos == -1 && (ch != '\r' || nextCh != '\n'))
+                        {
+                            m_log.Write(this, LogPrio.Warning, "Request line is not found.");
+                            throw new BadRequestException("Invalid request line.");
+                        }
+                        if (startPos != -1 && (ch == '\r' || ch == '\n'))
+                        {
+                            int size = GetLineBreakSize(buffer, currentPos);
+                            OnFirstLine(Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos));
+                            CurrentState = CurrentState + 1;
+                            currentPos += size - 1;
+                            handledBytes = currentPos + size - 1;
+                            startPos = -1;
+                        }
+                        break;
+                    case RequestParserState.HeaderName:
+                        if (ch == '\r' || ch == '\n')
+                        {
+                            currentPos += GetLineBreakSize(buffer, currentPos);
+                            if (m_bodyBytesLeft == 0)
+                            {
+                                CurrentState = RequestParserState.FirstLine;
+                                m_log.Write(this, LogPrio.Trace, "Request parsed successfully (no content).");
+                                OnRequestCompleted();
+                                Clear();
+                                return currentPos;
+                            }
+
+                            CurrentState = RequestParserState.Body;
+                            if (currentPos + 1 < endOfBufferPos)
+                            {
+                                m_log.Write(this, LogPrio.Trace, "Adding bytes to the body");
+                                return AddToBody(buffer, currentPos, endOfBufferPos - currentPos);
+                            }
+
+                            return currentPos;
+                        }
+                        if (char.IsWhiteSpace(ch) || ch == ':')
+                        {
+                            if (startPos == -1)
+                            {
+                                m_log.Write(this, LogPrio.Warning,
+                                           "Expected header name, got colon on line " + currentLine);
+                                throw new BadRequestException("Expected header name, got colon on line " + currentLine);
+                            }
+                            m_curHeaderName = Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos);
+                            handledBytes = currentPos + 1;
+                            startPos = -1;
+                            CurrentState = CurrentState + 1;
+                            if (ch == ':')
+                                CurrentState = CurrentState + 1;
+                        }
+                        else if (startPos == -1)
+                            startPos = currentPos;
+                        else if (!char.IsLetterOrDigit(ch) && ch != '-')
+                        {
+                            m_log.Write(this, LogPrio.Warning, "Invalid character in header name on line " + currentLine);
+                            throw new BadRequestException("Invalid character in header name on line " + currentLine);
+                        }
+                        if (startPos != -1 && currentPos - startPos > 200)
+                        {
+                            m_log.Write(this, LogPrio.Warning, "Invalid header name on line " + currentLine);
+                            throw new BadRequestException("Invalid header name on line " + currentLine);
+                        }
+                        break;
+                    case RequestParserState.AfterName:
+                        if (ch == ':')
+                        {
+                            handledBytes = currentPos + 1;
+                            CurrentState = CurrentState + 1;
+                        }
+                        break;
+                    case RequestParserState.Between:
+                    {
+                        if (ch == ' ' || ch == '\t')
+                            continue;
+                        int newLineSize = GetLineBreakSize(buffer, currentPos);
+                        if (newLineSize > 0 && currentPos + newLineSize < endOfBufferPos &&
+                            char.IsWhiteSpace((char)buffer[currentPos + newLineSize]))
+                        {
+                            ++currentPos;
+                            continue;
+                        }
+                        startPos = currentPos;
+                        CurrentState = CurrentState + 1;
+                        handledBytes = currentPos;
+                        continue;
+                    }
+                    case RequestParserState.HeaderValue:
+                    {
+                        if (ch != '\r' && ch != '\n')
+                            continue;
+                        int newLineSize = GetLineBreakSize(buffer, currentPos);
+                        if (startPos == -1)
+                            continue; // allow new lines before start of value
+
+                        if (m_curHeaderName == string.Empty)
+                            throw new BadRequestException("Missing header on line " + currentLine);
+                        if (startPos == -1)
+                        {
+                            m_log.Write(this, LogPrio.Warning, "Missing header value for '" + m_curHeaderName);
+                            throw new BadRequestException("Missing header value for '" + m_curHeaderName);
+                        }
+                        if (currentPos - startPos > 8190)
+                        {
+                            m_log.Write(this, LogPrio.Warning, "Too large header value on line " + currentLine);
+                            throw new BadRequestException("Too large header value on line " + currentLine);
+                        }
+
+                        // Header fields can be extended over multiple lines by preceding each extra line with at
+                        // least one SP or HT.
+                        if (endOfBufferPos > currentPos + newLineSize
+                            && (buffer[currentPos + newLineSize] == ' ' || buffer[currentPos + newLineSize] == '\t'))
+                        {
+                            if (startPos != -1)
+                                m_curHeaderValue = Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos);
+
+                            m_log.Write(this, LogPrio.Trace, "Header value is on multiple lines.");
+                            CurrentState = RequestParserState.Between;
+                            startPos = -1;
+                            currentPos += newLineSize - 1;
+                            handledBytes = currentPos + newLineSize - 1;
+                            continue;
+                        }
+
+                        m_curHeaderValue += Encoding.UTF8.GetString(buffer, startPos, currentPos - startPos);
+                        m_log.Write(this, LogPrio.Trace, "Header [" + m_curHeaderName + ": " + m_curHeaderValue + "]");
+                        OnHeader(m_curHeaderName, m_curHeaderValue);
+
+                        startPos = -1;
+                        CurrentState = RequestParserState.HeaderName;
+                        m_curHeaderValue = string.Empty;
+                        m_curHeaderName = string.Empty;
+                        ++currentPos;
+                        handledBytes = currentPos + 1;
+
+                        // Check if we got a colon so we can cut header name, or crlf for end of header.
+                        bool canContinue = false;
+                        for (int j = currentPos; j < endOfBufferPos; ++j)
+                        {
+                            if (buffer[j] != ':' && buffer[j] != '\r' && buffer[j] != '\n') continue;
+                            canContinue = true;
+                            break;
+                        }
+                        if (!canContinue)
+                        {
+                            m_log.Write(this, LogPrio.Trace, "Cant continue, no colon.");
+                            return currentPos + 1;
+                        }
+                    }
+                    break;
+                }
+            }
+
+            return handledBytes;
+        }
+
+        int GetLineBreakSize(byte[] buffer, int offset)
+        {
+            if (buffer[offset] == '\r')
+            {
+                if (buffer.Length > offset + 1 && buffer[offset + 1] == '\n')
+                    return 2;
+                else
+                    throw new BadRequestException("Got invalid linefeed.");
+            }
+            else if (buffer[offset] == '\n')
+            {
+                if (buffer.Length == offset + 1)
+                    return 1;   // linux line feed
+                if (buffer[offset + 1] != '\r')
+                    return 1;   // linux line feed
+                else
+                    return 2;   // win line feed
+            }
+            else
+                return 0;
+        }
+
+        /// <summary>
+        /// A request have been successfully parsed.
+        /// </summary>
+        public event EventHandler RequestCompleted;
+
+        #endregion
+    }
+}

+ 670 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/HttpResponse.cs

@@ -0,0 +1,670 @@
+using System;
+using System.Collections.Specialized;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OSHttpServer
+{
+    public class HttpResponse : IHttpResponse
+    {
+        private const string DefaultContentType = "text/html;charset=UTF-8";
+        private readonly IHttpClientContext m_context;
+        private readonly ResponseCookies m_cookies = new ResponseCookies();
+        private readonly NameValueCollection m_headers = new NameValueCollection();
+        private string m_httpVersion;
+        private Stream m_body;
+        private long m_contentLength;
+        private string m_contentType;
+        private Encoding m_encoding = Encoding.UTF8;
+        private int m_keepAlive = 60;
+        public uint requestID { get; private set; }
+        public byte[] RawBuffer { get; set; }
+        public int RawBufferStart { get; set; }
+        public int RawBufferLen { get; set; }
+
+        internal byte[] m_headerBytes = null;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="IHttpResponse"/> class.
+        /// </summary>
+        /// <param name="context">Client that send the <see cref="IHttpRequest"/>.</param>
+        /// <param name="request">Contains information of what the client want to receive.</param>
+        /// <exception cref="ArgumentException"><see cref="IHttpRequest.HttpVersion"/> cannot be empty.</exception>
+        public HttpResponse(IHttpClientContext context, IHttpRequest request)
+        {
+            m_httpVersion = request.HttpVersion;
+            if (string.IsNullOrEmpty(m_httpVersion))
+                m_httpVersion = "HTTP/1.0";
+
+            Status = HttpStatusCode.OK;
+            m_context = context;
+            m_Connetion = request.Connection;
+            requestID = request.ID;
+            RawBufferStart = -1;
+            RawBufferLen = -1;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="IHttpResponse"/> class.
+        /// </summary>
+        /// <param name="context">Client that send the <see cref="IHttpRequest"/>.</param>
+        /// <param name="httpVersion">Version of HTTP protocol that the client uses.</param>
+        /// <param name="connectionType">Type of HTTP connection used.</param>
+        internal HttpResponse(IHttpClientContext context, string httpVersion, ConnectionType connectionType)
+        {
+            Status = HttpStatusCode.OK;
+            m_context = context;
+            m_httpVersion = httpVersion;
+            m_Connetion = connectionType;
+        }
+        private ConnectionType m_Connetion;
+        public ConnectionType Connection
+        {
+            get { return m_Connetion; }
+            set { return; }
+        }
+
+        private int m_priority = 0;
+        public int Priority
+        {
+            get { return m_priority;}
+            set { m_priority = (value > 0 && m_priority < 3)? value : 0;}
+        }
+
+        #region IHttpResponse Members
+
+        /// <summary>
+        /// The body stream is used to cache the body contents
+        /// before sending everything to the client. It's the simplest
+        /// way to serve documents.
+        /// </summary>
+        public Stream Body
+        {
+            get
+            { 
+                if(m_body == null)
+                    m_body = new MemoryStream();
+                return m_body;
+            }
+            set { m_body = value; }
+        }
+
+        /// <summary>
+        /// The chunked encoding modifies the body of a message in order to
+        /// transfer it as a series of chunks, each with its own size indicator,
+        /// followed by an OPTIONAL trailer containing entity-header fields. This
+        /// allows dynamically produced content to be transferred along with the
+        /// information necessary for the recipient to verify that it has
+        /// received the full message.
+        /// </summary>
+        public bool Chunked { get; set; }
+
+
+        /// <summary>
+        /// Defines the version of the HTTP Response for applications where it's required
+        /// for this to be forced.
+        /// </summary>
+        public string ProtocolVersion
+        {
+            get { return m_httpVersion; }
+            set { m_httpVersion = value; }
+        }
+
+        /// <summary>
+        /// Encoding to use when sending stuff to the client.
+        /// </summary>
+        /// <remarks>Default is UTF8</remarks>
+        public Encoding Encoding
+        {
+            get { return m_encoding; }
+            set { m_encoding = value; }
+        }
+
+
+        /// <summary>
+        /// Number of seconds to keep connection alive
+        /// </summary>
+        /// <remarks>Only used if Connection property is set to <see cref="ConnectionType.KeepAlive"/>.</remarks>
+        public int KeepAlive
+        {
+            get { return m_keepAlive; }
+            set
+            {
+                if (value > 400)
+                    m_keepAlive = 400;
+                else if (value <= 0)
+                    m_keepAlive = 0;
+                else
+                    m_keepAlive = value;
+            }
+        }
+
+        /// <summary>
+        /// Status code that is sent to the client.
+        /// </summary>
+        /// <remarks>Default is <see cref="HttpStatusCode.OK"/></remarks>
+        public HttpStatusCode Status { get; set; }
+
+        /// <summary>
+        /// Information about why a specific status code was used.
+        /// </summary>
+        public string Reason { get; set; }
+
+        /// <summary>
+        /// Size of the body. MUST be specified before sending the header,
+        /// unless property Chunked is set to true.
+        /// </summary>
+        public long ContentLength
+        {
+            get { return m_contentLength; }
+            set { m_contentLength = value; }
+        }
+
+        /// <summary>
+        /// Kind of content in the body
+        /// </summary>
+        /// <remarks>Default type is "text/html"</remarks>
+        public string ContentType
+        {
+            get { return m_contentType; }
+            set { m_contentType = value; }
+        }
+
+        /// <summary>
+        /// Headers have been sent to the client-
+        /// </summary>
+        /// <remarks>You can not send any additional headers if they have already been sent.</remarks>
+        public bool HeadersSent { get; private set; }
+
+        /// <summary>
+        /// The whole response have been sent.
+        /// </summary>
+        public bool Sent { get; private set; }
+
+        /// <summary>
+        /// Cookies that should be created/changed.
+        /// </summary>
+        public ResponseCookies Cookies
+        {
+            get { return m_cookies; }
+        }
+
+        /// <summary>
+        /// Add another header to the document.
+        /// </summary>
+        /// <param name="name">Name of the header, case sensitive, use lower cases.</param>
+        /// <param name="value">Header values can span over multiple lines as long as each line starts with a white space. New line chars should be \r\n</param>
+        /// <exception cref="InvalidOperationException">If headers already been sent.</exception>
+        /// <exception cref="ArgumentException">If value conditions have not been met.</exception>
+        /// <remarks>Adding any header will override the default ones and those specified by properties.</remarks>
+        public void AddHeader(string name, string value)
+        {
+            if (HeadersSent)
+                throw new InvalidOperationException("Headers have already been sent.");
+
+            for (int i = 1; i < value.Length; ++i)
+            {
+                if (value[i] == '\r' && !char.IsWhiteSpace(value[i - 1]))
+                    throw new ArgumentException("New line in value do not start with a white space.");
+                if (value[i] == '\n' && value[i - 1] != '\r')
+                    throw new ArgumentException("Invalid new line sequence, should be \\r\\n (crlf).");
+            }
+
+            m_headers[name] = value;
+        }
+
+        /// <summary>
+        /// Send headers and body to the browser.
+        /// </summary>
+        /// <exception cref="InvalidOperationException">If content have already been sent.</exception>
+        public void SendOri()
+        {
+            if (Sent)
+                throw new InvalidOperationException("Everything have already been sent.");
+
+            m_context.ReqResponseAboutToSend(requestID);
+            if (m_context.MAXRequests == 0 || m_keepAlive == 0)
+            {
+                Connection = ConnectionType.Close;
+                m_context.TimeoutKeepAlive = 0;
+            }
+            else
+            {
+                if (m_keepAlive > 0)
+                    m_context.TimeoutKeepAlive = m_keepAlive * 1000;
+            }
+
+            if (!HeadersSent)
+            {
+                if (!SendHeaders())
+                {
+                    m_body.Dispose();
+                    Sent = true;
+                    return;
+                }
+            }
+
+            if(RawBuffer != null)
+            {
+                if(RawBufferStart >= 0 && RawBufferLen > 0)
+                {
+                    if (RawBufferStart > RawBuffer.Length)
+                        RawBufferStart = 0;
+
+                    if (RawBufferLen + RawBufferStart > RawBuffer.Length)
+                        RawBufferLen = RawBuffer.Length - RawBufferStart;
+
+                    /*
+                    int curlen;
+                    while(RawBufferLen > 0)
+                    {
+                        curlen = RawBufferLen;
+                        if(curlen > 8192)
+                            curlen = 8192;
+                        if (!_context.Send(RawBuffer, RawBufferStart, curlen))
+                        {
+                            RawBuffer = null;
+                            RawBufferStart = -1;
+                            RawBufferLen = -1;
+                            Body.Dispose();
+                            return;
+                        }
+                        RawBufferLen -= curlen;
+                        RawBufferStart += curlen;
+                    }
+                    */
+                    if(RawBufferLen > 0)
+                    {
+                        if (!m_context.Send(RawBuffer, RawBufferStart, RawBufferLen))
+                        {
+                            RawBuffer = null;
+                            RawBufferStart = -1;
+                            RawBufferLen = -1;
+                            if(m_body != null)
+                                m_body.Dispose();
+                            Sent = true;
+                            return;
+                        }
+                    }
+                }
+
+                RawBuffer = null;
+                RawBufferStart = -1;
+                RawBufferLen = -1;
+            }
+
+            if(m_body != null && m_body.Length > 0)
+            {
+                m_body.Flush();
+                m_body.Seek(0, SeekOrigin.Begin);
+
+                var buffer = new byte[8192];
+                int bytesRead = m_body.Read(buffer, 0, 8192);
+                while (bytesRead > 0)
+                {
+                    if (!m_context.Send(buffer, 0, bytesRead))
+                        break;
+                    bytesRead = m_body.Read(buffer, 0, 8192);
+                }
+
+                m_body.Dispose();
+            }
+            Sent = true;
+            m_context.ReqResponseSent(requestID, Connection);
+        }
+
+
+        /// <summary>
+        /// Make sure that you have specified <see cref="ContentLength"/> and sent the headers first.
+        /// </summary>
+        /// <param name="buffer"></param>
+        /// <exception cref="InvalidOperationException">If headers have not been sent.</exception>
+        /// <see cref="SendHeaders"/>
+        /// <param name="offset">offset of first byte to send</param>
+        /// <param name="count">number of bytes to send.</param>
+        /// <seealso cref="Send"/>
+        /// <seealso cref="SendHeaders"/>
+        /// <remarks>This method can be used if you want to send body contents without caching them first. This
+        /// is recommended for larger files to keep the memory usage low.</remarks>
+        public bool SendBody(byte[] buffer, int offset, int count)
+        {
+            if (!HeadersSent)
+                throw new InvalidOperationException("Send headers, and remember to specify ContentLength first.");
+
+            bool sent = m_context.Send(buffer, offset, count);
+            Sent = true;
+            if (sent)
+                m_context.ReqResponseSent(requestID, Connection);
+            return sent;
+        }
+
+        /// <summary>
+        /// Make sure that you have specified <see cref="ContentLength"/> and sent the headers first.
+        /// </summary>
+        /// <param name="buffer"></param>
+        /// <exception cref="InvalidOperationException">If headers have not been sent.</exception>
+        /// <see cref="SendHeaders"/>
+        /// <seealso cref="Send"/>
+        /// <seealso cref="SendHeaders"/>
+        /// <remarks>This method can be used if you want to send body contents without caching them first. This
+        /// is recommended for larger files to keep the memory usage low.</remarks>
+        public bool SendBody(byte[] buffer)
+        {
+            if (!HeadersSent)
+                throw new InvalidOperationException("Send headers, and remember to specify ContentLength first.");
+
+            bool sent = m_context.Send(buffer);
+            if (sent)
+                m_context.ReqResponseSent(requestID, Connection);
+            Sent = true;
+            return sent;
+        }
+
+        /// <summary>
+        /// Send headers to the client.
+        /// </summary>
+        /// <exception cref="InvalidOperationException">If headers already been sent.</exception>
+        /// <seealso cref="AddHeader"/>
+        /// <seealso cref="Send"/>
+        /// <seealso cref="SendBody(byte[])"/>
+        public bool SendHeaders()
+        {
+            if (HeadersSent)
+                throw new InvalidOperationException("Header have already been sent.");
+
+            HeadersSent = true;
+
+            if (m_headers["Date"] == null)
+                m_headers["Date"] = DateTime.Now.ToString("r");
+            if (m_headers["Content-Length"] == null)
+            {
+                int len = (int)m_contentLength;
+                if(len == 0)
+                {
+                    if(m_body != null)
+                        len = (int)m_body.Length;
+                    if(RawBuffer != null)
+                        len += RawBufferLen;
+                }
+                m_headers["Content-Length"] = len.ToString();
+            }
+            if (m_headers["Content-Type"] == null)
+                m_headers["Content-Type"] = m_contentType ?? DefaultContentType;
+            if (m_headers["Server"] == null)
+                m_headers["Server"] = "Tiny WebServer";
+
+            int keepaliveS = m_context.TimeoutKeepAlive / 1000;
+            if (Connection == ConnectionType.KeepAlive && keepaliveS > 0 && m_context.MAXRequests > 0)
+            {
+                m_headers["Keep-Alive"] = "timeout=" + keepaliveS + ", max=" + m_context.MAXRequests;
+                m_headers["Connection"] = "Keep-Alive";
+            }
+            else
+                m_headers["Connection"] = "close";
+
+            var sb = new StringBuilder();
+            sb.AppendFormat("{0} {1} {2}\r\n", m_httpVersion, (int)Status,
+                            string.IsNullOrEmpty(Reason) ? Status.ToString() : Reason);
+
+            for (int i = 0; i < m_headers.Count; ++i)
+            {
+                string headerName = m_headers.AllKeys[i];
+                string[] values = m_headers.GetValues(i);
+                if (values == null) continue;
+                foreach (string value in values)
+                    sb.AppendFormat("{0}: {1}\r\n", headerName, value);
+            }
+
+            foreach (ResponseCookie cookie in Cookies)
+                sb.AppendFormat("Set-Cookie: {0}\r\n", cookie);
+
+            sb.Append("\r\n");
+
+            m_headers.Clear();
+
+            return m_context.Send(Encoding.GetBytes(sb.ToString()));
+        }
+
+        public byte[] GetHeaders()
+        {
+            HeadersSent = true;
+
+            var sb = new StringBuilder();
+            if(string.IsNullOrWhiteSpace(m_httpVersion))
+                sb.AppendFormat("HTTP1/0 {0} {1}\r\n", (int)Status,
+                                string.IsNullOrEmpty(Reason) ? Status.ToString() : Reason);
+            else
+                sb.AppendFormat("{0} {1} {2}\r\n", m_httpVersion, (int)Status,
+                                string.IsNullOrEmpty(Reason) ? Status.ToString() : Reason);
+
+            if (m_headers["Date"] == null)
+                sb.AppendFormat("Date: {0}\r\n", DateTime.Now.ToString("r"));
+            if (m_headers["Content-Length"] == null)
+            {
+                long len = m_contentLength;
+                if (len == 0)
+                {
+                    len = Body.Length;
+                    if (RawBuffer != null && RawBufferLen > 0)
+                        len += RawBufferLen;
+                }
+                sb.AppendFormat("Content-Length: {0}\r\n", len);
+            }
+            if (m_headers["Content-Type"] == null)
+                sb.AppendFormat("Content-Type: {0}\r\n", m_contentType ?? DefaultContentType);
+            if (m_headers["Server"] == null)
+                sb.Append("Server: OSWebServer\r\n");
+
+            int keepaliveS = m_context.TimeoutKeepAlive / 1000;
+            if (Connection == ConnectionType.KeepAlive && keepaliveS > 0 && m_context.MAXRequests > 0)
+            {
+                sb.AppendFormat("Keep-Alive:timeout={0}, max={1}\r\n", keepaliveS, m_context.MAXRequests);
+                sb.Append("Connection: Keep-Alive\r\n");
+            }
+            else
+                sb.Append("Connection: close\r\n");
+
+            if (m_headers["Connection"] != null)
+                m_headers["Connection"] = null;
+            if (m_headers["Keep-Alive"] != null)
+                m_headers["Keep-Alive"] = null;
+
+            for (int i = 0; i < m_headers.Count; ++i)
+            {
+                string headerName = m_headers.AllKeys[i];
+                string[] values = m_headers.GetValues(i);
+                if (values == null) continue;
+                foreach (string value in values)
+                    sb.AppendFormat("{0}: {1}\r\n", headerName, value);
+            }
+
+            foreach (ResponseCookie cookie in Cookies)
+                sb.AppendFormat("Set-Cookie: {0}\r\n", cookie);
+
+            sb.Append("\r\n");
+
+            m_headers.Clear();
+
+            return Encoding.GetBytes(sb.ToString());
+        }
+
+        public void Send()
+        {
+            if (Sent)
+                throw new InvalidOperationException("Everything have already been sent.");
+
+            if (m_context.MAXRequests == 0 || m_keepAlive == 0)
+            {
+                Connection = ConnectionType.Close;
+                m_context.TimeoutKeepAlive = 0;
+            }
+            else
+            {
+                if (m_keepAlive > 0)
+                    m_context.TimeoutKeepAlive = m_keepAlive * 1000;
+            }
+
+            m_headerBytes = GetHeaders();
+            if (RawBuffer != null)
+            {
+                if (RawBufferStart < 0 || RawBufferStart > RawBuffer.Length)
+                    return;
+
+                if (RawBufferLen < 0)
+                    RawBufferLen = RawBuffer.Length;
+
+                if (RawBufferLen + RawBufferStart > RawBuffer.Length)
+                    RawBufferLen = RawBuffer.Length - RawBufferStart;
+
+                int tlen = m_headerBytes.Length + RawBufferLen;
+                if(RawBufferLen > 0 && tlen < 16384)
+                {
+                    byte[] tmp = new byte[tlen];
+                    Array.Copy(m_headerBytes, tmp, m_headerBytes.Length);
+                    Array.Copy(RawBuffer, RawBufferStart, tmp, m_headerBytes.Length, RawBufferLen);
+                    m_headerBytes = null;
+                    RawBuffer = tmp;
+                    RawBufferStart = 0;
+                    RawBufferLen = tlen;
+                }
+            }
+            m_context.StartSendResponse(this);
+        }
+
+        public async Task SendNextAsync(int bytesLimit)
+        {
+            if (m_headerBytes != null)
+            {
+                if(!await m_context.SendAsync(m_headerBytes, 0, m_headerBytes.Length).ConfigureAwait(false))
+                {
+                    if(m_body != null)
+                        m_body.Dispose();
+                    RawBuffer = null;
+                    Sent = true;
+                    return;
+                }
+                bytesLimit -= m_headerBytes.Length;
+                m_headerBytes = null;
+                if(bytesLimit <= 0)
+                {
+                    m_context.ContinueSendResponse();
+                    return;
+                }
+            }
+
+            if (RawBuffer != null)
+            {
+                if (RawBufferLen > 0)
+                {
+                    bool sendRes;
+                    if(RawBufferLen > bytesLimit)
+                    {
+                        sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, bytesLimit).ConfigureAwait(false);
+                        RawBufferLen -= bytesLimit;
+                        RawBufferStart += bytesLimit;
+                    }
+                    else
+                    {
+                        sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, RawBufferLen).ConfigureAwait(false);
+                        RawBufferLen = 0;
+                    }
+
+                    if (!sendRes)
+                    {
+                        RawBuffer = null;
+                        if(m_body != null)
+                            Body.Dispose();
+                        Sent = true;
+                        return;
+                    }
+                }
+                if (RawBufferLen <= 0)
+                    RawBuffer = null;
+                else
+                {
+                    m_context.ContinueSendResponse();
+                    return;
+                }
+            }
+
+            if (m_body != null && m_body.Length != 0)
+            {
+                m_body.Flush();
+                m_body.Seek(0, SeekOrigin.Begin);
+
+                RawBuffer = new byte[m_body.Length];
+                RawBufferLen = m_body.Read(RawBuffer, 0, (int)m_body.Length);
+                m_body.Dispose();
+
+                if(RawBufferLen > 0)
+                {
+                    bool sendRes;
+                    if (RawBufferLen > bytesLimit)
+                    {
+                        sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, bytesLimit).ConfigureAwait(false);
+                        RawBufferLen -= bytesLimit;
+                        RawBufferStart += bytesLimit;
+                    }
+                    else
+                    {
+                        sendRes = await m_context.SendAsync(RawBuffer, RawBufferStart, RawBufferLen).ConfigureAwait(false);
+                        RawBufferLen = 0;
+                    }
+
+                    if (!sendRes)
+                    {
+                        RawBuffer = null;
+                        Sent = true;
+                        return;
+                    }
+                }
+                if (RawBufferLen > 0)
+                {
+                    m_context.ContinueSendResponse();
+                    return;
+                }
+            }
+
+            if (m_body != null)
+                m_body.Dispose();
+            Sent = true;
+            m_context.ReqResponseSent(requestID, Connection);
+        }
+
+        /// <summary>
+        /// Redirect client to somewhere else using the 302 status code.
+        /// </summary>
+        /// <param name="uri">Destination of the redirect</param>
+        /// <exception cref="InvalidOperationException">If headers already been sent.</exception>
+        /// <remarks>You can not do anything more with the request when a redirect have been done. This should be your last
+        /// action.</remarks>
+        public void Redirect(Uri uri)
+        {
+            Status = HttpStatusCode.Redirect;
+            m_headers["location"] = uri.ToString();
+        }
+
+        /// <summary>
+        /// redirect to somewhere
+        /// </summary>
+        /// <param name="url">where the redirect should go</param>
+        /// <remarks>
+        /// No body are allowed when doing redirects.
+        /// </remarks>
+        public void Redirect(string url)
+        {
+            Status = HttpStatusCode.Redirect;
+            m_headers["location"] = url;
+        }
+
+        public void Clear()
+        {
+            if(Body != null && Body.CanRead)
+                Body.Dispose();
+        }
+        #endregion
+    }
+}

+ 146 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpClientContext.cs

@@ -0,0 +1,146 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Contains a connection to a browser/client.
+    /// </summary>
+    public interface IHttpClientContext
+    {
+
+        /// <summary>
+        /// Get SSL commonName of remote peer
+        /// </summary>
+        string SSLCommonName { get; }
+
+        /// <summary>
+        /// Using SSL or other encryption method.
+        /// </summary>
+        bool IsSecured { get; }
+
+        int contextID {get;}
+        int TimeoutKeepAlive {get; set; }
+        int MAXRequests{get; set; }
+
+        bool CanSend();
+        bool IsSending();
+
+        /// <summary>
+        /// Disconnect from client
+        /// </summary>
+        /// <param name="error">error to report in the <see cref="Disconnected"/> event.</param>
+        void Disconnect(SocketError error);
+
+        /// <summary>
+        /// Send a response.
+        /// </summary>
+        /// <param name="httpVersion">Either <see cref="HttpHelper.HTTP10"/> or <see cref="HttpHelper.HTTP11"/></param>
+        /// <param name="statusCode">HTTP status code</param>
+        /// <param name="reason">reason for the status code.</param>
+        /// <param name="body">HTML body contents, can be null or empty.</param>
+        /// <param name="contentType">A content type to return the body as, i.e. 'text/html' or 'text/plain', defaults to 'text/html' if null or empty</param>
+        /// <exception cref="ArgumentException">If <paramref name="httpVersion"/> is invalid.</exception>
+        void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body, string contentType);
+
+        /// <summary>
+        /// Send a response.
+        /// </summary>
+        /// <param name="httpVersion">Either <see cref="HttpHelper.HTTP10"/> or <see cref="HttpHelper.HTTP11"/></param>
+        /// <param name="statusCode">HTTP status code</param>
+        /// <param name="reason">reason for the status code.</param>
+        void Respond(string httpVersion, HttpStatusCode statusCode, string reason);
+
+        /// <summary>
+        /// send a whole buffer
+        /// </summary>
+        /// <param name="buffer">buffer to send</param>
+        /// <exception cref="ArgumentNullException"></exception>
+        bool Send(byte[] buffer);
+
+        /// <summary>
+        /// Send data using the stream
+        /// </summary>
+        /// <param name="buffer">Contains data to send</param>
+        /// <param name="offset">Start position in buffer</param>
+        /// <param name="size">number of bytes to send</param>
+        /// <exception cref="ArgumentNullException"></exception>
+        /// <exception cref="ArgumentOutOfRangeException"></exception>
+        bool Send(byte[] buffer, int offset, int size);
+        Task<bool> SendAsync(byte[] buffer, int offset, int size);
+
+        /// <summary>
+        /// Closes the streams and disposes of the unmanaged resources
+        /// </summary>
+        void Close();
+
+        /// <summary>
+        /// The context have been disconnected.
+        /// </summary>
+        /// <remarks>
+        /// Event can be used to clean up a context, or to reuse it.
+        /// </remarks>
+        event EventHandler<DisconnectedEventArgs> Disconnected;
+
+        /// <summary>
+        /// A request have been received in the context.
+        /// </summary>
+        event EventHandler<RequestEventArgs> RequestReceived;
+
+        HTTPNetworkContext GiveMeTheNetworkStreamIKnowWhatImDoing();
+
+        void StartSendResponse(HttpResponse response);
+        void ContinueSendResponse();
+        void ReqResponseAboutToSend(uint requestID);
+        void ReqResponseSent(uint requestID, ConnectionType connection);
+        bool TrySendResponse(int limit);
+    }
+    public class HTTPNetworkContext
+    {
+        public NetworkStream Stream;
+        public Socket Socket;
+    }
+
+    /// <summary>
+    /// A <see cref="IHttpClientContext"/> have been disconnected.
+    /// </summary>
+    public class DisconnectedEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Gets reason to why client disconnected.
+        /// </summary>
+        public SocketError Error { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="DisconnectedEventArgs"/> class.
+        /// </summary>
+        /// <param name="error">Reason to disconnection.</param>
+        public DisconnectedEventArgs(SocketError error)
+        {
+            Error = error;
+        }
+    }
+
+    /// <summary>
+    /// 
+    /// </summary>
+    public class RequestEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Gets received request.
+        /// </summary>
+        public IHttpRequest Request { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RequestEventArgs"/> class.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        public RequestEventArgs(IHttpRequest request)
+        {
+            Request = request;
+        }
+    }
+
+}

+ 165 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpRequest.cs

@@ -0,0 +1,165 @@
+using System;
+using System.Collections.Specialized;
+using System.IO;
+using OSHttpServer.Exceptions;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Contains server side HTTP request information.
+    /// </summary>
+    public interface IHttpRequest : ICloneable
+    {
+        /// <summary>
+        /// Gets kind of types accepted by the client.
+        /// </summary>
+        string[] AcceptTypes { get; }
+
+        uint ID {get; }
+        /// <summary>
+        /// Gets or sets body stream.
+        /// </summary>
+        Stream Body { get; set; }
+
+        /// <summary>
+        /// Gets whether the body is complete.
+        /// </summary>
+        bool BodyIsComplete { get; }
+
+        /// <summary>
+        /// Gets or sets kind of connection used for the session.
+        /// </summary>
+        ConnectionType Connection { get; set; }
+
+        IHttpClientContext Context { get; }
+
+        /// <summary>
+        /// Gets or sets number of bytes in the body.
+        /// </summary>
+        int ContentLength { get; set; }
+
+        /// <summary>
+        /// Gets cookies that was sent with the request.
+        /// </summary>
+        RequestCookies Cookies { get; }
+
+        /// <summary>
+        /// Gets form parameters.
+        /// </summary>
+        //HttpForm Form { get; }
+
+        /// <summary>
+        /// Gets headers sent by the client.
+        /// </summary>
+        NameValueCollection Headers { get; }
+
+        /// <summary>
+        /// Gets or sets version of HTTP protocol that's used. 
+        /// </summary>
+        /// <remarks>
+        /// Probably <see cref="HttpHelper.HTTP10"/> or <see cref="HttpHelper.HTTP11"/>.
+        /// </remarks>
+        /// <seealso cref="HttpHelper"/>
+        string HttpVersion { get; set; }
+
+        /// <summary>
+        /// Gets whether the request was made by Ajax (Asynchronous JavaScript)
+        /// </summary>
+        bool IsAjax { get; }
+
+        /// <summary>
+        /// Gets or sets requested method.
+        /// </summary>
+        /// <remarks>
+        /// Will always be in upper case.
+        /// </remarks>
+        /// <see cref="Method"/>
+        string Method { get; set; }
+
+        /// <summary>
+        /// Gets parameter from <see cref="QueryString"/> or <see cref="Form"/>.
+        /// </summary>
+        HttpParam Param { get; }
+
+        /// <summary>
+        /// Gets variables sent in the query string
+        /// </summary>
+        HttpInput QueryString { get; }
+
+        /// <summary>
+        /// Gets or sets requested URI.
+        /// </summary>
+        Uri Uri { get; set; }
+
+        /// <summary>
+        /// Gets URI absolute path divided into parts.
+        /// </summary>
+        /// <example>
+        /// // URI is: http://gauffin.com/code/tiny/
+        /// Console.WriteLine(request.UriParts[0]); // result: code
+        /// Console.WriteLine(request.UriParts[1]); // result: tiny
+        /// </example>
+        /// <remarks>
+        /// If you're using controllers than the first part is controller name,
+        /// the second part is method name and the third part is Id property.
+        /// </remarks>
+        /// <seealso cref="Uri"/>
+        string[] UriParts { get; }
+
+        /// <summary>
+        /// Gets or sets path and query.
+        /// </summary>
+        /// <see cref="Uri"/>
+        /// <remarks>
+        /// Are only used during request parsing. Cannot be set after "Host" header have been
+        /// added.
+        /// </remarks>
+        string UriPath { get; set; }
+
+        /// <summary>
+        /// Called during parsing of a <see cref="IHttpRequest"/>.
+        /// </summary>
+        /// <param name="name">Name of the header, should not be URL encoded</param>
+        /// <param name="value">Value of the header, should not be URL encoded</param>
+        /// <exception cref="BadRequestException">If a header is incorrect.</exception>
+        void AddHeader(string name, string value);
+
+        /// <summary>
+        /// Add bytes to the body
+        /// </summary>
+        /// <param name="bytes">buffer to read bytes from</param>
+        /// <param name="offset">where to start read</param>
+        /// <param name="length">number of bytes to read</param>
+        /// <returns>Number of bytes actually read (same as length unless we got all body bytes).</returns>
+        /// <exception cref="InvalidOperationException">If body is not writable</exception>
+        /// <exception cref="ArgumentNullException"><c>bytes</c> is null.</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><c>offset</c> is out of range.</exception>
+        int AddToBody(byte[] bytes, int offset, int length);
+
+        /// <summary>
+        /// Clear everything in the request
+        /// </summary>
+        void Clear();
+
+        /// <summary>
+        /// Decode body into a form.
+        /// </summary>
+        /// <param name="providers">A list with form decoders.</param>
+        /// <exception cref="InvalidDataException">If body contents is not valid for the chosen decoder.</exception>
+        /// <exception cref="InvalidOperationException">If body is still being transferred.</exception>
+        //void DecodeBody(FormDecoderProvider providers);
+
+        /// <summary>
+        /// Sets the cookies.
+        /// </summary>
+        /// <param name="cookies">The cookies.</param>
+        void SetCookies(RequestCookies cookies);
+
+		/// <summary>
+		/// Create a response object.
+		/// </summary>
+		/// <param name="context">Context for the connected client.</param>
+		/// <returns>A new <see cref="IHttpResponse"/>.</returns>
+		//IHttpResponse CreateResponse(IHttpClientContext context);
+    }
+}

+ 94 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpRequestParser.cs

@@ -0,0 +1,94 @@
+using System;
+using OSHttpServer.Exceptions;
+using OSHttpServer.Parser;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Event driven parser used to parse incoming HTTP requests.
+    /// </summary>
+    /// <remarks>
+    /// The parser supports partial messages and keeps the states between
+    /// each parsed buffer. It's therefore important that the parser gets
+    /// <see cref="Clear"/>ed if a client disconnects.
+    /// </remarks>
+    public interface IHttpRequestParser
+    {
+        /// <summary>
+        /// Current state in parser.
+        /// </summary>
+        RequestParserState CurrentState { get; }
+
+        /// <summary>
+        /// Parse partial or complete message.
+        /// </summary>
+        /// <param name="buffer">buffer containing incoming bytes</param>
+        /// <param name="offset">where in buffer that parsing should start</param>
+        /// <param name="count">number of bytes to parse</param>
+        /// <returns>Unparsed bytes left in buffer.</returns>
+        /// <exception cref="BadRequestException"><c>BadRequestException</c>.</exception>
+        int Parse(byte[] buffer, int offset, int count);
+
+        /// <summary>
+        /// A request have been successfully parsed.
+        /// </summary>
+        event EventHandler RequestCompleted;
+
+        /// <summary>
+        /// More body bytes have been received.
+        /// </summary>
+        event EventHandler<BodyEventArgs> BodyBytesReceived;
+
+        /// <summary>
+        /// Request line have been received.
+        /// </summary>
+        event EventHandler<RequestLineEventArgs> RequestLineReceived;
+
+        /// <summary>
+        /// A header have been received.
+        /// </summary>
+        event EventHandler<HeaderEventArgs> HeaderReceived;
+
+		/// <summary>
+		/// Clear parser state.
+		/// </summary>
+    	void Clear();
+
+		/// <summary>
+		/// Gets or sets the log writer.
+		/// </summary>
+    	ILogWriter LogWriter { get; set; }
+    }
+
+    /// <summary>
+    /// Current state in the parsing.
+    /// </summary>
+    public enum RequestParserState
+    {
+        /// <summary>
+        /// Should parse the request line
+        /// </summary>
+        FirstLine,
+        /// <summary>
+        /// Searching for a complete header name
+        /// </summary>
+        HeaderName,
+        /// <summary>
+        /// Searching for colon after header name (ignoring white spaces)
+        /// </summary>
+        AfterName,
+        /// <summary>
+        /// Searching for start of header value (ignoring white spaces)
+        /// </summary>
+        Between,
+        /// <summary>
+        /// Searching for a complete header value (can span over multiple lines, as long as they are prefixed with one/more whitespaces)
+        /// </summary>
+        HeaderValue,
+
+        /// <summary>
+        /// Adding bytes to body
+        /// </summary>
+        Body
+    }
+}

+ 180 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/IHttpResponse.cs

@@ -0,0 +1,180 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Text;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Response that is sent back to the web browser / client.
+    /// 
+    /// A response can be sent if different ways. The easiest one is
+    /// to just fill the Body stream with content, everything else
+    /// will then be taken care of by the framework. The default content-type
+    /// is text/html, you should change it if you send anything else.
+    /// 
+    /// The second and slighty more complex way is to send the response
+    /// as parts. Start with sending the header using the SendHeaders method and 
+    /// then you can send the body using SendBody method, but do not forget
+    /// to set ContentType and ContentLength before doing so.
+    /// </summary>
+    /// <example>
+    /// public void MyHandler(IHttpRequest request, IHttpResponse response)
+    /// {
+    ///   
+    /// }
+    /// </example>
+    public interface IHttpResponse
+    {
+        /// <summary>
+        /// The body stream is used to cache the body contents
+        /// before sending everything to the client. It's the simplest
+        /// way to serve documents.
+        /// </summary>
+        Stream Body { get; set; }
+        byte[] RawBuffer { get; set; }
+        int RawBufferStart { get; set; }
+        int RawBufferLen { get; set; }
+        uint requestID { get; }
+
+        /// <summary>
+        /// Defines the version of the HTTP Response for applications where it's required
+        /// for this to be forced.
+        /// </summary>
+        string ProtocolVersion { get; set; }
+        int Priority { get; set; }
+
+        /// <summary>
+        /// The chunked encoding modifies the body of a message in order to
+        /// transfer it as a series of chunks, each with its own size indicator,
+        /// followed by an OPTIONAL trailer containing entity-header fields. This
+        /// allows dynamically produced content to be transferred along with the
+        /// information necessary for the recipient to verify that it has
+        /// received the full message.
+        /// </summary>
+        bool Chunked { get; set; }
+
+        /// <summary>
+        /// Kind of connection
+        /// </summary>
+        ConnectionType Connection { get; set; }
+
+        /// <summary>
+        /// Encoding to use when sending stuff to the client.
+        /// </summary>
+        /// <remarks>Default is UTF8</remarks>
+        Encoding Encoding { get; set; }
+
+        /// <summary>
+        /// Number of seconds to keep connection alive
+        /// </summary>
+        /// <remarks>Only used if Connection property is set to ConnectionType.KeepAlive</remarks>
+        int KeepAlive { get; set; }
+
+        /// <summary>
+        /// Status code that is sent to the client.
+        /// </summary>
+        /// <remarks>Default is HttpStatusCode.Ok</remarks>
+        HttpStatusCode Status { get; set; }
+
+        /// <summary>
+        /// Information about why a specific status code was used.
+        /// </summary>
+        string Reason { get; set; }
+
+        /// <summary>
+        /// Size of the body. MUST be specified before sending the header,
+        /// unless property Chunked is set to true.
+        /// </summary>
+        long ContentLength { get; set; }
+
+        /// <summary>
+        /// Kind of content in the body
+        /// </summary>
+        /// <remarks>Default is text/html</remarks>
+        string ContentType { get; set; }
+
+        /// <summary>
+        /// Headers have been sent to the client-
+        /// </summary>
+        /// <remarks>You can not send any additional headers if they have already been sent.</remarks>
+        bool HeadersSent { get; }
+
+        /// <summary>
+        /// The whole response have been sent.
+        /// </summary>
+        bool Sent { get; }
+
+        /// <summary>
+        /// Cookies that should be created/changed.
+        /// </summary>
+        ResponseCookies Cookies { get; }
+
+        /// <summary>
+        /// Add another header to the document.
+        /// </summary>
+        /// <param name="name">Name of the header, case sensitive, use lower cases.</param>
+        /// <param name="value">Header values can span over multiple lines as long as each line starts with a white space. New line chars should be \r\n</param>
+        /// <exception cref="InvalidOperationException">If headers already been sent.</exception>
+        /// <exception cref="ArgumentException">If value conditions have not been met.</exception>
+        /// <remarks>Adding any header will override the default ones and those specified by properties.</remarks>
+        void AddHeader(string name, string value);
+
+        /// <summary>
+        /// Send headers and body to the browser.
+        /// </summary>
+        /// <exception cref="InvalidOperationException">If content have already been sent.</exception>
+        void Send();
+
+        /// <summary>
+        /// Make sure that you have specified ContentLength and sent the headers first.
+        /// </summary>
+        /// <param name="buffer"></param>
+        /// <exception cref="InvalidOperationException">If headers have not been sent.</exception>
+        /// <see cref="IHttpResponse.SendHeaders"/>
+        /// <param name="offset">offest of first byte to send</param>
+        /// <param name="count">number of bytes to send.</param>
+        /// <seealso cref="IHttpResponse.Send"/>
+        /// <seealso cref="IHttpResponse.SendHeaders"/>
+        /// <remarks>This method can be used if you want to send body contents without caching them first. This
+        /// is recommended for larger files to keep the memory usage low.</remarks>
+        bool SendBody(byte[] buffer, int offset, int count);
+
+        /// <summary>
+        /// Make sure that you have specified ContentLength and sent the headers first.
+        /// </summary>
+        /// <param name="buffer"></param>
+        /// <exception cref="InvalidOperationException">If headers have not been sent.</exception>
+        /// <see cref="IHttpResponse.SendHeaders"/>
+        /// <seealso cref="IHttpResponse.Send"/>
+        /// <seealso cref="IHttpResponse.SendHeaders"/>
+        /// <remarks>This method can be used if you want to send body contents without caching them first. This
+        /// is recommended for larger files to keep the memory usage low.</remarks>
+        bool SendBody(byte[] buffer);
+
+        /// <summary>
+        /// Send headers to the client.
+        /// </summary>
+        /// <exception cref="InvalidOperationException">If headers already been sent.</exception>
+        /// <seealso cref="IHttpResponse.AddHeader"/>
+        /// <seealso cref="IHttpResponse.Send"/>
+        /// <seealso cref="IHttpResponse.SendBody(byte[])"/>
+        bool SendHeaders();
+    }
+
+    /// <summary>
+    /// Type of HTTP connection
+    /// </summary>
+    public enum ConnectionType
+    {
+        /// <summary>
+        /// Connection is closed after each request-response
+        /// </summary>
+        Close,
+
+        /// <summary>
+        /// Connection is kept alive for X seconds (unless another request have been made)
+        /// </summary>
+        KeepAlive
+    }
+}

+ 81 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/ILogWriter.cs

@@ -0,0 +1,81 @@
+using System;
+using System.Diagnostics;
+using System.Text;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Priority for log entries
+    /// </summary>
+    /// <seealso cref="ILogWriter"/>
+
+    public enum LogPrio
+    {
+        None,
+        /// <summary>
+        /// Very detailed logs to be able to follow the flow of the program.
+        /// </summary>
+        Trace,
+
+        /// <summary>
+        /// Logs to help debug errors in the application
+        /// </summary>
+        Debug,
+
+        /// <summary>
+        /// Information to be able to keep track of state changes etc.
+        /// </summary>
+        Info,
+
+        /// <summary>
+        /// Something did not go as we expected, but it's no problem.
+        /// </summary>
+        Warning,
+
+        /// <summary>
+        /// Something that should not fail failed, but we can still keep
+        /// on going.
+        /// </summary>
+        Error,
+
+        /// <summary>
+        /// Something failed, and we cannot handle it properly.
+        /// </summary>
+        Fatal
+    }
+
+    /// <summary>
+    /// Interface used to write to log files.
+    /// </summary>
+    public interface ILogWriter
+    {
+        /// <summary>
+        /// Write an entry to the log file.
+        /// </summary>
+        /// <param name="source">object that is writing to the log</param>
+        /// <param name="priority">importance of the log message</param>
+        /// <param name="message">the message</param>
+        void Write(object source, LogPrio priority, string message);
+    }
+
+    /// <summary>
+    /// Default log writer, writes everything to null (nowhere).
+    /// </summary>
+    /// <seealso cref="ILogWriter"/>
+
+    public sealed class NullLogWriter : ILogWriter
+    {
+        /// <summary>
+        /// The logging instance.
+        /// </summary>
+        public static readonly NullLogWriter Instance = new NullLogWriter();
+
+        /// <summary>
+        /// Writes everything to null
+        /// </summary>
+        /// <param name="source">object that wrote the log entry.</param>
+        /// <param name="prio">Importance of the log message</param>
+        /// <param name="message">The message.</param>
+        public void Write(object source, LogPrio prio, string message) {}
+    }
+}

+ 38 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/InternalServerException.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Net;
+
+namespace OSHttpServer.Exceptions
+{
+    /// <summary>
+    /// The server encountered an unexpected condition which prevented it from fulfilling the request.
+    /// </summary>
+    public class InternalServerException : HttpException
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="InternalServerException"/> class.
+        /// </summary>
+        public InternalServerException()
+            : base(HttpStatusCode.InternalServerError, "The server encountered an unexpected condition which prevented it from fulfilling the request.")
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="InternalServerException"/> class.
+        /// </summary>
+        /// <param name="message">error message.</param>
+        public InternalServerException(string message)
+            : base(HttpStatusCode.InternalServerError, message)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="InternalServerException"/> class.
+        /// </summary>
+        /// <param name="message">error message.</param>
+        /// <param name="inner">inner exception.</param>
+        public InternalServerException(string message, Exception inner)
+            : base(HttpStatusCode.InternalServerError, message, inner)
+        {
+        }
+    }
+}

+ 29 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/NotFoundException.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Net;
+
+namespace OSHttpServer.Exceptions
+{
+    /// <summary>
+    /// The requested resource was not found in the web server.
+    /// </summary>
+    public class NotFoundException : HttpException
+    {
+        /// <summary>
+        /// Create a new exception
+        /// </summary>
+        /// <param name="message">message describing the error</param>
+        /// <param name="inner">inner exception</param>
+        public NotFoundException(string message, Exception inner) : base(HttpStatusCode.NotFound, message, inner)
+        {
+        }
+
+        /// <summary>
+        /// Create a new exception
+        /// </summary>
+        /// <param name="message">message describing the error</param>
+        public NotFoundException(string message)
+            : base(HttpStatusCode.NotFound, message)
+        {
+        }
+    }
+}

+ 70 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestCookie.cs

@@ -0,0 +1,70 @@
+using System;
+using System.Web;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// cookie sent by the client/browser
+    /// </summary>
+    /// <seealso cref="ResponseCookie"/>
+    public class RequestCookie
+    {
+        private readonly string _name = null;
+        private string _value = null;
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="id">cookie identifier</param>
+        /// <param name="content">cookie content</param>
+        /// <exception cref="ArgumentNullException">id or content is null</exception>
+        /// <exception cref="ArgumentException">id is empty</exception>
+        public RequestCookie(string id, string content)
+        {
+            if (string.IsNullOrEmpty(id)) throw new ArgumentNullException("id");
+            if (content == null) throw new ArgumentNullException("content");
+
+            _name = id;
+            _value = content;
+        }
+
+
+        #region inherited methods
+
+        /// <summary>
+        /// Gets the cookie HTML representation.
+        /// </summary>
+        /// <returns>cookie string</returns>
+        public override string ToString()
+        {
+            return string.Format("{0}={1}; ", HttpUtility.UrlEncode(_name), HttpUtility.UrlEncode(_value));
+        }
+
+        #endregion
+
+        #region public properties
+
+        /// <summary>
+        /// Gets the cookie identifier.
+        /// </summary>
+        public string Name
+        {
+            get { return _name; }
+        }
+
+
+        /// <summary>
+        /// Cookie value. Set to null to remove cookie.
+        /// </summary>
+        public string Value
+        {
+            get { return _value; }
+            set
+            {
+                _value = value;
+            }
+        }
+
+        #endregion
+    }
+}

+ 164 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestCookies.cs

@@ -0,0 +1,164 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// This class is created as a wrapper, since there are two different cookie types in .Net (Cookie and HttpCookie).
+    /// The framework might switch class in the future and we dont want to have to replace all instances
+    /// </summary>
+    public sealed class RequestCookies : IEnumerable<RequestCookie>
+    {
+        private readonly IDictionary<string, RequestCookie> _items = new Dictionary<string, RequestCookie>();
+
+        /// <summary>
+        /// Let's copy all the cookies.
+        /// </summary>
+        /// <param name="cookies">value from cookie header.</param>
+        public RequestCookies(string cookies)
+        {
+            if (string.IsNullOrEmpty(cookies))
+                return;
+
+            string name = string.Empty;
+            int state = 0;
+            int start = -1;
+            for (int i = 0; i < cookies.Length; ++i)
+            {
+                char ch = cookies[i];
+
+                // searching for start of cookie name
+                switch (state)
+                {
+                    case 0:
+                        if (char.IsWhiteSpace(ch))
+                            continue;
+                        start = i;
+                        ++state;
+                        break;
+                    case 1:
+                        if (char.IsWhiteSpace(ch) || ch == '=')
+                        {
+                            if (start == -1)
+                                return; // todo: decide if an exception should be thrown.
+                            name = cookies.Substring(start, i - start);
+                            start = -1;
+                            ++state;
+                        }
+                        break;
+                    case 2:
+                        if (!char.IsWhiteSpace(ch) && ch != '=')
+                        {
+                            start = i;
+                            ++state;
+                        }
+                        break;
+                    case 3:
+                        if (ch == ';')
+                        {
+                            if (start >= -1)
+                                Add(new RequestCookie(name, cookies.Substring(start, i - start)));
+                            start = -1;
+                            state = 0;
+                            name = string.Empty;
+                        }
+                        break;
+                }
+            }
+
+            // last cookie
+            if (name != string.Empty)
+                Add(new RequestCookie(name, cookies.Substring(start, cookies.Length - start)));
+        }
+
+        /// <summary>
+        /// Adds a cookie in the collection.
+        /// </summary>
+        /// <param name="cookie">cookie to add</param>
+        /// <exception cref="ArgumentNullException">cookie is null</exception>
+        internal void Add(RequestCookie cookie)
+        {
+            // Verifies the parameter
+            if (cookie == null)
+                throw new ArgumentNullException("cookie");
+            if (cookie.Name == null || cookie.Name.Trim() == string.Empty)
+                throw new ArgumentException("Name must be specified.");
+            if (cookie.Value == null || cookie.Value.Trim() == string.Empty)
+                throw new ArgumentException("Content must be specified.");
+
+            if (_items.ContainsKey(cookie.Name))
+                _items[cookie.Name] = cookie;
+            else _items.Add(cookie.Name, cookie);
+        }
+
+        /// <summary>
+        /// Gets the count of cookies in the collection.
+        /// </summary>
+        public int Count
+        {
+            get { return _items.Count; }
+        }
+
+
+        /// <summary>
+        /// Gets the cookie of a given identifier (null if not existing).
+        /// </summary>
+        public RequestCookie this[string id]
+        {
+            get 
+            {
+                return _items.ContainsKey(id) ? _items[id] : null;
+            }
+        }
+        /// <summary>
+        /// Gets a collection enumerator on the cookie list.
+        /// </summary>
+        /// <returns>collection enumerator</returns>
+        public IEnumerator GetEnumerator()
+        {
+            return _items.Values.GetEnumerator();
+        }
+
+
+        /// <summary>
+        /// Remove all cookies.
+        /// </summary>
+        public void Clear()
+        {
+            _items.Clear();
+        }
+
+        #region IEnumerable<RequestCookie> Members
+
+        ///<summary>
+        ///Returns an enumerator that iterates through the collection.
+        ///</summary>
+        ///
+        ///<returns>
+        ///A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
+        ///</returns>
+        ///<filterpriority>1</filterpriority>
+        IEnumerator<RequestCookie> IEnumerable<RequestCookie>.GetEnumerator()
+        {
+            return _items.Values.GetEnumerator();
+        }
+
+        #endregion
+
+		/// <summary>
+		/// Remove a cookie from the collection.
+		/// </summary>
+		/// <param name="cookieName">Name of cookie.</param>
+        public void Remove(string cookieName)
+        {
+            lock (_items)
+            {
+                if (!_items.ContainsKey(cookieName))
+                    return;
+
+                _items.Remove(cookieName);
+            }
+        }
+    }
+}

+ 48 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestLineEventArgs.cs

@@ -0,0 +1,48 @@
+using System;
+
+namespace OSHttpServer.Parser
+{
+    /// <summary>
+    /// Used when the request line have been successfully parsed.
+    /// </summary>
+    public class RequestLineEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RequestLineEventArgs"/> class.
+        /// </summary>
+        /// <param name="httpMethod">The HTTP method.</param>
+        /// <param name="uriPath">The URI path.</param>
+        /// <param name="httpVersion">The HTTP version.</param>
+        public RequestLineEventArgs(string httpMethod, string uriPath, string httpVersion)
+        {
+            HttpMethod = httpMethod;
+            UriPath = uriPath;
+            HttpVersion = httpVersion;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RequestLineEventArgs"/> class.
+        /// </summary>
+        public RequestLineEventArgs()
+        {
+        }
+
+        /// <summary>
+        /// Gets or sets http method.
+        /// </summary>
+        /// <remarks>
+        /// Should be one of the methods declared in <see cref="Method"/>.
+        /// </remarks>
+        public string HttpMethod { get; set; }
+
+        /// <summary>
+        /// Gets or sets the version of the HTTP protocol that the client want to use.
+        /// </summary>
+        public string HttpVersion { get; set; }
+
+        /// <summary>
+        /// Gets or sets requested URI path.
+        /// </summary>
+        public string UriPath { get; set; }
+    }
+}

+ 33 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/RequestParserFactory.cs

@@ -0,0 +1,33 @@
+using OSHttpServer.Parser;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Creates request parsers when needed.
+    /// </summary>
+    public class RequestParserFactory : IRequestParserFactory
+    {
+        /// <summary>
+        /// Create a new request parser.
+        /// </summary>
+        /// <param name="logWriter">Used when logging should be enabled.</param>
+        /// <returns>A new request parser.</returns>
+        public IHttpRequestParser CreateParser(ILogWriter logWriter)
+        {
+            return new HttpRequestParser(logWriter);
+        }
+    }
+
+    /// <summary>
+    /// Creates request parsers when needed.
+    /// </summary>
+    public interface IRequestParserFactory
+    {
+        /// <summary>
+        /// Create a new request parser.
+        /// </summary>
+        /// <param name="logWriter">Used when logging should be enabled.</param>
+        /// <returns>A new request parser.</returns>
+        IHttpRequestParser CreateParser(ILogWriter logWriter);
+    }
+}

+ 123 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/ResponseCookie.cs

@@ -0,0 +1,123 @@
+using System;
+using System.Web;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// cookie being sent back to the browser.
+    /// </summary>
+    /// <seealso cref="ResponseCookie"/>
+    public class ResponseCookie : RequestCookie
+    {
+        private const string _nullPath = "/";
+        private bool _persistant = false;
+        private DateTime _expires;
+        private string _path = "/";
+        private readonly string _domain;
+
+        #region constructors
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="id">cookie identifier</param>
+        /// <param name="content">cookie content</param>
+        /// <param name="expiresAt">cookie expiration date. Use DateTime.MinValue for session cookie.</param>
+        /// <exception cref="ArgumentNullException">id or content is null</exception>
+        /// <exception cref="ArgumentException">id is empty</exception>
+        public ResponseCookie(string id, string content, DateTime expiresAt)
+            : base(id, content)
+        {
+            if (expiresAt != DateTime.MinValue)
+            {
+                _expires = expiresAt;
+                _persistant = true;
+            }
+        }
+
+        /// <summary>
+        /// Create a new cookie
+        /// </summary>
+        /// <param name="name">name identifying the cookie</param>
+        /// <param name="value">cookie value</param>
+        /// <param name="expires">when the cookie expires. Setting DateTime.MinValue will delete the cookie when the session is closed.</param>
+        /// <param name="path">Path to where the cookie is valid</param>
+        /// <param name="domain">Domain that the cookie is valid for.</param>
+        public ResponseCookie(string name, string value, DateTime expires, string path, string domain)
+            : this(name, value, expires)
+        {
+            _domain = domain;
+            _path = path;
+        }
+
+        /// <summary>
+        /// Create a new cookie
+        /// </summary>
+        /// <param name="cookie">Name and value will be used</param>
+        /// <param name="expires">when the cookie expires.</param>
+        public ResponseCookie(RequestCookie cookie, DateTime expires)
+            : this(cookie.Name, cookie.Value, expires)
+        {}
+
+        #endregion
+
+        #region inherited methods
+
+        /// <summary>
+        /// Gets the cookie HTML representation.
+        /// </summary>
+        /// <returns>cookie string</returns>
+        public override string ToString()
+        {
+            string temp = string.Format("{0}={1}; ", HttpUtility.UrlEncode(Name), HttpUtility.UrlEncode(Value));
+            if (_persistant)
+            {
+                TimeSpan span = DateTime.Now - DateTime.UtcNow;
+                DateTime utc = _expires.Subtract(span);
+                temp += string.Format("expires={0};", utc.ToString("r"));
+            }
+            if (!string.IsNullOrEmpty(_path))
+                temp += string.Format("path={0}; ", _path);
+            if (!string.IsNullOrEmpty(_domain))
+                temp += string.Format("domain={0}; ", _domain);
+
+            return temp;
+        }
+
+        #endregion
+
+        #region public properties
+
+        /// <summary>
+        /// When the cookie expires.
+        /// DateTime.MinValue means that the cookie expires when the session do so.
+        /// </summary>
+        public DateTime Expires
+        {
+            get { return _expires; }
+            set
+            {
+                _expires = value;
+                _persistant = value != DateTime.MinValue;
+            }
+        }
+
+        /// <summary>
+        /// Cookie is only valid under this path.
+        /// </summary>
+        public string Path
+        {
+            get { return _path; }
+            set
+            {
+                if (!string.IsNullOrEmpty(value))
+                    _path = value;
+                else
+                    _path = _nullPath;
+            }
+        }
+
+        #endregion
+    }
+}

+ 108 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/ResponseCookies.cs

@@ -0,0 +1,108 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace OSHttpServer
+{
+    /// <summary>
+    /// Cookies that should be set.
+    /// </summary>
+    public sealed class ResponseCookies : IEnumerable<ResponseCookie>
+    {
+        private readonly IDictionary<string, ResponseCookie> _items = new Dictionary<string, ResponseCookie>();
+
+        /// <summary>
+        /// Adds a cookie in the collection.
+        /// </summary>
+        /// <param name="cookie">cookie to add</param>
+        /// <exception cref="ArgumentNullException">cookie is null</exception>
+        public void Add(ResponseCookie cookie)
+        {
+            // Verifies the parameter
+            if (cookie == null)
+                throw new ArgumentNullException("cookie");
+            if (cookie.Name == null || cookie.Name.Trim() == string.Empty)
+                throw new ArgumentException("Name must be specified.");
+            if (cookie.Value == null || cookie.Value.Trim() == string.Empty)
+                throw new ArgumentException("Content must be specified.");
+
+            if (_items.ContainsKey(cookie.Name))
+                _items[cookie.Name] = cookie;
+            else _items.Add(cookie.Name, cookie);
+        }
+
+        /// <summary>
+        /// Copy a request cookie
+        /// </summary>
+        /// <param name="cookie"></param>
+        /// <param name="expires">When the cookie should expire</param>
+        public void Add(RequestCookie cookie, DateTime expires)
+        {
+            Add(new ResponseCookie(cookie, expires));
+        }
+
+        /// <summary>
+        /// Gets the count of cookies in the collection.
+        /// </summary>
+        public int Count
+        {
+            get { return _items.Count; }
+        }
+
+
+        /// <summary>
+        /// Gets the cookie of a given identifier (null if not existing).
+        /// </summary>
+        public ResponseCookie this[string id]
+        {
+            get
+            {
+                if (_items.ContainsKey(id))
+                    return _items[id];
+                else
+                    return null;
+            }
+            set
+            {
+                if (_items.ContainsKey(id))
+                    _items[id] = value;
+                else
+                    Add(value);
+            }
+        }
+        /// <summary>
+        /// Gets a collection enumerator on the cookie list.
+        /// </summary>
+        /// <returns>collection enumerator</returns>
+        public IEnumerator GetEnumerator()
+        {
+            return _items.Values.GetEnumerator();
+        }
+
+
+        /// <summary>
+        /// Remove all cookies
+        /// </summary>
+        public void Clear()
+        {
+            _items.Clear();
+        }
+
+        #region IEnumerable<ResponseCookie> Members
+
+        ///<summary>
+        ///Returns an enumerator that iterates through the collection.
+        ///</summary>
+        ///
+        ///<returns>
+        ///A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
+        ///</returns>
+        ///<filterpriority>1</filterpriority>
+        IEnumerator<ResponseCookie> IEnumerable<ResponseCookie>.GetEnumerator()
+        {
+            return _items.Values.GetEnumerator();
+        }
+
+        #endregion
+    }
+}

+ 56 - 0
OpenSim/Framework/Servers/HttpServer/OSHttpServer/UnauthorizedException.cs

@@ -0,0 +1,56 @@
+using System;
+using System.Net;
+
+namespace OSHttpServer.Exceptions
+{
+    /// <summary>
+    /// The request requires user authentication. The response MUST include a 
+    /// WWW-Authenticate header field (section 14.47) containing a challenge 
+    /// applicable to the requested resource. 
+    /// 
+    /// The client MAY repeat the request with a suitable Authorization header 
+    /// field (section 14.8). If the request already included Authorization 
+    /// credentials, then the 401 response indicates that authorization has been 
+    /// refused for those credentials. If the 401 response contains the same challenge 
+    /// as the prior response, and the user agent has already attempted authentication 
+    /// at least once, then the user SHOULD be presented the entity that was given in the response, 
+    /// since that entity might include relevant diagnostic information. 
+    /// 
+    /// HTTP access authentication is explained in rfc2617:
+    /// http://www.ietf.org/rfc/rfc2617.txt
+    /// 
+    /// (description is taken from 
+    /// http://www.submissionchamber.com/help-guides/error-codes.php#sec10.4.2)
+    /// </summary>
+    public class UnauthorizedException : HttpException
+    {
+        /// <summary>
+        /// Create a new unauhtorized exception.
+        /// </summary>
+        /// <seealso cref="UnauthorizedException"/>
+        public UnauthorizedException()
+            : base(HttpStatusCode.Unauthorized, "The request requires user authentication.")
+        {
+            
+        }
+
+        /// <summary>
+        /// Create a new unauhtorized exception.
+        /// </summary>
+        /// <param name="message">reason to why the request was unauthorized.</param>
+        /// <param name="inner">inner exception</param>
+        public UnauthorizedException(string message, Exception inner)
+            : base(HttpStatusCode.Unauthorized, message, inner)
+        {
+        }
+
+        /// <summary>
+        /// Create a new unauhtorized exception.
+        /// </summary>
+        /// <param name="message">reason to why the request was unauthorized.</param>
+        public UnauthorizedException(string message)
+            : base(HttpStatusCode.Unauthorized, message)
+        {
+        }
+    }
+}

+ 0 - 180
OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs

@@ -1,180 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the OpenSimulator Project nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-using System.Reflection;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Xml;
-using log4net;
-using Nwc.XmlRpc;
-
-namespace OpenSim.Framework.Servers.HttpServer
-{
-    public delegate XmlRpcResponse OSHttpXmlRpcProcessor(XmlRpcRequest request);
-
-    public class OSHttpXmlRpcHandler: OSHttpHandler
-    {
-        private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
-        /// <summary>
-        /// XmlRpcMethodMatch tries to reify (deserialize) an incoming
-        /// XmlRpc request (and posts it to the "whiteboard") and
-        /// checks whether the method name is one we are interested
-        /// in.
-        /// </summary>
-        /// <returns>true if the handler is interested in the content;
-        /// false otherwise</returns>
-        protected bool XmlRpcMethodMatch(OSHttpRequest req)
-        {
-            XmlRpcRequest xmlRpcRequest = null;
-
-            // check whether req is already reified
-            // if not: reify (and post to whiteboard)
-            try
-            {
-                if (req.Whiteboard.ContainsKey("xmlrequest"))
-                {
-                    xmlRpcRequest = req.Whiteboard["xmlrequest"] as XmlRpcRequest;
-                }
-                else
-                {
-                    StreamReader body = new StreamReader(req.InputStream);
-                    string requestBody = body.ReadToEnd();
-                    xmlRpcRequest = (XmlRpcRequest)(new XmlRpcRequestDeserializer()).Deserialize(requestBody);
-                    req.Whiteboard["xmlrequest"] = xmlRpcRequest;
-                }
-            }
-            catch (XmlException)
-            {
-                _log.ErrorFormat("[OSHttpXmlRpcHandler] failed to deserialize XmlRpcRequest from {0}", req.ToString());
-                return false;
-            }
-
-            // check against methodName
-            if ((null != xmlRpcRequest)
-                && !String.IsNullOrEmpty(xmlRpcRequest.MethodName)
-                && xmlRpcRequest.MethodName == _methodName)
-            {
-                _log.DebugFormat("[OSHttpXmlRpcHandler] located handler {0} for {1}", _methodName, req.ToString());
-                return true;
-            }
-
-            return false;
-        }
-
-        // contains handler for processing XmlRpc Request
-        private XmlRpcMethod _handler;
-
-        // contains XmlRpc method name
-        private string _methodName;
-
-
-        /// <summary>
-        /// Instantiate an XmlRpc handler.
-        /// </summary>
-        /// <param name="handler">XmlRpcMethod
-        /// delegate</param>
-        /// <param name="methodName">XmlRpc method name</param>
-        /// <param name="path">XmlRpc path prefix (regular expression)</param>
-        /// <param name="headers">Dictionary with header names and
-        /// regular expressions to match content of headers</param>
-        /// <param name="whitelist">IP whitelist of remote end points
-        /// to accept (regular expression)</param>
-        /// <remarks>
-        /// Except for handler and methodName, all other parameters
-        /// can be null, in which case they are not taken into account
-        /// when the handler is being looked up.
-        /// </remarks>
-        public OSHttpXmlRpcHandler(XmlRpcMethod handler, string methodName, Regex path,
-                                   Dictionary<string, Regex> headers, Regex whitelist)
-            : base(new Regex(@"^POST$", RegexOptions.IgnoreCase | RegexOptions.Compiled), path, null, headers,
-                   new Regex(@"^(text|application)/xml", RegexOptions.IgnoreCase | RegexOptions.Compiled),
-                   whitelist)
-        {
-            _handler = handler;
-            _methodName = methodName;
-        }
-
-
-        /// <summary>
-        /// Instantiate an XmlRpc handler.
-        /// </summary>
-        /// <param name="handler">XmlRpcMethod
-        /// delegate</param>
-        /// <param name="methodName">XmlRpc method name</param>
-        public OSHttpXmlRpcHandler(XmlRpcMethod handler, string methodName)
-            : this(handler, methodName, null, null, null)
-        {
-        }
-
-
-        /// <summary>
-        /// Invoked by OSHttpRequestPump.
-        /// </summary>
-        public override OSHttpHandlerResult Process(OSHttpRequest request)
-        {
-            XmlRpcResponse xmlRpcResponse;
-            string responseString;
-
-            // check whether we are interested in this request
-            if (!XmlRpcMethodMatch(request)) return OSHttpHandlerResult.Pass;
-
-
-            OSHttpResponse resp = new OSHttpResponse(request);
-            try
-            {
-                // reified XmlRpcRequest must still be on the whiteboard
-                XmlRpcRequest xmlRpcRequest = request.Whiteboard["xmlrequest"] as XmlRpcRequest;
-                xmlRpcResponse = _handler(xmlRpcRequest);
-                responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse);
-
-                resp.ContentType = "text/xml";
-                byte[] buffer = Encoding.UTF8.GetBytes(responseString);
-
-                resp.SendChunked = false;
-                resp.ContentLength = buffer.Length;
-                resp.ContentEncoding = Encoding.UTF8;
-
-                resp.Body.Write(buffer, 0, buffer.Length);
-                resp.Body.Flush();
-
-                resp.Send();
-
-            }
-            catch (Exception ex)
-            {
-                _log.WarnFormat("[OSHttpXmlRpcHandler]: Error: {0}", ex.Message);
-                return OSHttpHandlerResult.Pass;
-            }
-            return OSHttpHandlerResult.Done;
-        }
-    }
-}

+ 14 - 10
OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs

@@ -30,7 +30,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Reflection;
 using System.Text;
-using HttpServer;
+using OSHttpServer;
 using log4net;
 using OpenMetaverse;
 
@@ -130,8 +130,16 @@ namespace OpenSim.Framework.Servers.HttpServer
                 return;
             }
 
-            if (responsedata.ContainsKey("error_status_text"))
-                response.StatusDescription = (string)responsedata["error_status_text"];
+
+            response.StatusCode = responsecode;
+            if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently)
+            {
+                response.AddHeader("Location:", (string)responsedata["str_redirect_location"]);
+                response.KeepAlive = false;
+                PollServiceArgs.RequestsHandled++;
+                response.Send();
+                return;
+            }
 
             if (responsedata.ContainsKey("http_protocol_version"))
                 response.ProtocolVersion = (string)responsedata["http_protocol_version"];
@@ -142,17 +150,13 @@ namespace OpenSim.Framework.Servers.HttpServer
             if (responsedata.ContainsKey("prio"))
                 response.Priority = (int)responsedata["prio"];
 
+            if (responsedata.ContainsKey("error_status_text"))
+                response.StatusDescription = (string)responsedata["error_status_text"];
+
             // Cross-Origin Resource Sharing with simple requests
             if (responsedata.ContainsKey("access_control_allow_origin"))
                 response.AddHeader("Access-Control-Allow-Origin", (string)responsedata["access_control_allow_origin"]);
 
-            response.StatusCode = responsecode;
-
-            if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently)
-            {
-                response.RedirectLocation = (string)responsedata["str_redirect_location"];
-            }
-
             if (string.IsNullOrEmpty(contentType))
                 response.AddHeader("Content-Type", "text/html");
             else

+ 1 - 1
OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs

@@ -32,7 +32,7 @@ using System.Net;
 using System.Security.Cryptography;
 using System.Text;
 using System.Threading;
-using HttpServer;
+using OSHttpServer;
 
 namespace OpenSim.Framework.Servers.HttpServer
 {

+ 0 - 439
OpenSim/Framework/Servers/Tests/OSHttpTests.cs

@@ -1,439 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the OpenSimulator Project nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Collections.Specialized;
-using System.IO;
-using System.Net;
-using System.Net.Sockets;
-using System.Text;
-using HttpServer;
-using HttpServer.FormDecoders;
-using NUnit.Framework;
-using OpenSim.Framework.Servers.HttpServer;
-using OpenSim.Tests.Common;
-
-namespace OpenSim.Framework.Servers.Tests
-{
-/*
-
-    [TestFixture]
-    public class OSHttpTests : OpenSimTestCase
-    {
-        // we need an IHttpClientContext for our tests
-        public class TestHttpClientContext: IHttpClientContext
-        {
-            private bool _secured;
-            public bool IsSecured
-            {
-                get { return _secured; }
-            }
-            public bool Secured
-            {
-                get { return _secured; }
-            }
-
-            public TestHttpClientContext(bool secured)
-            {
-                _secured = secured;
-            }
-
-            public void Disconnect(SocketError error) {}
-            public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body) {}
-            public void Respond(string httpVersion, HttpStatusCode statusCode, string reason) {}
-            public void Respond(string body) {}
-            public void Send(byte[] buffer) {}
-            public void Send(byte[] buffer, int offset, int size) {}
-            public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body, string contentType) {}
-            public void Close() { }
-            public bool EndWhenDone { get { return false;} set { return;}}
-
-            public HTTPNetworkContext GiveMeTheNetworkStreamIKnowWhatImDoing()
-            {
-                return new HTTPNetworkContext();
-            }
-
-            public event EventHandler<DisconnectedEventArgs> Disconnected = delegate { };
-            /// <summary>
-            /// A request have been received in the context.
-            /// </summary>
-            public event EventHandler<RequestEventArgs> RequestReceived = delegate { };
-
-            public bool CanSend { get { return true; } }
-            public string RemoteEndPoint { get { return ""; } }
-            public string RemoteEndPointAddress { get { return ""; } }
-            public string RemoteEndPointPort { get { return ""; } }
-        }
-
-        public class TestHttpRequest: IHttpRequest
-        {
-            private string _uriPath;
-            public bool BodyIsComplete
-            {
-                get { return true; }
-            }
-            public string[] AcceptTypes
-            {
-                get {return _acceptTypes; }
-            }
-            private string[] _acceptTypes;
-            public Stream Body
-            {
-                get { return _body; }
-                set { _body = value;}
-            }
-            private Stream _body;
-            public ConnectionType Connection
-            {
-                get { return _connection; }
-                set { _connection = value; }
-            }
-            private ConnectionType _connection;
-            public int ContentLength
-            {
-                get { return _contentLength; }
-                set { _contentLength = value; }
-            }
-            private int _contentLength;
-            public NameValueCollection Headers
-            {
-                get { return _headers; }
-            }
-            private NameValueCollection _headers = new NameValueCollection();
-            public string HttpVersion
-            {
-                get { return _httpVersion; }
-                set { _httpVersion = value; }
-            }
-            private string _httpVersion = null;
-            public string Method
-            {
-                get { return _method; }
-                set { _method = value; }
-            }
-            private string _method = null;
-            public HttpInput QueryString
-            {
-                get { return _queryString;  }
-            }
-            private HttpInput _queryString = null;
-            public Uri Uri
-            {
-                get { return _uri; }
-                set { _uri = value; }
-            }
-            private Uri _uri = null;
-            public string[] UriParts
-            {
-                get { return _uri.Segments; }
-            }
-            public HttpParam Param
-            {
-                get { return null; }
-            }
-            public HttpForm Form
-            {
-                get { return null; }
-            }
-            public bool IsAjax
-            {
-                get { return false; }
-            }
-            public RequestCookies Cookies
-            {
-                get { return null; }
-            }
-
-            public TestHttpRequest() {}
-
-            public TestHttpRequest(string contentEncoding, string contentType, string userAgent,
-                                   string remoteAddr, string remotePort, string[] acceptTypes,
-                                   ConnectionType connectionType, int contentLength, Uri uri)
-            {
-                _headers["content-encoding"] = contentEncoding;
-                _headers["content-type"] = contentType;
-                _headers["user-agent"] = userAgent;
-                _headers["remote_addr"] = remoteAddr;
-                _headers["remote_port"] = remotePort;
-
-                _acceptTypes = acceptTypes;
-                _connection = connectionType;
-                _contentLength = contentLength;
-                _uri = uri;
-            }
-
-            public void DecodeBody(FormDecoderProvider providers) {}
-            public void SetCookies(RequestCookies cookies) {}
-            public void AddHeader(string name, string value)
-            {
-                _headers.Add(name, value);
-            }
-            public int AddToBody(byte[] bytes, int offset, int length)
-            {
-                return 0;
-            }
-            public void Clear() {}
-
-            public object Clone()
-            {
-                TestHttpRequest clone = new TestHttpRequest();
-                clone._acceptTypes = _acceptTypes;
-                clone._connection = _connection;
-                clone._contentLength = _contentLength;
-                clone._uri = _uri;
-                clone._headers = new NameValueCollection(_headers);
-
-                return clone;
-            }
-            public IHttpResponse CreateResponse(IHttpClientContext context)
-            {
-                return new HttpResponse(context, this);
-            }
-            /// <summary>
-            /// Path and query (will be merged with the host header) and put in Uri
-            /// </summary>
-            /// <see cref="Uri"/>
-            public string UriPath
-            {
-                get { return _uriPath; }
-                set
-                {
-                    _uriPath = value;
-
-                }
-            }
-
-        }
-
-        public class TestHttpResponse: IHttpResponse
-        {
-            public Stream Body
-            {
-                get { return _body; }
-
-                set { _body = value; }
-            }
-            private Stream _body;
-
-            public string ProtocolVersion
-            {
-                get { return _protocolVersion; }
-                set { _protocolVersion = value; }
-            }
-            private string _protocolVersion;
-
-            public bool Chunked
-            {
-                get { return _chunked; }
-
-                set { _chunked = value; }
-            }
-            private bool _chunked;
-
-            public ConnectionType Connection
-            {
-                get { return _connection; }
-
-                set { _connection = value; }
-            }
-            private ConnectionType _connection;
-
-            public Encoding Encoding
-            {
-                get { return _encoding; }
-
-                set { _encoding = value; }
-            }
-            private Encoding _encoding;
-
-            public int KeepAlive
-            {
-                get { return _keepAlive; }
-
-                set { _keepAlive = value; }
-            }
-            private int _keepAlive;
-
-            public HttpStatusCode Status
-            {
-                get { return _status; }
-
-                set { _status = value; }
-            }
-            private HttpStatusCode _status;
-
-            public string Reason
-            {
-                get { return _reason; }
-
-                set { _reason = value; }
-            }
-            private string _reason;
-
-            public long ContentLength
-            {
-                get { return _contentLength; }
-
-                set { _contentLength = value; }
-            }
-            private long _contentLength;
-
-            public string ContentType
-            {
-                get { return _contentType; }
-
-                set { _contentType = value; }
-            }
-            private string _contentType;
-
-            public bool HeadersSent
-            {
-                get { return _headersSent; }
-            }
-            private bool _headersSent;
-
-            public bool Sent
-            {
-                get { return _sent; }
-            }
-            private bool _sent;
-
-            public ResponseCookies Cookies
-            {
-                get { return _cookies; }
-            }
-            private ResponseCookies _cookies = null;
-
-            public TestHttpResponse()
-            {
-                _headersSent = false;
-                _sent = false;
-            }
-
-            public void AddHeader(string name, string value) {}
-            public void Send()
-            {
-                if (!_headersSent) SendHeaders();
-                if (_sent) throw new InvalidOperationException("stuff already sent");
-                _sent = true;
-            }
-
-            public void SendBody(byte[] buffer, int offset, int count)
-            {
-                if (!_headersSent) SendHeaders();
-                _sent = true;
-            }
-            public void SendBody(byte[] buffer)
-            {
-                if (!_headersSent) SendHeaders();
-                _sent = true;
-            }
-
-            public void SendHeaders()
-            {
-                if (_headersSent) throw new InvalidOperationException("headers already sent");
-                _headersSent = true;
-            }
-
-            public void Redirect(Uri uri) {}
-            public void Redirect(string url) {}
-        }
-
-
-        public OSHttpRequest req0;
-        public OSHttpRequest req1;
-
-        public OSHttpResponse rsp0;
-
-        public IPEndPoint ipEP0;
-
-        [TestFixtureSetUp]
-        public void Init()
-        {
-            TestHttpRequest threq0 = new TestHttpRequest("utf-8", "text/xml", "OpenSim Test Agent", "192.168.0.1", "4711",
-                                                       new string[] {"text/xml"},
-                                                       ConnectionType.KeepAlive, 4711,
-                                                       new Uri("http://127.0.0.1/admin/inventory/Dr+Who/Tardis"));
-            threq0.Method = "GET";
-            threq0.HttpVersion = HttpHelper.HTTP10;
-
-            TestHttpRequest threq1 = new TestHttpRequest("utf-8", "text/xml", "OpenSim Test Agent", "192.168.0.1", "4711",
-                                                       new string[] {"text/xml"},
-                                                       ConnectionType.KeepAlive, 4711,
-                                                       new Uri("http://127.0.0.1/admin/inventory/Dr+Who/Tardis?a=0&b=1&c=2"));
-            threq1.Method = "POST";
-            threq1.HttpVersion = HttpHelper.HTTP11;
-            threq1.Headers["x-wuff"] = "wuffwuff";
-            threq1.Headers["www-authenticate"] = "go away";
-
-            req0 = new OSHttpRequest(new TestHttpClientContext(false), threq0);
-            req1 = new OSHttpRequest(new TestHttpClientContext(false), threq1);
-
-            rsp0 = new OSHttpResponse(new TestHttpResponse());
-
-            ipEP0 = new IPEndPoint(IPAddress.Parse("192.168.0.1"), 4711);
-
-        }
-
-        [Test]
-        public void T000_OSHttpRequest()
-        {
-            Assert.That(req0.HttpMethod, Is.EqualTo("GET"));
-            Assert.That(req0.ContentType, Is.EqualTo("text/xml"));
-            Assert.That(req0.ContentLength, Is.EqualTo(4711));
-
-            Assert.That(req1.HttpMethod, Is.EqualTo("POST"));
-        }
-
-        [Test]
-        public void T001_OSHttpRequestHeaderAccess()
-        {
-            Assert.That(req1.Headers["x-wuff"], Is.EqualTo("wuffwuff"));
-            Assert.That(req1.Headers.Get("x-wuff"), Is.EqualTo("wuffwuff"));
-
-            Assert.That(req1.Headers["www-authenticate"], Is.EqualTo("go away"));
-            Assert.That(req1.Headers.Get("www-authenticate"), Is.EqualTo("go away"));
-
-            Assert.That(req0.RemoteIPEndPoint, Is.EqualTo(ipEP0));
-        }
-
-        [Test]
-        public void T002_OSHttpRequestUriParsing()
-        {
-            Assert.That(req0.RawUrl, Is.EqualTo("/admin/inventory/Dr+Who/Tardis"));
-            Assert.That(req1.Url.ToString(), Is.EqualTo("http://127.0.0.1/admin/inventory/Dr+Who/Tardis?a=0&b=1&c=2"));
-        }
-
-        [Test]
-        public void T100_OSHttpResponse()
-        {
-            rsp0.ContentType = "text/xml";
-            Assert.That(rsp0.ContentType, Is.EqualTo("text/xml"));
-        }
-    }
-*/
-}

+ 2 - 1
OpenSim/Region/Framework/Scenes/UuidGatherer.cs

@@ -603,7 +603,8 @@ namespace OpenSim.Region.Framework.Scenes
                     || (sbyte)AssetType.Notecard == assetType
                     || (sbyte)AssetType.LSLText == assetType
                     || (sbyte)OpenSimAssetType.Material == assetType
-                    || (sbyte)AssetType.Object == assetType)
+                    || (sbyte)AssetType.Object == assetType
+                    || (sbyte)AssetType.Settings == assetType)
                 {
                     AddForInspection(assetUuid);
                 }

+ 1 - 1
OpenSim/Tests/Common/Mock/TestHttpClientContext.cs

@@ -31,7 +31,7 @@ using System.IO;
 using System.Net;
 using System.Net.Sockets;
 using System.Text;
-using HttpServer;
+using OSHttpServer;
 using OpenSim.Framework;
 
 namespace OpenSim.Tests.Common

+ 1 - 2
OpenSim/Tests/Common/Mock/TestHttpRequest.cs

@@ -28,8 +28,7 @@
 using System;
 using System.Collections.Specialized;
 using System.IO;
-using HttpServer;
-using HttpServer.FormDecoders;
+using OSHttpServer;
 
 namespace OpenSim.Tests.Common
 {

+ 1 - 1
OpenSim/Tests/Common/Mock/TestHttpResponse.cs

@@ -29,7 +29,7 @@ using System;
 using System.IO;
 using System.Net;
 using System.Text;
-using HttpServer;
+using OSHttpServer;
 
 namespace OpenSim.Tests.Common
 {

+ 5 - 5
OpenSim/Tests/Common/Mock/TestOSHttpResponse.cs

@@ -68,6 +68,11 @@ namespace OpenSim.Tests.Common
         /// </summary>
         public long ContentLength64 { get; set; }
 
+        public int Priority { get; set; }
+        public byte[] RawBuffer { get; set; }
+        public int RawBufferStart { get; set; }
+        public int RawBufferLen { get; set; }
+
         /// <summary>
         /// Encoding of the body content.
         /// </summary>
@@ -97,11 +102,6 @@ namespace OpenSim.Tests.Common
         /// </summary>
         public Stream Body { get; private set; }
 
-        /// <summary>
-        /// Set a redirct location.
-        /// </summary>
-        public string RedirectLocation { private get; set; }
-
         /// <summary>
         /// Chunk transfers.
         /// </summary>

二進制
bin/HttpServer_OpenSim.dll


+ 1 - 13
prebuild.xml

@@ -209,20 +209,11 @@
       <Reference name="OpenMetaverseTypes" path="../../../../bin/"/>
       <Reference name="XMLRPC" path="../../../../bin/"/>
       <Reference name="log4net" path="../../../../bin/"/>
-      <Reference name="HttpServer_OpenSim" path="../../../../bin/"/>
       <Reference name="SmartThreadPool"/>
 
       <Files>
         <Match pattern="*.cs" recurse="true">
           <Exclude name="obj" pattern="obj"/>
-          <Exclude pattern="Tests"/>
-          <!-- on temporary suspension -->
-          <Exclude pattern="OSHttpHandler\.cs"/>
-          <Exclude pattern="OSHttpHttpHandler\.cs"/>
-          <Exclude pattern="OSHttpRequestPump\.cs"/>
-          <Exclude pattern="OSHttpRequestQueue\.cs"/>
-          <Exclude pattern="OSHttpServer.*\.cs"/>
-          <Exclude pattern="OSHttpXmlRpcHandler.*\.cs"/>
         </Match>
       </Files>
     </Project>
@@ -369,11 +360,11 @@
       <Reference name="OpenMetaverseTypes" path="../../../bin/"/>
       <Reference name="XMLRPC" path="../../../bin/"/>
       <Reference name="log4net" path="../../../bin/"/>
-      <Reference name="HttpServer_OpenSim" path="../../../bin/"/>
       <Reference name="Nini" path="../../../bin/"/>
 
       <Files>
         <Match pattern="*.cs" recurse="false"/>
+         <Exclude name="obj" pattern="obj"/>
       </Files>
     </Project>
 
@@ -2700,7 +2691,6 @@
       <Reference name="System.Drawing"/>
       <Reference name="System.Xml"/>
       <Reference name="System.Web"/>
-      <Reference name="HttpServer_OpenSim" path="../../../bin/"/>
       <Reference name="log4net" path="../../../bin/"/>
       <Reference name="Mono.Addins" path="../../../bin/"/>
       <Reference name="Nini" path="../../../bin/"/>
@@ -3018,7 +3008,6 @@
       <Reference name="OpenSim.Framework.Servers"/>
       <Reference name="OpenSim.Framework.Servers.HttpServer"/>
       <Reference name="log4net" path="../../../../bin/"/>
-      <Reference name="HttpServer_OpenSim" path="../../../../bin/"/>
       <Reference name="nunit.framework" path="../../../../bin/"/>
 
       <Files>
@@ -3222,7 +3211,6 @@
       <ReferencePath>../../../../../bin/</ReferencePath>
       <Reference name="System"/>
       <Reference name="System.Xml"/>
-      <Reference name="HttpServer_OpenSim" path="../../../../../bin/"/>
       <Reference name="log4net" path="../../../../../bin/"/>
       <Reference name="Nini" path="../../../../../bin/"/>
       <Reference name="nunit.framework" path="../../../../../bin/"/>