aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorMelanie2012-09-14 21:24:25 +0200
committerMelanie2012-09-14 21:24:25 +0200
commit387e59ff7f60f2b12526eaacd93581f76abe26e1 (patch)
tree6255bde5b8aed92e48eee4fb2e1caa03819c355f
parentAllow setting connection limits, part 2 (diff)
downloadopensim-SC-387e59ff7f60f2b12526eaacd93581f76abe26e1.zip
opensim-SC-387e59ff7f60f2b12526eaacd93581f76abe26e1.tar.gz
opensim-SC-387e59ff7f60f2b12526eaacd93581f76abe26e1.tar.bz2
opensim-SC-387e59ff7f60f2b12526eaacd93581f76abe26e1.tar.xz
Revamp the HTTP textures handler to allow a maximum of four fetches
at any time and to drop requests for avatars n longer in the scene
Diffstat (limited to '')
-rw-r--r--OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs167
-rw-r--r--OpenSim/Capabilities/Handlers/GetTexture/GetTextureServerConnector.cs6
-rw-r--r--OpenSim/Capabilities/Handlers/GetTexture/Tests/GetTextureHandlerTests.cs4
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs47
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs3
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs3
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs223
7 files changed, 295 insertions, 158 deletions
diff --git a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
index f040ff7..4e755aa 100644
--- a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
+++ b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
@@ -47,36 +47,36 @@ using Caps = OpenSim.Framework.Capabilities.Caps;
47 47
48namespace OpenSim.Capabilities.Handlers 48namespace OpenSim.Capabilities.Handlers
49{ 49{
50 public class GetTextureHandler : BaseStreamHandler 50 public class GetTextureHandler
51 { 51 {
52 private static readonly ILog m_log = 52 private static readonly ILog m_log =
53 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 53 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54
54 private IAssetService m_assetService; 55 private IAssetService m_assetService;
55 56
56 public const string DefaultFormat = "x-j2c"; 57 public const string DefaultFormat = "x-j2c";
57 58
58 // TODO: Change this to a config option 59 public GetTextureHandler(IAssetService assService)
59 const string REDIRECT_URL = null;
60
61 public GetTextureHandler(string path, IAssetService assService, string name, string description)
62 : base("GET", path, name, description)
63 { 60 {
64 m_assetService = assService; 61 m_assetService = assService;
65 } 62 }
66 63
67 public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) 64 public Hashtable Handle(Hashtable request)
68 { 65 {
69 // Try to parse the texture ID from the request URL 66 Hashtable ret = new Hashtable();
70 NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); 67 ret["int_response_code"] = (int)System.Net.HttpStatusCode.NotFound;
71 string textureStr = query.GetOne("texture_id"); 68 ret["content_type"] = "text/plain";
72 string format = query.GetOne("format"); 69 ret["keepalive"] = false;
70 ret["reusecontext"] = false;
71
72 string textureStr = (string)request["texture_id"];
73 string format = (string)request["format"];
73 74
74 //m_log.DebugFormat("[GETTEXTURE]: called {0}", textureStr); 75 //m_log.DebugFormat("[GETTEXTURE]: called {0}", textureStr);
75 76
76 if (m_assetService == null) 77 if (m_assetService == null)
77 { 78 {
78 m_log.Error("[GETTEXTURE]: Cannot fetch texture " + textureStr + " without an asset service"); 79 m_log.Error("[GETTEXTURE]: Cannot fetch texture " + textureStr + " without an asset service");
79 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
80 } 80 }
81 81
82 UUID textureID; 82 UUID textureID;
@@ -91,30 +91,30 @@ namespace OpenSim.Capabilities.Handlers
91 } 91 }
92 else 92 else
93 { 93 {
94 formats = WebUtil.GetPreferredImageTypes(httpRequest.Headers.Get("Accept")); 94 formats = new string[1] { DefaultFormat }; // default
95 if (((Hashtable)request["headers"])["Accept"] != null)
96 formats = WebUtil.GetPreferredImageTypes((string)((Hashtable)request["headers"])["Accept"]);
95 if (formats.Length == 0) 97 if (formats.Length == 0)
96 formats = new string[1] { DefaultFormat }; // default 98 formats = new string[1] { DefaultFormat }; // default
97 99
98 } 100 }
99 // OK, we have an array with preferred formats, possibly with only one entry 101 // OK, we have an array with preferred formats, possibly with only one entry
100 102
101 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
102 foreach (string f in formats) 103 foreach (string f in formats)
103 { 104 {
104 if (FetchTexture(httpRequest, httpResponse, textureID, f)) 105 if (FetchTexture(request, ret, textureID, f))
105 break; 106 break;
106 } 107 }
107 } 108 }
108 else 109 else
109 { 110 {
110 m_log.Warn("[GETTEXTURE]: Failed to parse a texture_id from GetTexture request: " + httpRequest.Url); 111 m_log.Warn("[GETTEXTURE]: Failed to parse a texture_id from GetTexture request: " + (string)request["uri"]);
111 } 112 }
112 113
113// m_log.DebugFormat( 114// m_log.DebugFormat(
114// "[GETTEXTURE]: For texture {0} sending back response {1}, data length {2}", 115// "[GETTEXTURE]: For texture {0} sending back response {1}, data length {2}",
115// textureID, httpResponse.StatusCode, httpResponse.ContentLength); 116// textureID, httpResponse.StatusCode, httpResponse.ContentLength);
116 117 return ret;
117 return null;
118 } 118 }
119 119
120 /// <summary> 120 /// <summary>
@@ -125,7 +125,7 @@ namespace OpenSim.Capabilities.Handlers
125 /// <param name="textureID"></param> 125 /// <param name="textureID"></param>
126 /// <param name="format"></param> 126 /// <param name="format"></param>
127 /// <returns>False for "caller try another codec"; true otherwise</returns> 127 /// <returns>False for "caller try another codec"; true otherwise</returns>
128 private bool FetchTexture(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse, UUID textureID, string format) 128 private bool FetchTexture(Hashtable request, Hashtable response, UUID textureID, string format)
129 { 129 {
130// m_log.DebugFormat("[GETTEXTURE]: {0} with requested format {1}", textureID, format); 130// m_log.DebugFormat("[GETTEXTURE]: {0} with requested format {1}", textureID, format);
131 AssetBase texture; 131 AssetBase texture;
@@ -134,84 +134,61 @@ namespace OpenSim.Capabilities.Handlers
134 if (format != DefaultFormat) 134 if (format != DefaultFormat)
135 fullID = fullID + "-" + format; 135 fullID = fullID + "-" + format;
136 136
137 if (!String.IsNullOrEmpty(REDIRECT_URL)) 137 // try the cache
138 texture = m_assetService.GetCached(fullID);
139
140 if (texture == null)
138 { 141 {
139 // Only try to fetch locally cached textures. Misses are redirected 142 //m_log.DebugFormat("[GETTEXTURE]: texture was not in the cache");
140 texture = m_assetService.GetCached(fullID); 143
144 // Fetch locally or remotely. Misses return a 404
145 texture = m_assetService.Get(textureID.ToString());
141 146
142 if (texture != null) 147 if (texture != null)
143 { 148 {
144 if (texture.Type != (sbyte)AssetType.Texture) 149 if (texture.Type != (sbyte)AssetType.Texture)
150 return true;
151
152 if (format == DefaultFormat)
145 { 153 {
146 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; 154 WriteTextureData(request, response, texture, format);
147 return true; 155 return true;
148 } 156 }
149 WriteTextureData(httpRequest, httpResponse, texture, format); 157 else
150 }
151 else
152 {
153 string textureUrl = REDIRECT_URL + textureID.ToString();
154 m_log.Debug("[GETTEXTURE]: Redirecting texture request to " + textureUrl);
155 httpResponse.RedirectLocation = textureUrl;
156 return true;
157 }
158 }
159 else // no redirect
160 {
161 // try the cache
162 texture = m_assetService.GetCached(fullID);
163
164 if (texture == null)
165 {
166 //m_log.DebugFormat("[GETTEXTURE]: texture was not in the cache");
167
168 // Fetch locally or remotely. Misses return a 404
169 texture = m_assetService.Get(textureID.ToString());
170
171 if (texture != null)
172 { 158 {
173 if (texture.Type != (sbyte)AssetType.Texture) 159 AssetBase newTexture = new AssetBase(texture.ID + "-" + format, texture.Name, (sbyte)AssetType.Texture, texture.Metadata.CreatorID);
174 { 160 newTexture.Data = ConvertTextureData(texture, format);
175 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; 161 if (newTexture.Data.Length == 0)
176 return true; 162 return false; // !!! Caller try another codec, please!
177 } 163
178 if (format == DefaultFormat) 164 newTexture.Flags = AssetFlags.Collectable;
179 { 165 newTexture.Temporary = true;
180 WriteTextureData(httpRequest, httpResponse, texture, format); 166 m_assetService.Store(newTexture);
181 return true; 167 WriteTextureData(request, response, newTexture, format);
182 } 168 return true;
183 else
184 {
185 AssetBase newTexture = new AssetBase(texture.ID + "-" + format, texture.Name, (sbyte)AssetType.Texture, texture.Metadata.CreatorID);
186 newTexture.Data = ConvertTextureData(texture, format);
187 if (newTexture.Data.Length == 0)
188 return false; // !!! Caller try another codec, please!
189
190 newTexture.Flags = AssetFlags.Collectable;
191 newTexture.Temporary = true;
192 m_assetService.Store(newTexture);
193 WriteTextureData(httpRequest, httpResponse, newTexture, format);
194 return true;
195 }
196 } 169 }
197 } 170 }
198 else // it was on the cache 171 }
199 { 172 else // it was on the cache
200 //m_log.DebugFormat("[GETTEXTURE]: texture was in the cache"); 173 {
201 WriteTextureData(httpRequest, httpResponse, texture, format); 174 //m_log.DebugFormat("[GETTEXTURE]: texture was in the cache");
202 return true; 175 WriteTextureData(request, response, texture, format);
203 } 176 return true;
204 } 177 }
205 178
206 // not found 179 // not found
207// m_log.Warn("[GETTEXTURE]: Texture " + textureID + " not found"); 180// m_log.Warn("[GETTEXTURE]: Texture " + textureID + " not found");
208 httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
209 return true; 181 return true;
210 } 182 }
211 183
212 private void WriteTextureData(IOSHttpRequest request, IOSHttpResponse response, AssetBase texture, string format) 184 private void WriteTextureData(Hashtable request, Hashtable response, AssetBase texture, string format)
213 { 185 {
214 string range = request.Headers.GetOne("Range"); 186 Hashtable headers = new Hashtable();
187 response["headers"] = headers;
188
189 string range = String.Empty;
190 if (((Hashtable)request["headers"])["Range"] != null)
191 range = (string)((Hashtable)request["headers"])["Range"];
215 192
216 if (!String.IsNullOrEmpty(range)) // JP2's only 193 if (!String.IsNullOrEmpty(range)) // JP2's only
217 { 194 {
@@ -226,7 +203,7 @@ namespace OpenSim.Capabilities.Handlers
226 { 203 {
227// response.StatusCode = (int)System.Net.HttpStatusCode.RequestedRangeNotSatisfiable; 204// response.StatusCode = (int)System.Net.HttpStatusCode.RequestedRangeNotSatisfiable;
228 // viewers don't seem to handle RequestedRangeNotSatisfiable and keep retrying with same parameters 205 // viewers don't seem to handle RequestedRangeNotSatisfiable and keep retrying with same parameters
229 response.StatusCode = (int)System.Net.HttpStatusCode.NotFound; 206 response["int_response_code"] = (int)System.Net.HttpStatusCode.NotFound;
230 } 207 }
231 else 208 else
232 { 209 {
@@ -240,31 +217,33 @@ namespace OpenSim.Capabilities.Handlers
240 // We were accidentally sending back 404 before in this situation 217 // We were accidentally sending back 404 before in this situation
241 // https://issues.apache.org/bugzilla/show_bug.cgi?id=51878 supports sending 206 even if the 218 // https://issues.apache.org/bugzilla/show_bug.cgi?id=51878 supports sending 206 even if the
242 // entire range is requested, and viewer 3.2.2 (and very probably earlier) seems fine with this. 219 // entire range is requested, and viewer 3.2.2 (and very probably earlier) seems fine with this.
243 response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent; 220 response["int_response_code"] = (int)System.Net.HttpStatusCode.PartialContent;
244 221 response["content-type"] = texture.Metadata.ContentType;
245 response.ContentLength = len; 222 headers["Content-Range"] = String.Format("bytes {0}-{1}/{2}", start, end, texture.Data.Length);
246 response.ContentType = texture.Metadata.ContentType;
247 response.AddHeader("Content-Range", String.Format("bytes {0}-{1}/{2}", start, end, texture.Data.Length));
248 223
249 response.Body.Write(texture.Data, start, len); 224 byte[] d = new byte[len];
225 Array.Copy(texture.Data, start, d, 0, len);
226 response["bin_response_data"] = d;
227// response.Body.Write(texture.Data, start, len);
250 } 228 }
251 } 229 }
252 else 230 else
253 { 231 {
254 m_log.Warn("[GETTEXTURE]: Malformed Range header: " + range); 232 m_log.Warn("[GETTEXTURE]: Malformed Range header: " + range);
255 response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest; 233 response["int_response_code"] = (int)System.Net.HttpStatusCode.BadRequest;
256 } 234 }
257 } 235 }
258 else // JP2's or other formats 236 else // JP2's or other formats
259 { 237 {
260 // Full content request 238 // Full content request
261 response.StatusCode = (int)System.Net.HttpStatusCode.OK; 239 response["int_response_code"] = (int)System.Net.HttpStatusCode.OK;
262 response.ContentLength = texture.Data.Length;
263 if (format == DefaultFormat) 240 if (format == DefaultFormat)
264 response.ContentType = texture.Metadata.ContentType; 241 response["content_type"] = texture.Metadata.ContentType;
265 else 242 else
266 response.ContentType = "image/" + format; 243 response["content_type"] = "image/" + format;
267 response.Body.Write(texture.Data, 0, texture.Data.Length); 244
245 response["bin_response_data"] = texture.Data;
246// response.Body.Write(texture.Data, 0, texture.Data.Length);
268 } 247 }
269 248
270// if (response.StatusCode < 200 || response.StatusCode > 299) 249// if (response.StatusCode < 200 || response.StatusCode > 299)
@@ -368,4 +347,4 @@ namespace OpenSim.Capabilities.Handlers
368 return null; 347 return null;
369 } 348 }
370 } 349 }
371} \ No newline at end of file 350}
diff --git a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureServerConnector.cs b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureServerConnector.cs
index 71cf033..bf66acb 100644
--- a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureServerConnector.cs
+++ b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureServerConnector.cs
@@ -33,6 +33,7 @@ using OpenSim.Framework.Servers.HttpServer;
33using OpenSim.Server.Handlers.Base; 33using OpenSim.Server.Handlers.Base;
34using OpenMetaverse; 34using OpenMetaverse;
35 35
36/*
36namespace OpenSim.Capabilities.Handlers 37namespace OpenSim.Capabilities.Handlers
37{ 38{
38 public class GetTextureServerConnector : ServiceConnector 39 public class GetTextureServerConnector : ServiceConnector
@@ -63,7 +64,8 @@ namespace OpenSim.Capabilities.Handlers
63 throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName)); 64 throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName));
64 65
65 server.AddStreamHandler( 66 server.AddStreamHandler(
66 new GetTextureHandler("/CAPS/GetTexture/" /*+ UUID.Random() */, m_AssetService, "GetTexture", null)); 67 new GetTextureHandler("/CAPS/GetTexture/", m_AssetService, "GetTexture", null));
67 } 68 }
68 } 69 }
69} \ No newline at end of file 70}
71*/
diff --git a/OpenSim/Capabilities/Handlers/GetTexture/Tests/GetTextureHandlerTests.cs b/OpenSim/Capabilities/Handlers/GetTexture/Tests/GetTextureHandlerTests.cs
index 761e4e7..b6ae41b 100644
--- a/OpenSim/Capabilities/Handlers/GetTexture/Tests/GetTextureHandlerTests.cs
+++ b/OpenSim/Capabilities/Handlers/GetTexture/Tests/GetTextureHandlerTests.cs
@@ -39,6 +39,7 @@ using OpenSim.Region.Framework.Scenes;
39using OpenSim.Tests.Common; 39using OpenSim.Tests.Common;
40using OpenSim.Tests.Common.Mock; 40using OpenSim.Tests.Common.Mock;
41 41
42/*
42namespace OpenSim.Capabilities.Handlers.GetTexture.Tests 43namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
43{ 44{
44 [TestFixture] 45 [TestFixture]
@@ -60,4 +61,5 @@ namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
60 Assert.That(resp.StatusCode, Is.EqualTo((int)System.Net.HttpStatusCode.NotFound)); 61 Assert.That(resp.StatusCode, Is.EqualTo((int)System.Net.HttpStatusCode.NotFound));
61 } 62 }
62 } 63 }
63} \ No newline at end of file 64}
65*/
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
index 57c9d7c..6121371 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -1449,7 +1449,8 @@ namespace OpenSim.Framework.Servers.HttpServer
1449 internal byte[] DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response) 1449 internal byte[] DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response)
1450 { 1450 {
1451 int responsecode; 1451 int responsecode;
1452 string responseString; 1452 string responseString = String.Empty;
1453 byte[] responseData = null;
1453 string contentType; 1454 string contentType;
1454 1455
1455 if (responsedata == null) 1456 if (responsedata == null)
@@ -1465,7 +1466,10 @@ namespace OpenSim.Framework.Servers.HttpServer
1465 { 1466 {
1466 //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response"); 1467 //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response");
1467 responsecode = (int)responsedata["int_response_code"]; 1468 responsecode = (int)responsedata["int_response_code"];
1468 responseString = (string)responsedata["str_response_string"]; 1469 if (responsedata["bin_response_data"] != null)
1470 responseData = (byte[])responsedata["bin_response_data"];
1471 else
1472 responseString = (string)responsedata["str_response_string"];
1469 contentType = (string)responsedata["content_type"]; 1473 contentType = (string)responsedata["content_type"];
1470 } 1474 }
1471 catch 1475 catch
@@ -1520,25 +1524,40 @@ namespace OpenSim.Framework.Servers.HttpServer
1520 1524
1521 response.AddHeader("Content-Type", contentType); 1525 response.AddHeader("Content-Type", contentType);
1522 1526
1527 if (responsedata.ContainsKey("headers"))
1528 {
1529 Hashtable headerdata = (Hashtable)responsedata["headers"];
1530
1531 foreach (string header in headerdata.Keys)
1532 response.AddHeader(header, (string)headerdata[header]);
1533 }
1534
1523 byte[] buffer; 1535 byte[] buffer;
1524 1536
1525 if (!(contentType.Contains("image") 1537 if (responseData != null)
1526 || contentType.Contains("x-shockwave-flash")
1527 || contentType.Contains("application/x-oar")
1528 || contentType.Contains("application/vnd.ll.mesh")))
1529 { 1538 {
1530 // Text 1539 buffer = responseData;
1531 buffer = Encoding.UTF8.GetBytes(responseString);
1532 } 1540 }
1533 else 1541 else
1534 { 1542 {
1535 // Binary! 1543 if (!(contentType.Contains("image")
1536 buffer = Convert.FromBase64String(responseString); 1544 || contentType.Contains("x-shockwave-flash")
1537 } 1545 || contentType.Contains("application/x-oar")
1546 || contentType.Contains("application/vnd.ll.mesh")))
1547 {
1548 // Text
1549 buffer = Encoding.UTF8.GetBytes(responseString);
1550 }
1551 else
1552 {
1553 // Binary!
1554 buffer = Convert.FromBase64String(responseString);
1555 }
1538 1556
1539 response.SendChunked = false; 1557 response.SendChunked = false;
1540 response.ContentLength64 = buffer.Length; 1558 response.ContentLength64 = buffer.Length;
1541 response.ContentEncoding = Encoding.UTF8; 1559 response.ContentEncoding = Encoding.UTF8;
1560 }
1542 1561
1543 return buffer; 1562 return buffer;
1544 } 1563 }
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
index c24a000..a80b1d7 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
@@ -52,7 +52,8 @@ namespace OpenSim.Framework.Servers.HttpServer
52 { 52 {
53 Normal = 0, 53 Normal = 0,
54 LslHttp = 1, 54 LslHttp = 1,
55 Inventory = 2 55 Inventory = 2,
56 Texture = 3
56 } 57 }
57 58
58 public PollServiceEventArgs( 59 public PollServiceEventArgs(
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
index a1dee4e..db088e7 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
@@ -231,8 +231,7 @@ namespace OpenSim.Framework.Servers.HttpServer
231 { 231 {
232 if (m_running) 232 if (m_running)
233 { 233 {
234 if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LslHttp || 234 if (req.PollServiceArgs.Type != PollServiceEventArgs.EventType.Normal)
235 req.PollServiceArgs.Type == PollServiceEventArgs.EventType.Inventory)
236 { 235 {
237 m_requests.Enqueue(req); 236 m_requests.Enqueue(req);
238 } 237 }
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
index 5ae9cc3..5b125ea 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
@@ -27,18 +27,13 @@
27 27
28using System; 28using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Specialized; 30using System.Collections.Generic;
31using System.Drawing;
32using System.Drawing.Imaging;
33using System.Reflection; 31using System.Reflection;
34using System.IO; 32using System.Threading;
35using System.Web;
36using log4net; 33using log4net;
37using Nini.Config; 34using Nini.Config;
38using Mono.Addins; 35using Mono.Addins;
39using OpenMetaverse; 36using OpenMetaverse;
40using OpenMetaverse.StructuredData;
41using OpenMetaverse.Imaging;
42using OpenSim.Framework; 37using OpenSim.Framework;
43using OpenSim.Framework.Servers; 38using OpenSim.Framework.Servers;
44using OpenSim.Framework.Servers.HttpServer; 39using OpenSim.Framework.Servers.HttpServer;
@@ -47,64 +42,73 @@ using OpenSim.Region.Framework.Scenes;
47using OpenSim.Services.Interfaces; 42using OpenSim.Services.Interfaces;
48using Caps = OpenSim.Framework.Capabilities.Caps; 43using Caps = OpenSim.Framework.Capabilities.Caps;
49using OpenSim.Capabilities.Handlers; 44using OpenSim.Capabilities.Handlers;
45using OpenSim.Framework.Monitoring;
50 46
51namespace OpenSim.Region.ClientStack.Linden 47namespace OpenSim.Region.ClientStack.Linden
52{ 48{
53 49
54 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] 50 /// <summary>
51 /// This module implements both WebFetchTextureDescendents and FetchTextureDescendents2 capabilities.
52 /// </summary>
53 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GetTextureModule")]
55 public class GetTextureModule : INonSharedRegionModule 54 public class GetTextureModule : INonSharedRegionModule
56 { 55 {
57// private static readonly ILog m_log = 56 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
58// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 57
59
60 private Scene m_scene; 58 private Scene m_scene;
61 private IAssetService m_assetService;
62 59
63 private bool m_Enabled = false; 60 private static GetTextureHandler m_getTextureHandler;
61
62 private IAssetService m_assetService = null;
64 63
65 // TODO: Change this to a config option 64 private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>();
66 const string REDIRECT_URL = null; 65 private static Thread[] m_workerThreads = null;
67 66
68 private string m_URL; 67 private static OpenMetaverse.BlockingQueue<PollServiceTextureEventArgs> m_queue =
68 new OpenMetaverse.BlockingQueue<PollServiceTextureEventArgs>();
69 69
70 #region ISharedRegionModule Members 70 #region ISharedRegionModule Members
71 71
72 public void Initialise(IConfigSource source) 72 public void Initialise(IConfigSource source)
73 { 73 {
74 IConfig config = source.Configs["ClientStack.LindenCaps"];
75 if (config == null)
76 return;
77
78 m_URL = config.GetString("Cap_GetTexture", string.Empty);
79 // Cap doesn't exist
80 if (m_URL != string.Empty)
81 m_Enabled = true;
82 } 74 }
83 75
84 public void AddRegion(Scene s) 76 public void AddRegion(Scene s)
85 { 77 {
86 if (!m_Enabled)
87 return;
88
89 m_scene = s; 78 m_scene = s;
79 m_assetService = s.AssetService;
90 } 80 }
91 81
92 public void RemoveRegion(Scene s) 82 public void RemoveRegion(Scene s)
93 { 83 {
94 if (!m_Enabled)
95 return;
96
97 m_scene.EventManager.OnRegisterCaps -= RegisterCaps; 84 m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
85 m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
98 m_scene = null; 86 m_scene = null;
99 } 87 }
100 88
101 public void RegionLoaded(Scene s) 89 public void RegionLoaded(Scene s)
102 { 90 {
103 if (!m_Enabled) 91 // We'll reuse the same handler for all requests.
104 return; 92 m_getTextureHandler = new GetTextureHandler(m_assetService);
105 93
106 m_assetService = m_scene.RequestModuleInterface<IAssetService>();
107 m_scene.EventManager.OnRegisterCaps += RegisterCaps; 94 m_scene.EventManager.OnRegisterCaps += RegisterCaps;
95 m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
96
97 if (m_workerThreads == null)
98 {
99 m_workerThreads = new Thread[4];
100
101 for (uint i = 0; i < 4; i++)
102 {
103 m_workerThreads[i] = Watchdog.StartThread(DoTextureRequests,
104 String.Format("TextureWorkerThread{0}", i),
105 ThreadPriority.Normal,
106 false,
107 true,
108 null,
109 int.MaxValue);
110 }
111 }
108 } 112 }
109 113
110 public void PostInitialise() 114 public void PostInitialise()
@@ -122,24 +126,155 @@ namespace OpenSim.Region.ClientStack.Linden
122 126
123 #endregion 127 #endregion
124 128
125 public void RegisterCaps(UUID agentID, Caps caps) 129 ~GetTextureModule()
130 {
131 foreach (Thread t in m_workerThreads)
132 t.Abort();
133 }
134
135 private class PollServiceTextureEventArgs : PollServiceEventArgs
126 { 136 {
127 UUID capID = UUID.Random(); 137 private List<Hashtable> requests =
138 new List<Hashtable>();
139 private Dictionary<UUID, Hashtable> responses =
140 new Dictionary<UUID, Hashtable>();
141
142 private Scene m_scene;
128 143
129 //caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture)); 144 public PollServiceTextureEventArgs(UUID pId, Scene scene) :
130 if (m_URL == "localhost") 145 base(null, null, null, null, pId, 30000)
131 { 146 {
132// m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); 147 m_scene = scene;
133 caps.RegisterHandler( 148
134 "GetTexture", 149 HasEvents = (x, y) => { return this.responses.ContainsKey(x); };
135 new GetTextureHandler("/CAPS/" + capID + "/", m_assetService, "GetTexture", agentID.ToString())); 150 GetEvents = (x, y, s) =>
151 {
152 try
153 {
154 return this.responses[x];
155 }
156 finally
157 {
158 responses.Remove(x);
159 }
160 };
161
162 Request = (x, y) =>
163 {
164 y["RequestID"] = x.ToString();
165 lock (this.requests)
166 this.requests.Add(y);
167
168 m_queue.Enqueue(this);
169 };
170
171 NoEvents = (x, y) =>
172 {
173 lock (this.requests)
174 {
175 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
176 requests.Remove(request);
177 }
178
179 Hashtable response = new Hashtable();
180
181 response["int_response_code"] = 500;
182 response["str_response_string"] = "Script timeout";
183 response["content_type"] = "text/plain";
184 response["keepalive"] = false;
185 response["reusecontext"] = false;
186
187 return response;
188 };
136 } 189 }
137 else 190
191 public void Process()
138 { 192 {
139// m_log.DebugFormat("[GETTEXTURE]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName); 193 Hashtable response;
140 caps.RegisterHandler("GetTexture", m_URL); 194 Hashtable request = null;
195
196 try
197 {
198 lock (this.requests)
199 {
200 request = requests[0];
201 requests.RemoveAt(0);
202 }
203 }
204 catch
205 {
206 return;
207 }
208
209 UUID requestID = new UUID(request["RequestID"].ToString());
210
211 // If the avatar is gone, don't bother to get the texture
212 if (m_scene.GetScenePresence(Id) == null)
213 {
214 response = new Hashtable();
215
216 response["int_response_code"] = 500;
217 response["str_response_string"] = "Script timeout";
218 response["content_type"] = "text/plain";
219 response["keepalive"] = false;
220 response["reusecontext"] = false;
221
222 responses[requestID] = response;
223 return;
224 }
225
226 response = m_getTextureHandler.Handle(request);
227
228 responses[requestID] = response;
229 }
230 }
231
232 private void RegisterCaps(UUID agentID, Caps caps)
233 {
234 string capUrl = "/CAPS/" + UUID.Random() + "/";
235
236 // Register this as a poll service
237 // absurd large timeout to tune later to make a bit less than viewer
238 PollServiceTextureEventArgs args = new PollServiceTextureEventArgs(agentID, m_scene);
239
240 args.Type = PollServiceEventArgs.EventType.Texture;
241 MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
242
243 string hostName = m_scene.RegionInfo.ExternalHostName;
244 uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
245 string protocol = "http";
246
247 if (MainServer.Instance.UseSSL)
248 {
249 hostName = MainServer.Instance.SSLCommonName;
250 port = MainServer.Instance.SSLPort;
251 protocol = "https";
141 } 252 }
253 caps.RegisterHandler("GetTexture", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
254
255 m_capsDict[agentID] = capUrl;
142 } 256 }
143 257
258 private void DeregisterCaps(UUID agentID, Caps caps)
259 {
260 string capUrl;
261
262 if (m_capsDict.TryGetValue(agentID, out capUrl))
263 {
264 MainServer.Instance.RemoveHTTPHandler("", capUrl);
265 m_capsDict.Remove(agentID);
266 }
267 }
268
269 private void DoTextureRequests()
270 {
271 while (true)
272 {
273 PollServiceTextureEventArgs args = m_queue.Dequeue();
274
275 args.Process();
276 }
277 }
144 } 278 }
279
145} 280}