revised GetMesh to not use intermediate base64 coding scheme
it delivers binary and has binary as input. base64 intermediate coding makes no sense. Signed-off-by: BlueWall <jamesh@bluewallgroup.com>inv-download
parent
5a413c1b2f
commit
4de10a45e9
|
@ -25,90 +25,229 @@
|
|||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using log4net;
|
||||
using Nini.Config;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using OpenMetaverse.Imaging;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Servers;
|
||||
using OpenSim.Framework.Servers.HttpServer;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using Caps = OpenSim.Framework.Capabilities.Caps;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
|
||||
namespace OpenSim.Capabilities.Handlers
|
||||
{
|
||||
public class GetMeshHandler : BaseStreamHandler
|
||||
{
|
||||
// private static readonly ILog m_log =
|
||||
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private static readonly ILog m_log =
|
||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private IAssetService m_assetService;
|
||||
|
||||
public GetMeshHandler(string path, IAssetService assService, string name, string description)
|
||||
// TODO: Change this to a config option
|
||||
private string m_RedirectURL = null;
|
||||
|
||||
public GetMeshHandler(string path, IAssetService assService, string name, string description, string redirectURL)
|
||||
: base("GET", path, name, description)
|
||||
{
|
||||
m_assetService = assService;
|
||||
m_RedirectURL = redirectURL;
|
||||
if (m_RedirectURL != null && !m_RedirectURL.EndsWith("/"))
|
||||
m_RedirectURL += "/";
|
||||
}
|
||||
|
||||
protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
||||
{
|
||||
// Try to parse the texture ID from the request URL
|
||||
NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query);
|
||||
string meshStr = query.GetOne("mesh_id");
|
||||
string textureStr = query.GetOne("mesh_id");
|
||||
|
||||
// m_log.DebugFormat("Fetching mesh {0}", meshStr);
|
||||
|
||||
UUID meshID = UUID.Zero;
|
||||
if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID))
|
||||
{
|
||||
if (m_assetService == null)
|
||||
{
|
||||
httpResponse.StatusCode = 404;
|
||||
httpResponse.ContentType = "text/plain";
|
||||
byte[] data = Encoding.UTF8.GetBytes("The asset service is unavailable. So is your mesh.");
|
||||
httpResponse.Body.Write(data, 0, data.Length);
|
||||
return null;
|
||||
m_log.Error("[GETMESH]: Cannot fetch mesh " + textureStr + " without an asset service");
|
||||
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
|
||||
}
|
||||
|
||||
AssetBase mesh = m_assetService.Get(meshID.ToString());
|
||||
UUID meshID;
|
||||
if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out meshID))
|
||||
{
|
||||
// OK, we have an array with preferred formats, possibly with only one entry
|
||||
|
||||
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
|
||||
AssetBase mesh;
|
||||
|
||||
if (!String.IsNullOrEmpty(m_RedirectURL))
|
||||
{
|
||||
// Only try to fetch locally cached meshes. Misses are redirected
|
||||
mesh = m_assetService.GetCached(meshID.ToString());
|
||||
|
||||
if (mesh != null)
|
||||
{
|
||||
if (mesh.Type == (SByte)AssetType.Mesh)
|
||||
if (mesh.Type != (sbyte)AssetType.Mesh)
|
||||
{
|
||||
byte[] data = mesh.Data;
|
||||
httpResponse.Body.Write(data, 0, data.Length);
|
||||
httpResponse.ContentType = "application/vnd.ll.mesh";
|
||||
httpResponse.StatusCode = 200;
|
||||
}
|
||||
// Optionally add additional mesh types here
|
||||
else
|
||||
{
|
||||
httpResponse.StatusCode = 404;
|
||||
httpResponse.ContentType = "text/plain";
|
||||
byte[] data = Encoding.UTF8.GetBytes("Unfortunately, this asset isn't a mesh.");
|
||||
httpResponse.Body.Write(data, 0, data.Length);
|
||||
httpResponse.KeepAlive = false;
|
||||
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
|
||||
}
|
||||
WriteMeshData(httpRequest, httpResponse, mesh);
|
||||
}
|
||||
else
|
||||
{
|
||||
httpResponse.StatusCode = 404;
|
||||
httpResponse.ContentType = "text/plain";
|
||||
byte[] data = Encoding.UTF8.GetBytes("Your Mesh wasn't found. Sorry!");
|
||||
httpResponse.Body.Write(data, 0, data.Length);
|
||||
httpResponse.KeepAlive = false;
|
||||
string textureUrl = m_RedirectURL + "?mesh_id="+ meshID.ToString();
|
||||
m_log.Debug("[GETMESH]: Redirecting mesh request to " + textureUrl);
|
||||
httpResponse.StatusCode = (int)OSHttpStatusCode.RedirectMovedPermanently;
|
||||
httpResponse.RedirectLocation = textureUrl;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else // no redirect
|
||||
{
|
||||
// try the cache
|
||||
mesh = m_assetService.GetCached(meshID.ToString());
|
||||
|
||||
if (mesh == null)
|
||||
{
|
||||
// Fetch locally or remotely. Misses return a 404
|
||||
mesh = m_assetService.Get(meshID.ToString());
|
||||
|
||||
if (mesh != null)
|
||||
{
|
||||
if (mesh.Type != (sbyte)AssetType.Mesh)
|
||||
{
|
||||
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
|
||||
return null;
|
||||
}
|
||||
WriteMeshData(httpRequest, httpResponse, mesh);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else // it was on the cache
|
||||
{
|
||||
if (mesh.Type != (sbyte)AssetType.Mesh)
|
||||
{
|
||||
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
|
||||
return null;
|
||||
}
|
||||
WriteMeshData(httpRequest, httpResponse, mesh);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// not found
|
||||
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.Warn("[GETTEXTURE]: Failed to parse a mesh_id from GetMesh request: " + httpRequest.Url);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void WriteMeshData(IOSHttpRequest request, IOSHttpResponse response, AssetBase texture)
|
||||
{
|
||||
string range = request.Headers.GetOne("Range");
|
||||
|
||||
if (!String.IsNullOrEmpty(range))
|
||||
{
|
||||
// Range request
|
||||
int start, end;
|
||||
if (TryParseRange(range, out start, out end))
|
||||
{
|
||||
// Before clamping start make sure we can satisfy it in order to avoid
|
||||
// sending back the last byte instead of an error status
|
||||
if (start >= texture.Data.Length)
|
||||
{
|
||||
response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
|
||||
response.ContentType = texture.Metadata.ContentType;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle the case where no second range value was given. This is equivalent to requesting
|
||||
// the rest of the entity.
|
||||
if (end == -1)
|
||||
end = int.MaxValue;
|
||||
|
||||
end = Utils.Clamp(end, 0, texture.Data.Length - 1);
|
||||
start = Utils.Clamp(start, 0, end);
|
||||
int len = end - start + 1;
|
||||
|
||||
if (0 == start && len == texture.Data.Length)
|
||||
{
|
||||
response.StatusCode = (int)System.Net.HttpStatusCode.OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
|
||||
response.AddHeader("Content-Range", String.Format("bytes {0}-{1}/{2}", start, end, texture.Data.Length));
|
||||
}
|
||||
|
||||
response.ContentLength = len;
|
||||
response.ContentType = "application/vnd.ll.mesh";
|
||||
|
||||
response.Body.Write(texture.Data, start, len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.Warn("[GETMESH]: Malformed Range header: " + range);
|
||||
response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Full content request
|
||||
response.StatusCode = (int)System.Net.HttpStatusCode.OK;
|
||||
response.ContentLength = texture.Data.Length;
|
||||
response.ContentType = "application/vnd.ll.mesh";
|
||||
response.Body.Write(texture.Data, 0, texture.Data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a range header.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html,
|
||||
/// this obeys range headers with two values (e.g. 533-4165) and no second value (e.g. 533-).
|
||||
/// Where there is no value, -1 is returned.
|
||||
/// FIXME: Need to cover the case where only a second value is specified (e.g. -4165), probably by returning -1
|
||||
/// for start.</remarks>
|
||||
/// <returns></returns>
|
||||
/// <param name='header'></param>
|
||||
/// <param name='start'>Start of the range. Undefined if this was not a number.</param>
|
||||
/// <param name='end'>End of the range. Will be -1 if no end specified. Undefined if there was a raw string but this was not a number.</param>
|
||||
private bool TryParseRange(string header, out int start, out int end)
|
||||
{
|
||||
start = end = 0;
|
||||
|
||||
if (header.StartsWith("bytes="))
|
||||
{
|
||||
string[] rangeValues = header.Substring(6).Split('-');
|
||||
|
||||
if (rangeValues.Length == 2)
|
||||
{
|
||||
if (!Int32.TryParse(rangeValues[0], out start))
|
||||
return false;
|
||||
|
||||
string rawEnd = rangeValues[1];
|
||||
|
||||
if (rawEnd == "")
|
||||
{
|
||||
end = -1;
|
||||
return true;
|
||||
}
|
||||
else if (Int32.TryParse(rawEnd, out end))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start = end = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,16 +25,13 @@
|
|||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Nini.Config;
|
||||
using OpenSim.Server.Base;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Framework.Servers.HttpServer;
|
||||
using OpenSim.Server.Handlers.Base;
|
||||
using OpenSim.Framework.Servers;
|
||||
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework.Servers.HttpServer;
|
||||
using OpenSim.Server.Base;
|
||||
using OpenSim.Server.Handlers.Base;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using System;
|
||||
|
||||
namespace OpenSim.Capabilities.Handlers
|
||||
{
|
||||
|
@ -65,8 +62,15 @@ namespace OpenSim.Capabilities.Handlers
|
|||
if (m_AssetService == null)
|
||||
throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName));
|
||||
|
||||
string rurl = serverConfig.GetString("GetMeshRedirectURL");
|
||||
|
||||
server.AddStreamHandler(
|
||||
new GetMeshHandler("/CAPS/GetMesh/" /*+ UUID.Random() */, m_AssetService, "GetMesh", null));
|
||||
new GetTextureHandler("/CAPS/GetMesh/" /*+ UUID.Random() */, m_AssetService, "GetMesh", null, rurl));
|
||||
|
||||
rurl = serverConfig.GetString("GetMesh2RedirectURL");
|
||||
|
||||
server.AddStreamHandler(
|
||||
new GetTextureHandler("/CAPS/GetMesh2/" /*+ UUID.Random() */, m_AssetService, "GetMesh2", null, rurl));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,6 +57,9 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
private IAssetService m_AssetService;
|
||||
private bool m_Enabled = true;
|
||||
private string m_URL;
|
||||
private string m_URL2;
|
||||
private string m_RedirectURL = null;
|
||||
private string m_RedirectURL2 = null;
|
||||
|
||||
#region Region Module interfaceBase Members
|
||||
|
||||
|
@ -74,7 +77,18 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
m_URL = config.GetString("Cap_GetMesh", string.Empty);
|
||||
// Cap doesn't exist
|
||||
if (m_URL != string.Empty)
|
||||
{
|
||||
m_Enabled = true;
|
||||
m_RedirectURL = config.GetString("GetMeshRedirectURL");
|
||||
}
|
||||
|
||||
m_URL2 = config.GetString("Cap_GetMesh2", string.Empty);
|
||||
// Cap doesn't exist
|
||||
if (m_URL2 != string.Empty)
|
||||
{
|
||||
m_Enabled = true;
|
||||
m_RedirectURL2 = config.GetString("GetMesh2RedirectURL");
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRegion(Scene pScene)
|
||||
|
@ -110,29 +124,46 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
|
||||
#endregion
|
||||
|
||||
|
||||
public void RegisterCaps(UUID agentID, Caps caps)
|
||||
{
|
||||
|
||||
//caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
|
||||
if (m_URL == "localhost")
|
||||
{
|
||||
// m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
|
||||
|
||||
UUID capID = UUID.Random();
|
||||
bool getMeshRegistered = false;
|
||||
|
||||
if (m_URL == string.Empty)
|
||||
{
|
||||
|
||||
}
|
||||
else if (m_URL == "localhost")
|
||||
{
|
||||
getMeshRegistered = true;
|
||||
caps.RegisterHandler(
|
||||
"GetMesh",
|
||||
new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh", agentID.ToString()));
|
||||
new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh", agentID.ToString(), m_RedirectURL));
|
||||
}
|
||||
else
|
||||
{
|
||||
// m_log.DebugFormat("[GETTEXTURE]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName);
|
||||
IExternalCapsModule handler = m_scene.RequestModuleInterface<IExternalCapsModule>();
|
||||
if (handler != null)
|
||||
handler.RegisterExternalUserCapsHandler(agentID, caps, "GetMesh", m_URL);
|
||||
else
|
||||
caps.RegisterHandler("GetMesh", m_URL);
|
||||
}
|
||||
|
||||
if(m_URL2 == string.Empty)
|
||||
{
|
||||
|
||||
}
|
||||
else if (m_URL2 == "localhost")
|
||||
{
|
||||
if (!getMeshRegistered)
|
||||
{
|
||||
caps.RegisterHandler(
|
||||
"GetMesh2",
|
||||
new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh2", agentID.ToString(), m_RedirectURL2));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
caps.RegisterHandler("GetMesh2", m_URL2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue