From 5e4d6cab00cb29cd088ab7b62ab13aff103b64cb Mon Sep 17 00:00:00 2001 From: onefang Date: Sun, 19 May 2019 21:24:15 +1000 Subject: Dump OpenSim 0.9.0.1 into it's own branch. --- .../Handlers/GetMesh/GetMeshHandler.cs | 332 +++++++++++---------- 1 file changed, 175 insertions(+), 157 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 6b67da1..a9b81f3 100644 --- a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs +++ b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs @@ -25,224 +25,242 @@ * 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.Imaging; +using OpenMetaverse.StructuredData; using OpenSim.Framework; +using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; using OpenSim.Services.Interfaces; -using System; -using System.Collections.Specialized; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Reflection; -using System.Web; +using Caps = OpenSim.Framework.Capabilities.Caps; + + + namespace OpenSim.Capabilities.Handlers { - public class GetMeshHandler : BaseStreamHandler + public class GetMeshHandler { private static readonly ILog m_log = - LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private IAssetService m_assetService; - // TODO: Change this to a config option - private string m_RedirectURL = null; + public const string DefaultFormat = "vnd.ll.mesh"; - public GetMeshHandler(string path, IAssetService assService, string name, string description, string redirectURL) - : base("GET", path, name, description) + public GetMeshHandler(IAssetService assService) { 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) + public Hashtable Handle(Hashtable request) { - // Try to parse the texture ID from the request URL - NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); - string textureStr = query.GetOne("mesh_id"); + Hashtable ret = new Hashtable(); + ret["int_response_code"] = (int)System.Net.HttpStatusCode.NotFound; + ret["content_type"] = "text/plain"; + ret["keepalive"] = false; + ret["reusecontext"] = false; + ret["int_bytes"] = 0; + ret["int_lod"] = 0; + string MeshStr = (string)request["mesh_id"]; + + + //m_log.DebugFormat("[GETMESH]: called {0}", MeshStr); if (m_assetService == null) { - m_log.Error("[GETMESH]: Cannot fetch mesh " + textureStr + " without an asset service"); - httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + m_log.Error("[GETMESH]: Cannot fetch mesh " + MeshStr + " without an asset service"); } UUID meshID; - if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out meshID)) + if (!String.IsNullOrEmpty(MeshStr) && UUID.TryParse(MeshStr, 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()); + // m_log.DebugFormat("[GETMESH]: Received request for mesh id {0}", meshID); - 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()); + ret = ProcessGetMesh(request, UUID.Zero, null); - 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); + m_log.Warn("[GETMESH]: Failed to parse a mesh_id from GetMesh request: " + (string)request["uri"]); } - return null; - } - private void WriteMeshData(IOSHttpRequest request, IOSHttpResponse response, AssetBase texture) + return ret; + } + public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap) { - string range = request.Headers.GetOne("Range"); + 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"; + responsedata["reusecontext"] = false; + responsedata["int_lod"] = 0; + responsedata["int_bytes"] = 0; + + string meshStr = string.Empty; - if (!String.IsNullOrEmpty(range)) + if (request.ContainsKey("mesh_id")) + meshStr = request["mesh_id"].ToString(); + + UUID meshID = UUID.Zero; + if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID)) { - // Range request - int start, end; - if (TryParseRange(range, out start, out end)) + if (m_assetService == null) { - // 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 + 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."; + responsedata["reusecontext"] = false; + return responsedata; + } + + AssetBase mesh = m_assetService.Get(meshID.ToString()); + + if (mesh != null) + { + if (mesh.Type == (SByte)AssetType.Mesh) { - // 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; + Hashtable headers = new Hashtable(); + responsedata["headers"] = headers; + + string range = String.Empty; + + if (((Hashtable)request["headers"])["range"] != null) + range = (string)((Hashtable)request["headers"])["range"]; - if (0 == start && len == texture.Data.Length) + else if (((Hashtable)request["headers"])["Range"] != null) + range = (string)((Hashtable)request["headers"])["Range"]; + + if (!String.IsNullOrEmpty(range)) // Mesh Asset LOD // Physics { - response.StatusCode = (int)System.Net.HttpStatusCode.OK; + // 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 >= mesh.Data.Length) + { + responsedata["int_response_code"] = 404; //501; //410; //404; + responsedata["content_type"] = "text/plain"; + responsedata["keepalive"] = false; + responsedata["str_response_string"] = "This range doesnt exist."; + responsedata["reusecontext"] = false; + responsedata["int_lod"] = 3; + return responsedata; + } + else + { + end = Utils.Clamp(end, 0, mesh.Data.Length - 1); + start = Utils.Clamp(start, 0, end); + int len = end - start + 1; + + //m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID); + + if (start > 20000) + { + responsedata["int_lod"] = 3; + } + else if (start < 4097) + { + responsedata["int_lod"] = 1; + } + else + { + responsedata["int_lod"] = 2; + } + + + if (start == 0 && len == mesh.Data.Length) // well redudante maybe + { + responsedata["int_response_code"] = (int)System.Net.HttpStatusCode.OK; + responsedata["bin_response_data"] = mesh.Data; + responsedata["int_bytes"] = mesh.Data.Length; + responsedata["reusecontext"] = false; + responsedata["int_lod"] = 3; + + } + else + { + responsedata["int_response_code"] = + (int)System.Net.HttpStatusCode.PartialContent; + headers["Content-Range"] = String.Format("bytes {0}-{1}/{2}", start, end, + mesh.Data.Length); + + byte[] d = new byte[len]; + Array.Copy(mesh.Data, start, d, 0, len); + responsedata["bin_response_data"] = d; + responsedata["int_bytes"] = len; + responsedata["reusecontext"] = false; + } + } + } + else + { + m_log.Warn("[GETMESH]: Failed to parse a range from GetMesh request, sending full asset: " + (string)request["uri"]); + responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data); + responsedata["content_type"] = "application/vnd.ll.mesh"; + responsedata["int_response_code"] = 200; + responsedata["reusecontext"] = false; + responsedata["int_lod"] = 3; + } } else { - response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent; - response.AddHeader("Content-Range", String.Format("bytes {0}-{1}/{2}", start, end, texture.Data.Length)); + responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data); + responsedata["content_type"] = "application/vnd.ll.mesh"; + responsedata["int_response_code"] = 200; + responsedata["reusecontext"] = false; + responsedata["int_lod"] = 3; } - - response.ContentLength = len; - response.ContentType = "application/vnd.ll.mesh"; - - response.Body.Write(texture.Data, start, len); + } + // 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."; + responsedata["reusecontext"] = false; + responsedata["int_lod"] = 1; + return responsedata; } } else { - m_log.Warn("[GETMESH]: Malformed Range header: " + range); - response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest; + 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!"; + responsedata["reusecontext"] = false; + responsedata["int_lod"] = 0; + return responsedata; } } - 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. + return responsedata; + } 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; + if (Int32.TryParse(rangeValues[0], out start) && Int32.TryParse(rangeValues[1], out end)) return true; - } - else if (Int32.TryParse(rawEnd, out end)) - { - return true; - } } } -- cgit v1.1