aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs412
1 files changed, 374 insertions, 38 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs
index bb932f2..b01c7dc 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,6 +42,7 @@ 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{
@@ -54,27 +50,49 @@ namespace OpenSim.Region.ClientStack.Linden
54 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GetTextureModule")] 50 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GetTextureModule")]
55 public class GetTextureModule : INonSharedRegionModule 51 public class GetTextureModule : INonSharedRegionModule
56 { 52 {
57// private static readonly ILog m_log = 53
58// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 54 struct aPollRequest
59 55 {
56 public PollServiceTextureEventArgs thepoll;
57 public UUID reqID;
58 public Hashtable request;
59 public bool send503;
60 }
61
62 public class aPollResponse
63 {
64 public Hashtable response;
65 public int bytes;
66 }
67
68
69 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
70
60 private Scene m_scene; 71 private Scene m_scene;
61 private IAssetService m_assetService;
62 72
63 private bool m_Enabled = false; 73 private static GetTextureHandler m_getTextureHandler;
74
75 private IAssetService m_assetService = null;
76
77 private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>();
78 private static Thread[] m_workerThreads = null;
79 private static int m_NumberScenes = 0;
80 private static OpenSim.Framework.BlockingQueue<aPollRequest> m_queue =
81 new OpenSim.Framework.BlockingQueue<aPollRequest>();
64 82
65 // TODO: Change this to a config option 83 private Dictionary<UUID,PollServiceTextureEventArgs> m_pollservices = new Dictionary<UUID,PollServiceTextureEventArgs>();
66 private string m_RedirectURL = null;
67 84
68 private string m_URL; 85 private string m_Url = "localhost";
69 86
70 #region ISharedRegionModule Members 87 #region ISharedRegionModule Members
71 88
72 public void Initialise(IConfigSource source) 89 public void Initialise(IConfigSource source)
73 { 90 {
74 IConfig config = source.Configs["ClientStack.LindenCaps"]; 91 IConfig config = source.Configs["ClientStack.LindenCaps"];
92
75 if (config == null) 93 if (config == null)
76 return; 94 return;
77 95/*
78 m_URL = config.GetString("Cap_GetTexture", string.Empty); 96 m_URL = config.GetString("Cap_GetTexture", string.Empty);
79 // Cap doesn't exist 97 // Cap doesn't exist
80 if (m_URL != string.Empty) 98 if (m_URL != string.Empty)
@@ -82,39 +100,108 @@ namespace OpenSim.Region.ClientStack.Linden
82 m_Enabled = true; 100 m_Enabled = true;
83 m_RedirectURL = config.GetString("GetTextureRedirectURL"); 101 m_RedirectURL = config.GetString("GetTextureRedirectURL");
84 } 102 }
103*/
104 m_Url = config.GetString("Cap_GetTexture", "localhost");
85 } 105 }
86 106
87 public void AddRegion(Scene s) 107 public void AddRegion(Scene s)
88 { 108 {
89 if (!m_Enabled)
90 return;
91
92 m_scene = s; 109 m_scene = s;
110 m_assetService = s.AssetService;
93 } 111 }
94 112
95 public void RemoveRegion(Scene s) 113 public void RemoveRegion(Scene s)
96 { 114 {
97 if (!m_Enabled)
98 return;
99
100 m_scene.EventManager.OnRegisterCaps -= RegisterCaps; 115 m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
116 m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
117 m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate;
118 m_NumberScenes--;
101 m_scene = null; 119 m_scene = null;
102 } 120 }
103 121
104 public void RegionLoaded(Scene s) 122 public void RegionLoaded(Scene s)
105 { 123 {
106 if (!m_Enabled) 124 // We'll reuse the same handler for all requests.
107 return; 125 m_getTextureHandler = new GetTextureHandler(m_assetService);
108 126
109 m_assetService = m_scene.RequestModuleInterface<IAssetService>();
110 m_scene.EventManager.OnRegisterCaps += RegisterCaps; 127 m_scene.EventManager.OnRegisterCaps += RegisterCaps;
128 m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
129 m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate;
130
131 m_NumberScenes++;
132
133 if (m_workerThreads == null)
134 {
135 m_workerThreads = new Thread[2];
136
137 for (uint i = 0; i < 2; i++)
138 {
139 m_workerThreads[i] = WorkManager.StartThread(DoTextureRequests,
140 String.Format("GetTextureWorker{0}", i),
141 ThreadPriority.Normal,
142 true,
143 false,
144 null,
145 int.MaxValue);
146 }
147 }
148 }
149 private int ExtractImageThrottle(byte[] pthrottles)
150 {
151
152 byte[] adjData;
153 int pos = 0;
154
155 if (!BitConverter.IsLittleEndian)
156 {
157 byte[] newData = new byte[7 * 4];
158 Buffer.BlockCopy(pthrottles, 0, newData, 0, 7 * 4);
159
160 for (int i = 0; i < 7; i++)
161 Array.Reverse(newData, i * 4, 4);
162
163 adjData = newData;
164 }
165 else
166 {
167 adjData = pthrottles;
168 }
169
170 pos = pos + 20;
171 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); //pos += 4;
172 //int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
173 return texture;
174 }
175
176 // Now we know when the throttle is changed by the client in the case of a root agent or by a neighbor region in the case of a child agent.
177 public void ThrottleUpdate(ScenePresence p)
178 {
179 byte[] throttles = p.ControllingClient.GetThrottlesPacked(1);
180 UUID user = p.UUID;
181 int imagethrottle = ExtractImageThrottle(throttles);
182 PollServiceTextureEventArgs args;
183 if (m_pollservices.TryGetValue(user,out args))
184 {
185 args.UpdateThrottle(imagethrottle);
186 }
111 } 187 }
112 188
113 public void PostInitialise() 189 public void PostInitialise()
114 { 190 {
115 } 191 }
116 192
117 public void Close() { } 193 public void Close()
194 {
195 if(m_NumberScenes <= 0 && m_workerThreads != null)
196 {
197 m_log.DebugFormat("[GetTextureModule] Closing");
198
199 foreach (Thread t in m_workerThreads)
200 Watchdog.AbortThread(t.ManagedThreadId);
201
202 m_queue.Clear();
203 }
204 }
118 205
119 public string Name { get { return "GetTextureModule"; } } 206 public string Name { get { return "GetTextureModule"; } }
120 207
@@ -125,28 +212,277 @@ namespace OpenSim.Region.ClientStack.Linden
125 212
126 #endregion 213 #endregion
127 214
128 public void RegisterCaps(UUID agentID, Caps caps) 215 private class PollServiceTextureEventArgs : PollServiceEventArgs
129 { 216 {
130 UUID capID = UUID.Random(); 217 private List<Hashtable> requests =
218 new List<Hashtable>();
219 private Dictionary<UUID, aPollResponse> responses =
220 new Dictionary<UUID, aPollResponse>();
221
222 private Scene m_scene;
223 private CapsDataThrottler m_throttler = new CapsDataThrottler(100000);
224 public PollServiceTextureEventArgs(UUID pId, Scene scene) :
225 base(null, "", null, null, null, pId, int.MaxValue)
226 {
227 m_scene = scene;
228 // x is request id, y is userid
229 HasEvents = (x, y) =>
230 {
231 lock (responses)
232 {
233 bool ret = m_throttler.hasEvents(x, responses);
234 return ret;
235
236 }
237 };
238 GetEvents = (x, y) =>
239 {
240 lock (responses)
241 {
242 try
243 {
244 return responses[x].response;
245 }
246 finally
247 {
248 responses.Remove(x);
249 m_throttler.PassTime();
250 }
251 }
252 };
253 // x is request id, y is request data hashtable
254 Request = (x, y) =>
255 {
256 aPollRequest reqinfo = new aPollRequest();
257 reqinfo.thepoll = this;
258 reqinfo.reqID = x;
259 reqinfo.request = y;
260 reqinfo.send503 = false;
261
262 lock (responses)
263 {
264 if (responses.Count > 0)
265 {
266 if (m_queue.Count() >= 4)
267 {
268 // Never allow more than 4 fetches to wait
269 reqinfo.send503 = true;
270 }
271 }
272 }
273 m_queue.Enqueue(reqinfo);
274 m_throttler.PassTime();
275 };
276
277 // this should never happen except possible on shutdown
278 NoEvents = (x, y) =>
279 {
280/*
281 lock (requests)
282 {
283 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
284 requests.Remove(request);
285 }
286*/
287 Hashtable response = new Hashtable();
131 288
132 //caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture)); 289 response["int_response_code"] = 500;
133 if (m_URL == "localhost") 290 response["str_response_string"] = "Script timeout";
291 response["content_type"] = "text/plain";
292 response["keepalive"] = false;
293 response["reusecontext"] = false;
294
295 return response;
296 };
297 }
298
299 public void Process(aPollRequest requestinfo)
134 { 300 {
135// m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); 301 Hashtable response;
136 caps.RegisterHandler( 302
137 "GetTexture", 303 UUID requestID = requestinfo.reqID;
138 new GetTextureHandler("/CAPS/" + capID + "/", m_assetService, "GetTexture", agentID.ToString(), m_RedirectURL)); 304
305 if(m_scene.ShuttingDown)
306 return;
307
308 if (requestinfo.send503)
309 {
310 response = new Hashtable();
311
312 response["int_response_code"] = 503;
313 response["str_response_string"] = "Throttled";
314 response["content_type"] = "text/plain";
315 response["keepalive"] = false;
316 response["reusecontext"] = false;
317
318 Hashtable headers = new Hashtable();
319 headers["Retry-After"] = 30;
320 response["headers"] = headers;
321
322 lock (responses)
323 responses[requestID] = new aPollResponse() {bytes = 0, response = response};
324
325 return;
326 }
327
328 // If the avatar is gone, don't bother to get the texture
329 if (m_scene.GetScenePresence(Id) == null)
330 {
331 response = new Hashtable();
332
333 response["int_response_code"] = 500;
334 response["str_response_string"] = "Script timeout";
335 response["content_type"] = "text/plain";
336 response["keepalive"] = false;
337 response["reusecontext"] = false;
338
339 lock (responses)
340 responses[requestID] = new aPollResponse() {bytes = 0, response = response};
341
342 return;
343 }
344
345 response = m_getTextureHandler.Handle(requestinfo.request);
346 lock (responses)
347 {
348 responses[requestID] = new aPollResponse()
349 {
350 bytes = (int) response["int_bytes"],
351 response = response
352 };
353
354 }
355 m_throttler.PassTime();
139 } 356 }
140 else 357
358 internal void UpdateThrottle(int pimagethrottle)
141 { 359 {
142// m_log.DebugFormat("[GETTEXTURE]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName); 360 int tmp = 2 * pimagethrottle;
361 if(tmp < 10000)
362 tmp = 10000;
363 m_throttler.ThrottleBytes = tmp;
364 }
365 }
366
367 private void RegisterCaps(UUID agentID, Caps caps)
368 {
369 if (m_Url == "localhost")
370 {
371 string capUrl = "/CAPS/" + UUID.Random() + "/";
372
373 // Register this as a poll service
374 PollServiceTextureEventArgs args = new PollServiceTextureEventArgs(agentID, m_scene);
375
376 args.Type = PollServiceEventArgs.EventType.Texture;
377 MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
378
379 string hostName = m_scene.RegionInfo.ExternalHostName;
380 uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
381 string protocol = "http";
382
383 if (MainServer.Instance.UseSSL)
384 {
385 hostName = MainServer.Instance.SSLCommonName;
386 port = MainServer.Instance.SSLPort;
387 protocol = "https";
388 }
143 IExternalCapsModule handler = m_scene.RequestModuleInterface<IExternalCapsModule>(); 389 IExternalCapsModule handler = m_scene.RequestModuleInterface<IExternalCapsModule>();
144 if (handler != null) 390 if (handler != null)
145 handler.RegisterExternalUserCapsHandler(agentID,caps,"GetTexture", m_URL); 391 handler.RegisterExternalUserCapsHandler(agentID, caps, "GetTexture", capUrl);
146 else 392 else
147 caps.RegisterHandler("GetTexture", m_URL); 393 caps.RegisterHandler("GetTexture", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
394 m_pollservices[agentID] = args;
395 m_capsDict[agentID] = capUrl;
396 }
397 else
398 {
399 caps.RegisterHandler("GetTexture", m_Url);
400 }
401 }
402
403 private void DeregisterCaps(UUID agentID, Caps caps)
404 {
405 PollServiceTextureEventArgs args;
406
407 MainServer.Instance.RemoveHTTPHandler("", m_Url);
408 m_capsDict.Remove(agentID);
409
410 if (m_pollservices.TryGetValue(agentID, out args))
411 {
412 m_pollservices.Remove(agentID);
413 }
414 }
415
416 private static void DoTextureRequests()
417 {
418 while (true)
419 {
420 aPollRequest poolreq = m_queue.Dequeue(4500);
421 Watchdog.UpdateThread();
422 if(m_NumberScenes <= 0)
423 return;
424 if(poolreq.reqID != UUID.Zero)
425 poolreq.thepoll.Process(poolreq);
148 } 426 }
149 } 427 }
150 428
429 internal sealed class CapsDataThrottler
430 {
431 private double lastTimeElapsed = 0;
432 private volatile int BytesSent = 0;
433 public CapsDataThrottler(int pBytes)
434 {
435 if(pBytes < 10000)
436 pBytes = 10000;
437 ThrottleBytes = pBytes;
438 lastTimeElapsed = Util.GetTimeStampMS();
439 }
440 public bool hasEvents(UUID key, Dictionary<UUID, GetTextureModule.aPollResponse> responses)
441 {
442 PassTime();
443 // Note, this is called IN LOCK
444 bool haskey = responses.ContainsKey(key);
445 if (!haskey)
446 {
447 return false;
448 }
449 GetTextureModule.aPollResponse response;
450 if (responses.TryGetValue(key, out response))
451 {
452 // This is any error response
453 if (response.bytes == 0)
454 return true;
455
456 // Normal
457 if (BytesSent <= ThrottleBytes)
458 {
459 BytesSent += response.bytes;
460 return true;
461 }
462 else
463 {
464 return false;
465 }
466 }
467
468 return haskey;
469 }
470
471 public void PassTime()
472 {
473 double currenttime = Util.GetTimeStampMS();
474 double timeElapsed = currenttime - lastTimeElapsed;
475 if(timeElapsed < 50.0)
476 return;
477 int add = (int)(ThrottleBytes * timeElapsed * 0.001);
478 if (add >= 1000)
479 {
480 lastTimeElapsed = currenttime;
481 BytesSent -= add;
482 if (BytesSent < 0) BytesSent = 0;
483 }
484 }
485 public int ThrottleBytes;
486 }
151 } 487 }
152} 488}