From: Alan Webb <alan_webb@us.ibm.com>

cleanups of the REST inventory code.
0.6.0-stable
Dr Scofield 2008-08-20 10:11:11 +00:00
parent 2b83169c4b
commit 5e83a75815
6 changed files with 1609 additions and 931 deletions

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@
* 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;
@ -38,9 +38,11 @@ using Nini.Config;
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
public class Rest
{
internal static readonly log4net.ILog Log =
internal static readonly log4net.ILog Log =
log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
internal static bool DEBUG = Log.IsDebugEnabled;
@ -53,7 +55,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
/// RestHandler class during start-up.
/// </summary>
internal static RestHandler Plugin = null;
internal static IRestHandler Plugin = null;
internal static OpenSimBase main = null;
internal static CommunicationsManager Comms = null;
internal static IInventoryServices InventoryServices = null;
@ -66,10 +68,47 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
internal static bool Secure = true;
internal static bool ExtendedEscape = true;
internal static bool DumpAsset = false;
internal static bool Fill = true;
internal static bool FlushEnabled = true;
internal static string Realm = "REST";
internal static int CreationDate = (int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
internal static int DumpLineSize = 32; // Should be a multiple of 16 or (possibly) 4
/// <summary>
/// HTTP requires that status information be generated for PUT
/// and POST opertaions. This is in support of that. The
/// operation verb gets substituted into the first string,
/// and the completion code is inserted into the tail. The
/// strings are put here to encourage consistency.
/// </summary>
internal static string statusHead = "<html><body><title>{0} status</title><break>";
internal static string statusTail = "</body></html>";
internal static Dictionary<int,string> HttpStatusDesc;
static Rest()
{
HttpStatusDesc = new Dictionary<int,string>();
if (HttpStatusCodeArray.Length != HttpStatusDescArray.Length)
{
Log.ErrorFormat("{0} HTTP Status Code and Description arrays do not match");
throw new Exception("HTTP Status array discrepancy");
}
// Repackage the data into something more tractable. The sparse
// nature of HTTP return codes makes an array a bad choice.
for (int i=0; i<HttpStatusCodeArray.Length; i++)
{
HttpStatusDesc.Add(HttpStatusCodeArray[i], HttpStatusDescArray[i]);
}
}
internal static int CreationDate
{
get { return (int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; }
}
internal static string MsgId
{
get { return Plugin.MsgId; }
@ -104,7 +143,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
/// supported by all servers. See Respond
/// to see how these are handled.
/// </summary>
// REST AGENT 1.0 interpretations
public const string GET = "get"; // information retrieval - server state unchanged
public const string HEAD = "head"; // same as get except only the headers are returned.
@ -136,7 +175,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public static readonly char C_PERIOD = '.';
public static readonly char C_COMMA = ',';
public static readonly char C_DQUOTE = '"';
public static readonly string CS_SPACE = " ";
public static readonly string CS_SLASH = "/";
public static readonly string CS_PATHSEP = "/";
@ -145,7 +184,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public static readonly string CS_PERIOD = ".";
public static readonly string CS_COMMA = ",";
public static readonly string CS_DQUOTE = "\"";
public static readonly char[] CA_SPACE = { C_SPACE };
public static readonly char[] CA_SLASH = { C_SLASH };
public static readonly char[] CA_PATHSEP = { C_PATHSEP };
@ -203,53 +242,97 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public const int HttpStatusCodeGatewayTimeout = 504;
public const int HttpStatusCodeHttpVersionError = 505;
public static readonly int[] HttpStatusCodeArray = {
HttpStatusCodeContinue,
HttpStatusCodeSwitchingProtocols,
HttpStatusCodeOK,
HttpStatusCodeCreated,
HttpStatusCodeAccepted,
HttpStatusCodeNonAuthoritative,
HttpStatusCodeNoContent,
HttpStatusCodeResetContent,
HttpStatusCodePartialContent,
HttpStatusCodeMultipleChoices,
HttpStatusCodePermanentRedirect,
HttpStatusCodeFound,
HttpStatusCodeSeeOther,
HttpStatusCodeNotModified,
HttpStatusCodeUseProxy,
HttpStatusCodeReserved306,
HttpStatusCodeTemporaryRedirect,
HttpStatusCodeBadRequest,
HttpStatusCodeNotAuthorized,
HttpStatusCodePaymentRequired,
HttpStatusCodeForbidden,
HttpStatusCodeNotFound,
HttpStatusCodeMethodNotAllowed,
HttpStatusCodeNotAcceptable,
HttpStatusCodeProxyAuthenticate,
HttpStatusCodeTimeOut,
HttpStatusCodeConflict,
HttpStatusCodeGone,
HttpStatusCodeLengthRequired,
HttpStatusCodePreconditionFailed,
HttpStatusCodeEntityTooLarge,
HttpStatusCodeUriTooLarge,
HttpStatusCodeUnsupportedMedia,
HttpStatusCodeRangeNotSatsified,
HttpStatusCodeExpectationFailed,
HttpStatusCodeServerError,
HttpStatusCodeNotImplemented,
HttpStatusCodeBadGateway,
HttpStatusCodeServiceUnavailable,
HttpStatusCodeGatewayTimeout,
HttpStatusCodeHttpVersionError
};
// HTTP Status Descriptions (in status code order)
// This array must be kept strictly consistent with respect
// to the status code array above.
public const string HttpStatusDescContinue = "Continue Request"; // 100
public const string HttpStatusDescSwitchingProtocols = "Switching Protocols"; // 101
public const string HttpStatusDescOK = "OK";
public const string HttpStatusDescCreated = "CREATED";
public const string HttpStatusDescAccepted = "ACCEPTED";
public const string HttpStatusDescNonAuthoritative = "NON-AUTHORITATIVE INFORMATION";
public const string HttpStatusDescNoContent = "NO CONTENT";
public const string HttpStatusDescResetContent = "RESET CONTENT";
public const string HttpStatusDescPartialContent = "PARTIAL CONTENT";
public const string HttpStatusDescMultipleChoices = "MULTIPLE CHOICES";
public const string HttpStatusDescPermanentRedirect = "PERMANENT REDIRECT";
public const string HttpStatusDescFound = "FOUND";
public const string HttpStatusDescSeeOther = "SEE OTHER";
public const string HttpStatusDescNotModified = "NOT MODIFIED";
public const string HttpStatusDescUseProxy = "USE PROXY";
public const string HttpStatusDescReserved306 = "RESERVED CODE 306";
public const string HttpStatusDescTemporaryRedirect = "TEMPORARY REDIRECT";
public const string HttpStatusDescBadRequest = "BAD REQUEST";
public const string HttpStatusDescNotAuthorized = "NOT AUTHORIZED";
public const string HttpStatusDescPaymentRequired = "PAYMENT REQUIRED";
public const string HttpStatusDescForbidden = "FORBIDDEN";
public const string HttpStatusDescNotFound = "NOT FOUND";
public const string HttpStatusDescMethodNotAllowed = "METHOD NOT ALLOWED";
public const string HttpStatusDescNotAcceptable = "NOT ACCEPTABLE";
public const string HttpStatusDescProxyAuthenticate = "PROXY AUTHENTICATION REQUIRED";
public const string HttpStatusDescTimeOut = "TIMEOUT";
public const string HttpStatusDescConflict = "CONFLICT";
public const string HttpStatusDescGone = "GONE";
public const string HttpStatusDescLengthRequired = "LENGTH REQUIRED";
public const string HttpStatusDescPreconditionFailed = "PRECONDITION FAILED";
public const string HttpStatusDescEntityTooLarge = "ENTITY TOO LARGE";
public const string HttpStatusDescUriTooLarge = "URI TOO LARGE";
public const string HttpStatusDescUnsupportedMedia = "UNSUPPORTED MEDIA";
public const string HttpStatusDescRangeNotSatisfied = "RANGE NOT SATISFIED";
public const string HttpStatusDescExpectationFailed = "EXPECTATION FAILED";
public const string HttpStatusDescServerError = "SERVER ERROR";
public const string HttpStatusDescNotImplemented = "NOT IMPLEMENTED";
public const string HttpStatusDescBadGateway = "BAD GATEWAY";
public const string HttpStatusDescServiceUnavailable = "SERVICE UNAVAILABLE";
public const string HttpStatusDescGatewayTimeout = "GATEWAY TIMEOUT";
public const string HttpStatusDescHttpVersionError = "HTTP VERSION NOT SUPPORTED";
public static readonly string[] HttpStatusDescArray = {
"Continue Request",
"Switching Protocols",
"OK",
"CREATED",
"ACCEPTED",
"NON-AUTHORITATIVE INFORMATION",
"NO CONTENT",
"RESET CONTENT",
"PARTIAL CONTENT",
"MULTIPLE CHOICES",
"PERMANENT REDIRECT",
"FOUND",
"SEE OTHER",
"NOT MODIFIED",
"USE PROXY",
"RESERVED CODE 306",
"TEMPORARY REDIRECT",
"BAD REQUEST",
"NOT AUTHORIZED",
"PAYMENT REQUIRED",
"FORBIDDEN",
"NOT FOUND",
"METHOD NOT ALLOWED",
"NOT ACCEPTABLE",
"PROXY AUTHENTICATION REQUIRED",
"TIMEOUT",
"CONFLICT",
"GONE",
"LENGTH REQUIRED",
"PRECONDITION FAILED",
"ENTITY TOO LARGE",
"URI TOO LARGE",
"UNSUPPORTED MEDIA",
"RANGE NOT SATISFIED",
"EXPECTATION FAILED",
"SERVER ERROR",
"NOT IMPLEMENTED",
"BAD GATEWAY",
"SERVICE UNAVAILABLE",
"GATEWAY TIMEOUT",
"HTTP VERSION NOT SUPPORTED"
};
// HTTP Headers
@ -309,7 +392,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public const string AS_DIGEST = "Digest";
/// Supported Digest algorithms
public const string Digest_MD5 = "MD5"; // assumedd efault if omitted
public const string Digest_MD5Sess = "MD5-sess";
@ -357,7 +440,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
int val = 0;
int sum = 0;
string tmp = null;
if (hex != null)
{
tmp = hex.ToLower();
@ -372,40 +455,21 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
}
return sum;
}
public static string Int2Hex8(int val)
{
string res = String.Empty;
for (int i = 0; i < 8; i++)
{
res = (val % 16) + res;
val = val / 16;
}
return res;
}
public static string ToHex32(int val)
{
return String.Empty;
}
public static string ToHex32(string val)
{
return String.Empty;
}
// Nonce management
public static string NonceGenerator()
{
return StringToBase64(Guid.NewGuid().ToString());
return StringToBase64(CreationDate + Guid.NewGuid().ToString());
}
// Dump he specified data stream;
public static void Dump(byte[] data)
{
char[] buffer = new char[Rest.DumpLineSize];
int cc = 0;
@ -415,7 +479,6 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
if (i % Rest.DumpLineSize == 0) Console.Write("\n{0}: ",i.ToString("d8"));
if (i % 4 == 0) Console.Write(" ");
// if (i%16 == 0) Console.Write(" ");
Console.Write("{0}",data[i].ToString("x2"));
@ -431,6 +494,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Console.Write(" |"+(new String(buffer))+"|");
cc = 0;
}
}
// Finish off any incomplete line
@ -440,30 +504,33 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
for (int i = cc ; i < Rest.DumpLineSize; i++)
{
if (i % 4 == 0) Console.Write(" ");
// if (i%16 == 0) Console.Write(" ");
Console.Write(" ");
Console.Write(" ");
buffer[i % Rest.DumpLineSize] = ' ';
}
Console.WriteLine(" |"+(new String(buffer))+"|");
}
else
{
Console.Write("\n");
Console.Write("\n");
}
}
}
}
}
// Local exception type
public class RestException : Exception
{
internal int statusCode;
internal string statusDesc;
internal string httpmethod;
internal string httppath;
public RestException(string msg) : base(msg)
{
public RestException(string msg) : base(msg)
{
}
}
}

