diff --git a/OpenMetaverse/Capabilities/CapsBase.cs b/OpenMetaverse.Http/CapsBase.cs similarity index 85% rename from OpenMetaverse/Capabilities/CapsBase.cs rename to OpenMetaverse.Http/CapsBase.cs index c73b843f..ad0a1028 100644 --- a/OpenMetaverse/Capabilities/CapsBase.cs +++ b/OpenMetaverse.Http/CapsBase.cs @@ -28,8 +28,9 @@ using System; using System.Net; using System.IO; using System.Threading; +using System.Security.Cryptography.X509Certificates; -namespace OpenMetaverse.Capabilities +namespace OpenMetaverse.Http { public class CapsBase { @@ -145,6 +146,7 @@ namespace OpenMetaverse.Capabilities public WebHeaderCollection Headers = new WebHeaderCollection(); public IWebProxy Proxy; + public X509Certificate2 ClientCertificate; public Uri Location { get { return location; } } public bool IsBusy { get { return isBusy; } } @@ -156,9 +158,10 @@ namespace OpenMetaverse.Capabilities protected Thread asyncThread; protected System.Text.Encoding encoding = System.Text.Encoding.Default; - public CapsBase(Uri location) + public CapsBase(Uri location, X509Certificate2 clientCert) { this.location = location; + ClientCertificate = clientCert; } public void OpenWriteAsync(Uri address) @@ -178,36 +181,35 @@ namespace OpenMetaverse.Capabilities SetBusy(); - asyncThread = new Thread(delegate(object state) - { - object[] args = (object[])state; - WebRequest request = null; - - try + asyncThread = new Thread( + delegate() { - request = SetupRequest((Uri)args[0]); - Stream stream = request.GetRequestStream(); + WebRequest request = null; - OnOpenWriteCompleted(new OpenWriteCompletedEventArgs( - stream, null, false, args[2])); - } - catch (ThreadInterruptedException) - { - if (request != null) - request.Abort(); + try + { + request = SetupRequest(address); + Stream stream = request.GetRequestStream(); - OnOpenWriteCompleted(new OpenWriteCompletedEventArgs( - null, null, true, args[2])); - } - catch (Exception e) - { - OnOpenWriteCompleted(new OpenWriteCompletedEventArgs( - null, e, false, args[2])); - } - }); + OnOpenWriteCompleted(new OpenWriteCompletedEventArgs( + stream, null, false, userToken)); + } + catch (ThreadInterruptedException) + { + if (request != null) + request.Abort(); - object[] cbArgs = new object[] { address, method, userToken }; - asyncThread.Start(cbArgs); + OnOpenWriteCompleted(new OpenWriteCompletedEventArgs( + null, null, true, userToken)); + } + catch (Exception e) + { + OnOpenWriteCompleted(new OpenWriteCompletedEventArgs( + null, e, false, userToken)); + } + } + ); + asyncThread.Start(); } public void UploadDataAsync(Uri address, byte[] data) @@ -269,30 +271,28 @@ namespace OpenMetaverse.Capabilities SetBusy(); - asyncThread = new Thread(delegate(object state) - { - object[] args = (object[])state; - - try + asyncThread = new Thread( + delegate() { - string data = encoding.GetString(DownloadDataCore((Uri)args[0], args[1])); - OnDownloadStringCompleted( - new DownloadStringCompletedEventArgs(location, data, null, false, args[1])); + try + { + string data = encoding.GetString(DownloadDataCore(address, userToken)); + OnDownloadStringCompleted( + new DownloadStringCompletedEventArgs(location, data, null, false, userToken)); + } + catch (ThreadInterruptedException) + { + OnDownloadStringCompleted( + new DownloadStringCompletedEventArgs(location, null, null, true, userToken)); + } + catch (Exception e) + { + OnDownloadStringCompleted( + new DownloadStringCompletedEventArgs(location, null, e, false, userToken)); + } } - catch (ThreadInterruptedException) - { - OnDownloadStringCompleted( - new DownloadStringCompletedEventArgs(location, null, null, true, args[1])); - } - catch (Exception e) - { - OnDownloadStringCompleted( - new DownloadStringCompletedEventArgs(location, null, e, false, args[1])); - } - }); - - object[] cbArgs = new object[] { address, userToken }; - asyncThread.Start(cbArgs); + ); + asyncThread.Start(); } public void CancelAsync() @@ -391,6 +391,9 @@ namespace OpenMetaverse.Capabilities if (Proxy != null) request.Proxy = Proxy; + if (ClientCertificate != null) + request.ClientCertificates.Add(ClientCertificate); + request.Method = "POST"; if (Headers != null && Headers.Count != 0) @@ -428,7 +431,7 @@ namespace OpenMetaverse.Capabilities // Disable stupid Expect-100: Continue header request.ServicePoint.Expect100Continue = false; // Crank up the max number of connections (default is 2!) - request.ServicePoint.ConnectionLimit = 20; + request.ServicePoint.ConnectionLimit = Int32.MaxValue; return request; } diff --git a/OpenMetaverse/Capabilities/CapsClient.cs b/OpenMetaverse.Http/CapsClient.cs similarity index 80% rename from OpenMetaverse/Capabilities/CapsClient.cs rename to OpenMetaverse.Http/CapsClient.cs index 5d941fab..d13102a7 100644 --- a/OpenMetaverse/Capabilities/CapsClient.cs +++ b/OpenMetaverse.Http/CapsClient.cs @@ -26,9 +26,10 @@ using System; using System.Net; +using System.Security.Cryptography.X509Certificates; using OpenMetaverse.StructuredData; -namespace OpenMetaverse.Capabilities +namespace OpenMetaverse.Http { public class CapsClient { @@ -48,7 +49,17 @@ namespace OpenMetaverse.Capabilities public CapsClient(Uri capability) { - _Client = new CapsBase(capability); + Init(capability, null); + } + + public CapsClient(Uri capability, X509Certificate2 clientCert) + { + Init(capability, clientCert); + } + + void Init(Uri capability, X509Certificate2 clientCert) + { + _Client = new CapsBase(capability, clientCert); _Client.DownloadProgressChanged += new CapsBase.DownloadProgressChangedEventHandler(Client_DownloadProgressChanged); _Client.UploadProgressChanged += new CapsBase.UploadProgressChangedEventHandler(Client_UploadProgressChanged); _Client.UploadDataCompleted += new CapsBase.UploadDataCompletedEventHandler(Client_UploadDataCompleted); @@ -77,15 +88,7 @@ namespace OpenMetaverse.Capabilities _ContentType = contentType; if (_Client.IsBusy) - { - Logger.Log("New CAPS request to " + _Client.Location + - " initiated, closing previous request", Helpers.LogLevel.Warning); _Client.CancelAsync(); - } - else - { - Logger.DebugLog("New CAPS request to " + _Client.Location + " initiated"); - } _Client.Headers.Clear(); @@ -118,7 +121,7 @@ namespace OpenMetaverse.Capabilities if (OnProgress != null) { try { OnProgress(this, e.BytesReceived, 0, e.TotalBytesToReceive, 0); } - catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); } + catch (Exception ex) { Logger.Log.Error(ex.Message, ex); } } } @@ -127,7 +130,7 @@ namespace OpenMetaverse.Capabilities if (OnProgress != null) { try { OnProgress(this, e.BytesReceived, e.BytesSent, e.TotalBytesToReceive, e.TotalBytesToSend); } - catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); } + catch (Exception ex) { Logger.Log.Error(ex.Message, ex); } } } @@ -140,7 +143,7 @@ namespace OpenMetaverse.Capabilities OSD result = OSDParser.DeserializeLLSDXml(e.Result); try { OnComplete(this, result, e.Error); } - catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); } + catch (Exception ex) { Logger.Log.Error(ex.Message, ex); } } else { @@ -161,24 +164,24 @@ namespace OpenMetaverse.Capabilities else if (code != HttpStatusCode.OK) { // Status code was set to something unknown, this is a failure - Logger.DebugLog(String.Format("Caps error at {0}: {1}", _Client.Location, code)); + Logger.Log.DebugFormat("Caps error at {0}: {1}", _Client.Location, code); try { OnComplete(this, null, e.Error); } - catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); } + catch (Exception ex) { Logger.Log.Error(ex.Message, ex); } } else { // Status code was not set, some other error occurred. This is a failure - Logger.DebugLog(String.Format("Caps error at {0}: {1}", _Client.Location, e.Error.Message)); + Logger.Log.DebugFormat("Caps error at {0}: {1}", _Client.Location, e.Error.Message); try { OnComplete(this, null, e.Error); } - catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); } + catch (Exception ex) { Logger.Log.Error(ex.Message, ex); } } } } else if (e.Cancelled) { - Logger.DebugLog("Capability action at " + _Client.Location + " cancelled"); + Logger.Log.Debug("Capability action at " + _Client.Location + " cancelled"); } } @@ -191,7 +194,7 @@ namespace OpenMetaverse.Capabilities OSD result = OSDParser.DeserializeLLSDXml(e.Result); try { OnComplete(this, result, e.Error); } - catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); } + catch (Exception ex) { Logger.Log.Error(ex.Message, ex); } } else { @@ -212,24 +215,24 @@ namespace OpenMetaverse.Capabilities else if (code != HttpStatusCode.OK) { // Status code was set to something unknown, this is a failure - Logger.DebugLog(String.Format("Caps error at {0}: {1}", _Client.Location, code)); + Logger.Log.DebugFormat("Caps error at {0}: {1}", _Client.Location, code); try { OnComplete(this, null, e.Error); } - catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); } + catch (Exception ex) { Logger.Log.Error(ex.Message, ex); } } else { // Status code was not set, some other error occurred. This is a failure - Logger.DebugLog(String.Format("Caps error at {0}: {1}", _Client.Location, e.Error.Message)); + Logger.Log.DebugFormat("Caps error at {0}: {1}", _Client.Location, e.Error.Message); try { OnComplete(this, null, e.Error); } - catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); } + catch (Exception ex) { Logger.Log.Error(ex.Message, ex); } } } } else if (e.Cancelled) { - Logger.DebugLog("Capability action at " + _Client.Location + " cancelled"); + Logger.Log.Debug("Capability action at " + _Client.Location + " cancelled"); } } diff --git a/OpenMetaverse/Capabilities/CapsServer.cs b/OpenMetaverse.Http/CapsServer.cs similarity index 56% rename from OpenMetaverse/Capabilities/CapsServer.cs rename to OpenMetaverse.Http/CapsServer.cs index 1dad2caf..697ae1ad 100644 --- a/OpenMetaverse/Capabilities/CapsServer.cs +++ b/OpenMetaverse.Http/CapsServer.cs @@ -28,39 +28,50 @@ using System; using System.Collections.Generic; using System.IO; using System.Net; +using System.Security.Cryptography.X509Certificates; using OpenMetaverse.StructuredData; +using HttpServer; -namespace OpenMetaverse.Capabilities +namespace OpenMetaverse.Http { public class CapsServer { - private struct CapsRedirector + struct CapsRedirector { - public HttpServer.HttpRequestCallback LocalCallback; - public Uri RemoteResource; + public HttpRequestCallback LocalCallback; + public Uri RemoteHandler; + public bool ClientCertRequired; - public CapsRedirector(HttpServer.HttpRequestCallback localCallback, Uri remoteResource) + public CapsRedirector(HttpRequestCallback localCallback, Uri remoteHandler, bool clientCertRequired) { LocalCallback = localCallback; - RemoteResource = remoteResource; + RemoteHandler = remoteHandler; + ClientCertRequired = clientCertRequired; } } - HttpServer server; + WebServer server; bool serverOwned; - HttpServer.HttpRequestHandler capsHandler; + HttpRequestHandler capsHandler; ExpiringCache expiringCaps = new ExpiringCache(); Dictionary fixedCaps = new Dictionary(); object syncRoot = new object(); - public CapsServer(List listeningPrefixes) + public CapsServer(IPAddress address, int port) { serverOwned = true; capsHandler = BuildCapsHandler("^/"); - server = new HttpServer(listeningPrefixes); + server = new WebServer(address, port); } - public CapsServer(HttpServer httpServer, string handlerPath) + public CapsServer(IPAddress address, int port, X509Certificate sslCertificate, X509Certificate rootCA, bool requireClientCertificate) + { + serverOwned = true; + capsHandler = BuildCapsHandler("^/"); + server = new WebServer(address, port, sslCertificate, rootCA, requireClientCertificate); + } + + public CapsServer(WebServer httpServer, string handlerPath) { serverOwned = false; capsHandler = BuildCapsHandler(handlerPath); @@ -83,10 +94,10 @@ namespace OpenMetaverse.Capabilities server.RemoveHandler(capsHandler); } - public UUID CreateCapability(HttpServer.HttpRequestCallback localHandler) + public UUID CreateCapability(HttpRequestCallback localHandler, bool clientCertRequired) { UUID id = UUID.Random(); - CapsRedirector redirector = new CapsRedirector(localHandler, null); + CapsRedirector redirector = new CapsRedirector(localHandler, null, clientCertRequired); lock (syncRoot) fixedCaps.Add(id, redirector); @@ -94,10 +105,10 @@ namespace OpenMetaverse.Capabilities return id; } - public UUID CreateCapability(Uri remoteResource) + public UUID CreateCapability(Uri remoteHandler, bool clientCertRequired) { UUID id = UUID.Random(); - CapsRedirector redirector = new CapsRedirector(null, remoteResource); + CapsRedirector redirector = new CapsRedirector(null, remoteHandler, clientCertRequired); lock (syncRoot) fixedCaps.Add(id, redirector); @@ -105,10 +116,10 @@ namespace OpenMetaverse.Capabilities return id; } - public UUID CreateCapability(HttpServer.HttpRequestCallback localHandler, double ttlSeconds) + public UUID CreateCapability(HttpRequestCallback localHandler, bool clientCertRequired, double ttlSeconds) { UUID id = UUID.Random(); - CapsRedirector redirector = new CapsRedirector(localHandler, null); + CapsRedirector redirector = new CapsRedirector(localHandler, null, clientCertRequired); lock (syncRoot) expiringCaps.Add(id, redirector, DateTime.Now + TimeSpan.FromSeconds(ttlSeconds)); @@ -116,10 +127,10 @@ namespace OpenMetaverse.Capabilities return id; } - public UUID CreateCapability(Uri remoteResource, double ttlSeconds) + public UUID CreateCapability(Uri remoteHandler, bool clientCertRequired, double ttlSeconds) { UUID id = UUID.Random(); - CapsRedirector redirector = new CapsRedirector(null, remoteResource); + CapsRedirector redirector = new CapsRedirector(null, remoteHandler, clientCertRequired); lock (syncRoot) expiringCaps.Add(id, redirector, DateTime.Now + TimeSpan.FromSeconds(ttlSeconds)); @@ -138,12 +149,12 @@ namespace OpenMetaverse.Capabilities } } - bool CapsCallback(ref HttpListenerContext context) + bool CapsCallback(IHttpClientContext client, IHttpRequest request, IHttpResponse response) { UUID capsID; CapsRedirector redirector; bool success; - string uuidString = context.Request.Url.Segments[context.Request.Url.Segments.Length - 1]; + string uuidString = request.UriParts[request.UriParts.Length - 1]; if (UUID.TryParse(uuidString, out capsID)) { @@ -152,60 +163,72 @@ namespace OpenMetaverse.Capabilities if (success) { - if (redirector.RemoteResource != null) - ProxyCapCallback(ref context, redirector.RemoteResource); + if (redirector.ClientCertRequired) + { + success = false; + // FIXME: Implement this + /*X509Certificate2 clientCert = request.GetClientCertificate(); + if (clientCert != null) + { + Logger.Log.Info(clientCert.ToString()); + }*/ + } + + if (redirector.RemoteHandler != null) + ProxyCapCallback(client, request, response, redirector.RemoteHandler); else - redirector.LocalCallback(ref context); + return redirector.LocalCallback(client, request, response); return true; } } - context.Response.StatusCode = (int)HttpStatusCode.NotFound; + response.Status = HttpStatusCode.NotFound; return true; } - void ProxyCapCallback(ref HttpListenerContext context, Uri remoteResource) + void ProxyCapCallback(IHttpClientContext client, IHttpRequest request, IHttpResponse response, Uri remoteHandler) { const int BUFFER_SIZE = 2048; int numBytes; byte[] buffer = new byte[BUFFER_SIZE]; // Proxy the request - HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(remoteResource); + HttpWebRequest remoteRequest = (HttpWebRequest)HttpWebRequest.Create(remoteHandler); - request.Method = context.Request.HttpMethod; - request.Headers.Add(context.Request.Headers); + remoteRequest.Method = request.Method; + remoteRequest.Headers.Add(request.Headers); - if (context.Request.HasEntityBody) + // TODO: Support for using our own client certificate during the proxy + + if (request.Body.Length > 0) { // Copy the request stream - using (Stream writeStream = request.GetRequestStream()) + using (Stream writeStream = remoteRequest.GetRequestStream()) { - while ((numBytes = context.Request.InputStream.Read(buffer, 0, BUFFER_SIZE)) > 0) + while ((numBytes = request.Body.Read(buffer, 0, BUFFER_SIZE)) > 0) writeStream.Write(buffer, 0, numBytes); - context.Request.InputStream.Close(); + request.Body.Close(); } } - System.Security.Cryptography.X509Certificates.X509Certificate2 cert = context.Request.GetClientCertificate(); - ; - // Proxy the response - HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + HttpWebResponse remoteResponse = (HttpWebResponse)remoteRequest.GetResponse(); - context.Response.StatusCode = (int)response.StatusCode; - context.Response.StatusDescription = response.StatusDescription; - context.Response.Headers = response.Headers; + response.Status = remoteResponse.StatusCode; + response.Reason = remoteResponse.StatusDescription; + + for (int i = 0; i < remoteResponse.Headers.Count; i++) + response.AddHeader(remoteResponse.Headers.GetKey(i), remoteResponse.Headers[i]); // Copy the response stream - using (Stream readStream = response.GetResponseStream()) + using (Stream readStream = remoteResponse.GetResponseStream()) { while ((numBytes = readStream.Read(buffer, 0, BUFFER_SIZE)) > 0) - context.Response.OutputStream.Write(buffer, 0, numBytes); + response.Body.Write(buffer, 0, numBytes); - context.Response.OutputStream.Close(); + response.Body.Close(); } } diff --git a/OpenMetaverse/Capabilities/EventQueueClient.cs b/OpenMetaverse.Http/EventQueueClient.cs similarity index 81% rename from OpenMetaverse/Capabilities/EventQueueClient.cs rename to OpenMetaverse.Http/EventQueueClient.cs index 1a7d7b64..04adf36c 100644 --- a/OpenMetaverse/Capabilities/EventQueueClient.cs +++ b/OpenMetaverse.Http/EventQueueClient.cs @@ -29,7 +29,7 @@ using System.Net; using System.Threading; using OpenMetaverse.StructuredData; -namespace OpenMetaverse.Capabilities +namespace OpenMetaverse.Http { public class EventQueueClient { @@ -59,7 +59,7 @@ namespace OpenMetaverse.Capabilities public EventQueueClient(Uri eventQueueLocation) { - _Client = new CapsBase(eventQueueLocation); + _Client = new CapsBase(eventQueueLocation, null); _Client.OpenWriteCompleted += new CapsBase.OpenWriteCompletedEventHandler(Client_OpenWriteCompleted); _Client.UploadDataCompleted += new CapsBase.UploadDataCompletedEventHandler(Client_UploadDataCompleted); } @@ -78,14 +78,7 @@ namespace OpenMetaverse.Capabilities _Running = false; if (_Client.IsBusy) - { - Logger.DebugLog("Stopping a running event queue"); _Client.CancelAsync(); - } - else - { - Logger.DebugLog("Stopping an already dead event queue"); - } } #region Callback Handlers @@ -113,13 +106,13 @@ namespace OpenMetaverse.Capabilities if (raiseEvent) { - Logger.DebugLog("Capabilities event queue connected"); + Logger.Log.Debug("Capabilities event queue connected"); // The event queue is starting up for the first time if (OnConnected != null) { try { OnConnected(); } - catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); } + catch (Exception ex) { Logger.Log.Error(ex.Message, ex); } } } } @@ -137,8 +130,7 @@ namespace OpenMetaverse.Capabilities if (code == HttpStatusCode.NotFound || code == HttpStatusCode.Gone) { - Logger.Log(String.Format("Closing event queue at {0} due to missing caps URI", _Client.Location), - Helpers.LogLevel.Info); + Logger.Log.InfoFormat("Closing event queue at {0} due to missing caps URI", _Client.Location); _Running = false; _Dead = true; @@ -156,18 +148,18 @@ namespace OpenMetaverse.Capabilities // Try to log a meaningful error message if (code != HttpStatusCode.OK) { - Logger.Log(String.Format("Unrecognized caps connection problem from {0}: {1}", - _Client.Location, code), Helpers.LogLevel.Warning); + Logger.Log.WarnFormat("Unrecognized caps connection problem from {0}: {1}", + _Client.Location, code); } else if (e.Error.InnerException != null) { - Logger.Log(String.Format("Unrecognized caps exception from {0}: {1}", - _Client.Location, e.Error.InnerException.Message), Helpers.LogLevel.Warning); + Logger.Log.WarnFormat("Unrecognized caps exception from {0}: {1}", + _Client.Location, e.Error.InnerException.Message); } else { - Logger.Log(String.Format("Unrecognized caps exception from {0}: {1}", - _Client.Location, e.Error.Message), Helpers.LogLevel.Warning); + Logger.Log.WarnFormat("Unrecognized caps exception from {0}: {1}", + _Client.Location, e.Error.Message); } } } @@ -187,7 +179,7 @@ namespace OpenMetaverse.Capabilities else if (e.Cancelled) { // Connection was cancelled - Logger.DebugLog("Cancelled connection to event queue at " + _Client.Location); + Logger.Log.Debug("Cancelled connection to event queue at " + _Client.Location); } if (_Running) @@ -206,7 +198,7 @@ namespace OpenMetaverse.Capabilities if (_Dead) { _Running = false; - Logger.DebugLog("Sent event queue shutdown message"); + Logger.Log.Debug("Sent event queue shutdown message"); } } @@ -219,7 +211,7 @@ namespace OpenMetaverse.Capabilities OSDMap body = (OSDMap)evt["body"]; try { OnEvent(msg, body); } - catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, ex); } + catch (Exception ex) { Logger.Log.Error(ex.Message, ex); } } } } diff --git a/OpenMetaverse/Capabilities/EventQueueServer.cs b/OpenMetaverse.Http/EventQueueServer.cs similarity index 86% rename from OpenMetaverse/Capabilities/EventQueueServer.cs rename to OpenMetaverse.Http/EventQueueServer.cs index 911921c1..57d1d7ba 100644 --- a/OpenMetaverse/Capabilities/EventQueueServer.cs +++ b/OpenMetaverse.Http/EventQueueServer.cs @@ -29,10 +29,10 @@ using System.Collections.Generic; using System.Net; using System.IO; using System.Threading; -using System.Xml; using OpenMetaverse.StructuredData; +using HttpServer; -namespace OpenMetaverse.Capabilities +namespace OpenMetaverse.Http { public class EventQueueEvent { @@ -58,7 +58,7 @@ namespace OpenMetaverse.Capabilities /// client and completing the HTTP request. The interval also specifies the /// maximum time that can pass before the queue shuts down after Stop() or the /// class destructor is called - const int BATCH_WAIT_INTERVAL = 100; + const int BATCH_WAIT_INTERVAL = 200; /// Since multiple events can be batched together and sent in the same /// response, this prevents the event queue thread from infinitely dequeueing @@ -66,16 +66,14 @@ namespace OpenMetaverse.Capabilities /// events const int MAX_EVENTS_PER_RESPONSE = 5; - HttpServer server; - HttpServer.HttpRequestHandler handler; + WebServer server; BlockingQueue eventQueue = new BlockingQueue(); int currentID = 0; bool running = true; - public EventQueueServer(HttpServer server, HttpServer.HttpRequestHandler handler) + public EventQueueServer(WebServer server) { this.server = server; - this.handler = handler; } ~EventQueueServer() @@ -86,8 +84,6 @@ namespace OpenMetaverse.Capabilities public void Stop() { running = false; - try { server.RemoveHandler(handler); } - catch (Exception) { } } public void SendEvent(string eventName, OSDMap body) @@ -97,11 +93,17 @@ namespace OpenMetaverse.Capabilities public void SendEvent(EventQueueEvent eventQueueEvent) { + if (!running) + throw new InvalidOperationException("Cannot add event while the queue is stopped"); + eventQueue.Enqueue(eventQueueEvent); } public void SendEvents(IList events) { + if (!running) + throw new InvalidOperationException("Cannot add event while the queue is stopped"); + for (int i = 0; i < events.Count; i++) eventQueue.Enqueue(events[i]); } @@ -111,7 +113,7 @@ namespace OpenMetaverse.Capabilities // Decode the request OSD request = null; - try { request = OSDParser.DeserializeLLSDXml(new XmlTextReader(context.Request.InputStream)); } + try { request = OSDParser.DeserializeLLSDXml(context.Request.InputStream); } catch (Exception) { } if (request != null && request.Type == OSDType.Map) @@ -122,8 +124,8 @@ namespace OpenMetaverse.Capabilities if (ack != currentID - 1) { - Logger.Log(String.Format("[EventQueue] Received an ack for id {0}, last id sent was {1}", - ack, currentID - 1), Helpers.LogLevel.Warning); + Logger.Log.WarnFormat("[EventQueue] Received an ack for id {0}, last id sent was {1}", + ack, currentID - 1); } if (!done) @@ -135,8 +137,8 @@ namespace OpenMetaverse.Capabilities } else { - Logger.Log(String.Format("[EventQueue] Shutting down the event queue {0} at the client's request", - context.Request.Url), Helpers.LogLevel.Info); + Logger.Log.InfoFormat("[EventQueue] Shutting down the event queue {0} at the client's request", + context.Request.Url); Stop(); context.Response.KeepAlive = context.Request.KeepAlive; @@ -145,8 +147,8 @@ namespace OpenMetaverse.Capabilities } else { - Logger.Log(String.Format("[EventQueue] Received a request with invalid or missing LLSD at {0}, closing the connection", - context.Request.Url), Helpers.LogLevel.Warning); + Logger.Log.WarnFormat("[EventQueue] Received a request with invalid or missing LLSD at {0}, closing the connection", + context.Request.Url); context.Response.KeepAlive = context.Request.KeepAlive; context.Response.StatusCode = (int)HttpStatusCode.BadRequest; @@ -197,14 +199,16 @@ namespace OpenMetaverse.Capabilities if (totalMsPassed >= CONNECTION_TIMEOUT) { - Logger.DebugLog(String.Format( + Logger.Log.DebugFormat( "[EventQueue] {0}ms passed without an event, timing out the event queue", - totalMsPassed)); + totalMsPassed); SendResponse(httpContext, null); return; } } } + + Logger.Log.Info("[EventQueue] Handler thread is no longer running"); } )); diff --git a/OpenMetaverse.Http/Logger.cs b/OpenMetaverse.Http/Logger.cs new file mode 100644 index 00000000..94ec8040 --- /dev/null +++ b/OpenMetaverse.Http/Logger.cs @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2008, openmetaverse.org + * All rights reserved. + * + * - 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. + * - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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 log4net; +using log4net.Config; + +[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "log4net")] + +namespace OpenMetaverse.Http +{ + /// + /// Singleton logging class for the entire library + /// + internal static class Logger + { + /// log4net logging engine + public static ILog Log; + + static Logger() + { + Log = LogManager.GetLogger(System.Reflection.Assembly.GetExecutingAssembly().FullName); + + // If error level reporting isn't enabled we assume no logger is configured and initialize a default + // ConsoleAppender + if (!Log.Logger.IsEnabledFor(log4net.Core.Level.Error)) + { + log4net.Appender.ConsoleAppender appender = new log4net.Appender.ConsoleAppender(); + appender.Layout = new log4net.Layout.PatternLayout("%timestamp [%thread] %-5level - %message%newline"); + BasicConfigurator.Configure(appender); + + Log.Info("No log configuration found, defaulting to console logging"); + } + } + } +} diff --git a/OpenMetaverse.Http/Trusted.cs b/OpenMetaverse.Http/Trusted.cs new file mode 100644 index 00000000..8ecf059b --- /dev/null +++ b/OpenMetaverse.Http/Trusted.cs @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2008, openmetaverse.org + * All rights reserved. + * + * - 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. + * - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Security.Cryptography; +using Mono.Security; +using Mono.Security.X509; +using Mono.Security.X509.Extensions; +using Mono.Security.Authenticode; + +namespace OpenMetaverse.Http +{ + public static class Trusted + { + public static void CreateServerRootCA(string issuer, out byte[] rootKey, out byte[] rootCert) + { + if (!issuer.StartsWith("CN=")) + issuer = "CN=" + issuer; + + // Create a temporary file + string tempFile = Path.GetTempFileName(); + + // Generate a new signing key + RSA issuerKey = (RSA)RSA.Create(); + + // Generate a private key + PrivateKey key = new PrivateKey(); + key.RSA = issuerKey; + + // Save the private key and load it back into memory + key.Save(tempFile); + rootKey = File.ReadAllBytes(tempFile); + File.Delete(tempFile); + + // Serial number MUST be positive + byte[] sn = Guid.NewGuid().ToByteArray(); + if ((sn[0] & 0x80) == 0x80) + sn[0] -= 0x80; + + // Generate a self-signed certificate + X509CertificateBuilder cb = new X509CertificateBuilder(3); + cb.SerialNumber = sn; + cb.IssuerName = issuer; + cb.NotBefore = DateTime.Now; + cb.NotAfter = new DateTime(643445675990000000); // 12/31/2039 23:59:59Z + cb.SubjectName = issuer; + cb.SubjectPublicKey = issuerKey; + cb.Hash = "SHA1"; + + rootCert = cb.Sign(issuerKey); + } + + public static byte[] CreateServerCert(string subjectName, byte[] rootKey, byte[] rootCert) + { + if (!subjectName.StartsWith("CN=")) + subjectName = "CN=" + subjectName; + + // Copy the root key since the PrivateKey constructor will blow away the data + byte[] rootKeyCopy = new byte[rootKey.Length]; + Buffer.BlockCopy(rootKey, 0, rootKeyCopy, 0, rootKey.Length); + + // Load the server's private key and certificate + PrivateKey pvk = new PrivateKey(rootKeyCopy, null); + RSA issuerKey = pvk.RSA; + X509Certificate issuerCert = new X509Certificate(rootCert); + + // Serial number MUST be positive + byte[] sn = Guid.NewGuid().ToByteArray(); + if ((sn[0] & 0x80) == 0x80) + sn[0] -= 0x80; + + ExtendedKeyUsageExtension eku = new ExtendedKeyUsageExtension(); + eku.KeyPurpose.Add("1.3.6.1.5.5.7.3.1"); // Indicates the cert is intended for server auth + + // Generate a server certificate signed by the server root CA + X509CertificateBuilder cb = new X509CertificateBuilder(3); + cb.SerialNumber = sn; + cb.IssuerName = issuerCert.IssuerName; + cb.NotBefore = DateTime.Now; + cb.NotAfter = new DateTime(643445675990000000); // 12/31/2039 23:59:59Z + cb.SubjectName = subjectName; + cb.SubjectPublicKey = issuerKey; + cb.Hash = "SHA1"; + cb.Extensions.Add(eku); + byte[] serverCert = cb.Sign(issuerKey); + + // Generate a PKCS#12 file for the server containing the private key and certificate + PKCS12 p12 = new PKCS12(); + p12.Password = null; + + ArrayList list = new ArrayList(4); + // We use a fixed array to avoid endianess issues + // (in case some tools requires the ID to be 1). + list.Add(new byte[] { 1, 0, 0, 0 }); + Hashtable attributes = new Hashtable(1); + attributes.Add(PKCS9.localKeyId, list); + + p12.AddCertificate(new X509Certificate(serverCert), attributes); + p12.AddCertificate(issuerCert); + p12.AddPkcs8ShroudedKeyBag(issuerKey, attributes); + + return p12.GetBytes(); + } + + public static byte[] CreateClientCert(string subjectName, byte[] rootKey, byte[] rootCert) + { + if (!subjectName.StartsWith("CN=")) + subjectName = "CN=" + subjectName; + + // Copy the root key since the PrivateKey constructor will blow away the data + byte[] rootKeyCopy = new byte[rootKey.Length]; + Buffer.BlockCopy(rootKey, 0, rootKeyCopy, 0, rootKey.Length); + + // Load the server's private key and certificate + PrivateKey pvk = new PrivateKey(rootKeyCopy, null); + RSA issuerKey = pvk.RSA; + X509Certificate issuerCert = new X509Certificate(rootCert); + + // Serial number MUST be positive + byte[] sn = Guid.NewGuid().ToByteArray(); + if ((sn[0] & 0x80) == 0x80) + sn[0] -= 0x80; + + ExtendedKeyUsageExtension eku = new ExtendedKeyUsageExtension(); + eku.KeyPurpose.Add("1.3.6.1.5.5.7.3.2"); // Indicates the cert is intended for client auth + + // Generate a client certificate signed by the server root CA + X509CertificateBuilder cb = new X509CertificateBuilder(3); + cb.SerialNumber = sn; + cb.IssuerName = issuerCert.IssuerName; + cb.NotBefore = DateTime.Now; + cb.NotAfter = new DateTime(643445675990000000); // 12/31/2039 23:59:59Z + cb.SubjectName = subjectName; + cb.SubjectPublicKey = issuerKey; + cb.Hash = "SHA1"; + cb.Extensions.Add(eku); + byte[] clientCert = cb.Sign(issuerKey); + + // Generate a PKCS#12 file for the client containing the private key and certificate + PKCS12 p12 = new PKCS12(); + p12.Password = null; + + ArrayList list = new ArrayList(4); + // We use a fixed array to avoid endianess issues + // (in case some tools requires the ID to be 1). + list.Add(new byte[] { 1, 0, 0, 0 }); + Hashtable attributes = new Hashtable(1); + attributes.Add(PKCS9.localKeyId, list); + + p12.AddCertificate(new X509Certificate(clientCert), attributes); + p12.AddCertificate(issuerCert); + p12.AddPkcs8ShroudedKeyBag(issuerKey, attributes); + + return p12.GetBytes(); + } + } +} diff --git a/OpenMetaverse.StructuredData/LLSD/XmlLLSD.cs b/OpenMetaverse.StructuredData/LLSD/XmlLLSD.cs index 16b6ef16..4f1038c4 100644 --- a/OpenMetaverse.StructuredData/LLSD/XmlLLSD.cs +++ b/OpenMetaverse.StructuredData/LLSD/XmlLLSD.cs @@ -53,6 +53,11 @@ namespace OpenMetaverse.StructuredData return DeserializeLLSDXml(new XmlTextReader(new MemoryStream(xmlData, false))); } + public static OSD DeserializeLLSDXml(Stream xmlStream) + { + return DeserializeLLSDXml(new XmlTextReader(xmlStream)); + } + /// /// /// diff --git a/OpenMetaverse.Utilities/RegistrationApi.cs b/OpenMetaverse.Utilities/RegistrationApi.cs index c89af7b1..170b1658 100644 --- a/OpenMetaverse.Utilities/RegistrationApi.cs +++ b/OpenMetaverse.Utilities/RegistrationApi.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Threading; using System.Text; using OpenMetaverse.StructuredData; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; namespace OpenMetaverse { diff --git a/OpenMetaverse.Utilities/VoiceManager.cs b/OpenMetaverse.Utilities/VoiceManager.cs index 1225e053..2bd971d8 100644 --- a/OpenMetaverse.Utilities/VoiceManager.cs +++ b/OpenMetaverse.Utilities/VoiceManager.cs @@ -33,7 +33,7 @@ using System.Xml; using System.Threading; using OpenMetaverse; using OpenMetaverse.StructuredData; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; namespace OpenMetaverse.Utilities { diff --git a/OpenMetaverse/AgentManager.cs b/OpenMetaverse/AgentManager.cs index ade4cf93..7b6a5cd5 100644 --- a/OpenMetaverse/AgentManager.cs +++ b/OpenMetaverse/AgentManager.cs @@ -31,7 +31,7 @@ using System.Threading; using System.Text; using System.Reflection; using OpenMetaverse.StructuredData; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; using OpenMetaverse.Packets; namespace OpenMetaverse diff --git a/OpenMetaverse/Capabilities/HttpRequestSignature.cs b/OpenMetaverse/Capabilities/HttpRequestSignature.cs deleted file mode 100644 index b698e7c6..00000000 --- a/OpenMetaverse/Capabilities/HttpRequestSignature.cs +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2008, openmetaverse.org - * All rights reserved. - * - * - 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. - * - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Net; -using System.Text.RegularExpressions; - -namespace OpenMetaverse.Capabilities -{ - /// - /// Used to match incoming HTTP requests against registered handlers. - /// Matches based on any combination of HTTP Method, Content-Type header, - /// and URL path. URL path matching supports the * wildcard character - /// - public struct HttpRequestSignature : IEquatable - { - private string method; - private string contentType; - private string path; - - /// HTTP method - public string Method - { - get { return method; } - set - { - if (!String.IsNullOrEmpty(value)) method = value.ToLower(); - else method = String.Empty; - } - } - /// HTTP Content-Type - public string ContentType - { - get { return contentType; } - set - { - if (!String.IsNullOrEmpty(value)) contentType = value.ToLower(); - else contentType = String.Empty; - } - } - /// Relative URL path - public string Path - { - get { return path; } - set - { - if (!String.IsNullOrEmpty(value)) path = value; - else path = String.Empty; - } - } - - /// - /// Constructor - /// - /// HTTP request to build a signature for - public HttpRequestSignature(HttpListenerContext context) - { - method = contentType = path = String.Empty; - - Method = context.Request.HttpMethod; - ContentType = context.Request.ContentType; - Path = context.Request.RawUrl; - } - - /// - /// Test if two HTTP request signatures contain exactly the same data - /// - /// Signature to test against - /// True if the contents of both signatures are identical, - /// otherwise false - public bool ExactlyEquals(HttpRequestSignature signature) - { - return (method.Equals(signature.Method) && contentType.Equals(signature.ContentType) && path.Equals(signature.Path)); - } - - /// - /// Does pattern matching to determine if an incoming HTTP request - /// matches a given pattern. Equals can only be called on an incoming - /// request; the pattern to match against is the parameter - /// - /// The pattern to test against this request - /// True if the request matches the given pattern, otherwise - /// false - public override bool Equals(object obj) - { - return (obj is HttpRequestSignature) ? this == (HttpRequestSignature)obj : false; - } - - /// - /// Does pattern matching to determine if an incoming HTTP request - /// matches a given pattern. Equals can only be called on an incoming - /// request; the pattern to match against is the parameter - /// - /// The pattern to test against this request - /// True if the request matches the given pattern, otherwise - /// false - public bool Equals(HttpRequestSignature pattern) - { - return (this == pattern); - } - - public override int GetHashCode() - { - int hash = (method != null) ? method.GetHashCode() : 0; - hash ^= (contentType != null) ? contentType.GetHashCode() : 0; - hash ^= (path != null) ? path.GetHashCode() : 0; - return hash; - } - - public override string ToString() - { - return String.Format("{0} {1} Content-Type: {2}", method, path, contentType); - } - - /// - /// Does pattern matching to determine if an incoming HTTP request - /// matches a given pattern. The incoming request must be on the - /// left-hand side, and the pattern to match against must be on the - /// right-hand side - /// - /// The incoming HTTP request signature - /// The pattern to test against the incoming request - /// True if the request matches the given pattern, otherwise - /// false - public static bool operator ==(HttpRequestSignature request, HttpRequestSignature pattern) - { - bool methodMatch = (String.IsNullOrEmpty(pattern.Method) || request.Method.Equals(pattern.Method)); - bool contentTypeMatch = (String.IsNullOrEmpty(pattern.ContentType) || request.ContentType.Equals(pattern.ContentType)); - bool pathMatch = true; - - if (methodMatch && contentTypeMatch && !String.IsNullOrEmpty(pattern.Path)) - pathMatch = Regex.IsMatch(request.Path, pattern.Path, RegexOptions.IgnoreCase); - - return (methodMatch && contentTypeMatch && pathMatch); - } - - /// - /// Does pattern matching to determine if an incoming HTTP request - /// matches a given pattern. The incoming request must be on the - /// left-hand side, and the pattern to match against must be on the - /// right-hand side - /// - /// The incoming HTTP request signature - /// The pattern to test against the incoming request - /// True if the request does not match the given pattern, otherwise - /// false - public static bool operator !=(HttpRequestSignature request, HttpRequestSignature pattern) - { - return !(request == pattern); - } - } -} diff --git a/OpenMetaverse/Capabilities/HttpServer.cs b/OpenMetaverse/Capabilities/HttpServer.cs deleted file mode 100644 index f6959a71..00000000 --- a/OpenMetaverse/Capabilities/HttpServer.cs +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2008, openmetaverse.org - * All rights reserved. - * - * - 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. - * - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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; - -namespace OpenMetaverse.Capabilities -{ - public class HttpServer - { - #region HttpRequestHandler struct - - public struct HttpRequestHandler : IEquatable - { - public HttpRequestSignature Signature; - public HttpRequestCallback Callback; - - public HttpRequestHandler(HttpRequestSignature signature, HttpRequestCallback callback) - { - Signature = signature; - Callback = callback; - } - - public override bool Equals(object obj) - { - return (obj is HttpRequestHandler) ? this.Signature == ((HttpRequestHandler)obj).Signature : false; - } - - public override int GetHashCode() - { - return Signature.GetHashCode(); - } - - public override string ToString() - { - return Signature.ToString(); - } - - public bool Equals(HttpRequestHandler handler) - { - return this.Signature == handler.Signature; - } - } - - #endregion HttpRequestHandler struct - - public delegate bool HttpRequestCallback(ref HttpListenerContext context); - - HttpListener server = null; - HttpRequestHandler[] requestHandlers = new HttpRequestHandler[0]; - bool isRunning = false; - - public HttpServer(int port, bool ssl) - { - server = new HttpListener(); - - if (ssl) - server.Prefixes.Add(String.Format("https://+:{0}/", port)); - else - server.Prefixes.Add(String.Format("http://+:{0}/", port)); - } - - public HttpServer(List prefixes) - { - server = new HttpListener(); - - for (int i = 0; i < prefixes.Count; i++) - server.Prefixes.Add(prefixes[i]); - } - - public void AddHandler(string method, string contentType, string path, HttpRequestCallback callback) - { - HttpRequestSignature signature = new HttpRequestSignature(); - signature.Method = method; - signature.ContentType = contentType; - signature.Path = path; - AddHandler(new HttpRequestHandler(signature, callback)); - } - - public void AddHandler(HttpRequestHandler handler) - { - HttpRequestHandler[] newHandlers = new HttpRequestHandler[requestHandlers.Length + 1]; - - for (int i = 0; i < requestHandlers.Length; i++) - newHandlers[i] = requestHandlers[i]; - newHandlers[requestHandlers.Length] = handler; - - // CLR guarantees this is an atomic operation - requestHandlers = newHandlers; - } - - public void RemoveHandler(HttpRequestHandler handler) - { - HttpRequestHandler[] newHandlers = new HttpRequestHandler[requestHandlers.Length - 1]; - - int j = 0; - for (int i = 0; i < requestHandlers.Length; i++) - if (!requestHandlers[i].Signature.ExactlyEquals(handler.Signature)) - newHandlers[j++] = handler; - - // CLR guarantees this is an atomic operation - requestHandlers = newHandlers; - } - - public void Start() - { - server.Start(); - server.BeginGetContext(BeginGetContextCallback, server); - isRunning = true; - } - - public void Stop() - { - isRunning = false; - try { server.Stop(); } - catch (ObjectDisposedException) { } - } - - protected void BeginGetContextCallback(IAsyncResult result) - { - HttpListenerContext context = null; - - // Retrieve the incoming request - try { context = server.EndGetContext(result); } - catch (Exception) { } - - if (isRunning) - { - // Immediately start listening again - try { server.BeginGetContext(BeginGetContextCallback, server); } - catch (Exception) - { - // Something went wrong, can't resume listening. Bail out now - // since this is a shutdown (whether it was meant to be or not) - isRunning = false; - return; - } - - // Process the incoming request - if (context != null) - { - // Create a request signature - HttpRequestSignature signature = new HttpRequestSignature(context); - - // Look for a signature match in our handlers - for (int i = 0; i < requestHandlers.Length; i++) - { - HttpRequestHandler handler = requestHandlers[i]; - - if (handler.Signature != null && signature == handler.Signature) - { - bool closeConnection = true; - - // Request signature matched, handle it - try { closeConnection = handler.Callback(ref context); } - catch (Exception ex) { Logger.Log("Exception in HTTP handler: " + ex.Message, Helpers.LogLevel.Error, ex); } - - if (closeConnection) - { - // Close the connection - try { context.Response.Close(); } - catch (Exception) { } - } - - return; - } - } - - // No registered handler matched this request's signature. Send a 404 - try - { - context.Response.StatusCode = (int)HttpStatusCode.NotFound; - context.Response.StatusDescription = String.Format( - "No request handler registered for Method=\"{0}\", Content-Type=\"{1}\", Path=\"{2}\"", - signature.Method, signature.ContentType, signature.Path); - context.Response.Close(); - } - catch (Exception) { } - } - } - } - } -} diff --git a/OpenMetaverse/Caps.cs b/OpenMetaverse/Caps.cs index 71c72fc1..9c189252 100644 --- a/OpenMetaverse/Caps.cs +++ b/OpenMetaverse/Caps.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Text; using System.Threading; using OpenMetaverse.StructuredData; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; namespace OpenMetaverse { diff --git a/OpenMetaverse/GridManager.cs b/OpenMetaverse/GridManager.cs index cc6b0e61..2c7ab3df 100644 --- a/OpenMetaverse/GridManager.cs +++ b/OpenMetaverse/GridManager.cs @@ -30,7 +30,7 @@ using System.Collections; using System.Collections.Generic; using System.Threading; using OpenMetaverse.StructuredData; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; using OpenMetaverse.Packets; namespace OpenMetaverse diff --git a/OpenMetaverse/InventoryManager.cs b/OpenMetaverse/InventoryManager.cs index 51747c0f..212e3be9 100644 --- a/OpenMetaverse/InventoryManager.cs +++ b/OpenMetaverse/InventoryManager.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using System.Threading; using System.Text; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; using OpenMetaverse.StructuredData; using OpenMetaverse.Packets; diff --git a/OpenMetaverse/Login.cs b/OpenMetaverse/Login.cs index eeb535fa..e183be35 100644 --- a/OpenMetaverse/Login.cs +++ b/OpenMetaverse/Login.cs @@ -32,7 +32,7 @@ using System.Net; using System.Xml; using System.Security.Cryptography.X509Certificates; using OpenMetaverse.StructuredData; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; using OpenMetaverse.Packets; namespace OpenMetaverse diff --git a/Programs/GridImageUpload/frmGridImageUpload.cs b/Programs/GridImageUpload/frmGridImageUpload.cs index 2d2a5f61..925ca67b 100644 --- a/Programs/GridImageUpload/frmGridImageUpload.cs +++ b/Programs/GridImageUpload/frmGridImageUpload.cs @@ -7,7 +7,7 @@ using System.Text; using System.Windows.Forms; using System.Threading; using OpenMetaverse; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; using OpenMetaverse.Imaging; namespace GridImageUpload diff --git a/Programs/GridProxy/GridProxy.cs b/Programs/GridProxy/GridProxy.cs index e2e6e434..111db74e 100644 --- a/Programs/GridProxy/GridProxy.cs +++ b/Programs/GridProxy/GridProxy.cs @@ -45,7 +45,7 @@ using System.Threading; using System.Xml; using Nwc.XmlRpc; using OpenMetaverse; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; using OpenMetaverse.StructuredData; using OpenMetaverse.Packets; diff --git a/Programs/Simian/Extensions/AccountManager.cs b/Programs/Simian/Extensions/AccountManager.cs index c47d6ffb..bb47b509 100644 --- a/Programs/Simian/Extensions/AccountManager.cs +++ b/Programs/Simian/Extensions/AccountManager.cs @@ -80,7 +80,9 @@ namespace Simian.Extensions accounts.ForEach(delegate(Agent agent) { - array.Add(OSD.SerializeMembers(agent)); + OSDMap agentMap = OSD.SerializeMembers(agent); + agentMap["AgentID"] = OSD.FromUUID(agent.Avatar.ID); + array.Add(agentMap); }); Logger.Log(String.Format("Serializing the agent store with {0} entries", accounts.Count), @@ -99,8 +101,10 @@ namespace Simian.Extensions { Agent agent = new Agent(); object agentRef = (object)agent; - OSD.DeserializeMembers(ref agentRef, (OSDMap)array[i]); + OSDMap map = array[i] as OSDMap; + OSD.DeserializeMembers(ref agentRef, map); agent = (Agent)agentRef; + agent.Avatar.ID = map["AgentID"].AsUUID(); accounts.Add(agent.FullName, agent.Avatar.ID, agent); } diff --git a/Programs/Simian/Extensions/FriendManager.cs b/Programs/Simian/Extensions/FriendManager.cs index 66de15c7..1511ac25 100644 --- a/Programs/Simian/Extensions/FriendManager.cs +++ b/Programs/Simian/Extensions/FriendManager.cs @@ -12,7 +12,6 @@ namespace Simian.Extensions public FriendManager() { - } public void Start(Simian server) diff --git a/Programs/Simian/Extensions/UDPManager.cs b/Programs/Simian/Extensions/UDPManager.cs index 85681879..ebecc543 100644 --- a/Programs/Simian/Extensions/UDPManager.cs +++ b/Programs/Simian/Extensions/UDPManager.cs @@ -90,7 +90,8 @@ namespace Simian public void Stop() { - udpServer.Stop(); + if (udpServer != null) + udpServer.Stop(); } public void AddClient(Agent agent, IPEndPoint endpoint) diff --git a/Programs/Simian/Simian.cs b/Programs/Simian/Simian.cs index 5d5b0805..82d6f3a4 100644 --- a/Programs/Simian/Simian.cs +++ b/Programs/Simian/Simian.cs @@ -8,8 +8,9 @@ using System.Threading; using System.Reflection; using ExtensionLoader; using ExtensionLoader.Config; +using HttpServer; using OpenMetaverse; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; namespace Simian { @@ -25,7 +26,7 @@ namespace Simian public int HttpPort = 8002; public string DataDir = "SimianData/"; - public HttpServer HttpServer; + public WebServer HttpServer; public IniConfigSource ConfigFile; public ulong RegionHandle; @@ -39,6 +40,7 @@ namespace Simian public IInventoryProvider Inventory; public IParcelProvider Parcels; public IMeshingProvider Mesher; + //public ICapabilitiesProvider Capabilities; // Persistent extensions public List PersistentExtensions = new List(); @@ -67,7 +69,7 @@ namespace Simian return false; } - InitHttpServer(HttpPort, false); + InitHttpServer(HttpPort, true); RegionHandle = Utils.UIntsToLong(REGION_X, REGION_Y); @@ -130,7 +132,7 @@ namespace Simian void InitHttpServer(int port, bool ssl) { - HttpServer = new HttpServer(port, ssl); + HttpServer = new WebServer(IPAddress.Any, port); // Login webpage HEAD request, used to check if the login webpage is alive HttpServer.AddHandler("head", null, "^/$", LoginWebpageHeadHandler); @@ -147,23 +149,21 @@ namespace Simian HttpServer.Start(); } - bool LoginWebpageHeadHandler(ref HttpListenerContext context) + bool LoginWebpageHeadHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) { - context.Response.StatusCode = (int)HttpStatusCode.OK; - context.Response.StatusDescription = "OK"; return true; } - bool LoginWebpageGetHandler(ref HttpListenerContext context) + bool LoginWebpageGetHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) { string pageContent = "Simian

