From 79b2e5ac71794dd4e55228e0ac146b527fb71ddf Mon Sep 17 00:00:00 2001 From: Teravus Ovares Date: Tue, 30 Sep 2008 16:56:33 +0000 Subject: * Replacing Net.HttpListener with HttpServer. * This is a HUGE update.. and should be considered fraut with peril. * SSL Mode isn't available *yet* but I'll work on that next. * DrScofld is still working on a radical new thread pump scheme for this which will be implemented soon. * This could break the Build! This could break your Grid! --- .../Framework/Communications/UserManagerBase.cs | 9 +- OpenSim/Framework/Servers/BaseHttpServer.cs | 272 ++++++++++++++++++--- OpenSim/Framework/Servers/OSHttpRequest.cs | 68 +++--- OpenSim/Framework/Servers/OSHttpResponse.cs | 30 ++- .../Modules/Framework/EventQueueGetModule.cs | 19 +- bin/HttpServer.dll | Bin 143872 -> 139264 bytes 6 files changed, 302 insertions(+), 96 deletions(-) diff --git a/OpenSim/Framework/Communications/UserManagerBase.cs b/OpenSim/Framework/Communications/UserManagerBase.cs index b7f9f5a..aa68367 100644 --- a/OpenSim/Framework/Communications/UserManagerBase.cs +++ b/OpenSim/Framework/Communications/UserManagerBase.cs @@ -368,9 +368,12 @@ namespace OpenSim.Framework.Communications if (request.Params.Count > 1) { - IPEndPoint RemoteIPEndPoint = (IPEndPoint)request.Params[1]; - agent.AgentIP = RemoteIPEndPoint.Address.ToString(); - agent.AgentPort = (uint)RemoteIPEndPoint.Port; + if (request.Params[1] != null) + { + IPEndPoint RemoteIPEndPoint = (IPEndPoint)request.Params[1]; + agent.AgentIP = RemoteIPEndPoint.Address.ToString(); + agent.AgentPort = (uint)RemoteIPEndPoint.Port; + } } // Generate sessions diff --git a/OpenSim/Framework/Servers/BaseHttpServer.cs b/OpenSim/Framework/Servers/BaseHttpServer.cs index 707f621..6090d1f 100644 --- a/OpenSim/Framework/Servers/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/BaseHttpServer.cs @@ -40,7 +40,9 @@ using System.Xml; using OpenMetaverse.StructuredData; using log4net; using Nwc.XmlRpc; - +using CoolHTTPListener = HttpServer.HttpListener; +using IHttpClientContext = HttpServer.IHttpClientContext; +using IHttpRequest = HttpServer.IHttpRequest; namespace OpenSim.Framework.Servers { @@ -50,6 +52,7 @@ namespace OpenSim.Framework.Servers protected Thread m_workerThread; protected HttpListener m_httpListener; + protected CoolHTTPListener m_httpListener2; protected Dictionary m_rpcHandlers = new Dictionary(); protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/ protected Dictionary m_llsdHandlers = new Dictionary(); @@ -102,13 +105,14 @@ namespace OpenSim.Framework.Servers m_port = port; if (m_ssl) { - SetupSsl((int)sslport, CN); + //SetupSsl((int)sslport, CN); m_sslport = sslport; } } - + /* + * public bool SetupSsl(int port, string CN) { string searchCN = Environment.MachineName.ToUpper(); @@ -211,7 +215,7 @@ namespace OpenSim.Framework.Servers } } - + */ /// /// Add a stream handler to the http server. If the handler already exists, then nothing happens. @@ -308,23 +312,135 @@ namespace OpenSim.Framework.Servers } /// - /// Handle an individual http request. This method is given to a worker in the thread pool. + /// HttpListener Handle an individual http request. This method is given to a worker in the thread pool. /// /// public virtual void HandleRequest(Object stateinfo) { // force the culture to en-US - Culture.SetCurrentCulture(); + // If we don't catch the exception here it will just disappear into the thread pool and we'll be none the wiser try { - HttpListenerContext context = (HttpListenerContext) stateinfo; + HttpListenerContext context = (HttpListenerContext)stateinfo; - OSHttpRequest request = new OSHttpRequest(context.Request); + OSHttpRequest request = new OSHttpRequest(context.Request); OSHttpResponse response = new OSHttpResponse(context.Response); - context.Response.ProtocolVersion = new Version("1.0"); - context.Response.KeepAlive = false; + + HandleRequest(request, response); + + } + catch (SocketException e) + { + // At least on linux, it appears that if the client makes a request without requiring the response, + // an unconnected socket exception is thrown when we close the response output stream. There's no + // obvious way to tell if the client didn't require the response, so instead we'll catch and ignore + // the exception instead. + // + // An alternative may be to turn off all response write exceptions on the HttpListener, but let's go + // with the minimum first + m_log.WarnFormat("[BASE HTTP SERVER]: HandleRequest threw {0}.\nNOTE: this may be spurious on Linux", e); + } + catch (Exception e) + { + m_log.ErrorFormat("[BASE HTTP SERVER]: HandleRequest() threw {0}", e); + } + } + + /* + /// + /// HttpListener Handle an individual http request. This method is given to a worker in the thread pool. + /// + /// + public virtual void HandleRequestHttpServer(Object stateinfo) + { + // force the culture to en-US + + + // If we don't catch the exception here it will just disappear into the thread pool and we'll be none the wiser + try + { + HttpServerContextObj context = (HttpServerContextObj)stateinfo; + + OSHttpRequest request = new OSHttpRequest(context.Request); + OSHttpResponse response = new OSHttpResponse(context.Response); + + HandleRequest(request, response); + + } + catch (SocketException e) + { + // At least on linux, it appears that if the client makes a request without requiring the response, + // an unconnected socket exception is thrown when we close the response output stream. There's no + // obvious way to tell if the client didn't require the response, so instead we'll catch and ignore + // the exception instead. + // + // An alternative may be to turn off all response write exceptions on the HttpListener, but let's go + // with the minimum first + m_log.WarnFormat("[BASE HTTP SERVER]: HandleRequest threw {0}.\nNOTE: this may be spurious on Linux", e); + } + catch (Exception e) + { + m_log.ErrorFormat("[BASE HTTP SERVER]: HandleRequest() threw {0}", e); + } + } + */ + public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) + { + OSHttpRequest req = new OSHttpRequest(context, request); + OSHttpResponse resp = new OSHttpResponse(new HttpServer.HttpResponse(context, request)); + + //HttpServerContextObj objstate= new HttpServerContextObj(req,resp); + //ThreadPool.QueueUserWorkItem(new WaitCallback(ConvertIHttpClientContextToOSHttp), (object)objstate); + HandleRequest(req, resp); + } + + public void ConvertIHttpClientContextToOSHttp(object stateinfo) + { + HttpServerContextObj objstate = (HttpServerContextObj)stateinfo; + //OSHttpRequest request = new OSHttpRequest(objstate.context,objstate.req); + //OSHttpResponse resp = new OSHttpResponse(new HttpServer.HttpResponse(objstate.context, objstate.req)); + + OSHttpRequest request = objstate.oreq; + OSHttpResponse resp = objstate.oresp; + //OSHttpResponse resp = new OSHttpResponse(new HttpServer.HttpResponse(objstate.context, objstate.req)); + + /* + request.AcceptTypes = objstate.req.AcceptTypes; + request.ContentLength = (long)objstate.req.ContentLength; + request.Headers = objstate.req.Headers; + request.HttpMethod = objstate.req.Method; + request.InputStream = objstate.req.Body; + foreach (string str in request.Headers) + { + if (str.ToLower().Contains("content-type: ")) + { + request.ContentType = str.Substring(13, str.Length - 13); + break; + } + } + //request.KeepAlive = objstate.req. + foreach (HttpServer.HttpInput httpinput in objstate.req.QueryString) + { + request.QueryString.Add(httpinput.Name, httpinput[httpinput.Name]); + } + + //request.Query = objstate.req.//objstate.req.QueryString; + //foreach ( + //request.QueryString = objstate.req.QueryString; + + */ + HandleRequest(request,resp); + + + } + + public virtual void HandleRequest(OSHttpRequest request, OSHttpResponse response) + { + try + { + Culture.SetCurrentCulture(); // This is the REST agent interface. We require an agent to properly identify // itself. If the REST handler recognizes the prefix it will attempt to // satisfy the request. If it is not recognizable, and no damage has occurred @@ -431,12 +547,22 @@ namespace OpenSim.Framework.Servers try { response.OutputStream.Write(buffer, 0, buffer.Length); - response.OutputStream.Close(); + //response.OutputStream.Close(); } catch (HttpListenerException) { m_log.WarnFormat("[BASE HTTP SERVER]: HTTP request abnormally terminated."); } + //response.OutputStream.Close(); + try + { + response.Send(); + } + catch (SocketException e) + { + // This has to be here to prevent a Linux/Mono crash + m_log.WarnFormat("[BASE HTTP SERVER] XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); + } return; } @@ -489,7 +615,7 @@ namespace OpenSim.Framework.Servers // with the minimum first m_log.WarnFormat("[BASE HTTP SERVER]: HandleRequest threw {0}.\nNOTE: this may be spurious on Linux", e); } - catch (Exception e) + catch (EndOfStreamException e) { m_log.ErrorFormat("[BASE HTTP SERVER]: HandleRequest() threw {0}", e); } @@ -739,7 +865,16 @@ namespace OpenSim.Framework.Servers } finally { - response.OutputStream.Close(); + //response.OutputStream.Close(); + try + { + response.Send(); + } + catch (SocketException e) + { + // This has to be here to prevent a Linux/Mono crash + m_log.WarnFormat("[BASE HTTP SERVER] XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); + } } } @@ -883,7 +1018,16 @@ namespace OpenSim.Framework.Servers response.SendChunked = false; response.KeepAlive = false; response.StatusCode = (int)OSHttpStatusCode.ServerErrorInternalError; - response.OutputStream.Close(); + //response.OutputStream.Close(); + try + { + response.Send(); + } + catch (SocketException f) + { + // This has to be here to prevent a Linux/Mono crash + m_log.WarnFormat("[BASE HTTP SERVER] XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", f); + } } catch(Exception) { @@ -996,6 +1140,10 @@ namespace OpenSim.Framework.Servers { response.StatusDescription = (string)responsedata["error_status_text"]; } + if (responsedata.ContainsKey("http_protocol_version")) + { + response.ProtocolVersion = (string)responsedata["http_protocol_version"]; + } if (responsedata.ContainsKey("keepalive")) { @@ -1049,7 +1197,16 @@ namespace OpenSim.Framework.Servers } finally { - response.OutputStream.Close(); + //response.OutputStream.Close(); + try + { + response.Send(); + } + catch (SocketException e) + { + // This has to be here to prevent a Linux/Mono crash + m_log.WarnFormat("[BASE HTTP SERVER] XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); + } } } @@ -1077,7 +1234,16 @@ namespace OpenSim.Framework.Servers } finally { - response.OutputStream.Close(); + //response.OutputStream.Close(); + try + { + response.Send(); + } + catch (SocketException e) + { + // This has to be here to prevent a Linux/Mono crash + m_log.WarnFormat("[BASE HTTP SERVER] XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); + } } } @@ -1103,7 +1269,16 @@ namespace OpenSim.Framework.Servers } finally { - response.OutputStream.Close(); + //response.OutputStream.Close(); + try + { + response.Send(); + } + catch (SocketException e) + { + // This has to be here to prevent a Linux/Mono crash + m_log.WarnFormat("[BASE HTTP SERVER] XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux.", e); + } } } @@ -1111,11 +1286,12 @@ namespace OpenSim.Framework.Servers { m_log.Info("[HTTPD]: Starting up HTTP Server"); - m_workerThread = new Thread(new ThreadStart(StartHTTP)); - m_workerThread.Name = "HttpThread"; - m_workerThread.IsBackground = true; - m_workerThread.Start(); - ThreadTracker.Add(m_workerThread); + //m_workerThread = new Thread(new ThreadStart(StartHTTP)); + //m_workerThread.Name = "HttpThread"; + //m_workerThread.IsBackground = true; + //m_workerThread.Start(); + //ThreadTracker.Add(m_workerThread); + StartHTTP(); } private void StartHTTP() @@ -1123,31 +1299,30 @@ namespace OpenSim.Framework.Servers try { m_log.Info("[HTTPD]: Spawned main thread OK"); - m_httpListener = new HttpListener(); - + //m_httpListener = new HttpListener(); + if (!m_ssl) { - m_httpListener.Prefixes.Add("http://+:" + m_port + "/"); + //m_httpListener.Prefixes.Add("http://+:" + m_port + "/"); //m_httpListener.Prefixes.Add("http://10.1.1.5:" + m_port + "/"); + m_httpListener2 = new HttpServer.HttpListener(IPAddress.Any, (int)m_port); } else { - m_httpListener.Prefixes.Add("https://+:" + (m_sslport) + "/"); - m_httpListener.Prefixes.Add("http://+:" + m_port + "/"); + //m_httpListener.Prefixes.Add("https://+:" + (m_sslport) + "/"); + //m_httpListener.Prefixes.Add("http://+:" + m_port + "/"); } - HttpListenerPrefixCollection prefixs = m_httpListener.Prefixes; - foreach (string prefix in prefixs) - System.Console.WriteLine("Listening on: " + prefix); - - m_httpListener.Start(); + m_httpListener2.RequestHandler += OnHandleRequestIOThread; + //m_httpListener.Start(); + m_httpListener2.Start(5); - HttpListenerContext context; - while (true) - { - context = m_httpListener.GetContext(); - ThreadPool.QueueUserWorkItem(new WaitCallback(HandleRequest), context); - } + //HttpListenerContext context; + //while (true) + //{ + // context = m_httpListener.GetContext(); + // ThreadPool.QueueUserWorkItem(new WaitCallback(HandleRequest), context); + // } } catch (Exception e) { @@ -1244,4 +1419,25 @@ namespace OpenSim.Framework.Servers return "500 Internal Server Error

