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

@ -38,8 +38,10 @@ using Nini.Config;
namespace OpenSim.ApplicationPlugins.Rest.Inventory namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
public class Rest public class Rest
{ {
internal static readonly log4net.ILog Log = internal static readonly log4net.ILog Log =
log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@ -53,7 +55,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
/// RestHandler class during start-up. /// RestHandler class during start-up.
/// </summary> /// </summary>
internal static RestHandler Plugin = null; internal static IRestHandler Plugin = null;
internal static OpenSimBase main = null; internal static OpenSimBase main = null;
internal static CommunicationsManager Comms = null; internal static CommunicationsManager Comms = null;
internal static IInventoryServices InventoryServices = null; internal static IInventoryServices InventoryServices = null;
@ -66,10 +68,47 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
internal static bool Secure = true; internal static bool Secure = true;
internal static bool ExtendedEscape = true; internal static bool ExtendedEscape = true;
internal static bool DumpAsset = false; internal static bool DumpAsset = false;
internal static bool Fill = true;
internal static bool FlushEnabled = true;
internal static string Realm = "REST"; 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 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 internal static string MsgId
{ {
get { return Plugin.MsgId; } get { return Plugin.MsgId; }
@ -203,53 +242,97 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public const int HttpStatusCodeGatewayTimeout = 504; public const int HttpStatusCodeGatewayTimeout = 504;
public const int HttpStatusCodeHttpVersionError = 505; 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) // 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 static readonly string[] HttpStatusDescArray = {
public const string HttpStatusDescSwitchingProtocols = "Switching Protocols"; // 101 "Continue Request",
"Switching Protocols",
public const string HttpStatusDescOK = "OK"; "OK",
public const string HttpStatusDescCreated = "CREATED"; "CREATED",
public const string HttpStatusDescAccepted = "ACCEPTED"; "ACCEPTED",
public const string HttpStatusDescNonAuthoritative = "NON-AUTHORITATIVE INFORMATION"; "NON-AUTHORITATIVE INFORMATION",
public const string HttpStatusDescNoContent = "NO CONTENT"; "NO CONTENT",
public const string HttpStatusDescResetContent = "RESET CONTENT"; "RESET CONTENT",
public const string HttpStatusDescPartialContent = "PARTIAL CONTENT"; "PARTIAL CONTENT",
"MULTIPLE CHOICES",
public const string HttpStatusDescMultipleChoices = "MULTIPLE CHOICES"; "PERMANENT REDIRECT",
public const string HttpStatusDescPermanentRedirect = "PERMANENT REDIRECT"; "FOUND",
public const string HttpStatusDescFound = "FOUND"; "SEE OTHER",
public const string HttpStatusDescSeeOther = "SEE OTHER"; "NOT MODIFIED",
public const string HttpStatusDescNotModified = "NOT MODIFIED"; "USE PROXY",
public const string HttpStatusDescUseProxy = "USE PROXY"; "RESERVED CODE 306",
public const string HttpStatusDescReserved306 = "RESERVED CODE 306"; "TEMPORARY REDIRECT",
public const string HttpStatusDescTemporaryRedirect = "TEMPORARY REDIRECT"; "BAD REQUEST",
"NOT AUTHORIZED",
public const string HttpStatusDescBadRequest = "BAD REQUEST"; "PAYMENT REQUIRED",
public const string HttpStatusDescNotAuthorized = "NOT AUTHORIZED"; "FORBIDDEN",
public const string HttpStatusDescPaymentRequired = "PAYMENT REQUIRED"; "NOT FOUND",
public const string HttpStatusDescForbidden = "FORBIDDEN"; "METHOD NOT ALLOWED",
public const string HttpStatusDescNotFound = "NOT FOUND"; "NOT ACCEPTABLE",
public const string HttpStatusDescMethodNotAllowed = "METHOD NOT ALLOWED"; "PROXY AUTHENTICATION REQUIRED",
public const string HttpStatusDescNotAcceptable = "NOT ACCEPTABLE"; "TIMEOUT",
public const string HttpStatusDescProxyAuthenticate = "PROXY AUTHENTICATION REQUIRED"; "CONFLICT",
public const string HttpStatusDescTimeOut = "TIMEOUT"; "GONE",
public const string HttpStatusDescConflict = "CONFLICT"; "LENGTH REQUIRED",
public const string HttpStatusDescGone = "GONE"; "PRECONDITION FAILED",
public const string HttpStatusDescLengthRequired = "LENGTH REQUIRED"; "ENTITY TOO LARGE",
public const string HttpStatusDescPreconditionFailed = "PRECONDITION FAILED"; "URI TOO LARGE",
public const string HttpStatusDescEntityTooLarge = "ENTITY TOO LARGE"; "UNSUPPORTED MEDIA",
public const string HttpStatusDescUriTooLarge = "URI TOO LARGE"; "RANGE NOT SATISFIED",
public const string HttpStatusDescUnsupportedMedia = "UNSUPPORTED MEDIA"; "EXPECTATION FAILED",
public const string HttpStatusDescRangeNotSatisfied = "RANGE NOT SATISFIED"; "SERVER ERROR",
public const string HttpStatusDescExpectationFailed = "EXPECTATION FAILED"; "NOT IMPLEMENTED",
"BAD GATEWAY",
public const string HttpStatusDescServerError = "SERVER ERROR"; "SERVICE UNAVAILABLE",
public const string HttpStatusDescNotImplemented = "NOT IMPLEMENTED"; "GATEWAY TIMEOUT",
public const string HttpStatusDescBadGateway = "BAD GATEWAY"; "HTTP VERSION NOT SUPPORTED"
public const string HttpStatusDescServiceUnavailable = "SERVICE UNAVAILABLE"; };
public const string HttpStatusDescGatewayTimeout = "GATEWAY TIMEOUT";
public const string HttpStatusDescHttpVersionError = "HTTP VERSION NOT SUPPORTED";
// HTTP Headers // HTTP Headers
@ -372,40 +455,21 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
} }
return sum; 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 // Nonce management
public static string NonceGenerator() public static string NonceGenerator()
{ {
return StringToBase64(Guid.NewGuid().ToString()); return StringToBase64(CreationDate + Guid.NewGuid().ToString());
} }
// Dump he specified data stream; // Dump he specified data stream;
public static void Dump(byte[] data) public static void Dump(byte[] data)
{ {
char[] buffer = new char[Rest.DumpLineSize]; char[] buffer = new char[Rest.DumpLineSize];
int cc = 0; 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 % Rest.DumpLineSize == 0) Console.Write("\n{0}: ",i.ToString("d8"));
if (i % 4 == 0) Console.Write(" "); if (i % 4 == 0) Console.Write(" ");
// if (i%16 == 0) Console.Write(" ");
Console.Write("{0}",data[i].ToString("x2")); Console.Write("{0}",data[i].ToString("x2"));
@ -431,6 +494,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Console.Write(" |"+(new String(buffer))+"|"); Console.Write(" |"+(new String(buffer))+"|");
cc = 0; cc = 0;
} }
} }
// Finish off any incomplete line // Finish off any incomplete line
@ -440,7 +504,6 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
for (int i = cc ; i < Rest.DumpLineSize; i++) for (int i = cc ; i < Rest.DumpLineSize; i++)
{ {
if (i % 4 == 0) Console.Write(" "); if (i % 4 == 0) Console.Write(" ");
// if (i%16 == 0) Console.Write(" ");
Console.Write(" "); Console.Write(" ");
buffer[i % Rest.DumpLineSize] = ' '; buffer[i % Rest.DumpLineSize] = ' ';
} }
@ -450,13 +513,16 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
Console.Write("\n"); Console.Write("\n");
} }
} }
} }
// Local exception type // Local exception type
public class RestException : Exception public class RestException : Exception
{ {
internal int statusCode; internal int statusCode;
internal string statusDesc; internal string statusDesc;
internal string httpmethod; internal string httpmethod;
@ -466,4 +532,5 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
} }
} }
} }