Welcome to Simian

"; byte[] pageData = Encoding.UTF8.GetBytes(pageContent); - context.Response.OutputStream.Write(pageData, 0, pageData.Length); - context.Response.OutputStream.Close(); + response.Body.Write(pageData, 0, pageData.Length); + response.Body.Flush(); return true; } - bool LoginXmlRpcPostHandler(ref HttpListenerContext context) + bool LoginXmlRpcPostHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) { string firstName = String.Empty, @@ -176,7 +176,7 @@ namespace Simian try { // Parse the incoming XML - XmlReader reader = XmlReader.Create(context.Request.InputStream); + XmlReader reader = XmlReader.Create(request.Body); reader.ReadStartElement("methodCall"); { @@ -247,12 +247,14 @@ namespace Simian reader.Close(); LoginResponseData responseData = HandleLogin(firstName, lastName, password, start, version, channel); + if (responseData.Success) responseData.InventorySkeleton = Inventory.CreateInventorySkeleton(responseData.AgentID); - XmlWriter writer = XmlWriter.Create(context.Response.OutputStream); + + XmlWriter writer = XmlWriter.Create(response.Body); responseData.ToXmlRpc(writer); + writer.Flush(); writer.Close(); - context.Response.Close(); } catch (Exception ex) { @@ -262,11 +264,11 @@ namespace Simian return true; } - bool LoginLLSDPostHandler(ref HttpListenerContext context) + bool LoginLLSDPostHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) { string body = String.Empty; - using (StreamReader reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding)) + using (StreamReader reader = new StreamReader(request.Body, request.ContentEncoding)) { body = reader.ReadToEnd(); } diff --git a/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs b/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs index b70f9b92..015d6e28 100644 --- a/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs @@ -4,7 +4,7 @@ using System.IO; using System.Threading; using System.Drawing; using OpenMetaverse; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; using OpenMetaverse.Imaging; namespace OpenMetaverse.TestClient diff --git a/Programs/importprimscript/importprimscript.cs b/Programs/importprimscript/importprimscript.cs index c7a630ef..61d19fa5 100644 --- a/Programs/importprimscript/importprimscript.cs +++ b/Programs/importprimscript/importprimscript.cs @@ -4,7 +4,7 @@ using System.Threading; using System.IO; using System.Drawing; using OpenMetaverse; -using OpenMetaverse.Capabilities; +using OpenMetaverse.Http; using OpenMetaverse.Imaging; namespace importprimscript diff --git a/bin/Mono.Security.dll b/bin/Mono.Security.dll new file mode 100644 index 00000000..c027c19f Binary files /dev/null and b/bin/Mono.Security.dll differ diff --git a/prebuild.xml b/prebuild.xml index e2e8aa98..f03e3b0f 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -31,6 +31,47 @@ false + + + + + + + ../bin/ + + + + + ../bin/ + + + + ../bin/ + + + + + + + + + + ../bin/ + + + + + ../bin/ + + + + ../bin/ + + + + + + @@ -75,6 +116,32 @@ + + + + + ../bin/ + + + + + ../bin/ + OpenMetaverse.Http.XML + + + + ../bin/ + + + + + + + + + + + @@ -97,6 +164,7 @@ + @@ -137,6 +205,7 @@ + @@ -166,25 +235,6 @@ - - - - - ../bin/ - - - - - ../bin/ - - - - ../bin/ - - - - - @@ -204,6 +254,7 @@ + @@ -375,6 +426,7 @@ + @@ -506,6 +558,7 @@ + @@ -727,6 +780,7 @@ + @@ -750,9 +804,11 @@ + +