aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Capabilities
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Capabilities')
-rw-r--r--OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs227
-rw-r--r--OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs20
2 files changed, 195 insertions, 52 deletions
diff --git a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
index 9fe00e0..6b67da1 100644
--- a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
+++ b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs
@@ -25,90 +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.Text;
34using System.Web;
35using log4net; 28using log4net;
36using Nini.Config;
37using OpenMetaverse; 29using OpenMetaverse;
38using OpenMetaverse.StructuredData; 30using OpenMetaverse.Imaging;
39using OpenSim.Framework; 31using OpenSim.Framework;
40using OpenSim.Framework.Servers;
41using OpenSim.Framework.Servers.HttpServer; 32using OpenSim.Framework.Servers.HttpServer;
42using OpenSim.Services.Interfaces; 33using OpenSim.Services.Interfaces;
43using 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;
44 41
45namespace OpenSim.Capabilities.Handlers 42namespace OpenSim.Capabilities.Handlers
46{ 43{
47 public class GetMeshHandler : BaseStreamHandler 44 public class GetMeshHandler : BaseStreamHandler
48 { 45 {
49// private static readonly ILog m_log = 46 private static readonly ILog m_log =
50// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 47 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51
52 private IAssetService m_assetService; 48 private IAssetService m_assetService;
53 49
54 public GetMeshHandler(string path, IAssetService assService, string name, string description) 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)
55 : base("GET", path, name, description) 54 : base("GET", path, name, description)
56 { 55 {
57 m_assetService = assService; 56 m_assetService = assService;
57 m_RedirectURL = redirectURL;
58 if (m_RedirectURL != null && !m_RedirectURL.EndsWith("/"))
59 m_RedirectURL += "/";
58 } 60 }
59 61
60 protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) 62 protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
61 { 63 {
62 // Try to parse the texture ID from the request URL 64 // Try to parse the texture ID from the request URL
63 NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); 65 NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query);
64 string meshStr = query.GetOne("mesh_id"); 66 string textureStr = query.GetOne("mesh_id");
65 67
66// m_log.DebugFormat("Fetching mesh {0}", meshStr); 68 if (m_assetService == null)
69 {
70 m_log.Error("[GETMESH]: Cannot fetch mesh " + textureStr + " without an asset service");
71 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
72 }
67 73
68 UUID meshID = UUID.Zero; 74 UUID meshID;
69 if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID)) 75 if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out meshID))
70 { 76 {
71 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))
72 { 83 {
73 httpResponse.StatusCode = 404; 84 // Only try to fetch locally cached meshes. Misses are redirected
74 httpResponse.ContentType = "text/plain"; 85 mesh = m_assetService.GetCached(meshID.ToString());
75 byte[] data = Encoding.UTF8.GetBytes("The asset service is unavailable. So is your mesh."); 86
76 httpResponse.Body.Write(data, 0, data.Length); 87 if (mesh != null)
77 return 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 }
78 } 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());
79 113
80 AssetBase mesh = m_assetService.Get(meshID.ToString()); 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 }
81 136
82 if (mesh != null) 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");
152
153 if (!String.IsNullOrEmpty(range))
154 {
155 // Range request
156 int start, end;
157 if (TryParseRange(range, out start, out end))
83 { 158 {
84 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)
85 { 162 {
86 byte[] data = mesh.Data; 163 response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
87 httpResponse.Body.Write(data, 0, data.Length); 164 response.ContentType = texture.Metadata.ContentType;
88 httpResponse.ContentType = "application/vnd.ll.mesh";
89 httpResponse.StatusCode = 200;
90 } 165 }
91 // Optionally add additional mesh types here
92 else 166 else
93 { 167 {
94 httpResponse.StatusCode = 404; 168 // Handle the case where no second range value was given. This is equivalent to requesting
95 httpResponse.ContentType = "text/plain"; 169 // the rest of the entity.
96 byte[] data = Encoding.UTF8.GetBytes("Unfortunately, this asset isn't a mesh."); 170 if (end == -1)
97 httpResponse.Body.Write(data, 0, data.Length); 171 end = int.MaxValue;
98 httpResponse.KeepAlive = false; 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);
99 } 191 }
100 } 192 }
101 else 193 else
102 { 194 {
103 httpResponse.StatusCode = 404; 195 m_log.Warn("[GETMESH]: Malformed Range header: " + range);
104 httpResponse.ContentType = "text/plain"; 196 response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
105 byte[] data = Encoding.UTF8.GetBytes("Your Mesh wasn't found. Sorry!");
106 httpResponse.Body.Write(data, 0, data.Length);
107 httpResponse.KeepAlive = false;
108 } 197 }
109 } 198 }
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 }
110 208
111 return null; 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 }
246 }
247 }
248
249 start = end = 0;
250 return false;
112 } 251 }
113 } 252 }
114} \ No newline at end of file 253} \ No newline at end of file
diff --git a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs
index 9c53862..19de3cf 100644
--- a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs
+++ b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs
@@ -25,16 +25,13 @@
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 Nini.Config; 28using Nini.Config;
31using OpenSim.Server.Base; 29using OpenMetaverse;
32using OpenSim.Services.Interfaces;
33using OpenSim.Framework.Servers.HttpServer; 30using OpenSim.Framework.Servers.HttpServer;
31using OpenSim.Server.Base;
34using OpenSim.Server.Handlers.Base; 32using OpenSim.Server.Handlers.Base;
35using OpenSim.Framework.Servers; 33using OpenSim.Services.Interfaces;
36 34using System;
37using OpenMetaverse;
38 35
39namespace OpenSim.Capabilities.Handlers 36namespace OpenSim.Capabilities.Handlers
40{ 37{
@@ -65,8 +62,15 @@ namespace OpenSim.Capabilities.Handlers
65 if (m_AssetService == null) 62 if (m_AssetService == null)
66 throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName)); 63 throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName));
67 64
65 string rurl = serverConfig.GetString("GetMeshRedirectURL");
66
67 server.AddStreamHandler(
68 new GetTextureHandler("/CAPS/GetMesh/" /*+ UUID.Random() */, m_AssetService, "GetMesh", null, rurl));
69
70 rurl = serverConfig.GetString("GetMesh2RedirectURL");
71
68 server.AddStreamHandler( 72 server.AddStreamHandler(
69 new GetMeshHandler("/CAPS/GetMesh/" /*+ UUID.Random() */, m_AssetService, "GetMesh", null)); 73 new GetTextureHandler("/CAPS/GetMesh2/" /*+ UUID.Random() */, m_AssetService, "GetMesh2", null, rurl));
70 } 74 }
71 } 75 }
72} \ No newline at end of file 76} \ No newline at end of file