View File

@ -23,6 +23,7 @@
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * 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 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/ */
using libsecondlife; using libsecondlife;
@ -39,8 +40,10 @@ using OpenSim.Framework.Communications.Cache;
namespace OpenSim.ApplicationPlugins.Rest.Inventory namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
public class RestAssetServices : IRest public class RestAssetServices : IRest
{ {
private bool enabled = false; private bool enabled = false;
private string qPrefix = "assets"; private string qPrefix = "assets";
@ -49,6 +52,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public RestAssetServices() public RestAssetServices()
{ {
Rest.Log.InfoFormat("{0} Asset services initializing", MsgId); Rest.Log.InfoFormat("{0} Asset services initializing", MsgId);
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version); Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
@ -69,6 +73,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
enabled = true; enabled = true;
Rest.Log.InfoFormat("{0} Asset services initialization complete", MsgId); Rest.Log.InfoFormat("{0} Asset services initialization complete", MsgId);
} }
// Post-construction, pre-enabled initialization opportunity // Post-construction, pre-enabled initialization opportunity
@ -106,8 +111,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
private void DoAsset(RequestData rparm) private void DoAsset(RequestData rparm)
{ {
if (!enabled)
return; if (!enabled) return;
AssetRequestData rdata = (AssetRequestData) rparm; AssetRequestData rdata = (AssetRequestData) rparm;
@ -131,7 +136,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
if (!rdata.IsAuthenticated) if (!rdata.IsAuthenticated)
{ {
rdata.Fail(Rest.HttpStatusCodeNotAuthorized, Rest.HttpStatusDescNotAuthorized); rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated"));
} }
} }
catch (RestException e) catch (RestException e)
@ -155,7 +160,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// the parameters we need, fail the request. Parameters do NOT include // the parameters we need, fail the request. Parameters do NOT include
// any supplied query values. // any supplied query values.
if (rdata.parameters.Length > 0) if (rdata.Parameters.Length > 0)
{ {
switch (rdata.method) switch (rdata.method)
{ {
@ -170,24 +175,25 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
default : default :
Rest.Log.WarnFormat("{0} Asset: Method not supported: {1}", Rest.Log.WarnFormat("{0} Asset: Method not supported: {1}",
MsgId, rdata.method); MsgId, rdata.method);
rdata.Fail(Rest.HttpStatusCodeBadRequest, rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method));
Rest.HttpStatusDescBadRequest);
break; break;
} }
} }
else else
{ {
Rest.Log.WarnFormat("{0} Asset: No agent information provided", MsgId); 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); Rest.Log.DebugFormat("{0} REST Asset handler EXIT", MsgId);
} }
#endregion Interface #endregion Interface
private void DoGet(AssetRequestData rdata) private void DoGet(AssetRequestData rdata)
{ {
bool istexture = false; bool istexture = false;
Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method); 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 only parameter we accept is an LLUUID for
// the asset // 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); AssetBase asset = Rest.AssetServices.GetAsset(uuid, istexture);
if (asset != null) 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(); rdata.initXmlWriter();
@ -218,17 +226,18 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
rdata.writer.WriteBase64(asset.Data,0,asset.Data.Length); rdata.writer.WriteBase64(asset.Data,0,asset.Data.Length);
rdata.writer.WriteFullEndElement(); rdata.writer.WriteFullEndElement();
} }
else else
{ {
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path); Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeNotFound, rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
Rest.HttpStatusDescNotFound);
} }
} }
rdata.Complete(); rdata.Complete();
rdata.Respond("Asset " + rdata.method + ": Normal completion"); rdata.Respond("Asset " + rdata.method + ": Normal completion");
} }
private void DoPut(AssetRequestData rdata) private void DoPut(AssetRequestData rdata)
@ -238,7 +247,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// The only parameter we accept is an LLUUID for // The only parameter we accept is an LLUUID for
// the asset // the asset
if (rdata.parameters.Length == 1) if (rdata.Parameters.Length == 1)
{ {
rdata.initXmlReader(); rdata.initXmlReader();
XmlReader xml = rdata.reader; XmlReader xml = rdata.reader;
@ -246,12 +255,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
if (!xml.ReadToFollowing("Asset")) if (!xml.ReadToFollowing("Asset"))
{ {
Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path); Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeBadRequest, rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
Rest.HttpStatusDescBadRequest);
} }
AssetBase asset = new AssetBase(); AssetBase asset = new AssetBase();
asset.ID = rdata.parameters[0]; asset.ID = rdata.Parameters[0];
asset.Name = xml.GetAttribute("name"); asset.Name = xml.GetAttribute("name");
asset.Description = xml.GetAttribute("desc"); asset.Description = xml.GetAttribute("desc");
asset.Type = SByte.Parse(xml.GetAttribute("type")); asset.Type = SByte.Parse(xml.GetAttribute("type"));
@ -264,12 +272,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
else else
{ {
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path); Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeNotFound, rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
Rest.HttpStatusDescNotFound);
} }
rdata.Complete(); rdata.Complete();
rdata.Respond("Asset " + rdata.method + ": Normal completion"); rdata.Respond("Asset " + rdata.method + ": Normal completion");
} }
internal class AssetRequestData : RequestData 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 * 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 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/ */
using System; using System;
@ -34,8 +35,27 @@ using OpenSim.ApplicationPlugins.Rest;
namespace OpenSim.ApplicationPlugins.Rest.Inventory 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> /// <remarks>
/// The handler delegates are not noteworthy. The allocator allows /// The handler delegates are not noteworthy. The allocator allows
/// a given handler to optionally subclass the base RequestData /// a given handler to optionally subclass the base RequestData
@ -43,8 +63,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
/// needed. /// needed.
/// </remarks> /// </remarks>
internal delegate void RestMethodHandler(RequestData rdata); // internal delegate void RestMethodHandler(RequestData rdata);
internal delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response); // internal delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response);
// Handler tables: both stream and REST are supported. The path handlers and their // Handler tables: both stream and REST are supported. The path handlers and their
// respective allocators are stored in separate tables. // respective allocators are stored in separate tables.
@ -76,6 +96,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
static RestHandler() static RestHandler()
{ {
Module[] mods = Assembly.GetExecutingAssembly().GetModules(); Module[] mods = Assembly.GetExecutingAssembly().GetModules();
foreach (Module m in mods) foreach (Module m in mods)
@ -97,6 +118,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
} }
} }
} }
} }
#endregion local static state #endregion local static state
@ -124,6 +146,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
if (!handlersLoaded) if (!handlersLoaded)
{ {
ConstructorInfo ci; ConstructorInfo ci;
Object ht; Object ht;
@ -171,12 +194,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// classes in our assembly and the base // classes in our assembly and the base
// names are protected. // names are protected.
internal string MsgId public string MsgId
{ {
get { return base.MsgID; } get { return base.MsgID; }
} }
internal string RequestId public string RequestId
{ {
get { return base.RequestID; } get { return base.RequestID; }
} }
@ -198,6 +221,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
try try
{ {
// This plugin will only be enabled if the broader // This plugin will only be enabled if the broader
// REST plugin mechanism is enabled. // REST plugin mechanism is enabled.
@ -221,7 +245,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Rest.main = openSim; Rest.main = openSim;
Rest.Plugin = this; Rest.Plugin = this;
Rest.Comms = App.CommunicationsManager; Rest.Comms = Rest.main.CommunicationsManager;
Rest.UserServices = Rest.Comms.UserService; Rest.UserServices = Rest.Comms.UserService;
Rest.InventoryServices = Rest.Comms.InventoryService; Rest.InventoryServices = Rest.Comms.InventoryService;
Rest.AssetServices = Rest.Comms.AssetCache; Rest.AssetServices = Rest.Comms.AssetCache;
@ -234,7 +258,9 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Rest.ExtendedEscape = Rest.Config.GetBoolean("extended-escape",true); Rest.ExtendedEscape = Rest.Config.GetBoolean("extended-escape",true);
Rest.Realm = Rest.Config.GetString("realm","OpenSim REST"); Rest.Realm = Rest.Config.GetString("realm","OpenSim REST");
Rest.DumpAsset = Rest.Config.GetBoolean("dump-asset",false); 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.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.Log.InfoFormat("{0} Authentication is {1}required", MsgId,
(Rest.Authenticate ? "" : "not ")); (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.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId,
(Rest.DumpAsset ? "" : "not ")); (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 // If data dumping is requested, report on the chosen line
// length. // length.
@ -308,6 +339,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgId, e.Message); Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgId, e.Message);
} }
} }
/// <summary> /// <summary>
@ -322,6 +354,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public override void Close() public override void Close()
{ {
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId); Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId);
try try
@ -334,6 +367,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
handler.Close(); handler.Close();
} }
} }
#endregion overriding methods #endregion overriding methods
@ -352,25 +386,57 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
string path = request.RawUrl; string path = request.RawUrl;
Rest.Log.DebugFormat("{0} Match ENTRY", MsgId);
try try
{ {
foreach (string key in pathHandlers.Keys) 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)) if (path.StartsWith(key))
{ {
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 || return ( path.Length == key.Length ||
path.Substring(key.Length,1) == Rest.UrlPathSeparator); path.Substring(key.Length,1) == Rest.UrlPathSeparator);
} }
} }
path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path); path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path);
foreach (string key in streamHandlers.Keys) 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)) 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) catch (Exception e)
{ {
@ -415,8 +481,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
try try
{ {
handled = FindPathHandler(request, response) || handled = ( FindPathHandler(request, response) ||
FindStreamHandler(request, response); FindStreamHandler(request, response) );
} }
catch (Exception e) catch (Exception e)
{ {
@ -430,6 +496,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
Rest.Log.DebugFormat("{0} EXIT", MsgId); Rest.Log.DebugFormat("{0} EXIT", MsgId);
return handled; return handled;
} }
#endregion interface methods #endregion interface methods
@ -477,6 +544,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
} }
return rdata.handled; return rdata.handled;
} }
/// <summary> /// <summary>
@ -489,6 +557,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
public void AddStreamHandler(string httpMethod, string path, RestMethod method) public void AddStreamHandler(string httpMethod, string path, RestMethod method)
{ {
if (!IsEnabled) if (!IsEnabled)
{ {
return; return;
@ -512,6 +581,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgId, path); Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgId, path);
} }
} }
/// <summary> /// <summary>
@ -526,6 +596,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response) internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response)
{ {
RequestData rdata = null; RequestData rdata = null;
string bestMatch = null; string bestMatch = null;
@ -551,6 +622,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
if (!String.IsNullOrEmpty(bestMatch)) if (!String.IsNullOrEmpty(bestMatch))
{ {
rdata = pathAllocators[bestMatch](request, response); rdata = pathAllocators[bestMatch](request, response);
Rest.Log.DebugFormat("{0} Path based REST handler matched with <{1}>", MsgId, bestMatch); Rest.Log.DebugFormat("{0} Path based REST handler matched with <{1}>", MsgId, bestMatch);
@ -567,9 +639,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
Rest.Log.WarnFormat("{0} Request failed: {1}", MsgId, r.Message); Rest.Log.WarnFormat("{0} Request failed: {1}", MsgId, r.Message);
} }
} }
return (rdata == null) ? false : rdata.handled; return (rdata == null) ? false : rdata.handled;
} }
/// <summary> /// <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. /// path as a key. If an entry already exists, it is replaced by the new one.
/// </summary> /// </summary>
internal void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra) public void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra)
{ {
if (!IsEnabled) if (!IsEnabled)
{ {
return; return;
@ -600,6 +675,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
pathHandlers.Add(path, mh); pathHandlers.Add(path, mh);
pathAllocators.Add(path, ra); pathAllocators.Add(path, ra);
} }
} }
} }