View File

@ -23,6 +23,7 @@
* 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 libsecondlife;
@ -39,8 +40,10 @@ using OpenSim.Framework.Communications.Cache;
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
public class RestAssetServices : IRest
{
private bool enabled = false;
private string qPrefix = "assets";
@ -49,6 +52,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public RestAssetServices()
{
Rest.Log.InfoFormat("{0} Asset services initializing", MsgId);
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
@ -69,6 +73,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
enabled = true;
Rest.Log.InfoFormat("{0} Asset services initialization complete", MsgId);
}
// Post-construction, pre-enabled initialization opportunity
@ -79,7 +84,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
}
// Called by the plug-in to halt REST processing. Local processing is
// disabled, and control blocks until all current processing has
// disabled, and control blocks until all current processing has
// completed. No new processing will be started
public void Close()
@ -106,14 +111,14 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
private void DoAsset(RequestData rparm)
{
if (!enabled)
return;
if (!enabled) return;
AssetRequestData rdata = (AssetRequestData) rparm;
Rest.Log.DebugFormat("{0} REST Asset handler ENTRY", MsgId);
// Now that we know this is a serious attempt to
// Now that we know this is a serious attempt to
// access inventory data, we should find out who
// is asking, and make sure they are authorized
// to do so. We need to validate the caller's
@ -124,14 +129,14 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// With the present HTTP server we can't use the
// builtin authentication mechanisms because they
// would be enforced for all in-bound requests.
// Instead we look at the headers ourselves and
// Instead we look at the headers ourselves and
// handle authentication directly.
try
{
if (!rdata.IsAuthenticated)
{
rdata.Fail(Rest.HttpStatusCodeNotAuthorized, Rest.HttpStatusDescNotAuthorized);
rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated"));
}
}
catch (RestException e)
@ -139,13 +144,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
{
Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
rdata.request.Headers.Get("Authorization"));
}
else
{
Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
rdata.request.Headers.Get("Authorization"));
}
throw (e);
@ -155,7 +160,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// the parameters we need, fail the request. Parameters do NOT include
// any supplied query values.
if (rdata.parameters.Length > 0)
if (rdata.Parameters.Length > 0)
{
switch (rdata.method)
{
@ -168,26 +173,27 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
case "post" :
case "delete" :
default :
Rest.Log.WarnFormat("{0} Asset: Method not supported: {1}",
Rest.Log.WarnFormat("{0} Asset: Method not supported: {1}",
MsgId, rdata.method);
rdata.Fail(Rest.HttpStatusCodeBadRequest,
Rest.HttpStatusDescBadRequest);
rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method));
break;
}
}
else
{
Rest.Log.WarnFormat("{0} Asset: No agent information provided", MsgId);
rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest);
rdata.Fail(Rest.HttpStatusCodeBadRequest, "no agent information provided");
}
Rest.Log.DebugFormat("{0} REST Asset handler EXIT", MsgId);
}
#endregion Interface
private void DoGet(AssetRequestData rdata)
{
bool istexture = false;
Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method);
@ -195,14 +201,16 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// The only parameter we accept is an LLUUID for
// the asset
if (rdata.parameters.Length == 1)
if (rdata.Parameters.Length == 1)
{
LLUUID uuid = new LLUUID(rdata.parameters[0]);
LLUUID uuid = new LLUUID(rdata.Parameters[0]);
AssetBase asset = Rest.AssetServices.GetAsset(uuid, istexture);
if (asset != null)
{
Rest.Log.DebugFormat("{0} Asset located <{1}>", MsgId, rdata.parameters[0]);
Rest.Log.DebugFormat("{0} Asset located <{1}>", MsgId, rdata.Parameters[0]);
rdata.initXmlWriter();
@ -218,17 +226,18 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
rdata.writer.WriteBase64(asset.Data,0,asset.Data.Length);
rdata.writer.WriteFullEndElement();
}
else
{
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeNotFound,
Rest.HttpStatusDescNotFound);
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
}
}
rdata.Complete();
rdata.Respond("Asset " + rdata.method + ": Normal completion");
}
private void DoPut(AssetRequestData rdata)
@ -238,7 +247,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// The only parameter we accept is an LLUUID for
// the asset
if (rdata.parameters.Length == 1)
if (rdata.Parameters.Length == 1)
{
rdata.initXmlReader();
XmlReader xml = rdata.reader;
@ -246,12 +255,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
if (!xml.ReadToFollowing("Asset"))
{
Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeBadRequest,
Rest.HttpStatusDescBadRequest);
rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
}
AssetBase asset = new AssetBase();
asset.ID = rdata.parameters[0];
asset.ID = rdata.Parameters[0];
asset.Name = xml.GetAttribute("name");
asset.Description = xml.GetAttribute("desc");
asset.Type = SByte.Parse(xml.GetAttribute("type"));
@ -264,12 +272,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
else
{
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeNotFound,
Rest.HttpStatusDescNotFound);
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
}
rdata.Complete();
rdata.Respond("Asset " + rdata.method + ": Normal completion");
}
internal class AssetRequestData : RequestData
@ -279,5 +287,6 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
}
}
}
}

