From 134f86e8d5c414409631b25b8c6f0ee45fbd8631 Mon Sep 17 00:00:00 2001
From: David Walter Seikel
Date: Thu, 3 Nov 2016 21:44:39 +1000
Subject: Initial update to OpenSim 0.8.2.1 source code.
---
.../Handlers/GetMesh/GetMeshHandler.cs | 239 ++++++++++++++++-----
1 file changed, 188 insertions(+), 51 deletions(-)
(limited to 'OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs')
diff --git a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
index 720640e..6b67da1 100644
--- a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
+++ b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
@@ -25,92 +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.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
+ 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(IAssetService assService)
+ // 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 += "/";
}
- public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap)
+ protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
- Hashtable responsedata = new Hashtable();
- responsedata["int_response_code"] = 400; //501; //410; //404;
- responsedata["content_type"] = "text/plain";
- responsedata["keepalive"] = false;
- responsedata["str_response_string"] = "Request wasn't what was expected";
+ // Try to parse the texture ID from the request URL
+ NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query);
+ string textureStr = query.GetOne("mesh_id");
- string meshStr = string.Empty;
-
- if (request.ContainsKey("mesh_id"))
- meshStr = request["mesh_id"].ToString();
+ if (m_assetService == null)
+ {
+ m_log.Error("[GETMESH]: Cannot fetch mesh " + textureStr + " without an asset service");
+ httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
+ }
- UUID meshID = UUID.Zero;
- if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID))
+ UUID meshID;
+ if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out meshID))
{
- if (m_assetService == null)
+ // 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))
{
- responsedata["int_response_code"] = 404; //501; //410; //404;
- responsedata["content_type"] = "text/plain";
- responsedata["keepalive"] = false;
- responsedata["str_response_string"] = "The asset service is unavailable. So is your mesh.";
- return responsedata;
+ // 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)
+ {
+ httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
+ }
+ WriteMeshData(httpRequest, httpResponse, mesh);
+ }
+ else
+ {
+ 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;
+ }
}
- AssetBase mesh = m_assetService.Get(meshID.ToString());
+ // 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 (mesh != null)
+ if (!String.IsNullOrEmpty(range))
+ {
+ // Range request
+ int start, end;
+ if (TryParseRange(range, out start, out end))
{
- if (mesh.Type == (SByte)AssetType.Mesh)
+ // 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)
{
- responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
- responsedata["content_type"] = "application/vnd.ll.mesh";
- responsedata["int_response_code"] = 200;
+ response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
+ response.ContentType = texture.Metadata.ContentType;
}
- // Optionally add additional mesh types here
else
{
- responsedata["int_response_code"] = 404; //501; //410; //404;
- responsedata["content_type"] = "text/plain";
- responsedata["keepalive"] = false;
- responsedata["str_response_string"] = "Unfortunately, this asset isn't a mesh.";
- return responsedata;
+ // 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
{
- responsedata["int_response_code"] = 404; //501; //410; //404;
- responsedata["content_type"] = "text/plain";
- responsedata["keepalive"] = false;
- responsedata["str_response_string"] = "Your Mesh wasn't found. Sorry!";
- return responsedata;
+ 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;
+ }
}
}
- return responsedata;
+ start = end = 0;
+ return false;
}
}
}
\ No newline at end of file
--
cgit v1.1