View File

@ -140,7 +140,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
if (!rdata.IsAuthenticated) 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) catch (RestException e)
@ -160,10 +161,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
// Check that a test was specified // Check that a test was specified
if (rdata.parameters.Length < 1) if (rdata.Parameters.Length < 1)
{ {
Rest.Log.DebugFormat("{0} Insufficient parameters", MsgId); Rest.Log.DebugFormat("{0} Insufficient parameters", MsgId);
rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest); rdata.Fail(Rest.HttpStatusCodeBadRequest, "not enough parameters");
} }
// Select the test // Select the test
@ -180,8 +181,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
private static bool testsLoaded = false; private static bool testsLoaded = false;
private static List<Type> classes = new List<Type>(); private static List<Type> classes = new List<Type>();
private static List<ITest> tests = new List<ITest>(); private static List<ITest> tests = new List<ITest>();
private static Type[] parms = new Type[1]; private static Type[] parms = new Type[0];
private static Object[] args = new Object[1]; private static Object[] args = new Object[0];
static RestTestServices() static RestTestServices()
{ {
@ -190,12 +191,19 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
{ {
Type[] types = m.GetTypes(); Type[] types = m.GetTypes();
foreach (Type t in types) foreach (Type t in types)
{
try
{ {
if (t.GetInterface("ITest") != null) if (t.GetInterface("ITest") != null)
{ {
classes.Add(t); 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. /// registering itself with this handler.
/// I was not able to make this code work in a constructor. /// I was not able to make this code work in a constructor.
/// </summary> /// </summary>
private void loadTests() private void loadTests()
{ {
lock (tests) lock (tests)
{ {
if (!testsLoaded) if (!testsLoaded)
{ {
parms[0] = this.GetType();
args[0] = this;
ConstructorInfo ci; ConstructorInfo ci;
Object ht; Object ht;
foreach (Type t in classes) foreach (Type t in classes)
{
try
{
if (t.GetInterface("ITest") != null)
{ {
ci = t.GetConstructor(parms); ci = t.GetConstructor(parms);
ht = ci.Invoke(args); ht = ci.Invoke(args);
tests.Add((ITest)ht); 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; testsLoaded = true;
} }
} }
} }
} }
} }