diff --git a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
index 9fe00e067c..6b67da1e3a 100644
--- a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
+++ b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
@@ -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)
{
- 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
- if (mesh != null)
+ httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
+ AssetBase mesh;
+
+ if (!String.IsNullOrEmpty(m_RedirectURL))
{
- if (mesh.Type == (SByte)AssetType.Mesh)
+ // Only try to fetch locally cached meshes. Misses are redirected
+ mesh = m_assetService.GetCached(meshID.ToString());
+
+ if (mesh != null)
{
- byte[] data = mesh.Data;
- httpResponse.Body.Write(data, 0, data.Length);
- httpResponse.ContentType = "application/vnd.ll.mesh";
- httpResponse.StatusCode = 200;
+ if (mesh.Type != (sbyte)AssetType.Mesh)
+ {
+ httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
+ }
+ WriteMeshData(httpRequest, httpResponse, mesh);
}
- // 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;
+ 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
+ else // no redirect
{
- 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;
+ // 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);
+ }
+ }
+
+ ///
+ /// Parse a range header.
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ /// Start of the range. Undefined if this was not a number.
+ /// End of the range. Will be -1 if no end specified. Undefined if there was a raw string but this was not a number.
+ 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;
+ }
}
}
\ No newline at end of file
diff --git a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs
index 9c538623de..19de3cfea5 100644
--- a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs
+++ b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs
@@ -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));
}
}
}
\ No newline at end of file
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
index 4aecc99243..f57d857594 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
@@ -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)
{
+ UUID capID = UUID.Random();
+ bool getMeshRegistered = false;
- //caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
- if (m_URL == "localhost")
+ if (m_URL == string.Empty)
{
- // m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
-
- UUID capID = UUID.Random();
+ }
+ 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();
- if (handler != null)
- handler.RegisterExternalUserCapsHandler(agentID, caps, "GetMesh", m_URL);
- else
- caps.RegisterHandler("GetMesh", m_URL);
+ 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);
+ }
+ }
+
}
-}
\ No newline at end of file
+}