aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs')
-rw-r--r--OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs218
1 files changed, 51 insertions, 167 deletions
diff --git a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
index 7fcc798..a9b81f3 100644
--- a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
+++ b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
@@ -25,37 +25,39 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System;
29using System.Collections;
30using System.Collections.Specialized;
31using System.Reflection;
32using System.IO;
33using System.Web;
28using log4net; 34using log4net;
35using Nini.Config;
29using OpenMetaverse; 36using OpenMetaverse;
30using OpenMetaverse.Imaging; 37using OpenMetaverse.StructuredData;
31using OpenSim.Framework; 38using OpenSim.Framework;
39using OpenSim.Framework.Servers;
32using OpenSim.Framework.Servers.HttpServer; 40using OpenSim.Framework.Servers.HttpServer;
33using OpenSim.Services.Interfaces; 41using OpenSim.Services.Interfaces;
34using System; 42using Caps = OpenSim.Framework.Capabilities.Caps;
35using System.Collections.Specialized; 43
36using System.Drawing; 44
37using System.Drawing.Imaging; 45
38using System.IO;
39using System.Reflection;
40using System.Web;
41 46
42namespace OpenSim.Capabilities.Handlers 47namespace OpenSim.Capabilities.Handlers
43{ 48{
44 public class GetMeshHandler : BaseStreamHandler 49 public class GetMeshHandler
45 { 50 {
46 private static readonly ILog m_log = 51 private static readonly ILog m_log =
47 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 52 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
48 53
49 private IAssetService m_assetService; 54 private IAssetService m_assetService;
50 55
51 public const string DefaultFormat = "vnd.ll.mesh"; 56 public const string DefaultFormat = "vnd.ll.mesh";
52 57
53 public GetMeshHandler(IAssetService assService) 58 public GetMeshHandler(IAssetService assService)
54 { 59 {
55 m_assetService = assService; 60 m_assetService = assService;
56 m_RedirectURL = redirectURL;
57 if (m_RedirectURL != null && !m_RedirectURL.EndsWith("/"))
58 m_RedirectURL += "/";
59 } 61 }
60 public Hashtable Handle(Hashtable request) 62 public Hashtable Handle(Hashtable request)
61 { 63 {
@@ -67,7 +69,7 @@ namespace OpenSim.Capabilities.Handlers
67 ret["int_bytes"] = 0; 69 ret["int_bytes"] = 0;
68 ret["int_lod"] = 0; 70 ret["int_lod"] = 0;
69 string MeshStr = (string)request["mesh_id"]; 71 string MeshStr = (string)request["mesh_id"];
70 72
71 73
72 //m_log.DebugFormat("[GETMESH]: called {0}", MeshStr); 74 //m_log.DebugFormat("[GETMESH]: called {0}", MeshStr);
73 75
@@ -81,122 +83,53 @@ namespace OpenSim.Capabilities.Handlers
81 { 83 {
82 // m_log.DebugFormat("[GETMESH]: Received request for mesh id {0}", meshID); 84 // m_log.DebugFormat("[GETMESH]: Received request for mesh id {0}", meshID);
83 85
84 86
85 ret = ProcessGetMesh(request, UUID.Zero, null); 87 ret = ProcessGetMesh(request, UUID.Zero, null);
86 88
87 89
88 } 90 }
89 else 91 else
90 { 92 {
91 m_log.Warn("[GETMESH]: Failed to parse a mesh_id from GetMesh request: " + (string)request["uri"]); 93 m_log.Warn("[GETMESH]: Failed to parse a mesh_id from GetMesh request: " + (string)request["uri"]);
92 } 94 }
93 95
94 96
95 return ret; 97 return ret;
96 } 98 }
97 public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap) 99 public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap)
98 { 100 {
99 // Try to parse the texture ID from the request URL 101 Hashtable responsedata = new Hashtable();
100 NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); 102 responsedata["int_response_code"] = 400; //501; //410; //404;
101 string textureStr = query.GetOne("mesh_id"); 103 responsedata["content_type"] = "text/plain";
104 responsedata["keepalive"] = false;
105 responsedata["str_response_string"] = "Request wasn't what was expected";
102 responsedata["reusecontext"] = false; 106 responsedata["reusecontext"] = false;
103 responsedata["int_lod"] = 0; 107 responsedata["int_lod"] = 0;
104 responsedata["int_bytes"] = 0; 108 responsedata["int_bytes"] = 0;
105 109
106 if (m_assetService == null) 110 string meshStr = string.Empty;
107 {
108 m_log.Error("[GETMESH]: Cannot fetch mesh " + textureStr + " without an asset service");
109 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
110 }
111
112 UUID meshID;
113 if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out meshID))
114 {
115 // OK, we have an array with preferred formats, possibly with only one entry
116
117 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
118 AssetBase mesh;
119 111
120 if (!String.IsNullOrEmpty(m_RedirectURL)) 112 if (request.ContainsKey("mesh_id"))
121 { 113 meshStr = request["mesh_id"].ToString();
122 // Only try to fetch locally cached meshes. Misses are redirected
123 mesh = m_assetService.GetCached(meshID.ToString());
124 114
125 if (mesh != null) 115 UUID meshID = UUID.Zero;
126 { 116 if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID))
127 if (mesh.Type != (sbyte)AssetType.Mesh) 117 {
128 { 118 if (m_assetService == null)
129 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
130 }
131 WriteMeshData(httpRequest, httpResponse, mesh);
132 }
133 else
134 {
135 string textureUrl = m_RedirectURL + "?mesh_id="+ meshID.ToString();
136 m_log.Debug("[GETMESH]: Redirecting mesh request to " + textureUrl);
137 httpResponse.StatusCode = (int)OSHttpStatusCode.RedirectMovedPermanently;
138 httpResponse.RedirectLocation = textureUrl;
139 return null;
140 }
141 }
142 else // no redirect
143 { 119 {
144 // try the cache 120 responsedata["int_response_code"] = 404; //501; //410; //404;
145 mesh = m_assetService.GetCached(meshID.ToString()); 121 responsedata["content_type"] = "text/plain";
146 122 responsedata["keepalive"] = false;
147 if (mesh == null) 123 responsedata["str_response_string"] = "The asset service is unavailable. So is your mesh.";
148 { 124 responsedata["reusecontext"] = false;
149 // Fetch locally or remotely. Misses return a 404 125 return responsedata;
150 mesh = m_assetService.Get(meshID.ToString());
151
152 if (mesh != null)
153 {
154 if (mesh.Type != (sbyte)AssetType.Mesh)
155 {
156 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
157 return null;
158 }
159 WriteMeshData(httpRequest, httpResponse, mesh);
160 return null;
161 }
162 }
163 else // it was on the cache
164 {
165 if (mesh.Type != (sbyte)AssetType.Mesh)
166 {
167 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
168 return null;
169 }
170 WriteMeshData(httpRequest, httpResponse, mesh);
171 return null;
172 }
173 } 126 }
174 127
175 // not found 128 AssetBase mesh = m_assetService.Get(meshID.ToString());
176 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
177 return null;
178 }
179 else
180 {
181 m_log.Warn("[GETTEXTURE]: Failed to parse a mesh_id from GetMesh request: " + httpRequest.Url);
182 }
183
184 return null;
185 }
186
187 private void WriteMeshData(IOSHttpRequest request, IOSHttpResponse response, AssetBase texture)
188 {
189 string range = request.Headers.GetOne("Range");
190 129
191 if (!String.IsNullOrEmpty(range)) 130 if (mesh != null)
192 {
193 // Range request
194 int start, end;
195 if (TryParseRange(range, out start, out end))
196 { 131 {
197 // Before clamping start make sure we can satisfy it in order to avoid 132 if (mesh.Type == (SByte)AssetType.Mesh)
198 // sending back the last byte instead of an error status
199 if (start >= texture.Data.Length)
200 { 133 {
201 134
202 Hashtable headers = new Hashtable(); 135 Hashtable headers = new Hashtable();
@@ -212,12 +145,12 @@ namespace OpenSim.Capabilities.Handlers
212 145
213 if (!String.IsNullOrEmpty(range)) // Mesh Asset LOD // Physics 146 if (!String.IsNullOrEmpty(range)) // Mesh Asset LOD // Physics
214 { 147 {
215 // Range request 148 // Range request
216 int start, end; 149 int start, end;
217 if (TryParseRange(range, out start, out end)) 150 if (TryParseRange(range, out start, out end))
218 { 151 {
219 // Before clamping start make sure we can satisfy it in order to avoid 152 // Before clamping start make sure we can satisfy it in order to avoid
220 // sending back the last byte instead of an error status 153 // sending back the last byte instead of an error status
221 if (start >= mesh.Data.Length) 154 if (start >= mesh.Data.Length)
222 { 155 {
223 responsedata["int_response_code"] = 404; //501; //410; //404; 156 responsedata["int_response_code"] = 404; //501; //410; //404;
@@ -249,20 +182,20 @@ namespace OpenSim.Capabilities.Handlers
249 responsedata["int_lod"] = 2; 182 responsedata["int_lod"] = 2;
250 } 183 }
251 184
252 185
253 if (start == 0 && len == mesh.Data.Length) // well redudante maybe 186 if (start == 0 && len == mesh.Data.Length) // well redudante maybe
254 { 187 {
255 responsedata["int_response_code"] = (int) System.Net.HttpStatusCode.OK; 188 responsedata["int_response_code"] = (int)System.Net.HttpStatusCode.OK;
256 responsedata["bin_response_data"] = mesh.Data; 189 responsedata["bin_response_data"] = mesh.Data;
257 responsedata["int_bytes"] = mesh.Data.Length; 190 responsedata["int_bytes"] = mesh.Data.Length;
258 responsedata["reusecontext"] = false; 191 responsedata["reusecontext"] = false;
259 responsedata["int_lod"] = 3; 192 responsedata["int_lod"] = 3;
260 193
261 } 194 }
262 else 195 else
263 { 196 {
264 responsedata["int_response_code"] = 197 responsedata["int_response_code"] =
265 (int) System.Net.HttpStatusCode.PartialContent; 198 (int)System.Net.HttpStatusCode.PartialContent;
266 headers["Content-Range"] = String.Format("bytes {0}-{1}/{2}", start, end, 199 headers["Content-Range"] = String.Format("bytes {0}-{1}/{2}", start, end,
267 mesh.Data.Length); 200 mesh.Data.Length);
268 201
@@ -293,6 +226,7 @@ namespace OpenSim.Capabilities.Handlers
293 responsedata["int_lod"] = 3; 226 responsedata["int_lod"] = 3;
294 } 227 }
295 } 228 }
229 // Optionally add additional mesh types here
296 else 230 else
297 { 231 {
298 responsedata["int_response_code"] = 404; //501; //410; //404; 232 responsedata["int_response_code"] = 404; //501; //410; //404;
@@ -315,58 +249,8 @@ namespace OpenSim.Capabilities.Handlers
315 return responsedata; 249 return responsedata;
316 } 250 }
317 } 251 }
318 else
319 {
320 // Full content request
321 response.StatusCode = (int)System.Net.HttpStatusCode.OK;
322 response.ContentLength = texture.Data.Length;
323 response.ContentType = "application/vnd.ll.mesh";
324 response.Body.Write(texture.Data, 0, texture.Data.Length);
325 }
326 }
327 252
328 /// <summary> 253 return responsedata;
329 /// Parse a range header.
330 /// </summary>
331 /// <remarks>
332 /// As per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html,
333 /// this obeys range headers with two values (e.g. 533-4165) and no second value (e.g. 533-).
334 /// Where there is no value, -1 is returned.
335 /// FIXME: Need to cover the case where only a second value is specified (e.g. -4165), probably by returning -1
336 /// for start.</remarks>
337 /// <returns></returns>
338 /// <param name='header'></param>
339 /// <param name='start'>Start of the range. Undefined if this was not a number.</param>
340 /// <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>
341 private bool TryParseRange(string header, out int start, out int end)
342 {
343 start = end = 0;
344
345 if (header.StartsWith("bytes="))
346 {
347 string[] rangeValues = header.Substring(6).Split('-');
348
349 if (rangeValues.Length == 2)
350 {
351 if (!Int32.TryParse(rangeValues[0], out start))
352 return false;
353
354 string rawEnd = rangeValues[1];
355
356 if (rawEnd == "")
357 {
358 end = -1;
359 return true;
360 }
361 else if (Int32.TryParse(rawEnd, out end))
362 {
363 return true;
364 }
365 }
366 }
367
368 start = end = 0;
369 return false;
370 } 254 }
371 private bool TryParseRange(string header, out int start, out int end) 255 private bool TryParseRange(string header, out int start, out int end)
372 { 256 {