View File

@ -23,6 +23,7 @@
* 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;
@ -34,8 +35,27 @@ using OpenSim.ApplicationPlugins.Rest;
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
public class RestHandler : RestPlugin, IHttpAgentHandler
/// <remarks>
/// The class signature reveals the roles that RestHandler plays.
///
/// [1] It is a sub-class of RestPlugin. It inherits and extends
/// the functionality of this class, constraining it to the
/// specific needs of this REST implementation. This relates
/// to the plug-in mechanism supported by OpenSim, the specifics
/// of which are mostly hidden by RestPlugin.
/// [2] IRestHandler describes the interface that this class
/// exports to service implementations. This is the services
/// management interface.
/// [3] IHttpAgentHandler describes the interface that is exported
/// to the BaseHttpServer in support of this particular HTTP
/// processing model. This is the request interface of the
/// handler.
/// </remarks>
public class RestHandler : RestPlugin, IRestHandler, IHttpAgentHandler
{
/// <remarks>
/// The handler delegates are not noteworthy. The allocator allows
/// a given handler to optionally subclass the base RequestData
@ -43,8 +63,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
/// needed.
/// </remarks>
internal delegate void RestMethodHandler(RequestData rdata);
internal delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response);
// internal delegate void RestMethodHandler(RequestData rdata);
// internal delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response);
// Handler tables: both stream and REST are supported. The path handlers and their
// respective allocators are stored in separate tables.
@ -64,10 +84,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
/// <summary>
/// This static initializer scans the ASSEMBLY for classes that
/// export the IRest interface and builds a list of them. These
/// are later activated by the handler. To add a new handler it
/// are later activated by the handler. To add a new handler it
/// is only necessary to create a new services class that implements
/// the IRest interface, and recompile the handler. This gives
/// all of the build-time flexibility of a modular approach
/// all of the build-time flexibility of a modular approach
/// while not introducing yet-another module loader. Note that
/// multiple assembles can still be built, each with its own set
/// of handlers. Examples of services classes are RestInventoryServices
@ -76,12 +96,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
static RestHandler()
{
Module[] mods = Assembly.GetExecutingAssembly().GetModules();
foreach (Module m in mods)
{
Type[] types = m.GetTypes();
foreach (Type t in types)
foreach (Type t in types)
{
try
{
@ -97,6 +118,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
}
}
}
}
#endregion local static state
@ -105,13 +127,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
/// <summary>
/// This routine loads all of the handlers discovered during
/// instance initialization.
/// instance initialization.
/// A table of all loaded and successfully constructed handlers
/// is built, and this table is then used by the constructor to
/// initialize each of the handlers in turn.
/// NOTE: The loading process does not automatically imply that
/// the handler has registered any kind of an interface, that
/// may be (optionally) done by the handler either during
/// the handler has registered any kind of an interface, that
/// may be (optionally) done by the handler either during
/// construction, or during initialization.
///
/// I was not able to make this code work within a constructor
@ -124,6 +146,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
if (!handlersLoaded)
{
ConstructorInfo ci;
Object ht;
@ -154,8 +177,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// Name is used to differentiate the message header.
public override string Name
{
public override string Name
{
get { return "HANDLER"; }
}
@ -168,15 +191,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// We have to rename these because we want
// to be able to share the values with other
// classes in our assembly and the base
// classes in our assembly and the base
// names are protected.
internal string MsgId
public string MsgId
{
get { return base.MsgID; }
}
internal string RequestId
public string RequestId
{
get { return base.RequestID; }
}
@ -198,6 +221,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
try
{
// This plugin will only be enabled if the broader
// REST plugin mechanism is enabled.
@ -208,7 +232,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// IsEnabled is implemented by the base class and
// reflects an overall RestPlugin status
if (!IsEnabled)
if (!IsEnabled)
{
Rest.Log.WarnFormat("{0} Plugins are disabled", MsgId);
return;
@ -221,7 +245,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Rest.main = openSim;
Rest.Plugin = this;
Rest.Comms = App.CommunicationsManager;
Rest.Comms = Rest.main.CommunicationsManager;
Rest.UserServices = Rest.Comms.UserService;
Rest.InventoryServices = Rest.Comms.InventoryService;
Rest.AssetServices = Rest.Comms.AssetCache;
@ -234,7 +258,9 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Rest.ExtendedEscape = Rest.Config.GetBoolean("extended-escape",true);
Rest.Realm = Rest.Config.GetString("realm","OpenSim REST");
Rest.DumpAsset = Rest.Config.GetBoolean("dump-asset",false);
Rest.Fill = Rest.Config.GetBoolean("path-fill",true);
Rest.DumpLineSize = Rest.Config.GetInt("dump-line-size",32);
Rest.FlushEnabled = Rest.Config.GetBoolean("flush-on-error",true);
Rest.Log.InfoFormat("{0} Authentication is {1}required", MsgId,
(Rest.Authenticate ? "" : "not "));
@ -248,6 +274,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId,
(Rest.DumpAsset ? "" : "not "));
// The supplied prefix MUST be absolute
if (Rest.Prefix.Substring(0,1) != Rest.UrlPathSeparator)
Rest.Prefix = Rest.UrlPathSeparator+Rest.Prefix;
// If data dumping is requested, report on the chosen line
// length.
@ -257,15 +288,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Rest.DumpLineSize);
}
// Load all of the handlers present in the
// Load all of the handlers present in the
// assembly
// In principle, as we're an application plug-in,
// most of what needs to be done could be done using
// static resources, however the Open Sim plug-in
// model makes this an instance, so that's what we
// static resources, however the Open Sim plug-in
// model makes this an instance, so that's what we
// need to be.
// There is only one Communications manager per
// There is only one Communications manager per
// server, and by inference, only one each of the
// user, asset, and inventory servers. So we can cache
// those using a static initializer.
@ -308,12 +339,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgId, e.Message);
}
}
/// <summary>
/// In the interests of efficiency, and because we cannot determine whether
/// or not this instance will actually be harvested, we clobber the only
/// anchoring reference to the working state for this plug-in. What the
/// anchoring reference to the working state for this plug-in. What the
/// call to close does is irrelevant to this class beyond knowing that it
/// can nullify the reference when it returns.
/// To make sure everything is copacetic we make sure the primary interface
@ -322,6 +354,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public override void Close()
{
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId);
try
@ -329,11 +362,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
RemoveAgentHandler(Rest.Name, this);
}
catch (KeyNotFoundException){}
foreach (IRest handler in handlers)
{
handler.Close();
}
}
#endregion overriding methods
@ -352,25 +386,57 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
string path = request.RawUrl;
Rest.Log.DebugFormat("{0} Match ENTRY", MsgId);
try
{
foreach (string key in pathHandlers.Keys)
{
Rest.Log.DebugFormat("{0} Match testing {1} against agent prefix <{2}>", MsgId, path, key);
// Note that Match will not necessarily find the handler that will
// actually be used - it does no test for the "closest" fit. It
// simply reflects that at least one possible handler exists.
if (path.StartsWith(key))
{
return (path.Length == key.Length ||
path.Substring(key.Length, 1) == Rest.UrlPathSeparator);
Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key);
// This apparently odd evaluation is needed to prevent a match
// on anything other than a URI token boundary. Otherwise we
// may match on URL's that were not intended for this handler.
return ( path.Length == key.Length ||
path.Substring(key.Length,1) == Rest.UrlPathSeparator);
}
}
path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path);
foreach (string key in streamHandlers.Keys)
{
Rest.Log.DebugFormat("{0} Match testing {1} against stream prefix <{2}>", MsgId, path, key);
// Note that Match will not necessarily find the handler that will
// actually be used - it does no test for the "closest" fit. It
// simply reflects that at least one possible handler exists.
if (path.StartsWith(key))
{
return true;
Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key);
// This apparently odd evaluation is needed to prevent a match
// on anything other than a URI token boundary. Otherwise we
// may match on URL's that were not intended for this handler.
return ( path.Length == key.Length ||
path.Substring(key.Length,1) == Rest.UrlPathSeparator);
}
}
}
catch (Exception e)
{
@ -404,7 +470,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
for (int i = 0; i < request.Headers.Count; i++)
{
Rest.Log.DebugFormat("{0} Header [{1}] : <{2}> = <{3}>",
Rest.Log.DebugFormat("{0} Header [{1}] : <{2}> = <{3}>",
MsgId, i, request.Headers.GetKey(i), request.Headers.Get(i));
}
Rest.Log.DebugFormat("{0} URI: {1}", MsgId, request.RawUrl);
@ -415,8 +481,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
try
{
handled = FindPathHandler(request, response) ||
FindStreamHandler(request, response);
handled = ( FindPathHandler(request, response) ||
FindStreamHandler(request, response) );
}
catch (Exception e)
{
@ -430,6 +496,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Rest.Log.DebugFormat("{0} EXIT", MsgId);
return handled;
}
#endregion interface methods
@ -477,6 +544,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
}
return rdata.handled;
}
/// <summary>
@ -489,12 +557,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public void AddStreamHandler(string httpMethod, string path, RestMethod method)
{
if (!IsEnabled)
{
return;
}
if (!path.StartsWith(Rest.Prefix))
if (!path.StartsWith(Rest.Prefix))
{
path = String.Format("{0}{1}", Rest.Prefix, path);
}
@ -512,6 +581,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgId, path);
}
}
/// <summary>
@ -526,9 +596,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response)
{
RequestData rdata = null;
string bestMatch = null;
if (!IsEnabled)
{
return false;
@ -551,6 +622,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
if (!String.IsNullOrEmpty(bestMatch))
{
rdata = pathAllocators[bestMatch](request, response);
Rest.Log.DebugFormat("{0} Path based REST handler matched with <{1}>", MsgId, bestMatch);
@ -559,7 +631,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
pathHandlers[bestMatch](rdata);
}
// A plugin generated error indicates a request-related error
// that has been handled by the plugin.
@ -567,9 +639,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
Rest.Log.WarnFormat("{0} Request failed: {1}", MsgId, r.Message);
}
}
return (rdata == null) ? false : rdata.handled;
}
/// <summary>
@ -577,8 +651,9 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
/// path as a key. If an entry already exists, it is replaced by the new one.
/// </summary>
internal void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra)
public void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra)
{
if (!IsEnabled)
{
return;
@ -600,6 +675,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
pathHandlers.Add(path, mh);
pathAllocators.Add(path, ra);
}
}
}

