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.cs332
1 files changed, 175 insertions, 157 deletions
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 @@
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);
53
48 private IAssetService m_assetService; 54 private IAssetService m_assetService;
49 55
50 // TODO: Change this to a config option 56 public const string DefaultFormat = "vnd.ll.mesh";
51 private string m_RedirectURL = null;
52 57
53 public GetMeshHandler(string path, IAssetService assService, string name, string description, string redirectURL) 58 public GetMeshHandler(IAssetService assService)
54 : base("GET", path, name, description)
55 { 59 {
56 m_assetService = assService; 60 m_assetService = assService;
57 m_RedirectURL = redirectURL;
58 if (m_RedirectURL != null && !m_RedirectURL.EndsWith("/"))
59 m_RedirectURL += "/";
60 } 61 }
61 62 public Hashtable Handle(Hashtable request)
62 protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
63 { 63 {
64 // Try to parse the texture ID from the request URL 64 Hashtable ret = new Hashtable();
65 NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); 65 ret["int_response_code"] = (int)System.Net.HttpStatusCode.NotFound;
66 string textureStr = query.GetOne("mesh_id"); 66 ret["content_type"] = "text/plain";
67 ret["keepalive"] = false;
68 ret["reusecontext"] = false;
69 ret["int_bytes"] = 0;
70 ret["int_lod"] = 0;
71 string MeshStr = (string)request["mesh_id"];
72
73
74 //m_log.DebugFormat("[GETMESH]: called {0}", MeshStr);
67 75
68 if (m_assetService == null) 76 if (m_assetService == null)
69 { 77 {
70 m_log.Error("[GETMESH]: Cannot fetch mesh " + textureStr + " without an asset service"); 78 m_log.Error("[GETMESH]: Cannot fetch mesh " + MeshStr + " without an asset service");
71 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
72 } 79 }
73 80
74 UUID meshID; 81 UUID meshID;
75 if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out meshID)) 82 if (!String.IsNullOrEmpty(MeshStr) && UUID.TryParse(MeshStr, out meshID))
76 { 83 {
77 // OK, we have an array with preferred formats, possibly with only one entry 84 // m_log.DebugFormat("[GETMESH]: Received request for mesh id {0}", meshID);
78
79 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
80 AssetBase mesh;
81
82 if (!String.IsNullOrEmpty(m_RedirectURL))
83 {
84 // Only try to fetch locally cached meshes. Misses are redirected
85 mesh = m_assetService.GetCached(meshID.ToString());
86 85
87 if (mesh != null)
88 {
89 if (mesh.Type != (sbyte)AssetType.Mesh)
90 {
91 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
92 }
93 WriteMeshData(httpRequest, httpResponse, mesh);
94 }
95 else
96 {
97 string textureUrl = m_RedirectURL + "?mesh_id="+ meshID.ToString();
98 m_log.Debug("[GETMESH]: Redirecting mesh request to " + textureUrl);
99 httpResponse.StatusCode = (int)OSHttpStatusCode.RedirectMovedPermanently;
100 httpResponse.RedirectLocation = textureUrl;
101 return null;
102 }
103 }
104 else // no redirect
105 {
106 // try the cache
107 mesh = m_assetService.GetCached(meshID.ToString());
108 86
109 if (mesh == null) 87 ret = ProcessGetMesh(request, UUID.Zero, null);
110 {
111 // Fetch locally or remotely. Misses return a 404
112 mesh = m_assetService.Get(meshID.ToString());
113 88
114 if (mesh != null)
115 {
116 if (mesh.Type != (sbyte)AssetType.Mesh)
117 {
118 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
119 return null;
120 }
121 WriteMeshData(httpRequest, httpResponse, mesh);
122 return null;
123 }
124 }
125 else // it was on the cache
126 {
127 if (mesh.Type != (sbyte)AssetType.Mesh)
128 {
129 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
130 return null;
131 }
132 WriteMeshData(httpRequest, httpResponse, mesh);
133 return null;
134 }
135 }
136 89
137 // not found
138 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
139 return null;
140 } 90 }
141 else 91 else
142 { 92 {
143 m_log.Warn("[GETTEXTURE]: Failed to parse a mesh_id from GetMesh request: " + httpRequest.Url); 93 m_log.Warn("[GETMESH]: Failed to parse a mesh_id from GetMesh request: " + (string)request["uri"]);
144 } 94 }
145 95
146 return null;
147 }
148 96
149 private void WriteMeshData(IOSHttpRequest request, IOSHttpResponse response, AssetBase texture) 97 return ret;
98 }
99 public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap)
150 { 100 {
151 string range = request.Headers.GetOne("Range"); 101 Hashtable responsedata = new Hashtable();
102 responsedata["int_response_code"] = 400; //501; //410; //404;
103 responsedata["content_type"] = "text/plain";
104 responsedata["keepalive"] = false;
105 responsedata["str_response_string"] = "Request wasn't what was expected";
106 responsedata["reusecontext"] = false;
107 responsedata["int_lod"] = 0;
108 responsedata["int_bytes"] = 0;
109
110 string meshStr = string.Empty;
152 111
153 if (!String.IsNullOrEmpty(range)) 112 if (request.ContainsKey("mesh_id"))
113 meshStr = request["mesh_id"].ToString();
114
115 UUID meshID = UUID.Zero;
116 if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID))
154 { 117 {
155 // Range request 118 if (m_assetService == null)
156 int start, end;
157 if (TryParseRange(range, out start, out end))
158 { 119 {
159 // Before clamping start make sure we can satisfy it in order to avoid 120 responsedata["int_response_code"] = 404; //501; //410; //404;
160 // sending back the last byte instead of an error status 121 responsedata["content_type"] = "text/plain";
161 if (start >= texture.Data.Length) 122 responsedata["keepalive"] = false;
162 { 123 responsedata["str_response_string"] = "The asset service is unavailable. So is your mesh.";
163 response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent; 124 responsedata["reusecontext"] = false;
164 response.ContentType = texture.Metadata.ContentType; 125 return responsedata;
165 } 126 }
166 else 127
128 AssetBase mesh = m_assetService.Get(meshID.ToString());
129
130 if (mesh != null)
131 {
132 if (mesh.Type == (SByte)AssetType.Mesh)
167 { 133 {
168 // Handle the case where no second range value was given. This is equivalent to requesting
169 // the rest of the entity.
170 if (end == -1)
171 end = int.MaxValue;
172 134
173 end = Utils.Clamp(end, 0, texture.Data.Length - 1); 135 Hashtable headers = new Hashtable();
174 start = Utils.Clamp(start, 0, end); 136 responsedata["headers"] = headers;
175 int len = end - start + 1; 137
138 string range = String.Empty;
139
140 if (((Hashtable)request["headers"])["range"] != null)
141 range = (string)((Hashtable)request["headers"])["range"];
176 142
177 if (0 == start && len == texture.Data.Length) 143 else if (((Hashtable)request["headers"])["Range"] != null)
144 range = (string)((Hashtable)request["headers"])["Range"];
145
146 if (!String.IsNullOrEmpty(range)) // Mesh Asset LOD // Physics
178 { 147 {
179 response.StatusCode = (int)System.Net.HttpStatusCode.OK; 148 // Range request
149 int start, end;
150 if (TryParseRange(range, out start, out end))
151 {
152 // Before clamping start make sure we can satisfy it in order to avoid
153 // sending back the last byte instead of an error status
154 if (start >= mesh.Data.Length)
155 {
156 responsedata["int_response_code"] = 404; //501; //410; //404;
157 responsedata["content_type"] = "text/plain";
158 responsedata["keepalive"] = false;
159 responsedata["str_response_string"] = "This range doesnt exist.";
160 responsedata["reusecontext"] = false;
161 responsedata["int_lod"] = 3;
162 return responsedata;
163 }
164 else
165 {
166 end = Utils.Clamp(end, 0, mesh.Data.Length - 1);
167 start = Utils.Clamp(start, 0, end);
168 int len = end - start + 1;
169
170 //m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID);
171
172 if (start > 20000)
173 {
174 responsedata["int_lod"] = 3;
175 }
176 else if (start < 4097)
177 {
178 responsedata["int_lod"] = 1;
179 }
180 else
181 {
182 responsedata["int_lod"] = 2;
183 }
184
185
186 if (start == 0 && len == mesh.Data.Length) // well redudante maybe
187 {
188 responsedata["int_response_code"] = (int)System.Net.HttpStatusCode.OK;
189 responsedata["bin_response_data"] = mesh.Data;
190 responsedata["int_bytes"] = mesh.Data.Length;
191 responsedata["reusecontext"] = false;
192 responsedata["int_lod"] = 3;
193
194 }
195 else
196 {
197 responsedata["int_response_code"] =
198 (int)System.Net.HttpStatusCode.PartialContent;
199 headers["Content-Range"] = String.Format("bytes {0}-{1}/{2}", start, end,
200 mesh.Data.Length);
201
202 byte[] d = new byte[len];
203 Array.Copy(mesh.Data, start, d, 0, len);
204 responsedata["bin_response_data"] = d;
205 responsedata["int_bytes"] = len;
206 responsedata["reusecontext"] = false;
207 }
208 }
209 }
210 else
211 {
212 m_log.Warn("[GETMESH]: Failed to parse a range from GetMesh request, sending full asset: " + (string)request["uri"]);
213 responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
214 responsedata["content_type"] = "application/vnd.ll.mesh";
215 responsedata["int_response_code"] = 200;
216 responsedata["reusecontext"] = false;
217 responsedata["int_lod"] = 3;
218 }
180 } 219 }
181 else 220 else
182 { 221 {
183 response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent; 222 responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
184 response.AddHeader("Content-Range", String.Format("bytes {0}-{1}/{2}", start, end, texture.Data.Length)); 223 responsedata["content_type"] = "application/vnd.ll.mesh";
224 responsedata["int_response_code"] = 200;
225 responsedata["reusecontext"] = false;
226 responsedata["int_lod"] = 3;
185 } 227 }
186 228 }
187 response.ContentLength = len; 229 // Optionally add additional mesh types here
188 response.ContentType = "application/vnd.ll.mesh"; 230 else
189 231 {
190 response.Body.Write(texture.Data, start, len); 232 responsedata["int_response_code"] = 404; //501; //410; //404;
233 responsedata["content_type"] = "text/plain";
234 responsedata["keepalive"] = false;
235 responsedata["str_response_string"] = "Unfortunately, this asset isn't a mesh.";
236 responsedata["reusecontext"] = false;
237 responsedata["int_lod"] = 1;
238 return responsedata;
191 } 239 }
192 } 240 }
193 else 241 else
194 { 242 {
195 m_log.Warn("[GETMESH]: Malformed Range header: " + range); 243 responsedata["int_response_code"] = 404; //501; //410; //404;
196 response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest; 244 responsedata["content_type"] = "text/plain";
245 responsedata["keepalive"] = false;
246 responsedata["str_response_string"] = "Your Mesh wasn't found. Sorry!";
247 responsedata["reusecontext"] = false;
248 responsedata["int_lod"] = 0;
249 return responsedata;
197 } 250 }
198 } 251 }
199 else
200 {
201 // Full content request
202 response.StatusCode = (int)System.Net.HttpStatusCode.OK;
203 response.ContentLength = texture.Data.Length;
204 response.ContentType = "application/vnd.ll.mesh";
205 response.Body.Write(texture.Data, 0, texture.Data.Length);
206 }
207 }
208 252
209 /// <summary> 253 return responsedata;
210 /// Parse a range header. 254 }
211 /// </summary>
212 /// <remarks>
213 /// As per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html,
214 /// this obeys range headers with two values (e.g. 533-4165) and no second value (e.g. 533-).
215 /// Where there is no value, -1 is returned.
216 /// FIXME: Need to cover the case where only a second value is specified (e.g. -4165), probably by returning -1
217 /// for start.</remarks>
218 /// <returns></returns>
219 /// <param name='header'></param>
220 /// <param name='start'>Start of the range. Undefined if this was not a number.</param>
221 /// <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>
222 private bool TryParseRange(string header, out int start, out int end) 255 private bool TryParseRange(string header, out int start, out int end)
223 { 256 {
224 start = end = 0;
225
226 if (header.StartsWith("bytes=")) 257 if (header.StartsWith("bytes="))
227 { 258 {
228 string[] rangeValues = header.Substring(6).Split('-'); 259 string[] rangeValues = header.Substring(6).Split('-');
229
230 if (rangeValues.Length == 2) 260 if (rangeValues.Length == 2)
231 { 261 {
232 if (!Int32.TryParse(rangeValues[0], out start)) 262 if (Int32.TryParse(rangeValues[0], out start) && Int32.TryParse(rangeValues[1], out end))
233 return false;
234
235 string rawEnd = rangeValues[1];
236
237 if (rawEnd == "")
238 {
239 end = -1;
240 return true; 263 return true;
241 }
242 else if (Int32.TryParse(rawEnd, out end))
243 {
244 return true;
245 }
246 } 264 }
247 } 265 }
248 266