From ec0d2c28fa04102ecbad4c5660efecbb970201dd Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Mon, 4 May 2009 20:19:21 +0000 Subject: Committing the changed tree --- .../Servers/HttpServer/BaseHTTPHandler.cs | 41 + .../Framework/Servers/HttpServer/BaseHttpServer.cs | 1626 ++++++++++++++++++++ .../Servers/HttpServer/BaseRequestHandler.cs | 71 + .../Servers/HttpServer/BaseStreamHandler.cs | 41 + .../Servers/HttpServer/BinaryStreamHandler.cs | 73 + .../Servers/HttpServer/GenericHTTPMethod.cs | 33 + .../HttpServer/Interfaces/IHttpAgentHandler.cs | 35 + .../Servers/HttpServer/Interfaces/IHttpServer.cs | 126 ++ .../HttpServer/Interfaces/IStreamHandler.cs | 61 + .../OpenSim.Framework.Servers.Interfaces.csproj | 120 ++ ...penSim.Framework.Servers.Interfaces.csproj.user | 12 + .../OpenSim.Framework.Servers.Interfaces.dll.build | 54 + .../OpenSim.Framework.Servers.Interfaces.mdp | 34 + OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs | 34 + .../Servers/HttpServer/LLSDMethodString.cs | 33 + .../Framework/Servers/HttpServer/OSHttpHandler.cs | 183 +++ .../Servers/HttpServer/OSHttpHttpHandler.cs | 145 ++ .../Framework/Servers/HttpServer/OSHttpRequest.cs | 228 +++ .../Servers/HttpServer/OSHttpRequestPump.cs | 298 ++++ .../Servers/HttpServer/OSHttpRequestQueue.cs | 68 + .../Framework/Servers/HttpServer/OSHttpResponse.cs | 302 ++++ .../Framework/Servers/HttpServer/OSHttpServer.cs | 210 +++ .../Servers/HttpServer/OSHttpStatusCodes.cs | 170 ++ .../Servers/HttpServer/OSHttpXmlRpcHandler.cs | 180 +++ .../OpenSim.Framework.Servers.HttpServer.csproj | 180 +++ ...penSim.Framework.Servers.HttpServer.csproj.user | 12 + .../OpenSim.Framework.Servers.HttpServer.dll.build | 74 + .../OpenSim.Framework.Servers.HttpServer.mdp | 54 + .../Servers/HttpServer/RestDeserialiseHandler.cs | 66 + .../Servers/HttpServer/RestHTTPHandler.cs | 56 + OpenSim/Framework/Servers/HttpServer/RestMethod.cs | 32 + .../Servers/HttpServer/RestObjectPoster.cs | 84 + .../Servers/HttpServer/RestObjectPosterResponse.cs | 107 ++ .../Servers/HttpServer/RestSessionService.cs | 291 ++++ .../Servers/HttpServer/RestStreamHandler.cs | 61 + .../HttpServer/SynchronousRestObjectPoster.cs | 83 + .../Framework/Servers/HttpServer/XmlRpcMethod.cs | 33 + .../Debug/OpenSim.Framework.Servers.HttpServer.dll | Bin 0 -> 41472 bytes .../OpenSim.Framework.Servers.HttpServer.dll.mdb | Bin 0 -> 14486 bytes 39 files changed, 5311 insertions(+) create mode 100644 OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.csproj create mode 100644 OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.csproj.user create mode 100644 OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.dll.build create mode 100644 OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.mdp create mode 100644 OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.csproj create mode 100644 OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.csproj.user create mode 100644 OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.dll.build create mode 100644 OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.mdp create mode 100644 OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/RestMethod.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/RestSessionService.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/SynchronousRestObjectPoster.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.cs create mode 100755 OpenSim/Framework/Servers/HttpServer/bin/Debug/OpenSim.Framework.Servers.HttpServer.dll create mode 100644 OpenSim/Framework/Servers/HttpServer/bin/Debug/OpenSim.Framework.Servers.HttpServer.dll.mdb (limited to 'OpenSim/Framework/Servers/HttpServer') diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs new file mode 100644 index 0000000..fc89e30 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs @@ -0,0 +1,41 @@ +/* + * 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 OpenSim 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.Collections; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public abstract class BaseHTTPHandler : BaseRequestHandler, IGenericHTTPHandler + { + public abstract Hashtable Handle(string path, Hashtable Request); + + protected BaseHTTPHandler(string httpMethod, string path) + : base(httpMethod, path) + { + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs new file mode 100644 index 0000000..779f577 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -0,0 +1,1626 @@ +/* + * 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 OpenSim 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.Net.Sockets; +using System.Reflection; +using System.Globalization; +using System.Text; +using System.Threading; +using System.Xml; +using HttpServer; +using log4net; +using Nwc.XmlRpc; +using OpenMetaverse.StructuredData; +using CoolHTTPListener = HttpServer.HttpListener; +using HttpListener=System.Net.HttpListener; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class BaseHttpServer : IHttpServer + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private HttpServerLogWriter httpserverlog = new HttpServerLogWriter(); + + private volatile int NotSocketErrors = 0; + public volatile bool HTTPDRunning = false; + + protected Thread m_workerThread; + // protected HttpListener m_httpListener; + protected CoolHTTPListener m_httpListener2; + protected Dictionary m_rpcHandlers = new Dictionary(); + protected Dictionary m_rpcHandlersKeepAlive = new Dictionary(); + protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/ + protected Dictionary m_llsdHandlers = new Dictionary(); + protected Dictionary m_streamHandlers = new Dictionary(); + protected Dictionary m_HTTPHandlers = new Dictionary(); + protected Dictionary m_agentHandlers = new Dictionary(); + + protected uint m_port; + protected uint m_sslport; + protected bool m_ssl; + protected bool m_firstcaps = true; + protected string m_SSLCommonName = ""; + + public uint SSLPort + { + get { return m_sslport; } + } + + public string SSLCommonName + { + get { return m_SSLCommonName; } + } + + public uint Port + { + get { return m_port; } + } + + public bool UseSSL + { + get { return m_ssl; } + } + + public BaseHttpServer(uint port) + { + m_port = port; + } + + public BaseHttpServer(uint port, bool ssl) : this (port) + { + m_ssl = ssl; + } + + public BaseHttpServer(uint port, bool ssl, uint sslport, string CN) : this (port, ssl) + { + if (m_ssl) + { + m_sslport = sslport; + } + } + + /// + /// Add a stream handler to the http server. If the handler already exists, then nothing happens. + /// + /// + public void AddStreamHandler(IRequestHandler handler) + { + string httpMethod = handler.HttpMethod; + string path = handler.Path; + string handlerKey = GetHandlerKey(httpMethod, path); + + lock (m_streamHandlers) + { + if (!m_streamHandlers.ContainsKey(handlerKey)) + { + // m_log.DebugFormat("[BASE HTTP SERVER]: Adding handler key {0}", handlerKey); + m_streamHandlers.Add(handlerKey, handler); + } + } + } + + private static string GetHandlerKey(string httpMethod, string path) + { + return httpMethod + ":" + path; + } + + public bool AddXmlRPCHandler(string method, XmlRpcMethod handler) + { + return AddXmlRPCHandler(method, handler, true); + } + + public bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive) + { + lock (m_rpcHandlers) + { + m_rpcHandlers[method] = handler; + m_rpcHandlersKeepAlive[method] = keepAlive; // default + } + + return true; + } + + public XmlRpcMethod GetXmlRPCHandler(string method) + { + lock (m_rpcHandlers) + { + if (m_rpcHandlers.ContainsKey(method)) + { + return m_rpcHandlers[method]; + } + else + { + return null; + } + } + } + + public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler) + { + //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName); + + lock (m_HTTPHandlers) + { + if (!m_HTTPHandlers.ContainsKey(methodName)) + { + m_HTTPHandlers.Add(methodName, handler); + return true; + } + } + + //must already have a handler for that path so return false + return false; + } + + // Note that the agent string is provided simply to differentiate + // the handlers - it is NOT required to be an actual agent header + // value. + public bool AddAgentHandler(string agent, IHttpAgentHandler handler) + { + lock (m_agentHandlers) + { + if (!m_agentHandlers.ContainsKey(agent)) + { + m_agentHandlers.Add(agent, handler); + return true; + } + } + + //must already have a handler for that path so return false + return false; + } + + public bool AddLLSDHandler(string path, LLSDMethod handler) + { + lock (m_llsdHandlers) + { + if (!m_llsdHandlers.ContainsKey(path)) + { + m_llsdHandlers.Add(path, handler); + return true; + } + } + return false; + } + + public bool SetDefaultLLSDHandler(DefaultLLSDMethod handler) + { + m_defaultLlsdHandler = handler; + return true; + } + + public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) + { + OSHttpRequest req = new OSHttpRequest(context, request); + OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request)); + //resp.KeepAlive = req.KeepAlive; + //m_log.Info("[Debug BASE HTTP SERVER]: Got 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 + { + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", true); + // 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 + // the request can be passed through to the other handlers. This is a low + // probability event; if a request is matched it is normally expected to be + // handled + //m_log.Debug("[BASE HTTP SERVER]: Handling Request" + request.RawUrl); + IHttpAgentHandler agentHandler; + + if (TryGetAgentHandler(request, response, out agentHandler)) + { + if (HandleAgentRequest(agentHandler, request, response)) + { + return; + } + } + + IRequestHandler requestHandler; + //response.KeepAlive = true; + response.SendChunked = false; + + string path = request.RawUrl; + string handlerKey = GetHandlerKey(request.HttpMethod, path); + + //m_log.DebugFormat("[BASE HTTP SERVER]: Handling {0} request for {1}", request.HttpMethod, path); + + if (TryGetStreamHandler(handlerKey, out requestHandler)) + { + //m_log.Debug("[BASE HTTP SERVER]: Found Stream Handler"); + // Okay, so this is bad, but should be considered temporary until everything is IStreamHandler. + byte[] buffer; + + response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type. + + if (requestHandler is IStreamedRequestHandler) + { + IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler; + + buffer = streamedRequestHandler.Handle(path, request.InputStream, request, response); + } + else if (requestHandler is IGenericHTTPHandler) + { + //m_log.Debug("[BASE HTTP SERVER]: Found Caps based HTTP Handler"); + IGenericHTTPHandler HTTPRequestHandler = requestHandler as IGenericHTTPHandler; + Stream requestStream = request.InputStream; + + Encoding encoding = Encoding.UTF8; + StreamReader reader = new StreamReader(requestStream, encoding); + + string requestBody = reader.ReadToEnd(); + + reader.Close(); + requestStream.Close(); + + Hashtable keysvals = new Hashtable(); + Hashtable headervals = new Hashtable(); + //string host = String.Empty; + + string[] querystringkeys = request.QueryString.AllKeys; + string[] rHeaders = request.Headers.AllKeys; + + + foreach (string queryname in querystringkeys) + { + keysvals.Add(queryname, request.QueryString[queryname]); + } + + foreach (string headername in rHeaders) + { + //m_log.Warn("[HEADER]: " + headername + "=" + request.Headers[headername]); + headervals[headername] = request.Headers[headername]; + } + + // if (headervals.Contains("Host")) + // { + // host = (string)headervals["Host"]; + // } + + keysvals.Add("requestbody", requestBody); + if (keysvals.Contains("method")) + { + //m_log.Warn("[HTTP]: Contains Method"); + //string method = (string)keysvals["method"]; + //m_log.Warn("[HTTP]: " + requestBody); + + } + DoHTTPGruntWork(HTTPRequestHandler.Handle(path, keysvals), response); + return; + } + else + { + IStreamHandler streamHandler = (IStreamHandler)requestHandler; + + using (MemoryStream memoryStream = new MemoryStream()) + { + streamHandler.Handle(path, request.InputStream, memoryStream, request, response); + memoryStream.Flush(); + buffer = memoryStream.ToArray(); + } + } + + request.InputStream.Close(); + + if (!response.SendChunked) + response.ContentLength64 = buffer.LongLength; + + try + { + response.OutputStream.Write(buffer, 0, buffer.Length); + //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; + } + + if (request.AcceptTypes != null && request.AcceptTypes.Length > 0) + { + foreach (string strAccept in request.AcceptTypes) + { + if (strAccept.Contains("application/llsd+xml")) + { + //m_log.Info("[Debug BASE HTTP SERVER]: Found an application/llsd+xml accept header"); + HandleLLSDRequests(request, response); + return; + } + } + } + + switch (request.ContentType) + { + case null: + case "text/html": + //m_log.Info("[Debug BASE HTTP SERVER]: found a text/html content type"); + HandleHTTPRequest(request, response); + return; + + case "application/llsd+xml": + case "application/xml+llsd": + //m_log.Info("[Debug BASE HTTP SERVER]: found a application/llsd+xml content type"); + HandleLLSDRequests(request, response); + return; + + case "text/xml": + case "application/xml": + default: + //m_log.Info("[Debug BASE HTTP SERVER]: in default handler"); + // Point of note.. the DoWeHaveA methods check for an EXACT path + // if (request.RawUrl.Contains("/CAPS/EQG")) + // { + // int i = 1; + // } + //m_log.Info("[Debug BASE HTTP SERVER]: Checking for LLSD Handler"); + if (DoWeHaveALLSDHandler(request.RawUrl)) + { + //m_log.Info("[Debug BASE HTTP SERVER]: Found LLSD Handler"); + HandleLLSDRequests(request, response); + return; + } + //m_log.Info("[Debug BASE HTTP SERVER]: Checking for HTTP Handler"); + if (DoWeHaveAHTTPHandler(request.RawUrl)) + { + //m_log.Info("[Debug BASE HTTP SERVER]: found HTTP Handler"); + HandleHTTPRequest(request, response); + return; + } + + //m_log.Info("[Debug BASE HTTP SERVER]: Generic XMLRPC"); + // generic login request. + HandleXmlRpcRequests(request, response); + + return; + } + } + 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 (EndOfStreamException e) + { + m_log.ErrorFormat("[BASE HTTP SERVER]: HandleRequest() threw {0}", e); + } + catch (InvalidOperationException e) + { + m_log.ErrorFormat("[BASE HTTP SERVER]: HandleRequest() threw {0}", e); + SendHTML500(response); + } + } + + private bool TryGetStreamHandler(string handlerKey, out IRequestHandler streamHandler) + { + string bestMatch = null; + + lock (m_streamHandlers) + { + foreach (string pattern in m_streamHandlers.Keys) + { + if (handlerKey.StartsWith(pattern)) + { + if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) + { + bestMatch = pattern; + } + } + } + + if (String.IsNullOrEmpty(bestMatch)) + { + streamHandler = null; + return false; + } + else + { + streamHandler = m_streamHandlers[bestMatch]; + return true; + } + } + } + + private bool TryGetHTTPHandler(string handlerKey, out GenericHTTPMethod HTTPHandler) + { + //m_log.DebugFormat("[BASE HTTP HANDLER]: Looking for HTTP handler for {0}", handlerKey); + + string bestMatch = null; + + lock (m_HTTPHandlers) + { + foreach (string pattern in m_HTTPHandlers.Keys) + { + if (handlerKey.StartsWith(pattern)) + { + if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) + { + bestMatch = pattern; + } + } + } + + if (String.IsNullOrEmpty(bestMatch)) + { + HTTPHandler = null; + return false; + } + else + { + HTTPHandler = m_HTTPHandlers[bestMatch]; + return true; + } + } + } + + private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler) + { + agentHandler = null; + try + { + foreach (IHttpAgentHandler handler in m_agentHandlers.Values) + { + if (handler.Match(request, response)) + { + agentHandler = handler; + return true; + } + } + } + catch(KeyNotFoundException) + { + } + + return false; + } + + /// + /// Try all the registered xmlrpc handlers when an xmlrpc request is received. + /// Sends back an XMLRPC unknown request response if no handler is registered for the requested method. + /// + /// + /// + private void HandleXmlRpcRequests(OSHttpRequest request, OSHttpResponse response) + { + Stream requestStream = request.InputStream; + + Encoding encoding = Encoding.UTF8; + StreamReader reader = new StreamReader(requestStream, encoding); + + string requestBody = reader.ReadToEnd(); + reader.Close(); + requestStream.Close(); + + string responseString = String.Empty; + XmlRpcRequest xmlRprcRequest = null; + + try + { + xmlRprcRequest = (XmlRpcRequest) (new XmlRpcRequestDeserializer()).Deserialize(requestBody); + } + catch (XmlException) + { + } + + if (xmlRprcRequest != null) + { + string methodName = xmlRprcRequest.MethodName; + if (methodName != null) + { + xmlRprcRequest.Params.Add(request.RemoteIPEndPoint); // Param[1] + XmlRpcResponse xmlRpcResponse; + + XmlRpcMethod method; + bool methodWasFound; + lock (m_rpcHandlers) + { + methodWasFound = m_rpcHandlers.TryGetValue(methodName, out method); + } + + if (methodWasFound) + { + xmlRprcRequest.Params.Add(request.Url); // Param[2] + + try + { + xmlRpcResponse = method(xmlRprcRequest); + } + catch(Exception e) + { + // if the registered XmlRpc method threw an exception, we pass a fault-code along + xmlRpcResponse = new XmlRpcResponse(); + // Code probably set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php + xmlRpcResponse.SetFault(-32603, String.Format("Requested method [{0}] threw exception: {1}", + methodName, e.Message)); + } + // if the method wasn't found, we can't determine KeepAlive state anyway, so lets do it only here + response.KeepAlive = m_rpcHandlersKeepAlive[methodName]; + } + else + { + xmlRpcResponse = new XmlRpcResponse(); + // Code set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php + xmlRpcResponse.SetFault(-32601, String.Format("Requested method [{0}] not found", methodName)); + } + + responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse); + } + else + { + //HandleLLSDRequests(request, response); + response.ContentType = "text/plain"; + response.StatusCode = 404; + response.StatusDescription = "Not Found"; + response.ProtocolVersion = "HTTP/1.0"; + byte[] buf = Encoding.UTF8.GetBytes("Not found"); + response.KeepAlive = false; + + m_log.ErrorFormat("[BASE HTTP SERVER] Handler not found for http request {0}", request.RawUrl); + + response.SendChunked = false; + response.ContentLength64 = buf.Length; + response.ContentEncoding = Encoding.UTF8; + try + { + response.OutputStream.Write(buf, 0, buf.Length); + } + catch (Exception ex) + { + m_log.Warn("[HTTPD]: Error - " + ex.Message); + } + finally + { + 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; + //responseString = "Error"; + } + } + + response.ContentType = "text/xml"; + + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + try + { + response.OutputStream.Write(buffer, 0, buffer.Length); + } + catch (Exception ex) + { + m_log.Warn("[HTTPD]: Error - " + ex.Message); + } + finally + { + 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); + } + } + } + + private void HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response) + { + //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request"); + Stream requestStream = request.InputStream; + + Encoding encoding = Encoding.UTF8; + StreamReader reader = new StreamReader(requestStream, encoding); + + string requestBody = reader.ReadToEnd(); + reader.Close(); + requestStream.Close(); + + //m_log.DebugFormat("[OGP]: {0}:{1}", request.RawUrl, requestBody); + response.KeepAlive = true; + + OSD llsdRequest = null; + OSD llsdResponse = null; + + bool LegacyLLSDLoginLibOMV = (requestBody.Contains("passwd") && requestBody.Contains("mac") && requestBody.Contains("viewer_digest")); + + if (requestBody.Length == 0) + // Get Request + { + requestBody = "requestget"; + } + try + { + llsdRequest = OSDParser.DeserializeLLSDXml(requestBody); + } + catch (Exception ex) + { + m_log.Warn("[HTTPD]: Error - " + ex.Message); + } + + if (llsdRequest != null)// && m_defaultLlsdHandler != null) + { + + LLSDMethod llsdhandler = null; + + if (TryGetLLSDHandler(request.RawUrl, out llsdhandler) && !LegacyLLSDLoginLibOMV) + { + // we found a registered llsd handler to service this request + llsdResponse = llsdhandler(request.RawUrl, llsdRequest, request.RemoteIPEndPoint.ToString()); + } + else + { + // we didn't find a registered llsd handler to service this request + // check if we have a default llsd handler + + if (m_defaultLlsdHandler != null) + { + // LibOMV path + llsdResponse = m_defaultLlsdHandler(llsdRequest); + } + else + { + // Oops, no handler for this.. give em the failed message + llsdResponse = GenerateNoLLSDHandlerResponse(); + } + } + + } + else + { + llsdResponse = GenerateNoLLSDHandlerResponse(); + } + byte[] buffer = new byte[0]; + if (llsdResponse.ToString() == "shutdown404!") + { + response.ContentType = "text/plain"; + response.StatusCode = 404; + response.StatusDescription = "Not Found"; + response.ProtocolVersion = "HTTP/1.0"; + buffer = Encoding.UTF8.GetBytes("Not found"); + } + else + { + response.ContentType = "application/llsd+xml"; + //m_log.Info("[Debug BASE HTTP SERVER]: Response: " + llsdResponse.ToString()); + buffer = OSDParser.SerializeLLSDXmlBytes(llsdResponse); + } + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + response.KeepAlive = true; + + try + { + response.OutputStream.Write(buffer, 0, buffer.Length); + } + catch (Exception ex) + { + m_log.Warn("[HTTPD]: Error - " + ex.Message); + } + finally + { + //response.OutputStream.Close(); + try + { + response.Send(); + response.OutputStream.Flush(); + response.OutputStream.Close(); + } + catch (IOException e) + { + m_log.DebugFormat("[BASE HTTP SERVER] LLSD IOException {0}.", e); + } + catch (SocketException e) + { + // This has to be here to prevent a Linux/Mono crash + m_log.WarnFormat("[BASE HTTP SERVER] LLSD issue {0}.\nNOTE: this may be spurious on Linux.", e); + } + } + } + + /// + /// Checks if we have an Exact path in the LLSD handlers for the path provided + /// + /// URI of the request + /// true if we have one, false if not + private bool DoWeHaveALLSDHandler(string path) + { + + string[] pathbase = path.Split('/'); + string searchquery = "/"; + + if (pathbase.Length < 1) + return false; + + for (int i = 1; i < pathbase.Length; i++) + { + searchquery += pathbase[i]; + if (pathbase.Length - 1 != i) + searchquery += "/"; + } + + string bestMatch = null; + + foreach (string pattern in m_llsdHandlers.Keys) + { + + if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length) + { + + bestMatch = pattern; + + } + } + + // extra kicker to remove the default XMLRPC login case.. just in case.. + if (path != "/" && bestMatch == "/" && searchquery != "/") + return false; + + if (path == "/") + return false; + + if (String.IsNullOrEmpty(bestMatch)) + { + + return false; + } + else + { + + return true; + } + } + + /// + /// Checks if we have an Exact path in the HTTP handlers for the path provided + /// + /// URI of the request + /// true if we have one, false if not + private bool DoWeHaveAHTTPHandler(string path) + { + string[] pathbase = path.Split('/'); + string searchquery = "/"; + + if (pathbase.Length < 1) + return false; + + for (int i = 1; i < pathbase.Length; i++) + { + searchquery += pathbase[i]; + if (pathbase.Length - 1 != i) + searchquery += "/"; + } + + string bestMatch = null; + + //m_log.DebugFormat("[BASE HTTP HANDLER]: Checking if we have an HTTP handler for {0}", searchquery); + + lock (m_HTTPHandlers) + { + foreach (string pattern in m_HTTPHandlers.Keys) + { + if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length) + { + bestMatch = pattern; + } + } + + // extra kicker to remove the default XMLRPC login case.. just in case.. + if (path == "/") + return false; + + if (String.IsNullOrEmpty(bestMatch)) + { + return false; + } + else + { + return true; + } + } + } + + private bool TryGetLLSDHandler(string path, out LLSDMethod llsdHandler) + { + llsdHandler = null; + // Pull out the first part of the path + // splitting the path by '/' means we'll get the following return.. + // {0}/{1}/{2} + // where {0} isn't something we really control 100% + + string[] pathbase = path.Split('/'); + string searchquery = "/"; + + if (pathbase.Length < 1) + return false; + + for (int i=1; i bestMatch.Length) + { + // You have to specifically register for '/' and to get it, you must specificaly request it + // + if (pattern == "/" && searchquery == "/" || pattern != "/") + bestMatch = pattern; + } + } + } + + if (String.IsNullOrEmpty(bestMatch)) + { + llsdHandler = null; + return false; + } + else + { + llsdHandler = m_llsdHandlers[bestMatch]; + return true; + } + } + + private OSDMap GenerateNoLLSDHandlerResponse() + { + OSDMap map = new OSDMap(); + map["reason"] = OSD.FromString("LLSDRequest"); + map["message"] = OSD.FromString("No handler registered for LLSD Requests"); + map["login"] = OSD.FromString("false"); + return map; + } + /// + /// A specific agent handler was provided. Such a handler is expecetd to have an + /// intimate, and highly specific relationship with the client. Consequently, + /// nothing is done here. + /// + /// + /// + /// + + private bool HandleAgentRequest(IHttpAgentHandler handler, OSHttpRequest request, OSHttpResponse response) + { + // In the case of REST, then handler is responsible for ALL aspects of + // the request/response handling. Nothing is done here, not even encoding. + + try + { + return handler.Handle(request, response); + } + catch (Exception e) + { + // If the handler did in fact close the stream, then this will blow + // chunks. So that that doesn't disturb anybody we throw away any + // and all exceptions raised. We've done our best to release the + // client. + try + { + m_log.Warn("[HTTP-AGENT]: Error - " + e.Message); + response.SendChunked = false; + response.KeepAlive = true; + response.StatusCode = (int)OSHttpStatusCode.ServerErrorInternalError; + //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) + { + } + } + + // Indicate that the request has been "handled" + + return true; + + } + + public void HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response) + { + switch (request.HttpMethod) + { + case "OPTIONS": + response.StatusCode = (int)OSHttpStatusCode.SuccessOk; + return; + + default: + HandleContentVerbs(request, response); + return; + } + } + + private void HandleContentVerbs(OSHttpRequest request, OSHttpResponse response) + { + // This is a test. There's a workable alternative.. as this way sucks. + // We'd like to put this into a text file parhaps that's easily editable. + // + // For this test to work, I used the following secondlife.exe parameters + // "C:\Program Files\SecondLifeWindLight\SecondLifeWindLight.exe" -settings settings_windlight.xml -channel "Second Life WindLight" -set SystemLanguage en-us -loginpage http://10.1.1.2:8002/?show_login_form=TRUE -loginuri http://10.1.1.2:8002 -user 10.1.1.2 + // + // Even after all that, there's still an error, but it's a start. + // + // I depend on show_login_form being in the secondlife.exe parameters to figure out + // to display the form, or process it. + // a better way would be nifty. + + Stream requestStream = request.InputStream; + + Encoding encoding = Encoding.UTF8; + StreamReader reader = new StreamReader(requestStream, encoding); + + string requestBody = reader.ReadToEnd(); + // avoid warning for now + reader.ReadToEnd(); + reader.Close(); + requestStream.Close(); + + Hashtable keysvals = new Hashtable(); + Hashtable headervals = new Hashtable(); + + Hashtable requestVars = new Hashtable(); + + string host = String.Empty; + + string[] querystringkeys = request.QueryString.AllKeys; + string[] rHeaders = request.Headers.AllKeys; + + keysvals.Add("body", requestBody); + keysvals.Add("uri", request.RawUrl); + keysvals.Add("content-type", request.ContentType); + keysvals.Add("http-method", request.HttpMethod); + + foreach (string queryname in querystringkeys) + { + keysvals.Add(queryname, request.QueryString[queryname]); + requestVars.Add(queryname, keysvals[queryname]); + } + + foreach (string headername in rHeaders) + { + //m_log.Warn("[HEADER]: " + headername + "=" + request.Headers[headername]); + headervals[headername] = request.Headers[headername]; + } + + if (headervals.Contains("Host")) + { + host = (string)headervals["Host"]; + } + + keysvals.Add("headers",headervals); + keysvals.Add("querystringkeys", querystringkeys); + keysvals.Add("requestvars", requestVars); + + if (keysvals.Contains("method")) + { + //m_log.Warn("[HTTP]: Contains Method"); + string method = (string) keysvals["method"]; + //m_log.Warn("[HTTP]: " + requestBody); + GenericHTTPMethod requestprocessor; + bool foundHandler = TryGetHTTPHandler(method, out requestprocessor); + if (foundHandler) + { + Hashtable responsedata1 = requestprocessor(keysvals); + DoHTTPGruntWork(responsedata1,response); + + //SendHTML500(response); + } + else + { + //m_log.Warn("[HTTP]: Handler Not Found"); + SendHTML404(response, host); + } + } + else + { + + GenericHTTPMethod requestprocessor; + bool foundHandler = TryGetHTTPHandlerPathBased(request.RawUrl, out requestprocessor); + if (foundHandler) + { + Hashtable responsedata2 = requestprocessor(keysvals); + DoHTTPGruntWork(responsedata2, response); + + //SendHTML500(response); + } + else + { + //m_log.Warn("[HTTP]: Handler Not Found"); + SendHTML404(response, host); + } + } + } + + private bool TryGetHTTPHandlerPathBased(string path, out GenericHTTPMethod httpHandler) + { + httpHandler = null; + // Pull out the first part of the path + // splitting the path by '/' means we'll get the following return.. + // {0}/{1}/{2} + // where {0} isn't something we really control 100% + + string[] pathbase = path.Split('/'); + string searchquery = "/"; + + if (pathbase.Length < 1) + return false; + + for (int i = 1; i < pathbase.Length; i++) + { + searchquery += pathbase[i]; + if (pathbase.Length - 1 != i) + searchquery += "/"; + } + + // while the matching algorithm below doesn't require it, we're expecting a query in the form + // + // [] = optional + // /resource/UUID/action[/action] + // + // now try to get the closest match to the reigstered path + // at least for OGP, registered path would probably only consist of the /resource/ + + string bestMatch = null; + +// m_log.DebugFormat( +// "[BASE HTTP HANDLER]: TryGetHTTPHandlerPathBased() looking for HTTP handler to match {0}", searchquery); + + lock (m_HTTPHandlers) + { + foreach (string pattern in m_HTTPHandlers.Keys) + { + if (searchquery.ToLower().StartsWith(pattern.ToLower())) + { + if (String.IsNullOrEmpty(bestMatch) || searchquery.Length > bestMatch.Length) + { + // You have to specifically register for '/' and to get it, you must specificaly request it + // + if (pattern == "/" && searchquery == "/" || pattern != "/") + bestMatch = pattern; + } + } + } + + if (String.IsNullOrEmpty(bestMatch)) + { + httpHandler = null; + return false; + } + else + { + if (bestMatch == "/" && searchquery != "/") + return false; + + httpHandler = m_HTTPHandlers[bestMatch]; + return true; + } + } + } + + private static void DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response) + { + //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response"); + int responsecode = (int)responsedata["int_response_code"]; + string responseString = (string)responsedata["str_response_string"]; + string contentType = (string)responsedata["content_type"]; + + if (responsedata.ContainsKey("error_status_text")) + { + response.StatusDescription = (string)responsedata["error_status_text"]; + } + if (responsedata.ContainsKey("http_protocol_version")) + { + response.ProtocolVersion = (string)responsedata["http_protocol_version"]; + } + + if (responsedata.ContainsKey("keepalive")) + { + bool keepalive = (bool)responsedata["keepalive"]; + response.KeepAlive = keepalive; + + } + //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"; + } + + // The client ignores anything but 200 here for web login, so ensure that this is 200 for that + + response.StatusCode = responsecode; + + 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") + || contentType.Contains("x-shockwave-flash") + || contentType.Contains("application/x-oar"))) + { + // Text + buffer = Encoding.UTF8.GetBytes(responseString); + } + else + { + // Binary! + buffer = Convert.FromBase64String(responseString); + } + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + try + { + response.OutputStream.Write(buffer, 0, buffer.Length); + } + catch (Exception ex) + { + m_log.Warn("[HTTPD]: Error - " + ex.Message); + } + finally + { + //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); + } + } + } + + public void SendHTML404(OSHttpResponse response, string host) + { + // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s + response.StatusCode = 404; + response.AddHeader("Content-type", "text/html"); + + string responseString = GetHTTP404(host); + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + try + { + response.OutputStream.Write(buffer, 0, buffer.Length); + } + catch (Exception ex) + { + m_log.Warn("[HTTPD]: Error - " + ex.Message); + } + finally + { + //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); + } + } + } + + public void SendHTML500(OSHttpResponse response) + { + // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s + response.StatusCode = (int)OSHttpStatusCode.SuccessOk; + response.AddHeader("Content-type", "text/html"); + + string responseString = GetHTTP500(); + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + try + { + response.OutputStream.Write(buffer, 0, buffer.Length); + } + catch (Exception ex) + { + m_log.Warn("[HTTPD]: Error - " + ex.Message); + } + finally + { + //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); + } + } + } + + public void Start() + { + 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); + StartHTTP(); + } + + private void StartHTTP() + { + try + { + m_log.Info("[HTTPD]: Spawned main thread OK"); + //m_httpListener = new HttpListener(); + 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 = new CoolHTTPListener(IPAddress.Any, (int)m_port); + m_httpListener2.ExceptionThrown += httpServerException; + m_httpListener2.LogWriter = httpserverlog; + + // 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.RequestHandler += OnHandleRequestIOThread; + //m_httpListener.Start(); + m_httpListener2.Start(64); + HTTPDRunning = true; + + //HttpListenerContext context; + //while (true) + //{ + // context = m_httpListener.GetContext(); + // ThreadPool.QueueUserWorkItem(new WaitCallback(HandleRequest), context); + // } + } + catch (Exception e) + { + m_log.Error("[HTTPD]: Error - " + e.Message); + m_log.Error("[HTTPD]: Tip: Do you have permission to listen on port " + m_port + ", " + m_sslport + "?"); + + // We want this exception to halt the entire server since in current configurations we aren't too + // useful without inbound HTTP. + throw e; + } + } + + public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err) + { + switch (err) + { + case SocketError.NotSocket: + NotSocketErrors++; + + break; + } + } + + public void httpServerException(object source, Exception exception) + { + m_log.ErrorFormat("[HTTPSERVER]: {0} had an exception {1}", source.ToString(), exception.ToString()); + /* + if (HTTPDRunning)// && NotSocketErrors > 5) + { + Stop(); + Thread.Sleep(200); + StartHTTP(); + m_log.Warn("[HTTPSERVER]: Died. Trying to kick....."); + } + */ + } + + public void Stop() + { + HTTPDRunning = false; + m_httpListener2.ExceptionThrown -= httpServerException; + m_httpListener2.DisconnectHandler = null; + + m_httpListener2.LogWriter = null; + m_httpListener2.RequestHandler -= OnHandleRequestIOThread; + + m_httpListener2.Stop(); + } + + public void RemoveStreamHandler(string httpMethod, string path) + { + string handlerKey = GetHandlerKey(httpMethod, path); + + //m_log.DebugFormat("[BASE HTTP SERVER]: Removing handler key {0}", handlerKey); + + lock (m_streamHandlers) m_streamHandlers.Remove(handlerKey); + } + + public void RemoveHTTPHandler(string httpMethod, string path) + { + lock (m_HTTPHandlers) + { + if (httpMethod != null && httpMethod.Length == 0) + { + m_HTTPHandlers.Remove(path); + return; + } + + m_HTTPHandlers.Remove(GetHandlerKey(httpMethod, path)); + } + } + + public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler) + { + try + { + if (handler == m_agentHandlers[agent]) + { + m_agentHandlers.Remove(agent); + return true; + } + } + catch(KeyNotFoundException) + { + } + + return false; + } + + public bool RemoveLLSDHandler(string path, LLSDMethod handler) + { + try + { + if (handler == m_llsdHandlers[path]) + { + m_llsdHandlers.Remove(path); + return true; + } + } + catch (KeyNotFoundException) + { + // This is an exception to prevent crashing because of invalid code + } + + return false; + } + + public string GetHTTP404(string host) + { + string file = Path.Combine(".", "http_404.html"); + if (!File.Exists(file)) + return getDefaultHTTP404(host); + + StreamReader sr = File.OpenText(file); + string result = sr.ReadToEnd(); + sr.Close(); + return result; + } + + public string GetHTTP500() + { + string file = Path.Combine(".", "http_500.html"); + if (!File.Exists(file)) + return getDefaultHTTP500(); + + StreamReader sr = File.OpenText(file); + string result = sr.ReadToEnd(); + sr.Close(); + return result; + } + + // Fallback HTTP responses in case the HTTP error response files don't exist + private static string getDefaultHTTP404(string host) + { + return "404 Page not found

Ooops!

The page you requested has been obsconded with by knomes. Find hippos quick!

If you are trying to log-in, your link parameters should have: "-loginpage http://" + host + "/?method=login -loginuri http://" + host + "/" in your link

"; + } + + private static string getDefaultHTTP500() + { + 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; + } + } + + /// + /// Relays HttpServer log messages to our own logging mechanism. + /// + /// There is also a UseTraceLogs line in this file that can be uncommented for more detailed log information + public class HttpServerLogWriter : ILogWriter + { + //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public void Write(object source, LogPrio priority, string message) + { + /* + switch (priority) + { + case HttpServer.LogPrio.Debug: + m_log.DebugFormat("[{0}]: {1}", source.ToString(), message); + break; + case HttpServer.LogPrio.Error: + m_log.ErrorFormat("[{0}]: {1}", source.ToString(), message); + break; + case HttpServer.LogPrio.Info: + m_log.InfoFormat("[{0}]: {1}", source.ToString(), message); + break; + case HttpServer.LogPrio.Warning: + m_log.WarnFormat("[{0}]: {1}", source.ToString(), message); + break; + case HttpServer.LogPrio.Fatal: + m_log.ErrorFormat("[{0}]: FATAL! - {1}", source.ToString(), message); + break; + default: + break; + } + */ + + return; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs new file mode 100644 index 0000000..9334972 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs @@ -0,0 +1,71 @@ +/* + * 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 OpenSim 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; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class BaseRequestHandler + { + public virtual string ContentType + { + get { return "application/xml"; } + } + + private readonly string m_httpMethod; + + public virtual string HttpMethod + { + get { return m_httpMethod; } + } + + private readonly string m_path; + + protected BaseRequestHandler(string httpMethod, string path) + { + m_httpMethod = httpMethod; + m_path = path; + } + + public virtual string Path + { + get { return m_path; } + } + + protected string GetParam(string path) + { + try + { + return path.Substring(m_path.Length); + } + catch (Exception) + { + return String.Empty; + } + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs new file mode 100644 index 0000000..734e3e4 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs @@ -0,0 +1,41 @@ +/* + * 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 OpenSim 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.IO; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public abstract class BaseStreamHandler : BaseRequestHandler, IStreamedRequestHandler + { + public abstract byte[] Handle(string path, Stream request, + OSHttpRequest httpRequest, OSHttpResponse httpResponse); + + protected BaseStreamHandler(string httpMethod, string path) : base(httpMethod, path) + { + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs new file mode 100644 index 0000000..fe5bcbc --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs @@ -0,0 +1,73 @@ +/* + * 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 OpenSim 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.IO; +using System.Text; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate string BinaryMethod(byte[] data, string path, string param); + + public class BinaryStreamHandler : BaseStreamHandler + { + private BinaryMethod m_method; + + public override byte[] Handle(string path, Stream request, OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + byte[] data = ReadFully(request); + string param = GetParam(path); + string responseString = m_method(data, path, param); + + return Encoding.UTF8.GetBytes(responseString); + } + + public BinaryStreamHandler(string httpMethod, string path, BinaryMethod binaryMethod) + : base(httpMethod, path) + { + m_method = binaryMethod; + } + + private static byte[] ReadFully(Stream stream) + { + byte[] buffer = new byte[32768]; + using (MemoryStream ms = new MemoryStream()) + { + while (true) + { + int read = stream.Read(buffer, 0, buffer.Length); + + if (read <= 0) + { + return ms.ToArray(); + } + + ms.Write(buffer, 0, read); + } + } + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs new file mode 100644 index 0000000..060761a --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs @@ -0,0 +1,33 @@ +/* + * 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 OpenSim 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.Collections; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate Hashtable GenericHTTPMethod(Hashtable request); +} diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs new file mode 100644 index 0000000..60c8e6e --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs @@ -0,0 +1,35 @@ +/* + * 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 OpenSim 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. + */ + +namespace OpenSim.Framework.Servers.HttpServer +{ + public interface IHttpAgentHandler + { + bool Handle(OSHttpRequest req, OSHttpResponse resp); + bool Match(OSHttpRequest req, OSHttpResponse resp); + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs new file mode 100644 index 0000000..6e3cc49 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs @@ -0,0 +1,126 @@ +/* + * 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 OpenSim 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 Nwc.XmlRpc; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Interface to OpenSimulator's built in HTTP server. Use this to register handlers (http, llsd, xmlrpc, etc.) + /// for given URLs. + /// + public interface IHttpServer + { + uint SSLPort { get; } + string SSLCommonName { get; } + + uint Port { get; } + bool UseSSL { get; } + + // Note that the agent string is provided simply to differentiate + // the handlers - it is NOT required to be an actual agent header + // value. + bool AddAgentHandler(string agent, IHttpAgentHandler handler); + + /// + /// Add a handler for an HTTP request + /// + /// + /// This handler can actually be invoked either as + /// + /// http://:/?method= + /// + /// or + /// + /// http://: + /// + /// if the method name starts with a slash. For example, AddHTTPHandler("/object/", ...) on a standalone region + /// server will register a handler that can be invoked with either + /// + /// http://localhost:9000/?method=/object/ + /// + /// or + /// + /// http://localhost:9000/object/ + /// + /// + /// + /// + /// true if the handler was successfully registered, false if a handler with the same name already existed. + /// + bool AddHTTPHandler(string methodName, GenericHTTPMethod handler); + + /// + /// Adds a LLSD handler, yay. + /// + /// /resource/ path + /// handle the LLSD response + /// + bool AddLLSDHandler(string path, LLSDMethod handler); + + /// + /// Add a stream handler to the http server. If the handler already exists, then nothing happens. + /// + /// + void AddStreamHandler(IRequestHandler handler); + + bool AddXmlRPCHandler(string method, XmlRpcMethod handler); + bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive); + + /// + /// Gets the XML RPC handler for given method name + /// + /// Name of the method + /// Returns null if not found + XmlRpcMethod GetXmlRPCHandler(string method); + + bool SetDefaultLLSDHandler(DefaultLLSDMethod handler); + + /// + /// Remove the agent if it is registered. + /// + /// + /// + /// + bool RemoveAgentHandler(string agent, IHttpAgentHandler handler); + + /// + /// Remove an HTTP handler + /// + /// + /// + void RemoveHTTPHandler(string httpMethod, string path); + + bool RemoveLLSDHandler(string path, LLSDMethod handler); + + void RemoveStreamHandler(string httpMethod, string path); + + string GetHTTP404(string host); + + string GetHTTP500(); + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs new file mode 100644 index 0000000..6e27aba --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs @@ -0,0 +1,61 @@ +/* + * 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 OpenSim 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.Collections; +using System.IO; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public interface IRequestHandler + { + // Return response content type + string ContentType { get; } + + // Return required http method + string HttpMethod { get; } + + // Return path + string Path { get; } + } + + public interface IStreamedRequestHandler : IRequestHandler + { + // Handle request stream, return byte array + byte[] Handle(string path, Stream request, OSHttpRequest httpRequest, OSHttpResponse httpResponse); + } + + public interface IStreamHandler : IRequestHandler + { + // Handle request stream, return byte array + void Handle(string path, Stream request, Stream response, OSHttpRequest httpReqbuest, OSHttpResponse httpResponse); + } + + public interface IGenericHTTPHandler : IRequestHandler + { + Hashtable Handle(string path, Hashtable request); + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.csproj b/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.csproj new file mode 100644 index 0000000..e8700f1 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.csproj @@ -0,0 +1,120 @@ + + + Local + 8.0.50727 + 2.0 + {8673D009-0000-0000-0000-000000000000} + Debug + AnyCPU + + + + OpenSim.Framework.Servers.Interfaces + JScript + Grid + IE50 + false + v2.0 + Library + + OpenSim.Framework.Servers.Interfaces + + + + + + + False + 285212672 + False + + + TRACE;DEBUG + + True + 4096 + False + ../../../../bin/ + False + False + False + 4 + False + + + + False + 285212672 + False + + + TRACE + + False + 4096 + True + ../../../../bin/ + False + False + False + 4 + False + + + + + HttpServer_OpenSim.dll + False + + + log4net.dll + False + + + OpenMetaverse.StructuredData.dll + False + + + OpenMetaverseTypes.dll + False + + + System + False + + + System.Xml + False + + + XMLRPC.dll + False + + + + + OpenSim.Data + {B75A430B-0000-0000-0000-000000000000} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + False + + + + + Code + + + Code + + + Code + + + + + + + + + + diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.csproj.user b/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.csproj.user new file mode 100644 index 0000000..b73b33f --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.csproj.user @@ -0,0 +1,12 @@ + + + Debug + AnyCPU + /root/opensim-commit/bin/ + 8.0.50727 + ProjectFiles + 0 + + + + diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.dll.build b/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.dll.build new file mode 100644 index 0000000..102300f --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.dll.build @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.mdp b/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.mdp new file mode 100644 index 0000000..96f6b46 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/OpenSim.Framework.Servers.Interfaces.mdp @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs b/OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs new file mode 100644 index 0000000..d669182 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs @@ -0,0 +1,34 @@ +/* + * 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 OpenSim 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 OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate OSD LLSDMethod( string path, OSD request, string endpoint ); + public delegate OSD DefaultLLSDMethod(OSD request); +} diff --git a/OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs b/OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs new file mode 100644 index 0000000..61def78 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs @@ -0,0 +1,33 @@ +/* + * 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 OpenSim 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 OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate OSD LLSDMethodString(OSD request, string thePath); +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs new file mode 100644 index 0000000..f1788a0 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs @@ -0,0 +1,183 @@ +/* + * 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 OpenSim 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 +{ + /// + /// Any OSHttpHandler must return one of the following results: + /// + /// + /// result code + /// meaning + /// + /// + /// Pass + /// handler did not process the request + /// + /// + /// Done + /// handler did process the request, OSHttpServer + /// can clean up and close the request + /// + /// + /// + public enum OSHttpHandlerResult + { + Unprocessed, + Pass, + Done, + } + + /// + /// An OSHttpHandler that matches on the "content-type" header can + /// supply an OSHttpContentTypeChecker delegate which will be + /// invoked by the request matcher in OSHttpRequestPump. + /// + /// true if the handler is interested in the content; + /// false otherwise + public delegate bool OSHttpContentTypeChecker(OSHttpRequest req); + + public abstract class OSHttpHandler + { + /// + /// 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 + /// '^$'. + /// + public virtual Regex Method + { + get { return _method; } + } + protected Regex _method; + + /// + /// 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 emtpy string use + /// '^$'. + /// + public virtual Regex Path + { + get { return _path; } + } + protected Regex _path; + + /// + /// Dictionary of (query name, regular expression) tuples, + /// allowing us to match on URI query fields. + /// + public virtual Dictionary Query + { + get { return _query; } + } + protected Dictionary _query; + + /// + /// Dictionary of (header name, regular expression) tuples, + /// allowing us to match on HTTP header fields. + /// + public virtual Dictionary Headers + { + get { return _headers; } + } + protected Dictionary _headers; + + /// + /// Dictionary of (header name, regular expression) tuples, + /// allowing us to match on HTTP header fields. + /// + /// + /// This feature is currently not implemented as it requires + /// (trivial) changes to HttpServer.HttpListener that have not + /// been implemented. + /// + public virtual Regex IPEndPointWhitelist + { + get { return _ipEndPointRegex; } + } + protected Regex _ipEndPointRegex; + + + /// + /// Base class constructor. + /// + /// null or path regex + /// null or dictionary of header + /// regexs + /// null or content type + /// regex + /// null or IP address regex + public OSHttpHandler(Regex method, Regex path, Dictionary query, + Dictionary headers, Regex contentType, Regex whitelist) + { + _method = method; + _path = path; + _query = query; + _ipEndPointRegex = whitelist; + + if (null == _headers && null != contentType) + { + _headers = new Dictionary(); + _headers.Add("content-type", contentType); + } + } + + + /// + /// Process an incoming OSHttpRequest that matched our + /// requirements. + /// + /// + /// OSHttpHandlerResult.Pass if we are after all not + /// interested in the request; OSHttpHandlerResult.Done if we + /// did process the request. + /// + 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(); + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs new file mode 100644 index 0000000..09d6f52 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs @@ -0,0 +1,145 @@ +/* + * 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 OpenSim 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; + + /// + /// Instantiate an HTTP handler. + /// + /// a GenericHTTPMethod + /// null or HTTP method regex + /// null or path regex + /// null or dictionary with query regexs + /// null or dictionary with header + /// regexs + /// null or IP address whitelist + public OSHttpHttpHandler(GenericHTTPMethod handler, Regex method, Regex path, + Dictionary query, + Dictionary headers, Regex whitelist) + : base(method, path, query, headers, new Regex(@"^text/html", RegexOptions.IgnoreCase | RegexOptions.Compiled), + whitelist) + { + _handler = handler; + } + + /// + /// Instantiate an HTTP handler. + /// + /// a GenericHTTPMethod + public OSHttpHttpHandler(GenericHTTPMethod handler) + : this(handler, new Regex(@"^GET$", RegexOptions.IgnoreCase | RegexOptions.Compiled), null, null, null, null) + { + } + + /// + /// Invoked by OSHttpRequestPump. + /// + 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; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs new file mode 100644 index 0000000..0ca868c --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs @@ -0,0 +1,228 @@ +/* + * 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 OpenSim 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.Collections.Specialized; +using System.IO; +using System.Net; +using System.Reflection; +using System.Text; +using HttpServer; +using log4net; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class OSHttpRequest + { + private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected IHttpRequest _request = null; + protected IHttpClientContext _context = null; + + public string[] AcceptTypes + { + get { return _request.AcceptTypes; } + } + + public Encoding ContentEncoding + { + get { return _contentEncoding; } + } + private Encoding _contentEncoding; + + public long ContentLength + { + get { return _request.ContentLength; } + } + + public long ContentLength64 + { + get { return ContentLength; } + } + + public string ContentType + { + get { return _contentType; } + } + private string _contentType; + + public bool HasEntityBody + { + get { return _request.ContentLength != 0; } + } + + public NameValueCollection Headers + { + get { return _request.Headers; } + } + + public string HttpMethod + { + get { return _request.Method; } + } + + public Stream InputStream + { + get { return _request.Body; } + } + + public bool IsSecured + { + get { return _context.Secured; } + } + + public bool KeepAlive + { + get { return ConnectionType.KeepAlive == _request.Connection; } + } + + public NameValueCollection QueryString + { + get { return _queryString; } + } + private NameValueCollection _queryString; + + public Hashtable Query + { + get { return _query; } + } + private Hashtable _query; + + public string RawUrl + { + get { return _request.Uri.AbsolutePath; } + } + + public IPEndPoint RemoteIPEndPoint + { + get { return _remoteIPEndPoint; } + } + private IPEndPoint _remoteIPEndPoint; + + public Uri Url + { + get { return _request.Uri; } + } + + public string UserAgent + { + get { return _userAgent; } + } + private string _userAgent; + + internal IHttpRequest IHttpRequest + { + get { return _request; } + } + + internal IHttpClientContext IHttpClientContext + { + get { return _context; } + } + + /// + /// Internal whiteboard for handlers to store temporary stuff + /// into. + /// + internal Dictionary Whiteboard + { + get { return _whiteboard; } + } + private Dictionary _whiteboard = new Dictionary(); + + + public OSHttpRequest() {} + + public OSHttpRequest(IHttpClientContext context, IHttpRequest req) + { + _request = req; + _context = context; + + if (null != req.Headers["content-encoding"]) + _contentEncoding = Encoding.GetEncoding(_request.Headers["content-encoding"]); + if (null != req.Headers["content-type"]) + _contentType = _request.Headers["content-type"]; + if (null != req.Headers["user-agent"]) + _userAgent = req.Headers["user-agent"]; + if (null != req.Headers["remote_addr"]) + { + try + { + IPAddress addr = IPAddress.Parse(req.Headers["remote_addr"]); + int port = Int32.Parse(req.Headers["remote_port"]); + _remoteIPEndPoint = new IPEndPoint(addr, port); + } + catch (FormatException) + { + _log.ErrorFormat("[OSHttpRequest]: format exception on addr/port {0}:{1}, ignoring", + req.Headers["remote_addr"], req.Headers["remote_port"]); + } + } + + _queryString = new NameValueCollection(); + _query = new Hashtable(); + try + { + foreach (HttpInputItem item in req.QueryString) + { + try + { + _queryString.Add(item.Name, item.Value); + _query[item.Name] = item.Value; + } + catch (InvalidCastException) + { + _log.DebugFormat("[OSHttpRequest]: error parsing {0} query item, skipping it", item.Name); + continue; + } + } + } + catch (Exception) + { + _log.ErrorFormat("[OSHttpRequest]: Error parsing querystring"); + } + } + + public override string ToString() + { + StringBuilder me = new StringBuilder(); + me.Append(String.Format("OSHttpRequest: {0} {1}\n", HttpMethod, RawUrl)); + foreach (string k in Headers.AllKeys) + { + me.Append(String.Format(" {0}: {1}\n", k, Headers[k])); + } + if (null != RemoteIPEndPoint) + { + me.Append(String.Format(" IP: {0}\n", RemoteIPEndPoint)); + } + + return me.ToString(); + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs new file mode 100644 index 0000000..893fa1b --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs @@ -0,0 +1,298 @@ +/* + * 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 OpenSim 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 +{ + /// + /// 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. + /// + 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.Name = EngineID; + _engine.IsBackground = true; + _engine.Start(); + + 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.Name = EngineID; + _engine.IsBackground = true; + _engine.Start(); + + 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 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(""); + body.WriteLine("
Ooops...
"); + body.WriteLine(String.Format("

{0}

", resp.StatusDescription)); + body.WriteLine(""); + 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 MatchHandlers(OSHttpRequest req, List handlers) + { + Dictionary scoredHandlers = new Dictionary(); + + _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 matchingHandlers = new List(scoredHandlers.Keys); + matchingHandlers.Sort(delegate(OSHttpHandler x, OSHttpHandler y) + { + return scoredHandlers[x] - scoredHandlers[y]; + }); + LogDumpHandlerList(matchingHandlers); + return matchingHandlers; + } + + protected int MatchOnNameValueCollection(NameValueCollection collection, Dictionary 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 l) + { + _log.DebugFormat("[{0}] OSHttpHandlerList dump:", EngineID); + foreach (OSHttpHandler h in l) + _log.DebugFormat(" ", h.ToString()); + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs new file mode 100644 index 0000000..4e34b41 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs @@ -0,0 +1,68 @@ +/* + * 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 OpenSim 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 +{ + /// + /// OSHttpRequestQueues are used to hand over incoming HTTP + /// requests to OSHttpRequestPump objects. + /// + public class OSHttpRequestQueue : Queue + { + 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; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs new file mode 100644 index 0000000..210d122 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs @@ -0,0 +1,302 @@ +/* + * 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 OpenSim 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.IO; +using System.Net; +using System.Text; +using HttpServer; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// OSHttpResponse is the OpenSim representation of an HTTP + /// response. + /// + public class OSHttpResponse + { + /// + /// Content type property. + /// + /// + /// Setting this property will also set IsContentTypeSet to + /// true. + /// + public string ContentType + { + get + { + return _httpResponse.ContentType; + } + + set + { + _httpResponse.ContentType = value; + } + } + + /// + /// Boolean property indicating whether the content type + /// property actively has been set. + /// + /// + /// IsContentTypeSet will go away together with .NET base. + /// + // public bool IsContentTypeSet + // { + // get { return _contentTypeSet; } + // } + // private bool _contentTypeSet; + + + /// + /// Length of the body content; 0 if there is no body. + /// + public long ContentLength + { + get + { + return _httpResponse.ContentLength; + } + + set + { + _httpResponse.ContentLength = value; + } + } + + /// + /// Alias for ContentLength. + /// + public long ContentLength64 + { + get { return ContentLength; } + set { ContentLength = value; } + } + + /// + /// Encoding of the body content. + /// + public Encoding ContentEncoding + { + get + { + return _httpResponse.Encoding; + } + + set + { + _httpResponse.Encoding = value; + } + } + + public bool KeepAlive + { + get + { + return _httpResponse.Connection == ConnectionType.KeepAlive; + } + + set + { + if (value) + _httpResponse.Connection = ConnectionType.KeepAlive; + else + _httpResponse.Connection = ConnectionType.Close; + } + } + + /// + /// Get or set the keep alive timeout property (default is + /// 20). Setting this to 0 also disables KeepAlive. Setting + /// this to something else but 0 also enable KeepAlive. + /// + public int KeepAliveTimeout + { + get + { + return _httpResponse.KeepAlive; + } + + set + { + if (value == 0) + { + _httpResponse.Connection = ConnectionType.Close; + _httpResponse.KeepAlive = 0; + } + else + { + _httpResponse.Connection = ConnectionType.KeepAlive; + _httpResponse.KeepAlive = value; + } + } + } + + /// + /// Return the output stream feeding the body. + /// + /// + /// On its way out... + /// + public Stream OutputStream + { + get + { + return _httpResponse.Body; + } + } + + public string ProtocolVersion + { + get + { + return _httpResponse.ProtocolVersion; + } + + set + { + _httpResponse.ProtocolVersion = value; + } + } + + /// + /// Return the output stream feeding the body. + /// + public Stream Body + { + get + { + return _httpResponse.Body; + } + } + + /// + /// Set a redirct location. + /// + public string RedirectLocation + { + // get { return _redirectLocation; } + set + { + _httpResponse.Redirect(value); + } + } + + + /// + /// Chunk transfers. + /// + public bool SendChunked + { + get + { + return _httpResponse.Chunked; + } + + set + { + _httpResponse.Chunked = value; + } + } + + /// + /// HTTP status code. + /// + public int StatusCode + { + get + { + return (int)_httpResponse.Status; + } + + set + { + _httpResponse.Status = (HttpStatusCode)value; + } + } + + + /// + /// HTTP status description. + /// + public string StatusDescription + { + get + { + return _httpResponse.Reason; + } + + set + { + _httpResponse.Reason = value; + } + } + + + protected IHttpResponse _httpResponse; + + public OSHttpResponse() {} + + public OSHttpResponse(IHttpResponse resp) + { + _httpResponse = resp; + } + + /// + /// Instantiate an OSHttpResponse object from an OSHttpRequest + /// object. + /// Incoming OSHttpRequest to which we are + /// replying + public OSHttpResponse(OSHttpRequest req) + { + _httpResponse = new HttpResponse(req.IHttpClientContext, req.IHttpRequest); + } + + /// + /// Add a header field and content to the response. + /// + /// string containing the header field + /// name + /// string containing the header field + /// value + public void AddHeader(string key, string value) + { + _httpResponse.AddHeader(key, value); + } + + /// + /// Send the response back to the remote client + /// + public void Send() + { + _httpResponse.Body.Flush(); + _httpResponse.Send(); + + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs new file mode 100644 index 0000000..e84f314 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs @@ -0,0 +1,210 @@ +/* + * 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 OpenSim 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 +{ + /// + /// 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. + /// + 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; } + } + + /// + /// True if this is an HTTPS connection; false otherwise. + /// + protected bool _isSecure; + public bool IsSecure + { + get { return _isSecure; } + } + + public int QueueSize + { + get { return _pumps.Length; } + } + + /// + /// List of registered OSHttpHandlers for this OSHttpServer instance. + /// + protected List _httpHandlers = new List(); + public List OSHttpHandlers + { + get + { + lock (_httpHandlers) + { + return new List(_httpHandlers); + } + } + } + + + /// + /// Instantiate an HTTP server. + /// + 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); + } + + /// + /// Instantiate an HTTPS server. + /// + 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); + } + + /// + /// Turn an HttpRequest into an OSHttpRequestItem and place it + /// in the queue. The OSHttpRequestQueue object will pulse the + /// next available idle pump. + /// + 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); + } + + /// + /// Start the HTTP server engine. + /// + public void Start() + { + _engine = new Thread(new ThreadStart(Engine)); + _engine.Name = _engineId; + _engine.IsBackground = true; + _engine.Start(); + + 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); + } + + /// + /// Engine keeps the HTTP server running. + /// + 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); + } + + + /// + /// Add an HTTP request handler. + /// + /// OSHttpHandler delegate + /// regex object for path matching + /// dictionary containing header names + /// and regular expressions to match against header values + 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); + } + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs new file mode 100644 index 0000000..2f1ca0f --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs @@ -0,0 +1,170 @@ +/* + * 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 OpenSim 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. + */ + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// HTTP status codes (almost) as defined by W3C in + /// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + /// + public enum OSHttpStatusCode: int + { + // 1xx Informational status codes providing a provisional + // response. + // 100 Tells client that to keep on going sending its request + InfoContinue = 100, + // 101 Server understands request, proposes to switch to different + // application level protocol + InfoSwitchingProtocols = 101, + + + // 2xx Success codes + // 200 Request successful + SuccessOk = 200, + // 201 Request successful, new resource created + SuccessOkCreated = 201, + // 202 Request accepted, processing still on-going + SuccessOkAccepted = 202, + // 203 Request successful, meta information not authoritative + SuccessOkNonAuthoritativeInformation = 203, + // 204 Request successful, nothing to return in the body + SuccessOkNoContent = 204, + // 205 Request successful, reset displayed content + SuccessOkResetContent = 205, + // 206 Request successful, partial content returned + SuccessOkPartialContent = 206, + + // 3xx Redirect code: user agent needs to go somewhere else + // 300 Redirect: different presentation forms available, take + // a pick + RedirectMultipleChoices = 300, + // 301 Redirect: requested resource has moved and now lives + // somewhere else + RedirectMovedPermanently = 301, + // 302 Redirect: Resource temporarily somewhere else, location + // might change + RedirectFound = 302, + // 303 Redirect: See other as result of a POST + RedirectSeeOther = 303, + // 304 Redirect: Resource still the same as before + RedirectNotModified = 304, + // 305 Redirect: Resource must be accessed via proxy provided + // in location field + RedirectUseProxy = 305, + // 307 Redirect: Resource temporarily somewhere else, location + // might change + RedirectMovedTemporarily = 307, + + // 4xx Client error: the client borked the request + // 400 Client error: bad request, server does not grok what + // the client wants + ClientErrorBadRequest = 400, + // 401 Client error: the client is not authorized, response + // provides WWW-Authenticate header field with a challenge + ClientErrorUnauthorized = 401, + // 402 Client error: Payment required (reserved for future use) + ClientErrorPaymentRequired = 402, + // 403 Client error: Server understood request, will not + // deliver, do not try again. + ClientErrorForbidden = 403, + // 404 Client error: Server cannot find anything matching the + // client request. + ClientErrorNotFound = 404, + // 405 Client error: The method specified by the client in the + // request is not allowed for the resource requested + ClientErrorMethodNotAllowed = 405, + // 406 Client error: Server cannot generate suitable response + // for the resource and content characteristics requested by + // the client + ClientErrorNotAcceptable = 406, + // 407 Client error: Similar to 401, Server requests that + // client authenticate itself with the proxy first + ClientErrorProxyAuthRequired = 407, + // 408 Client error: Server got impatient with client and + // decided to give up waiting for the client's request to + // arrive + ClientErrorRequestTimeout = 408, + // 409 Client error: Server could not fulfill the request for + // a resource as there is a conflict with the current state of + // the resource but thinks client can do something about this + ClientErrorConflict = 409, + // 410 Client error: The resource has moved somewhere else, + // but server has no clue where. + ClientErrorGone = 410, + // 411 Client error: The server is picky again and insists on + // having a content-length header field in the request + ClientErrorLengthRequired = 411, + // 412 Client error: one or more preconditions supplied in the + // client's request is false + ClientErrorPreconditionFailed = 412, + // 413 Client error: For fear of reflux, the server refuses to + // swallow that much data. + ClientErrorRequestEntityToLarge = 413, + // 414 Client error: The server considers the Request-URI to + // be indecently long and refuses to even look at it. + ClientErrorRequestURITooLong = 414, + // 415 Client error: The server has no clue about the media + // type requested by the client (contrary to popular belief it + // is not a warez server) + ClientErrorUnsupportedMediaType = 415, + // 416 Client error: The requested range cannot be delivered + // by the server. + ClientErrorRequestRangeNotSatisfiable = 416, + // 417 Client error: The expectations of the client as + // expressed in one or more Expect header fields cannot be met + // by the server, the server is awfully sorry about this. + ClientErrorExpectationFailed = 417, + // 499 Client error: Wildcard error. + ClientErrorJoker = 499, + + // 5xx Server errors (rare) + // 500 Server error: something really strange and unexpected + // happened + ServerErrorInternalError = 500, + // 501 Server error: The server does not do the functionality + // required to carry out the client request. not at + // all. certainly not before breakfast. but also not after + // breakfast. + ServerErrorNotImplemented = 501, + // 502 Server error: While acting as a proxy or a gateway, the + // server got ditched by the upstream server and as a + // consequence regretfully cannot fulfill the client's request + ServerErrorBadGateway = 502, + // 503 Server error: Due to unforseen circumstances the server + // cannot currently deliver the service requested. Retry-After + // header might indicate when to try again. + ServerErrorServiceUnavailable = 503, + // 504 Server error: The server blames the upstream server + // for not being able to deliver the service requested and + // claims that the upstream server is too slow delivering the + // goods. + ServerErrorGatewayTimeout = 504, + // 505 Server error: The server does not support the HTTP + // version conveyed in the client's request. + ServerErrorHttpVersionNotSupported = 505, + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs new file mode 100644 index 0000000..49bae48 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs @@ -0,0 +1,180 @@ +/* + * 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 OpenSim 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); + + /// + /// 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. + /// + /// true if the handler is interested in the content; + /// false otherwise + 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; + + + /// + /// Instantiate an XmlRpc handler. + /// + /// XmlRpcMethod + /// delegate + /// XmlRpc method name + /// XmlRpc path prefix (regular expression) + /// Dictionary with header names and + /// regular expressions to match content of headers + /// IP whitelist of remote end points + /// to accept (regular expression) + /// + /// 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. + /// + public OSHttpXmlRpcHandler(XmlRpcMethod handler, string methodName, Regex path, + Dictionary 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; + } + + + /// + /// Instantiate an XmlRpc handler. + /// + /// XmlRpcMethod + /// delegate + /// XmlRpc method name + public OSHttpXmlRpcHandler(XmlRpcMethod handler, string methodName) + : this(handler, methodName, null, null, null) + { + } + + + /// + /// Invoked by OSHttpRequestPump. + /// + 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; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.csproj b/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.csproj new file mode 100644 index 0000000..097a251 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.csproj @@ -0,0 +1,180 @@ + + + Local + 8.0.50727 + 2.0 + {1CBD339A-0000-0000-0000-000000000000} + Debug + AnyCPU + + + + OpenSim.Framework.Servers.HttpServer + JScript + Grid + IE50 + false + v2.0 + Library + + OpenSim.Framework.Servers.HttpServer + + + + + + + False + 285212672 + False + + + TRACE;DEBUG + + True + 4096 + False + ../../../../bin/ + False + False + False + 4 + False + + + + False + 285212672 + False + + + TRACE + + False + 4096 + True + ../../../../bin/ + False + False + False + 4 + False + + + + + HttpServer_OpenSim.dll + False + + + log4net.dll + False + + + OpenMetaverse.StructuredData.dll + False + + + OpenMetaverseTypes.dll + False + + + System + False + + + System.Xml + False + + + XMLRPC.dll + False + + + + + OpenSim.Data + {B75A430B-0000-0000-0000-000000000000} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + False + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + diff --git a/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.csproj.user b/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.csproj.user new file mode 100644 index 0000000..b73b33f --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.csproj.user @@ -0,0 +1,12 @@ + + + Debug + AnyCPU + /root/opensim-commit/bin/ + 8.0.50727 + ProjectFiles + 0 + + + + diff --git a/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.dll.build b/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.dll.build new file mode 100644 index 0000000..f814703 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.dll.build @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.mdp b/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.mdp new file mode 100644 index 0000000..7556d59 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OpenSim.Framework.Servers.HttpServer.mdp @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs new file mode 100644 index 0000000..d5ab926 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs @@ -0,0 +1,66 @@ +/* + * 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 OpenSim 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.IO; +using System.Xml; +using System.Xml.Serialization; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate TResponse RestDeserialiseMethod(TRequest request); + + public class RestDeserialiseHandler : BaseRequestHandler, IStreamHandler + where TRequest : new() + { + private RestDeserialiseMethod m_method; + + public RestDeserialiseHandler(string httpMethod, string path, RestDeserialiseMethod method) + : base(httpMethod, path) + { + m_method = method; + } + + public void Handle(string path, Stream request, Stream responseStream, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + TRequest deserial; + using (XmlTextReader xmlReader = new XmlTextReader(request)) + { + XmlSerializer deserializer = new XmlSerializer(typeof (TRequest)); + deserial = (TRequest) deserializer.Deserialize(xmlReader); + } + + TResponse response = m_method(deserial); + + using (XmlWriter xmlWriter = XmlTextWriter.Create(responseStream)) + { + XmlSerializer serializer = new XmlSerializer(typeof (TResponse)); + serializer.Serialize(xmlWriter, response); + } + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs new file mode 100644 index 0000000..175a0f2 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs @@ -0,0 +1,56 @@ +/* + * 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 OpenSim 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.Collections; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class RestHTTPHandler : BaseHTTPHandler + { + private GenericHTTPMethod m_dhttpMethod; + + public GenericHTTPMethod Method + { + get { return m_dhttpMethod; } + } + + public override Hashtable Handle(string path, Hashtable request) + { + + string param = GetParam(path); + request.Add("param", param); + request.Add("path", path); + return m_dhttpMethod(request); + } + + public RestHTTPHandler(string httpMethod, string path, GenericHTTPMethod dhttpMethod) + : base(httpMethod, path) + { + m_dhttpMethod = dhttpMethod; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestMethod.cs b/OpenSim/Framework/Servers/HttpServer/RestMethod.cs new file mode 100644 index 0000000..08ee35a --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestMethod.cs @@ -0,0 +1,32 @@ +/* + * 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 OpenSim 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. + */ + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate string RestMethod(string request, string path, string param, + OSHttpRequest httpRequest, OSHttpResponse httpResponse); +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs b/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs new file mode 100644 index 0000000..5a424d8 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs @@ -0,0 +1,84 @@ +/* + * 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 OpenSim 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.IO; +using System.Net; +using System.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Makes an asynchronous REST request which doesn't require us to do anything with the response. + /// + public class RestObjectPoster + { + public static void BeginPostObject(string requestUrl, TRequest obj) + { + BeginPostObject("POST", requestUrl, obj); + } + + public static void BeginPostObject(string verb, string requestUrl, TRequest obj) + { + Type type = typeof (TRequest); + + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + request.ContentType = "text/xml"; + + MemoryStream buffer = new MemoryStream(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } + + int length = (int) buffer.Length; + request.ContentLength = length; + + Stream requestStream = request.GetRequestStream(); + requestStream.Write(buffer.ToArray(), 0, length); + // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); + request.BeginGetResponse(AsyncCallback, request); + } + + private static void AsyncCallback(IAsyncResult result) + { + WebRequest request = (WebRequest) result.AsyncState; + using (WebResponse resp = request.EndGetResponse(result)) + { + } + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs b/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs new file mode 100644 index 0000000..690c583 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs @@ -0,0 +1,107 @@ +/* + * 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 OpenSim 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.IO; +using System.Net; +using System.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate void ReturnResponse(T reponse); + + /// + /// Makes an asynchronous REST request with a callback to invoke with the response. + /// + public class RestObjectPosterResponse + { +// private static readonly log4net.ILog m_log +// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public ReturnResponse ResponseCallback; + + public void BeginPostObject(string requestUrl, TRequest obj) + { + BeginPostObject("POST", requestUrl, obj); + } + + public void BeginPostObject(string verb, string requestUrl, TRequest obj) + { + Type type = typeof (TRequest); + + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + request.ContentType = "text/xml"; + request.Timeout = 10000; + + MemoryStream buffer = new MemoryStream(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } + + int length = (int) buffer.Length; + request.ContentLength = length; + + Stream requestStream = request.GetRequestStream(); + requestStream.Write(buffer.ToArray(), 0, length); + requestStream.Close(); + // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); + request.BeginGetResponse(AsyncCallback, request); + } + + private void AsyncCallback(IAsyncResult result) + { + WebRequest request = (WebRequest) result.AsyncState; + using (WebResponse resp = request.EndGetResponse(result)) + { + TResponse deserial; + XmlSerializer deserializer = new XmlSerializer(typeof (TResponse)); + Stream stream = resp.GetResponseStream(); + + // This is currently a bad debug stanza since it gobbles us the response... +// StreamReader reader = new StreamReader(stream); +// m_log.DebugFormat("[REST OBJECT POSTER RESPONSE]: Received {0}", reader.ReadToEnd()); + + deserial = (TResponse) deserializer.Deserialize(stream); + + if (deserial != null && ResponseCallback != null) + { + ResponseCallback(deserial); + } + } + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs new file mode 100644 index 0000000..f5e4248 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs @@ -0,0 +1,291 @@ +/* + * 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.IO; +using System.Net; +using System.Reflection; +using System.Text; +using System.Xml; +using System.Xml.Serialization; +using log4net; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class RestSessionObject + { + private string sid; + private string aid; + private TRequest request_body; + + public string SessionID + { + get { return sid; } + set { sid = value; } + } + + public string AvatarID + { + get { return aid; } + set { aid = value; } + } + + public TRequest Body + { + get { return request_body; } + set { request_body = value; } + } + } + + public class SynchronousRestSessionObjectPoster + { + public static TResponse BeginPostObject(string verb, string requestUrl, TRequest obj, string sid, string aid) + { + RestSessionObject sobj = new RestSessionObject(); + sobj.SessionID = sid; + sobj.AvatarID = aid; + sobj.Body = obj; + + Type type = typeof(RestSessionObject); + + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + request.ContentType = "text/xml"; + + MemoryStream buffer = new MemoryStream(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, sobj); + writer.Flush(); + } + + int length = (int)buffer.Length; + request.ContentLength = length; + + Stream requestStream = request.GetRequestStream(); + requestStream.Write(buffer.ToArray(), 0, length); + TResponse deserial = default(TResponse); + using (WebResponse resp = request.GetResponse()) + { + XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); + deserial = (TResponse)deserializer.Deserialize(resp.GetResponseStream()); + } + return deserial; + } + } + + public class RestSessionObjectPosterResponse + { + public ReturnResponse ResponseCallback; + + public void BeginPostObject(string requestUrl, TRequest obj, string sid, string aid) + { + BeginPostObject("POST", requestUrl, obj, sid, aid); + } + + public void BeginPostObject(string verb, string requestUrl, TRequest obj, string sid, string aid) + { + RestSessionObject sobj = new RestSessionObject(); + sobj.SessionID = sid; + sobj.AvatarID = aid; + sobj.Body = obj; + + Type type = typeof(RestSessionObject); + + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + request.ContentType = "text/xml"; + request.Timeout = 10000; + + MemoryStream buffer = new MemoryStream(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, sobj); + writer.Flush(); + } + + int length = (int)buffer.Length; + request.ContentLength = length; + + Stream requestStream = request.GetRequestStream(); + requestStream.Write(buffer.ToArray(), 0, length); + requestStream.Close(); + // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); + request.BeginGetResponse(AsyncCallback, request); + } + + private void AsyncCallback(IAsyncResult result) + { + WebRequest request = (WebRequest)result.AsyncState; + using (WebResponse resp = request.EndGetResponse(result)) + { + TResponse deserial; + XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); + Stream stream = resp.GetResponseStream(); + + // This is currently a bad debug stanza since it gobbles us the response... + // StreamReader reader = new StreamReader(stream); + // m_log.DebugFormat("[REST OBJECT POSTER RESPONSE]: Received {0}", reader.ReadToEnd()); + + deserial = (TResponse)deserializer.Deserialize(stream); + + if (deserial != null && ResponseCallback != null) + { + ResponseCallback(deserial); + } + } + } + } + + public delegate bool CheckIdentityMethod(string sid, string aid); + + public class RestDeserialiseSecureHandler : BaseRequestHandler, IStreamHandler + where TRequest : new() + { + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private RestDeserialiseMethod m_method; + private CheckIdentityMethod m_smethod; + + public RestDeserialiseSecureHandler( + string httpMethod, string path, + RestDeserialiseMethod method, CheckIdentityMethod smethod) + : base(httpMethod, path) + { + m_smethod = smethod; + m_method = method; + } + + public void Handle(string path, Stream request, Stream responseStream, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + RestSessionObject deserial = default(RestSessionObject); + bool fail = false; + + using (XmlTextReader xmlReader = new XmlTextReader(request)) + { + try + { + XmlSerializer deserializer = new XmlSerializer(typeof(RestSessionObject)); + deserial = (RestSessionObject)deserializer.Deserialize(xmlReader); + } + catch (Exception e) + { + m_log.Error("[REST]: Deserialization problem. Ignoring request. " + e); + fail = true; + } + } + + TResponse response = default(TResponse); + if (!fail && m_smethod(deserial.SessionID, deserial.AvatarID)) + { + response = m_method(deserial.Body); + } + + using (XmlWriter xmlWriter = XmlTextWriter.Create(responseStream)) + { + XmlSerializer serializer = new XmlSerializer(typeof(TResponse)); + serializer.Serialize(xmlWriter, response); + } + } + } + + public delegate bool CheckTrustedSourceMethod(IPEndPoint peer); + + public class RestDeserialiseTrustedHandler : BaseRequestHandler, IStreamHandler + where TRequest : new() + { + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// The operation to perform once trust has been established. + /// + /// + /// + /// + /// + private RestDeserialiseMethod m_method; + + /// + /// The method used to check whether a request is trusted. + /// + private CheckTrustedSourceMethod m_tmethod; + + public RestDeserialiseTrustedHandler(string httpMethod, string path, RestDeserialiseMethod method, CheckTrustedSourceMethod tmethod) + : base(httpMethod, path) + { + m_tmethod = tmethod; + m_method = method; + } + + public void Handle(string path, Stream request, Stream responseStream, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + TRequest deserial = default(TRequest); + bool fail = false; + + using (XmlTextReader xmlReader = new XmlTextReader(request)) + { + try + { + XmlSerializer deserializer = new XmlSerializer(typeof(TRequest)); + deserial = (TRequest)deserializer.Deserialize(xmlReader); + } + catch (Exception e) + { + m_log.Error("[REST]: Deserialization problem. Ignoring request. " + e); + fail = true; + } + } + + TResponse response = default(TResponse); + if (!fail && m_tmethod(httpRequest.RemoteIPEndPoint)) + { + response = m_method(deserial); + } + + using (XmlWriter xmlWriter = XmlTextWriter.Create(responseStream)) + { + XmlSerializer serializer = new XmlSerializer(typeof(TResponse)); + serializer.Serialize(xmlWriter, response); + } + } + } + +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs new file mode 100644 index 0000000..f213c15 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs @@ -0,0 +1,61 @@ +/* + * 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 OpenSim 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.IO; +using System.Text; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class RestStreamHandler : BaseStreamHandler + { + private RestMethod m_restMethod; + + public RestMethod Method + { + get { return m_restMethod; } + } + + public override byte[] Handle(string path, Stream request, OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + Encoding encoding = Encoding.UTF8; + StreamReader streamReader = new StreamReader(request, encoding); + + string requestBody = streamReader.ReadToEnd(); + streamReader.Close(); + + string param = GetParam(path); + string responseString = m_restMethod(requestBody, path, param, httpRequest, httpResponse); + + return Encoding.UTF8.GetBytes(responseString); + } + + public RestStreamHandler(string httpMethod, string path, RestMethod restMethod) : base(httpMethod, path) + { + m_restMethod = restMethod; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/SynchronousRestObjectPoster.cs b/OpenSim/Framework/Servers/HttpServer/SynchronousRestObjectPoster.cs new file mode 100644 index 0000000..b754c36 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/SynchronousRestObjectPoster.cs @@ -0,0 +1,83 @@ +/* + * 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 OpenSim 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.IO; +using System.Net; +using System.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class SynchronousRestObjectPoster + { + /// + /// Perform a synchronous REST request. + /// + /// + /// + /// + /// + /// + /// Thrown if we encounter a network issue while posting + /// the request. You'll want to make sure you deal with this as they're not uncommon + public static TResponse BeginPostObject(string verb, string requestUrl, TRequest obj) + { + Type type = typeof (TRequest); + + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + request.ContentType = "text/xml"; + + MemoryStream buffer = new MemoryStream(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } + + int length = (int) buffer.Length; + request.ContentLength = length; + + Stream requestStream = request.GetRequestStream(); + requestStream.Write(buffer.ToArray(), 0, length); + TResponse deserial = default(TResponse); + using (WebResponse resp = request.GetResponse()) + { + XmlSerializer deserializer = new XmlSerializer(typeof (TResponse)); + deserial = (TResponse) deserializer.Deserialize(resp.GetResponseStream()); + } + return deserial; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.cs b/OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.cs new file mode 100644 index 0000000..843b3f7 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.cs @@ -0,0 +1,33 @@ +/* + * 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 OpenSim 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 Nwc.XmlRpc; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate XmlRpcResponse XmlRpcMethod(XmlRpcRequest request); +} diff --git a/OpenSim/Framework/Servers/HttpServer/bin/Debug/OpenSim.Framework.Servers.HttpServer.dll b/OpenSim/Framework/Servers/HttpServer/bin/Debug/OpenSim.Framework.Servers.HttpServer.dll new file mode 100755 index 0000000..a191346 Binary files /dev/null and b/OpenSim/Framework/Servers/HttpServer/bin/Debug/OpenSim.Framework.Servers.HttpServer.dll differ diff --git a/OpenSim/Framework/Servers/HttpServer/bin/Debug/OpenSim.Framework.Servers.HttpServer.dll.mdb b/OpenSim/Framework/Servers/HttpServer/bin/Debug/OpenSim.Framework.Servers.HttpServer.dll.mdb new file mode 100644 index 0000000..25a50b9 Binary files /dev/null and b/OpenSim/Framework/Servers/HttpServer/bin/Debug/OpenSim.Framework.Servers.HttpServer.dll.mdb differ -- cgit v1.1