Ooops!

The server you requested is overun by knomes! Find hippos quick!

"; } } + + public class HttpServerContextObj + { + public IHttpClientContext context = null; + public IHttpRequest req = null; + public OSHttpRequest oreq = null; + public OSHttpResponse oresp = null; + + public HttpServerContextObj(IHttpClientContext contxt, IHttpRequest reqs) + { + context = contxt; + req = reqs; + } + + public HttpServerContextObj(OSHttpRequest osreq, OSHttpResponse osresp) + { + oreq = osreq; + oresp = osresp; + } + + } } diff --git a/OpenSim/Framework/Servers/OSHttpRequest.cs b/OpenSim/Framework/Servers/OSHttpRequest.cs index 9c1053c..28f4337 100644 --- a/OpenSim/Framework/Servers/OSHttpRequest.cs +++ b/OpenSim/Framework/Servers/OSHttpRequest.cs @@ -199,40 +199,40 @@ namespace OpenSim.Framework.Servers // _isAuthenticated = req.IsAuthenticated; } - // public OSHttpRequest(HttpClientContext context, HttpRequest req) - // { - // _context = context; - // _request = req; - - // _acceptTypes = req.AcceptTypes; - // if (null != req.Headers["content-encoding"]) - // _contentEncoding = Encoding.GetEncoding(_request.Headers["content-encoding"]); - // _contentLength64 = req.ContentLength; - // if (null != req.Headers["content-type"]) - // _contentType = _request.Headers["content-type"]; - // _headers = req.Headers; - // _httpMethod = req.Method; - // _hasbody = req.ContentLength != 0; - // _inputStream = req.Body; - // _keepAlive = ConnectionType.KeepAlive == req.Connection; - // _rawUrl = req.Uri.AbsolutePath; - // _url = req.Uri; - // if (null != req.Headers["user-agent"]) - // _userAgent = req.Headers["user-agent"]; - // _queryString = new NameValueCollection(); - // _query = new Hashtable(); - // foreach (KeyValuePair q in req.QueryString) - // { - // _queryString.Add(q.Key, q.Value.Value); - // _query[q.Key] = q.Value.Value; - // } - // // TODO: requires change to HttpServer.HttpRequest - // _ipEndPoint = null; - - // // _cookies = req.Cookies; - // // _isSecureConnection = req.IsSecureConnection; - // // _isAuthenticated = req.IsAuthenticated; - // } + public OSHttpRequest(HttpServer.IHttpClientContext context, HttpServer.IHttpRequest req) + { + //_context = context; + HttpServer.IHttpRequest _request = req; + + _acceptTypes = req.AcceptTypes; + if (null != req.Headers["content-encoding"]) + _contentEncoding = Encoding.GetEncoding(_request.Headers["content-encoding"]); + _contentLength64 = req.ContentLength; + if (null != req.Headers["content-type"]) + _contentType = _request.Headers["content-type"]; + _headers = req.Headers; + _httpMethod = req.Method; + _hasbody = req.ContentLength != 0; + _inputStream = req.Body; + _keepAlive = ConnectionType.KeepAlive == req.Connection; + _rawUrl = req.Uri.AbsolutePath; + _url = req.Uri; + if (null != req.Headers["user-agent"]) + _userAgent = req.Headers["user-agent"]; + _queryString = new NameValueCollection(); + _query = new Hashtable(); + foreach (KeyValuePair q in req.QueryString) + { + _queryString.Add(q.Key, q.Value.Value); + _query[q.Key] = q.Value.Value; + } + // TODO: requires change to HttpServer.HttpRequest + _ipEndPoint = null; + + // _cookies = req.Cookies; + // _isSecureConnection = req.IsSecureConnection; + // _isAuthenticated = req.IsAuthenticated; + } public override string ToString() { diff --git a/OpenSim/Framework/Servers/OSHttpResponse.cs b/OpenSim/Framework/Servers/OSHttpResponse.cs index f881a22..549ac27 100644 --- a/OpenSim/Framework/Servers/OSHttpResponse.cs +++ b/OpenSim/Framework/Servers/OSHttpResponse.cs @@ -200,19 +200,21 @@ namespace OpenSim.Framework.Servers } } - public Version ProtocolVersion + public string ProtocolVersion { get { - if (!HttpServer) - return _httpListenerResponse.ProtocolVersion; - - return new Version("1.0"); + if (HttpServer) + return _httpResponse.ProtocolVersion; + else + return _httpListenerResponse.ProtocolVersion.ToString(); } set { - if (!HttpServer) - _httpListenerResponse.ProtocolVersion = value; + if (HttpServer) + _httpResponse.ProtocolVersion = value; + else + _httpListenerResponse.ProtocolVersion = new Version(value); ; } } @@ -321,10 +323,10 @@ namespace OpenSim.Framework.Servers private HttpResponse _httpResponse; private HttpListenerResponse _httpListenerResponse; - // internal HttpResponse HttpResponse - // { - // get { return _httpResponse; } - // } + internal HttpResponse HttpResponse + { + get { return _httpResponse; } + } public OSHttpResponse() { @@ -342,7 +344,10 @@ namespace OpenSim.Framework.Servers { _httpListenerResponse = resp; } - + public OSHttpResponse(HttpServer.HttpResponse resp) + { + _httpResponse = resp; + } /// /// Instantiate an OSHttpResponse object from an OSHttpRequest /// object. @@ -378,6 +383,7 @@ namespace OpenSim.Framework.Servers { _httpResponse.Body.Flush(); _httpResponse.Send(); + } else { diff --git a/OpenSim/Region/Environment/Modules/Framework/EventQueueGetModule.cs b/OpenSim/Region/Environment/Modules/Framework/EventQueueGetModule.cs index 4f4c6ea..c11a4db 100644 --- a/OpenSim/Region/Environment/Modules/Framework/EventQueueGetModule.cs +++ b/OpenSim/Region/Environment/Modules/Framework/EventQueueGetModule.cs @@ -224,15 +224,16 @@ namespace OpenSim.Region.Environment.Modules.Framework lock (m_ids) thisID = m_ids[agentID]; - //if (element == null) - //{ - // responsedata["int_response_code"] = 502; - // responsedata["content_type"] = "text/plain"; - // responsedata["keepalive"] = false; - // responsedata["str_response_string"] = "Upstream error: "; - // responsedata["error_status_text"] = "Upstream error:"; - // return responsedata; - //} + if (element == null) + { + responsedata["int_response_code"] = 502; + responsedata["content_type"] = "text/plain"; + responsedata["keepalive"] = false; + responsedata["str_response_string"] = "Upstream error: "; + responsedata["error_status_text"] = "Upstream error:"; + responsedata["http_protocol_version"] = "HTTP/1.0"; + return responsedata; + } if (thisID == -1) // close-request { diff --git a/bin/HttpServer.dll b/bin/HttpServer.dll index 55f0609..39860be 100644 Binary files a/bin/HttpServer.dll and b/bin/HttpServer.dll differ -- cgit v1.1