View File

@ -140,7 +140,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
if (!rdata.IsAuthenticated)
{
rdata.Fail(Rest.HttpStatusCodeNotAuthorized, Rest.HttpStatusDescNotAuthorized);
rdata.Fail(Rest.HttpStatusCodeNotAuthorized,
String.Format("user \"{0}\" could not be authenticated", rdata.userName));
}
}
catch (RestException e)
@ -160,10 +161,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// Check that a test was specified
if (rdata.parameters.Length < 1)
if (rdata.Parameters.Length < 1)
{
Rest.Log.DebugFormat("{0} Insufficient parameters", MsgId);
rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest);
rdata.Fail(Rest.HttpStatusCodeBadRequest, "not enough parameters");
}
// Select the test
@ -180,8 +181,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
private static bool testsLoaded = false;
private static List<Type> classes = new List<Type>();
private static List<ITest> tests = new List<ITest>();
private static Type[] parms = new Type[1];
private static Object[] args = new Object[1];
private static Type[] parms = new Type[0];
private static Object[] args = new Object[0];
static RestTestServices()
{
@ -191,9 +192,16 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Type[] types = m.GetTypes();
foreach (Type t in types)
{
if (t.GetInterface("ITest") != null)
try
{
classes.Add(t);
if (t.GetInterface("ITest") != null)
{
classes.Add(t);
}
}
catch (Exception e)
{
Rest.Log.WarnFormat("[STATIC-TEST] Unable to include test {0} : {1}", t, e.Message);
}
}
}
@ -205,27 +213,38 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
/// registering itself with this handler.
/// I was not able to make this code work in a constructor.
/// </summary>
private void loadTests()
{
lock (tests)
{
if (!testsLoaded)
{
parms[0] = this.GetType();
args[0] = this;
ConstructorInfo ci;
Object ht;
foreach (Type t in classes)
{
ci = t.GetConstructor(parms);
ht = ci.Invoke(args);
tests.Add((ITest)ht);
try
{
if (t.GetInterface("ITest") != null)
{
ci = t.GetConstructor(parms);
ht = ci.Invoke(args);
tests.Add((ITest)ht);
Rest.Log.WarnFormat("{0} Test {1} added", MsgId, t);
}
}
catch (Exception e)
{
Rest.Log.WarnFormat("{0} Unable to load test {1} : {2}", MsgId, t, e.Message);
}
}
testsLoaded = true;
}
}
}
}
}