aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs239
1 files changed, 188 insertions, 51 deletions
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 @@
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;
34using log4net; 28using log4net;
35using Nini.Config;
36using OpenMetaverse; 29using OpenMetaverse;
37using OpenMetaverse.StructuredData; 30using OpenMetaverse.Imaging;
38using OpenSim.Framework; 31using OpenSim.Framework;
39using OpenSim.Framework.Servers;
40using OpenSim.Framework.Servers.HttpServer; 32using OpenSim.Framework.Servers.HttpServer;
41using OpenSim.Services.Interfaces; 33using OpenSim.Services.Interfaces;
42using Caps = OpenSim.Framework.Capabilities.Caps; 34using System;
35using System.Collections.Specialized;
36using System.Drawing;
37using System.Drawing.Imaging;
38using System.IO;
39using System.Reflection;
40using System.Web;
43 41
44namespace OpenSim.Capabilities.Handlers 42namespace OpenSim.Capabilities.Handlers
45{ 43{
46 public class GetMeshHandler 44 public class GetMeshHandler : BaseStreamHandler
47 { 45 {
48// private static readonly ILog m_log = 46 private static readonly ILog m_log =
49// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 47 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50
51 private IAssetService m_assetService; 48 private IAssetService m_assetService;
52 49
53 public GetMeshHandler(IAssetService assService) 50 // TODO: Change this to a config option
51 private string m_RedirectURL = null;
52
53 public GetMeshHandler(string path, IAssetService assService, string name, string description, string redirectURL)
54 : base("GET", path, name, description)
54 { 55 {
55 m_assetService = assService; 56 m_assetService = assService;
57 m_RedirectURL = redirectURL;
58 if (m_RedirectURL != null && !m_RedirectURL.EndsWith("/"))
59 m_RedirectURL += "/";
56 } 60 }
57 61
58 public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap) 62 protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
59 { 63 {
60 Hashtable responsedata = new Hashtable(); 64 // Try to parse the texture ID from the request URL
61 responsedata["int_response_code"] = 400; //501; //410; //404; 65 NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query);
62 responsedata["content_type"] = "text/plain"; 66 string textureStr = query.GetOne("mesh_id");
63 responsedata["keepalive"] = false;
64 responsedata["str_response_string"] = "Request wasn't what was expected";
65 67
66 string meshStr = string.Empty; 68 if (m_assetService == null)
67 69 {
68 if (request.ContainsKey("mesh_id")) 70 m_log.Error("[GETMESH]: Cannot fetch mesh " + textureStr + " without an asset service");
69 meshStr = request["mesh_id"].ToString(); 71 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
72 }
70 73
71 UUID meshID = UUID.Zero; 74 UUID meshID;
72 if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID)) 75 if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out meshID))
73 { 76 {
74 if (m_assetService == null) 77 // OK, we have an array with preferred formats, possibly with only one entry
78
79 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
80 AssetBase mesh;
81
82 if (!String.IsNullOrEmpty(m_RedirectURL))
75 { 83 {
76 responsedata["int_response_code"] = 404; //501; //410; //404; 84 // Only try to fetch locally cached meshes. Misses are redirected
77 responsedata["content_type"] = "text/plain"; 85 mesh = m_assetService.GetCached(meshID.ToString());
78 responsedata["keepalive"] = false; 86
79 responsedata["str_response_string"] = "The asset service is unavailable. So is your mesh."; 87 if (mesh != null)
80 return responsedata; 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
109 if (mesh == null)
110 {
111 // Fetch locally or remotely. Misses return a 404
112 mesh = m_assetService.Get(meshID.ToString());
113
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 }
81 } 135 }
82 136
83 AssetBase mesh = m_assetService.Get(meshID.ToString()); 137 // not found
138 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
139 return null;
140 }
141 else
142 {
143 m_log.Warn("[GETTEXTURE]: Failed to parse a mesh_id from GetMesh request: " + httpRequest.Url);
144 }
145
146 return null;
147 }
148
149 private void WriteMeshData(IOSHttpRequest request, IOSHttpResponse response, AssetBase texture)
150 {
151 string range = request.Headers.GetOne("Range");
84 152
85 if (mesh != null) 153 if (!String.IsNullOrEmpty(range))
154 {
155 // Range request
156 int start, end;
157 if (TryParseRange(range, out start, out end))
86 { 158 {
87 if (mesh.Type == (SByte)AssetType.Mesh) 159 // Before clamping start make sure we can satisfy it in order to avoid
160 // sending back the last byte instead of an error status
161 if (start >= texture.Data.Length)
88 { 162 {
89 responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data); 163 response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
90 responsedata["content_type"] = "application/vnd.ll.mesh"; 164 response.ContentType = texture.Metadata.ContentType;
91 responsedata["int_response_code"] = 200;
92 } 165 }
93 // Optionally add additional mesh types here
94 else 166 else
95 { 167 {
96 responsedata["int_response_code"] = 404; //501; //410; //404; 168 // Handle the case where no second range value was given. This is equivalent to requesting
97 responsedata["content_type"] = "text/plain"; 169 // the rest of the entity.
98 responsedata["keepalive"] = false; 170 if (end == -1)
99 responsedata["str_response_string"] = "Unfortunately, this asset isn't a mesh."; 171 end = int.MaxValue;
100 return responsedata; 172
173 end = Utils.Clamp(end, 0, texture.Data.Length - 1);
174 start = Utils.Clamp(start, 0, end);
175 int len = end - start + 1;
176
177 if (0 == start && len == texture.Data.Length)
178 {
179 response.StatusCode = (int)System.Net.HttpStatusCode.OK;
180 }
181 else
182 {
183 response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
184 response.AddHeader("Content-Range", String.Format("bytes {0}-{1}/{2}", start, end, texture.Data.Length));
185 }
186
187 response.ContentLength = len;
188 response.ContentType = "application/vnd.ll.mesh";
189
190 response.Body.Write(texture.Data, start, len);
101 } 191 }
102 } 192 }
103 else 193 else
104 { 194 {
105 responsedata["int_response_code"] = 404; //501; //410; //404; 195 m_log.Warn("[GETMESH]: Malformed Range header: " + range);
106 responsedata["content_type"] = "text/plain"; 196 response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
107 responsedata["keepalive"] = false; 197 }
108 responsedata["str_response_string"] = "Your Mesh wasn't found. Sorry!"; 198 }
109 return responsedata; 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
209 /// <summary>
210 /// Parse a range header.
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)
223 {
224 start = end = 0;
225
226 if (header.StartsWith("bytes="))
227 {
228 string[] rangeValues = header.Substring(6).Split('-');
229
230 if (rangeValues.Length == 2)
231 {
232 if (!Int32.TryParse(rangeValues[0], out start))
233 return false;
234
235 string rawEnd = rangeValues[1];
236
237 if (rawEnd == "")
238 {
239 end = -1;
240 return true;
241 }
242 else if (Int32.TryParse(rawEnd, out end))
243 {
244 return true;
245 }
110 } 246 }
111 } 247 }
112 248
113 return responsedata; 249 start = end = 0;
250 return false;
114 } 251 }
115 } 252 }
116} \ No newline at end of file 253} \ No newline at end of file