From: Alan M Webb <awebb@vnet.ibm.com>
This adds REST services for inventory access. It also allows inventory uploads.0.6.0-stable
parent
e4d68a8b64
commit
d40bea4a8e
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSim Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This interface represents the boundary between the general purpose
|
||||
/// REST plugin handling, and the functionally specific handlers. The
|
||||
/// handler knows only to initialzie and terminate all such handlers
|
||||
/// that it finds.
|
||||
/// </summary>
|
||||
|
||||
internal interface IRest
|
||||
{
|
||||
void Initialize();
|
||||
void Close();
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,479 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSim Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Servers;
|
||||
using OpenSim.Framework.Communications;
|
||||
using OpenSim.Framework.Communications.Cache;
|
||||
using Nini.Config;
|
||||
|
||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||
{
|
||||
|
||||
public class Rest
|
||||
{
|
||||
|
||||
internal static readonly log4net.ILog Log =
|
||||
log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
internal static bool DEBUG = Log.IsDebugEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// These values have a single value for the whole
|
||||
/// domain and lifetime of the plugin handler. We
|
||||
/// make them static for ease of reference within
|
||||
/// the assembly. These are initialized by the
|
||||
/// RestHandler class during start-up.
|
||||
/// </summary>
|
||||
|
||||
internal static RestHandler Plugin = null;
|
||||
internal static OpenSimBase main = null;
|
||||
internal static CommunicationsManager Comms = null;
|
||||
internal static IInventoryServices InventoryServices = null;
|
||||
internal static IUserService UserServices = null;
|
||||
internal static AssetCache AssetServices = null;
|
||||
internal static string Prefix = null;
|
||||
internal static IConfig Config = null;
|
||||
internal static string GodKey = null;
|
||||
internal static bool Authenticate = true;
|
||||
internal static bool Secure = true;
|
||||
internal static bool ExtendedEscape = true;
|
||||
internal static bool DumpAsset = false;
|
||||
internal static string Realm = "REST";
|
||||
internal static Dictionary<string,string> Domains = new Dictionary<string,string>();
|
||||
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 string MsgId
|
||||
{
|
||||
get { return Plugin.MsgId; }
|
||||
}
|
||||
|
||||
internal static string RequestId
|
||||
{
|
||||
get { return Plugin.RequestId; }
|
||||
}
|
||||
|
||||
internal static Encoding Encoding = Encoding.UTF8;
|
||||
|
||||
/// <summary>
|
||||
/// Version control for REST implementation. This
|
||||
/// refers to the overall infrastructure represented
|
||||
/// by the following classes
|
||||
/// RequestData
|
||||
/// RequestInventoryPlugin
|
||||
/// Rest
|
||||
/// It does no describe implementation classes such as
|
||||
/// RestInventoryServices, which may morph much more
|
||||
/// often. Such classes ARE dependent upon this however
|
||||
/// and should check it in their Initialize method.
|
||||
/// </summary>
|
||||
|
||||
public static readonly float Version = 1.0F;
|
||||
public const string Name = "REST 1.0";
|
||||
|
||||
/// <summary>
|
||||
/// Currently defined HTTP methods.
|
||||
/// Only GET and HEAD are required to be
|
||||
/// 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.
|
||||
public const string POST = "post"; // Replace the URI designated resource with the entity.
|
||||
public const string PUT = "put"; // Add the entity to the context represented by the URI
|
||||
public const string DELETE = "delete"; // Remove the URI designated resource from the server.
|
||||
|
||||
public const string OPTIONS = "options"; //
|
||||
public const string TRACE = "trace"; //
|
||||
public const string CONNECT = "connect"; //
|
||||
|
||||
// Define this in one place...
|
||||
|
||||
public const string UrlPathSeparator = "/";
|
||||
public const string UrlMethodSeparator = ":";
|
||||
|
||||
// Redirection qualifications
|
||||
|
||||
public const bool PERMANENT = false;
|
||||
public const bool TEMPORARY = true;
|
||||
|
||||
// Constant arrays used by String.Split
|
||||
|
||||
public static readonly char C_SPACE = ' ';
|
||||
public static readonly char C_SLASH = '/';
|
||||
public static readonly char C_PATHSEP = '/';
|
||||
public static readonly char C_COLON = ':';
|
||||
public static readonly char C_PLUS = '+';
|
||||
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 = "/";
|
||||
public static readonly string CS_COLON = ":";
|
||||
public static readonly string CS_PLUS = "+";
|
||||
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 };
|
||||
public static readonly char[] CA_COLON = { C_COLON };
|
||||
public static readonly char[] CA_PERIOD = { C_PERIOD };
|
||||
public static readonly char[] CA_PLUS = { C_PLUS };
|
||||
public static readonly char[] CA_COMMA = { C_COMMA };
|
||||
public static readonly char[] CA_DQUOTE = { C_DQUOTE };
|
||||
|
||||
// HTTP Code Values (in value order)
|
||||
|
||||
public const int HttpStatusCodeContinue = 100;
|
||||
public const int HttpStatusCodeSwitchingProtocols = 101;
|
||||
|
||||
public const int HttpStatusCodeOK = 200;
|
||||
public const int HttpStatusCodeCreated = 201;
|
||||
public const int HttpStatusCodeAccepted = 202;
|
||||
public const int HttpStatusCodeNonAuthoritative = 203;
|
||||
public const int HttpStatusCodeNoContent = 204;
|
||||
public const int HttpStatusCodeResetContent = 205;
|
||||
public const int HttpStatusCodePartialContent = 206;
|
||||
|
||||
public const int HttpStatusCodeMultipleChoices = 300;
|
||||
public const int HttpStatusCodePermanentRedirect = 301;
|
||||
public const int HttpStatusCodeFound = 302;
|
||||
public const int HttpStatusCodeSeeOther = 303;
|
||||
public const int HttpStatusCodeNotModified = 304;
|
||||
public const int HttpStatusCodeUseProxy = 305;
|
||||
public const int HttpStatusCodeReserved306 = 306;
|
||||
public const int HttpStatusCodeTemporaryRedirect = 307;
|
||||
|
||||
public const int HttpStatusCodeBadRequest = 400;
|
||||
public const int HttpStatusCodeNotAuthorized = 401;
|
||||
public const int HttpStatusCodePaymentRequired = 402;
|
||||
public const int HttpStatusCodeForbidden = 403;
|
||||
public const int HttpStatusCodeNotFound = 404;
|
||||
public const int HttpStatusCodeMethodNotAllowed = 405;
|
||||
public const int HttpStatusCodeNotAcceptable = 406;
|
||||
public const int HttpStatusCodeProxyAuthenticate = 407;
|
||||
public const int HttpStatusCodeTimeOut = 408;
|
||||
public const int HttpStatusCodeConflict = 409;
|
||||
public const int HttpStatusCodeGone = 410;
|
||||
public const int HttpStatusCodeLengthRequired = 411;
|
||||
public const int HttpStatusCodePreconditionFailed = 412;
|
||||
public const int HttpStatusCodeEntityTooLarge = 413;
|
||||
public const int HttpStatusCodeUriTooLarge = 414;
|
||||
public const int HttpStatusCodeUnsupportedMedia = 415;
|
||||
public const int HttpStatusCodeRangeNotSatsified = 416;
|
||||
public const int HttpStatusCodeExpectationFailed = 417;
|
||||
|
||||
public const int HttpStatusCodeServerError = 500;
|
||||
public const int HttpStatusCodeNotImplemented = 501;
|
||||
public const int HttpStatusCodeBadGateway = 502;
|
||||
public const int HttpStatusCodeServiceUnavailable = 503;
|
||||
public const int HttpStatusCodeGatewayTimeout = 504;
|
||||
public const int HttpStatusCodeHttpVersionError = 505;
|
||||
|
||||
// HTTP Status Descriptions (in status code order)
|
||||
|
||||
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";
|
||||
|
||||
// HTTP Headers
|
||||
|
||||
public const string HttpHeaderAccept = "Accept";
|
||||
public const string HttpHeaderAcceptCharset = "Accept-Charset";
|
||||
public const string HttpHeaderAcceptEncoding = "Accept-Encoding";
|
||||
public const string HttpHeaderAcceptLanguage = "Accept-Language";
|
||||
public const string HttpHeaderAcceptRanges = "Accept-Ranges";
|
||||
public const string HttpHeaderAge = "Age";
|
||||
public const string HttpHeaderAllow = "Allow";
|
||||
public const string HttpHeaderAuthorization = "Authorization";
|
||||
public const string HttpHeaderCacheControl = "Cache-Control";
|
||||
public const string HttpHeaderConnection = "Connection";
|
||||
public const string HttpHeaderContentEncoding = "Content-Encoding";
|
||||
public const string HttpHeaderContentLanguage = "Content-Language";
|
||||
public const string HttpHeaderContentLength = "Content-Length";
|
||||
public const string HttpHeaderContentLocation = "Content-Location";
|
||||
public const string HttpHeaderContentMD5 = "Content-MD5";
|
||||
public const string HttpHeaderContentRange = "Content-Range";
|
||||
public const string HttpHeaderContentType = "Content-Type";
|
||||
public const string HttpHeaderDate = "Date";
|
||||
public const string HttpHeaderETag = "ETag";
|
||||
public const string HttpHeaderExpect = "Expect";
|
||||
public const string HttpHeaderExpires = "Expires";
|
||||
public const string HttpHeaderFrom = "From";
|
||||
public const string HttpHeaderHost = "Host";
|
||||
public const string HttpHeaderIfMatch = "If-Match";
|
||||
public const string HttpHeaderIfModifiedSince = "If-Modified-Since";
|
||||
public const string HttpHeaderIfNoneMatch = "If-None-Match";
|
||||
public const string HttpHeaderIfRange = "If-Range";
|
||||
public const string HttpHeaderIfUnmodifiedSince = "If-Unmodified-Since";
|
||||
public const string HttpHeaderLastModified = "Last-Modified";
|
||||
public const string HttpHeaderLocation = "Location";
|
||||
public const string HttpHeaderMaxForwards = "Max-Forwards";
|
||||
public const string HttpHeaderPragma = "Pragma";
|
||||
public const string HttpHeaderProxyAuthenticate = "Proxy-Authenticate";
|
||||
public const string HttpHeaderProxyAuthorization = "Proxy-Authorization";
|
||||
public const string HttpHeaderRange = "Range";
|
||||
public const string HttpHeaderReferer = "Referer";
|
||||
public const string HttpHeaderRetryAfter = "Retry-After";
|
||||
public const string HttpHeaderServer = "Server";
|
||||
public const string HttpHeaderTE = "TE";
|
||||
public const string HttpHeaderTrailer = "Trailer";
|
||||
public const string HttpHeaderTransferEncoding = "Transfer-Encoding";
|
||||
public const string HttpHeaderUpgrade = "Upgrade";
|
||||
public const string HttpHeaderUserAgent = "User-Agent";
|
||||
public const string HttpHeaderVary = "Vary";
|
||||
public const string HttpHeaderVia = "Via";
|
||||
public const string HttpHeaderWarning = "Warning";
|
||||
public const string HttpHeaderWWWAuthenticate = "WWW-Authenticate";
|
||||
|
||||
/// <summary>
|
||||
/// Supported authentication schemes
|
||||
/// </summary>
|
||||
|
||||
public const string AS_BASIC = "Basic";
|
||||
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";
|
||||
|
||||
public const string Qop_Auth = "auth";
|
||||
public const string Qop_Int = "auth-int";
|
||||
|
||||
/// Utility routines
|
||||
|
||||
public static string StringToBase64(string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] encData_byte = new byte[str.Length];
|
||||
encData_byte = Encoding.UTF8.GetBytes(str);
|
||||
return Convert.ToBase64String(encData_byte);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static string Base64ToString(string str)
|
||||
{
|
||||
UTF8Encoding encoder = new UTF8Encoding();
|
||||
Decoder utf8Decode = encoder.GetDecoder();
|
||||
try
|
||||
{
|
||||
byte[] todecode_byte = Convert.FromBase64String(str);
|
||||
int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
|
||||
char[] decoded_char = new char[charCount];
|
||||
utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
|
||||
return new String(decoded_char);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private const string hvals = "0123456789abcdef";
|
||||
|
||||
public static int Hex2Int(string hex)
|
||||
{
|
||||
int val = 0;
|
||||
int sum = 0;
|
||||
string tmp = null;
|
||||
|
||||
if (hex != null)
|
||||
{
|
||||
tmp = hex.ToLower();
|
||||
for (int i = 0; i < tmp.Length; i++)
|
||||
{
|
||||
val = hvals.IndexOf(tmp[i]);
|
||||
if (val == -1)
|
||||
break;
|
||||
sum *= 16;
|
||||
sum += val;
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// Dump he specified data stream;
|
||||
|
||||
public static void Dump(byte[] data)
|
||||
{
|
||||
|
||||
char[] buffer = new char[Rest.DumpLineSize];
|
||||
int cc = 0;
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
|
||||
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"));
|
||||
|
||||
if (data[i] < 127 && data[i] > 31)
|
||||
buffer[i % Rest.DumpLineSize] = (char) data[i];
|
||||
else
|
||||
buffer[i % Rest.DumpLineSize] = '.';
|
||||
|
||||
cc++;
|
||||
|
||||
if (i != 0 && (i + 1) % Rest.DumpLineSize == 0)
|
||||
{
|
||||
Console.Write(" |"+(new String(buffer))+"|");
|
||||
cc = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Finish off any incomplete line
|
||||
|
||||
if (cc != 0)
|
||||
{
|
||||
for (int i = cc ; i < Rest.DumpLineSize; i++)
|
||||
{
|
||||
if (i % 4 == 0) Console.Write(" ");
|
||||
// if (i%16 == 0) Console.Write(" ");
|
||||
Console.Write(" ");
|
||||
buffer[i % Rest.DumpLineSize] = ' ';
|
||||
}
|
||||
Console.WriteLine(" |"+(new String(buffer))+"|");
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSim Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
using libsecondlife;
|
||||
using Nini.Config;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Servers;
|
||||
using OpenSim.Framework.Communications;
|
||||
using OpenSim.Framework.Communications.Cache;
|
||||
|
||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||
{
|
||||
|
||||
public class RestAssetServices : IRest
|
||||
{
|
||||
|
||||
private string key = "assets";
|
||||
private bool enabled = false;
|
||||
private string qPrefix = "assets";
|
||||
|
||||
// A simple constructor is used to handle any once-only
|
||||
// initialization of working classes.
|
||||
|
||||
public RestAssetServices(RestHandler p_rest)
|
||||
{
|
||||
|
||||
Rest.Log.InfoFormat("{0} Asset services initializing", MsgId);
|
||||
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
|
||||
|
||||
// Integrate domain
|
||||
|
||||
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
|
||||
{
|
||||
qPrefix = Rest.Prefix + Rest.UrlPathSeparator + qPrefix;
|
||||
}
|
||||
|
||||
// Authentication domain
|
||||
|
||||
Rest.Domains.Add(key,Rest.Config.GetString("asset-domain",qPrefix));
|
||||
|
||||
// Register interface
|
||||
|
||||
Rest.Plugin.AddPathHandler(DoAsset, qPrefix, Allocate);
|
||||
|
||||
// Activate
|
||||
|
||||
enabled = true;
|
||||
|
||||
Rest.Log.InfoFormat("{0} Asset services initialization complete", MsgId);
|
||||
|
||||
}
|
||||
|
||||
// Post-construction, pre-enabled initialization opportunity
|
||||
// Not currently exploited.
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
// Called by the plug-in to halt REST processing. Local processing is
|
||||
// disabled, and control blocks until all current processing has
|
||||
// completed. No new processing will be started
|
||||
|
||||
public void Close()
|
||||
{
|
||||
enabled = false;
|
||||
Rest.Log.InfoFormat("{0} Asset services closing down", MsgId);
|
||||
}
|
||||
|
||||
// Properties
|
||||
|
||||
internal string MsgId
|
||||
{
|
||||
get { return Rest.MsgId; }
|
||||
}
|
||||
|
||||
#region Interface
|
||||
|
||||
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response)
|
||||
{
|
||||
return (RequestData) new AssetRequestData(request, response, qPrefix);
|
||||
}
|
||||
|
||||
// Asset Handler
|
||||
|
||||
private void DoAsset(RequestData rparm)
|
||||
{
|
||||
|
||||
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
|
||||
// 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
|
||||
// identity before revealing anything about the
|
||||
// status quo. Authenticate throws an exception
|
||||
// via Fail if no identity information is present.
|
||||
//
|
||||
// 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
|
||||
// handle authentication directly.
|
||||
|
||||
try
|
||||
{
|
||||
if (!rdata.IsAuthenticated)
|
||||
{
|
||||
rdata.Fail(Rest.HttpStatusCodeNotAuthorized, Rest.HttpStatusDescNotAuthorized);
|
||||
}
|
||||
}
|
||||
catch (RestException e)
|
||||
{
|
||||
if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
|
||||
{
|
||||
Rest.Log.WarnFormat("{0} User not authenticated", 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,
|
||||
rdata.request.Headers.Get("Authorization"));
|
||||
}
|
||||
throw (e);
|
||||
}
|
||||
|
||||
// Remove the prefix and what's left are the parameters. If we don't have
|
||||
// the parameters we need, fail the request. Parameters do NOT include
|
||||
// any supplied query values.
|
||||
|
||||
if (rdata.parameters.Length > 0)
|
||||
{
|
||||
switch (rdata.method)
|
||||
{
|
||||
case "get" :
|
||||
DoGet(rdata);
|
||||
break;
|
||||
case "put" :
|
||||
case "post" :
|
||||
case "delete" :
|
||||
default :
|
||||
Rest.Log.WarnFormat("{0} Asset: Method not supported: {1}",
|
||||
MsgId, rdata.method);
|
||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
||||
Rest.HttpStatusDescBadRequest);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Rest.Log.WarnFormat("{0} Asset: No agent information provided", MsgId);
|
||||
rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// The only parameter we accept is an LLUUID for
|
||||
// the asset
|
||||
|
||||
if (rdata.parameters.Length == 1)
|
||||
{
|
||||
|
||||
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]);
|
||||
|
||||
rdata.initXmlWriter();
|
||||
|
||||
rdata.writer.WriteStartElement(String.Empty,"Asset",String.Empty);
|
||||
|
||||
rdata.writer.WriteAttributeString("id", asset.ID.ToString());
|
||||
rdata.writer.WriteAttributeString("name", asset.Name);
|
||||
rdata.writer.WriteAttributeString("desc", asset.Description);
|
||||
rdata.writer.WriteAttributeString("type", asset.Type.ToString());
|
||||
rdata.writer.WriteAttributeString("invtype", asset.InvType.ToString());
|
||||
rdata.writer.WriteAttributeString("local", asset.Local.ToString());
|
||||
rdata.writer.WriteAttributeString("temporary", asset.Temporary.ToString());
|
||||
|
||||
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.Complete();
|
||||
rdata.Respond("Asset " + rdata.method + ": Normal completion");
|
||||
|
||||
}
|
||||
|
||||
internal class AssetRequestData : RequestData
|
||||
{
|
||||
internal AssetRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
|
||||
: base(request, response, prefix)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSim Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Servers;
|
||||
using OpenSim.ApplicationPlugins.Rest;
|
||||
using Mono.Addins;
|
||||
|
||||
[assembly : Addin]
|
||||
[assembly : AddinDependency("OpenSim", "0.5")]
|
||||
|
||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||
{
|
||||
|
||||
[Extension("/OpenSim/Startup")]
|
||||
|
||||
public class RestHandler : RestPlugin, IHttpAgentHandler
|
||||
{
|
||||
|
||||
#region local static state
|
||||
|
||||
/// <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
|
||||
/// 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
|
||||
/// while not introducing yet-another module loader. Note that
|
||||
/// multiple assembles can still be built, each with its own set
|
||||
/// of handlers.
|
||||
/// </summary>
|
||||
|
||||
private static bool handlersLoaded = false;
|
||||
private static List<Type> classes = new List<Type>();
|
||||
private static List<IRest> handlers = new List<IRest>();
|
||||
private static Type[] parms = new Type[1];
|
||||
private static Object[] args = new Object[1];
|
||||
|
||||
static RestHandler()
|
||||
{
|
||||
Module[] mods = Assembly.GetExecutingAssembly().GetModules();
|
||||
foreach (Module m in mods)
|
||||
{
|
||||
Type[] types = m.GetTypes();
|
||||
foreach (Type t in types)
|
||||
{
|
||||
if (t.GetInterface("IRest") != null)
|
||||
{
|
||||
classes.Add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion local static state
|
||||
|
||||
#region local instance state
|
||||
|
||||
/// <remarks>
|
||||
/// The handler delegate is not noteworthy. The allocator allows
|
||||
/// a given handler to optionally subclass the base RequestData
|
||||
/// structure to carry any locally required per-request state
|
||||
/// needed.
|
||||
/// </remarks>
|
||||
internal delegate void RestMethodHandler(RequestData rdata);
|
||||
internal delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response);
|
||||
|
||||
// Handler tables: both stream and REST are supported
|
||||
|
||||
internal Dictionary<string,RestMethodHandler> pathHandlers = new Dictionary<string,RestMethodHandler>();
|
||||
internal Dictionary<string,RestMethodAllocator> pathAllocators = new Dictionary<string,RestMethodAllocator>();
|
||||
internal Dictionary<string,RestStreamHandler> streamHandlers = new Dictionary<string,RestStreamHandler>();
|
||||
|
||||
/// <summary>
|
||||
/// This routine loads all of the handlers discovered during
|
||||
/// instance initialization. Each handler is responsible for
|
||||
/// registering itself with this handler.
|
||||
/// I was not able to make this code work in a constructor.
|
||||
/// </summary>
|
||||
private void LoadHandlers()
|
||||
{
|
||||
lock(handlers)
|
||||
{
|
||||
if (!handlersLoaded)
|
||||
{
|
||||
parms[0] = this.GetType();
|
||||
args[0] = this;
|
||||
|
||||
ConstructorInfo ci;
|
||||
Object ht;
|
||||
|
||||
foreach (Type t in classes)
|
||||
{
|
||||
ci = t.GetConstructor(parms);
|
||||
ht = ci.Invoke(args);
|
||||
handlers.Add((IRest)ht);
|
||||
}
|
||||
handlersLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion local instance state
|
||||
|
||||
#region overriding properties
|
||||
|
||||
// Used to differentiate the message header.
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return "HANDLER"; }
|
||||
}
|
||||
|
||||
// Used to partition the configuration space.
|
||||
|
||||
public override string ConfigName
|
||||
{
|
||||
get { return "RestHandler"; }
|
||||
}
|
||||
|
||||
// We have to rename these because we want
|
||||
// to be able to share the values with other
|
||||
// classes in our assembly and the base
|
||||
// names are protected.
|
||||
|
||||
internal string MsgId
|
||||
{
|
||||
get { return base.MsgID; }
|
||||
}
|
||||
|
||||
internal string RequestId
|
||||
{
|
||||
get { return base.RequestID; }
|
||||
}
|
||||
|
||||
#endregion overriding properties
|
||||
|
||||
#region overriding methods
|
||||
|
||||
/// <summary>
|
||||
/// This method is called by OpenSimMain immediately after loading the
|
||||
/// plugin and after basic server setup, but before running any server commands.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that entries MUST be added to the active configuration files before
|
||||
/// the plugin can be enabled.
|
||||
/// </remarks>
|
||||
public override void Initialise(OpenSimBase openSim)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
/// <remarks>
|
||||
/// This plugin will only be enabled if the broader
|
||||
/// REST plugin mechanism is enabled.
|
||||
/// </remarks>
|
||||
|
||||
Rest.Log.InfoFormat("{0} Plugin is initializing", MsgID);
|
||||
|
||||
base.Initialise(openSim);
|
||||
|
||||
if (!IsEnabled)
|
||||
{
|
||||
Rest.Log.WarnFormat("{0} Plugins are disabled", MsgID);
|
||||
return;
|
||||
}
|
||||
|
||||
Rest.Log.InfoFormat("{0} Plugin will be enabled", MsgID);
|
||||
|
||||
/// <remarks>
|
||||
/// These are stored in static variables to make
|
||||
/// them easy to reach from anywhere in the assembly.
|
||||
/// </remarks>
|
||||
|
||||
Rest.main = openSim;
|
||||
Rest.Plugin = this;
|
||||
Rest.Comms = App.CommunicationsManager;
|
||||
Rest.UserServices = Rest.Comms.UserService;
|
||||
Rest.InventoryServices = Rest.Comms.InventoryService;
|
||||
Rest.AssetServices = Rest.Comms.AssetCache;
|
||||
Rest.Config = Config;
|
||||
Rest.Prefix = Prefix;
|
||||
Rest.GodKey = GodKey;
|
||||
|
||||
Rest.Authenticate = Rest.Config.GetBoolean("authenticate",true);
|
||||
Rest.Secure = Rest.Config.GetBoolean("secured",true);
|
||||
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.DumpLineSize = Rest.Config.GetInt("dump-line-size",32);
|
||||
|
||||
Rest.Log.InfoFormat("{0} Authentication is {1}required", MsgId,
|
||||
(Rest.Authenticate ? "" : "not "));
|
||||
|
||||
Rest.Log.InfoFormat("{0} Security is {1}enabled", MsgId,
|
||||
(Rest.Authenticate ? "" : "not "));
|
||||
|
||||
Rest.Log.InfoFormat("{0} Extended URI escape processing is {1}enabled", MsgId,
|
||||
(Rest.ExtendedEscape ? "" : "not "));
|
||||
|
||||
Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId,
|
||||
(Rest.DumpAsset ? "" : "not "));
|
||||
|
||||
if (Rest.DumpAsset)
|
||||
{
|
||||
Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId,
|
||||
Rest.DumpLineSize);
|
||||
}
|
||||
|
||||
// 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
|
||||
// need to be.
|
||||
// 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.
|
||||
// We move all of this processing off to another
|
||||
// services class to minimize overlap between function
|
||||
// and infrastructure.
|
||||
|
||||
LoadHandlers();
|
||||
|
||||
/// <remarks>
|
||||
/// The intention of a post construction initializer
|
||||
/// is to allow for setup that is dependent upon other
|
||||
/// activities outside of the agency. We don't currently
|
||||
/// have any, but the design allows for it.
|
||||
/// </remarks>
|
||||
|
||||
foreach (IRest handler in handlers)
|
||||
{
|
||||
handler.Initialize();
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Now that everything is setup we can proceed and
|
||||
/// add this agent to the HTTP server's handler list
|
||||
/// </remarks>
|
||||
|
||||
if (!AddAgentHandler(Rest.Name,this))
|
||||
{
|
||||
Rest.Log.ErrorFormat("{0} Unable to activate handler interface", MsgId);
|
||||
foreach (IRest handler in handlers)
|
||||
{
|
||||
handler.Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
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
|
||||
/// 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
|
||||
/// is disabled by deleting the handler from the HTTP server tables.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
|
||||
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgID);
|
||||
|
||||
try
|
||||
{
|
||||
RemoveAgentHandler(Rest.Name, this);
|
||||
}
|
||||
catch (KeyNotFoundException){}
|
||||
|
||||
foreach (IRest handler in handlers)
|
||||
{
|
||||
handler.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion overriding methods
|
||||
|
||||
#region interface methods
|
||||
|
||||
/// <summary>
|
||||
/// This method is called by the server to match the client, it could
|
||||
/// just return true if we only want one such handler. For now we
|
||||
/// match any explicitly specified client.
|
||||
/// </summary>
|
||||
public bool Match(OSHttpRequest request, OSHttpResponse response)
|
||||
{
|
||||
string path = request.RawUrl;
|
||||
foreach (string key in pathHandlers.Keys)
|
||||
{
|
||||
if (path.StartsWith(key))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (path.StartsWith(key))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Preconditions:
|
||||
/// [1] request != null and is a valid request object
|
||||
/// [2] response != null and is a valid response object
|
||||
/// Behavior is undefined if preconditions are not satisfied.
|
||||
/// </summary>
|
||||
public bool Handle(OSHttpRequest request, OSHttpResponse response)
|
||||
{
|
||||
bool handled;
|
||||
base.MsgID = base.RequestID;
|
||||
|
||||
if (Rest.DEBUG)
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} ENTRY", MsgId);
|
||||
Rest.Log.DebugFormat("{0} Agent: {1}", MsgId, request.UserAgent);
|
||||
Rest.Log.DebugFormat("{0} Method: {1}", MsgId, request.HttpMethod);
|
||||
|
||||
for (int i = 0; i < request.Headers.Count; i++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// If a path handler worked we're done, otherwise try any
|
||||
// available stream handlers too.
|
||||
|
||||
try
|
||||
{
|
||||
handled = FindPathHandler(request, response) ||
|
||||
FindStreamHandler(request, response);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// A raw exception indicates that something we weren't expecting has
|
||||
// happened. This should always reflect a shortcoming in the plugin,
|
||||
// or a failure to satisfy the preconditions.
|
||||
Rest.Log.ErrorFormat("{0} Plugin error: {1}", MsgId, e.Message);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
Rest.Log.DebugFormat("{0} EXIT", MsgId);
|
||||
|
||||
return handled;
|
||||
|
||||
}
|
||||
|
||||
#endregion interface methods
|
||||
|
||||
/// <summary>
|
||||
/// If there is a stream handler registered that can handle the
|
||||
/// request, then fine. If the request is not matched, do
|
||||
/// nothing.
|
||||
/// </summary>
|
||||
|
||||
private bool FindStreamHandler(OSHttpRequest request, OSHttpResponse response)
|
||||
{
|
||||
RequestData rdata = new RequestData(request, response, String.Empty);
|
||||
|
||||
string bestMatch = null;
|
||||
string path = String.Format("{0}:{1}", rdata.method, rdata.path);
|
||||
|
||||
Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path);
|
||||
|
||||
foreach (string pattern in streamHandlers.Keys)
|
||||
{
|
||||
if (path.StartsWith(pattern))
|
||||
{
|
||||
if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
|
||||
{
|
||||
bestMatch = pattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle using the best match available
|
||||
|
||||
if (!String.IsNullOrEmpty(bestMatch))
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} Stream-based handler matched with <{1}>", MsgId, bestMatch);
|
||||
RestStreamHandler handler = streamHandlers[bestMatch];
|
||||
rdata.buffer = handler.Handle(rdata.path, rdata.request.InputStream, rdata.request, rdata.response);
|
||||
rdata.AddHeader(rdata.response.ContentType,handler.ContentType);
|
||||
rdata.Respond("FindStreamHandler Completion");
|
||||
}
|
||||
|
||||
return rdata.handled;
|
||||
|
||||
}
|
||||
|
||||
// Preserves the original handler's semantics
|
||||
|
||||
public new void AddStreamHandler(string httpMethod, string path, RestMethod method)
|
||||
{
|
||||
|
||||
if (!IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!path.StartsWith(Rest.Prefix))
|
||||
{
|
||||
path = String.Format("{0}{1}", Rest.Prefix, path);
|
||||
}
|
||||
|
||||
path = String.Format("{0}{1}{2}", httpMethod, Rest.UrlMethodSeparator, path);
|
||||
|
||||
// Conditionally add to the list
|
||||
|
||||
if (!streamHandlers.ContainsKey(path))
|
||||
{
|
||||
streamHandlers.Add(path, new RestStreamHandler(httpMethod, path, method));
|
||||
Rest.Log.DebugFormat("{0} Added handler for {1}", MsgID, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgID, path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response)
|
||||
{
|
||||
|
||||
RequestData rdata = null;
|
||||
string bestMatch = null;
|
||||
|
||||
if (!IsEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Conditionally add to the list
|
||||
|
||||
Rest.Log.DebugFormat("{0} Checking for path handler for <{1}>", MsgId, request.RawUrl);
|
||||
|
||||
foreach (string pattern in pathHandlers.Keys)
|
||||
{
|
||||
if (request.RawUrl.StartsWith(pattern))
|
||||
{
|
||||
if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
|
||||
{
|
||||
bestMatch = pattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(bestMatch))
|
||||
{
|
||||
|
||||
rdata = pathAllocators[bestMatch](request, response);
|
||||
|
||||
Rest.Log.DebugFormat("{0} Path based REST handler matched with <{1}>", MsgId, bestMatch);
|
||||
|
||||
try
|
||||
{
|
||||
pathHandlers[bestMatch](rdata);
|
||||
}
|
||||
|
||||
// A plugin generated error indicates a request-related error
|
||||
// that has been handled by the plugin.
|
||||
|
||||
catch (RestException r)
|
||||
{
|
||||
Rest.Log.WarnFormat("{0} Request failed: {1}", MsgId, r.Message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (rdata == null) ? false : rdata.handled;
|
||||
|
||||
}
|
||||
|
||||
internal void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra)
|
||||
{
|
||||
if (pathHandlers.ContainsKey(path))
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} Replacing handler for <${1}>", MsgId, path);
|
||||
pathHandlers.Remove(path);
|
||||
}
|
||||
|
||||
if (pathAllocators.ContainsKey(path))
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} Replacing allocator for <${1}>", MsgId, path);
|
||||
pathAllocators.Remove(path);
|
||||
}
|
||||
|
||||
Rest.Log.DebugFormat("{0} Adding path handler for {1}", MsgId, path);
|
||||
|
||||
pathHandlers.Add(path, mh);
|
||||
pathAllocators.Add(path, ra);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
38
prebuild.xml
38
prebuild.xml
|
@ -1198,6 +1198,44 @@
|
|||
<Match pattern="*.cs" recurse="true"/>
|
||||
</Files>
|
||||
</Project>
|
||||
|
||||
<Project name="OpenSim.ApplicationPlugins.Rest.Inventory"
|
||||
path="OpenSim/ApplicationPlugins/Rest/Inventory" type="Library">
|
||||
<Configuration name="Debug">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
<Configuration name="Release">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
|
||||
<ReferencePath>../../../../bin/</ReferencePath>
|
||||
<Reference name="Mono.Addins.dll" />
|
||||
<Reference name="System"/>
|
||||
<Reference name="System.IO"/>
|
||||
<Reference name="System.Xml"/>
|
||||
<Reference name="System.Xml.Serialization"/>
|
||||
<Reference name="libsecondlife.dll" />
|
||||
<Reference name="Nini.dll" />
|
||||
<Reference name="XMLRPC.dll" />
|
||||
<Reference name="OpenSim"/>
|
||||
<Reference name="OpenSim.Region.ClientStack"/>
|
||||
<Reference name="OpenSim.Region.Environment"/>
|
||||
<Reference name="OpenSim.Framework.Communications"/>
|
||||
<Reference name="OpenSim.Framework"/>
|
||||
<Reference name="OpenSim.Framework.Servers"/>
|
||||
<Reference name="OpenSim.Framework.Console"/>
|
||||
<Reference name="OpenSim.ApplicationPlugins.Rest"/>
|
||||
<Reference name="log4net"/>
|
||||
|
||||
<Files>
|
||||
<Match pattern="*.cs" recurse="true"/>
|
||||
</Files>
|
||||
</Project>
|
||||
|
||||
<!-- /REST plugins -->
|
||||
|
||||
<!-- Scene Server API Example Apps -->
|
||||
|
|
Loading…
Reference in New Issue