diff options
author | Robin Cornelius | 2011-10-01 22:32:42 +0100 |
---|---|---|
committer | David Walter Seikel | 2013-04-05 23:10:32 +1000 |
commit | a0dedb8a1f050465b4e447689672ea2845f0cd5b (patch) | |
tree | 94f66fbf80718a49c96e101c8275e7b1c55e536d /linden/indra/newview/llmediadataclient.cpp | |
parent | MOAP Radar patch set (diff) | |
download | meta-impy-a0dedb8a1f050465b4e447689672ea2845f0cd5b.zip meta-impy-a0dedb8a1f050465b4e447689672ea2845f0cd5b.tar.gz meta-impy-a0dedb8a1f050465b4e447689672ea2845f0cd5b.tar.bz2 meta-impy-a0dedb8a1f050465b4e447689672ea2845f0cd5b.tar.xz |
And actually add the new files for MOAP radar
Diffstat (limited to '')
-rw-r--r-- | linden/indra/newview/llmediadataclient.cpp | 813 |
1 files changed, 813 insertions, 0 deletions
diff --git a/linden/indra/newview/llmediadataclient.cpp b/linden/indra/newview/llmediadataclient.cpp new file mode 100644 index 0000000..e2fa700 --- /dev/null +++ b/linden/indra/newview/llmediadataclient.cpp | |||
@@ -0,0 +1,813 @@ | |||
1 | /** | ||
2 | * @file llmediadataclient.cpp | ||
3 | * @brief class for queueing up requests for media data | ||
4 | * | ||
5 | * $LicenseInfo:firstyear=2001&license=viewergpl$ | ||
6 | * | ||
7 | * Copyright (c) 2001-2010, Linden Research, Inc. | ||
8 | * | ||
9 | * Second Life Viewer Source Code | ||
10 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
11 | * to you under the terms of the GNU General Public License, version 2.0 | ||
12 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
13 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
14 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
15 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
16 | * | ||
17 | * There are special exceptions to the terms and conditions of the GPL as | ||
18 | * it is applied to this Source Code. View the full text of the exception | ||
19 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
20 | * online at | ||
21 | * http://secondlife.com/developers/opensource/flossexception | ||
22 | * | ||
23 | * By copying, modifying or distributing this software, you acknowledge | ||
24 | * that you have read and understood your obligations described above, | ||
25 | * and agree to abide by those obligations. | ||
26 | * | ||
27 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
28 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
29 | * COMPLETENESS OR PERFORMANCE. | ||
30 | * $/LicenseInfo$ | ||
31 | * | ||
32 | */ | ||
33 | |||
34 | #include "llviewerprecompiledheaders.h" | ||
35 | |||
36 | #include "llmediadataclient.h" | ||
37 | |||
38 | #if LL_MSVC | ||
39 | // disable boost::lexical_cast warning | ||
40 | #pragma warning (disable:4702) | ||
41 | #endif | ||
42 | |||
43 | #include <boost/lexical_cast.hpp> | ||
44 | |||
45 | #include "llhttpstatuscodes.h" | ||
46 | #include "llsdutil.h" | ||
47 | #include "llmediaentry.h" | ||
48 | #include "lltextureentry.h" | ||
49 | #include "llviewerregion.h" | ||
50 | |||
51 | // | ||
52 | // When making a request | ||
53 | // - obtain the "overall interest score" of the object. | ||
54 | // This would be the sum of the impls' interest scores. | ||
55 | // - put the request onto a queue sorted by this score | ||
56 | // (highest score at the front of the queue) | ||
57 | // - On a timer, once a second, pull off the head of the queue and send | ||
58 | // the request. | ||
59 | // - Any request that gets a 503 still goes through the retry logic | ||
60 | // | ||
61 | |||
62 | // | ||
63 | // Forward decls | ||
64 | // | ||
65 | const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s) | ||
66 | const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 10.0; // secs | ||
67 | const U32 LLMediaDataClient::MAX_RETRIES = 10; | ||
68 | const U32 LLMediaDataClient::MAX_SORTED_QUEUE_SIZE = 10000; | ||
69 | const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000; | ||
70 | |||
71 | // << operators | ||
72 | std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q); | ||
73 | std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &q); | ||
74 | |||
75 | ////////////////////////////////////////////////////////////////////////////////////// | ||
76 | // | ||
77 | // LLMediaDataClient | ||
78 | // | ||
79 | ////////////////////////////////////////////////////////////////////////////////////// | ||
80 | |||
81 | LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay, | ||
82 | F32 retry_timer_delay, | ||
83 | U32 max_retries, | ||
84 | U32 max_sorted_queue_size, | ||
85 | U32 max_round_robin_queue_size) | ||
86 | : mQueueTimerDelay(queue_timer_delay), | ||
87 | mRetryTimerDelay(retry_timer_delay), | ||
88 | mMaxNumRetries(max_retries), | ||
89 | mMaxSortedQueueSize(max_sorted_queue_size), | ||
90 | mMaxRoundRobinQueueSize(max_round_robin_queue_size), | ||
91 | mQueueTimerIsRunning(false), | ||
92 | mCurrentQueueIsTheSortedQueue(true) | ||
93 | { | ||
94 | } | ||
95 | |||
96 | LLMediaDataClient::~LLMediaDataClient() | ||
97 | { | ||
98 | stopQueueTimer(); | ||
99 | |||
100 | // This should clear the queue, and hopefully call all the destructors. | ||
101 | LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " << | ||
102 | (isEmpty() ? "<empty> " : "<not empty> ") << LL_ENDL; | ||
103 | |||
104 | mSortedQueue.clear(); | ||
105 | mRoundRobinQueue.clear(); | ||
106 | } | ||
107 | |||
108 | bool LLMediaDataClient::isEmpty() const | ||
109 | { | ||
110 | return mSortedQueue.empty() && mRoundRobinQueue.empty(); | ||
111 | } | ||
112 | |||
113 | bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) | ||
114 | { | ||
115 | return (LLMediaDataClient::findOrRemove(mSortedQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull() | ||
116 | || (LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull())); | ||
117 | } | ||
118 | |||
119 | bool LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object) | ||
120 | { | ||
121 | bool removedFromSortedQueue = LLMediaDataClient::findOrRemove(mSortedQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull(); | ||
122 | bool removedFromRoundRobinQueue = LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull(); | ||
123 | return removedFromSortedQueue || removedFromRoundRobinQueue; | ||
124 | } | ||
125 | |||
126 | //static | ||
127 | LLMediaDataClient::request_ptr_t LLMediaDataClient::findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, LLMediaDataClient::Request::Type type) | ||
128 | { | ||
129 | request_ptr_t result; | ||
130 | request_queue_t::iterator iter = queue.begin(); | ||
131 | request_queue_t::iterator end = queue.end(); | ||
132 | while (iter != end) | ||
133 | { | ||
134 | if (obj->getID() == (*iter)->getObject()->getID() && (type == LLMediaDataClient::Request::ANY || type == (*iter)->getType())) | ||
135 | { | ||
136 | result = *iter; | ||
137 | if (remove) queue.erase(iter); | ||
138 | break; | ||
139 | } | ||
140 | iter++; | ||
141 | } | ||
142 | return result; | ||
143 | } | ||
144 | |||
145 | void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload) | ||
146 | { | ||
147 | if (object.isNull() || ! object->hasMedia()) return; | ||
148 | |||
149 | // Push the object on the queue | ||
150 | enqueue(new Request(getCapabilityName(), payload, object, this)); | ||
151 | } | ||
152 | |||
153 | void LLMediaDataClient::enqueue(const Request *request) | ||
154 | { | ||
155 | if (request->isNew()) | ||
156 | { | ||
157 | // Add to sorted queue | ||
158 | if (LLMediaDataClient::findOrRemove(mSortedQueue, request->getObject(), true/*remove*/, request->getType()).notNull()) | ||
159 | { | ||
160 | LL_DEBUGS("LLMediaDataClient") << "REMOVING OLD request for " << *request << " ALREADY THERE!" << LL_ENDL; | ||
161 | } | ||
162 | |||
163 | LL_DEBUGS("LLMediaDataClient") << "Queuing SORTED request for " << *request << LL_ENDL; | ||
164 | |||
165 | // Sadly, we have to const-cast because items put into the queue are not const | ||
166 | mSortedQueue.push_back(const_cast<LLMediaDataClient::Request*>(request)); | ||
167 | |||
168 | LL_DEBUGS("LLMediaDataClientQueue") << "SORTED queue:" << mSortedQueue << LL_ENDL; | ||
169 | } | ||
170 | else { | ||
171 | if (mRoundRobinQueue.size() > mMaxRoundRobinQueueSize) | ||
172 | { | ||
173 | LL_INFOS_ONCE("LLMediaDataClient") << "RR QUEUE MAXED OUT!!!" << LL_ENDL; | ||
174 | LL_DEBUGS("LLMediaDataClient") << "Not queuing " << *request << LL_ENDL; | ||
175 | return; | ||
176 | } | ||
177 | |||
178 | // ROUND ROBIN: if it is there, and it is a GET request, leave it. If not, put at front! | ||
179 | request_ptr_t existing_request; | ||
180 | if (request->getType() == Request::GET) | ||
181 | { | ||
182 | existing_request = LLMediaDataClient::findOrRemove(mRoundRobinQueue, request->getObject(), false/*remove*/, request->getType()); | ||
183 | } | ||
184 | if (existing_request.isNull()) | ||
185 | { | ||
186 | LL_DEBUGS("LLMediaDataClient") << "Queuing RR request for " << *request << LL_ENDL; | ||
187 | // Push the request on the pending queue | ||
188 | // Sadly, we have to const-cast because items put into the queue are not const | ||
189 | mRoundRobinQueue.push_front(const_cast<LLMediaDataClient::Request*>(request)); | ||
190 | |||
191 | LL_DEBUGS("LLMediaDataClientQueue") << "RR queue:" << mRoundRobinQueue << LL_ENDL; | ||
192 | } | ||
193 | else | ||
194 | { | ||
195 | LL_DEBUGS("LLMediaDataClient") << "ALREADY THERE: NOT Queuing request for " << *request << LL_ENDL; | ||
196 | |||
197 | existing_request->markSent(false); | ||
198 | } | ||
199 | } | ||
200 | // Start the timer if not already running | ||
201 | startQueueTimer(); | ||
202 | } | ||
203 | |||
204 | void LLMediaDataClient::startQueueTimer() | ||
205 | { | ||
206 | if (! mQueueTimerIsRunning) | ||
207 | { | ||
208 | LL_DEBUGS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL; | ||
209 | // LLEventTimer automagically takes care of the lifetime of this object | ||
210 | new QueueTimer(mQueueTimerDelay, this); | ||
211 | } | ||
212 | else { | ||
213 | LL_DEBUGS("LLMediaDataClient") << "not starting queue timer (it's already running, right???)" << LL_ENDL; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | void LLMediaDataClient::stopQueueTimer() | ||
218 | { | ||
219 | mQueueTimerIsRunning = false; | ||
220 | } | ||
221 | |||
222 | bool LLMediaDataClient::processQueueTimer() | ||
223 | { | ||
224 | sortQueue(); | ||
225 | |||
226 | if(!isEmpty()) | ||
227 | { | ||
228 | LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, SORTED queue size is: " << mSortedQueue.size() | ||
229 | << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL; | ||
230 | LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() started, SORTED queue is: " << mSortedQueue << LL_ENDL; | ||
231 | LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() started, RR queue is: " << mRoundRobinQueue << LL_ENDL; | ||
232 | } | ||
233 | |||
234 | serviceQueue(); | ||
235 | |||
236 | LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, SORTED queue size is: " << mSortedQueue.size() | ||
237 | << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL; | ||
238 | LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() finished, SORTED queue is: " << mSortedQueue << LL_ENDL; | ||
239 | LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() finished, RR queue is: " << mRoundRobinQueue << LL_ENDL; | ||
240 | |||
241 | return isEmpty(); | ||
242 | } | ||
243 | |||
244 | void LLMediaDataClient::sortQueue() | ||
245 | { | ||
246 | if(!mSortedQueue.empty()) | ||
247 | { | ||
248 | // Score all items first | ||
249 | request_queue_t::iterator iter = mSortedQueue.begin(); | ||
250 | request_queue_t::iterator end = mSortedQueue.end(); | ||
251 | while (iter != end) | ||
252 | { | ||
253 | (*iter)->updateScore(); | ||
254 | iter++; | ||
255 | } | ||
256 | |||
257 | // Re-sort the list... | ||
258 | // NOTE: should this be a stable_sort? If so we need to change to using a vector. | ||
259 | mSortedQueue.sort(LLMediaDataClient::compareRequests); | ||
260 | |||
261 | // ...then cull items over the max | ||
262 | U32 size = mSortedQueue.size(); | ||
263 | if (size > mMaxSortedQueueSize) | ||
264 | { | ||
265 | U32 num_to_cull = (size - mMaxSortedQueueSize); | ||
266 | LL_INFOS_ONCE("LLMediaDataClient") << "sorted queue MAXED OUT! Culling " | ||
267 | << num_to_cull << " items" << LL_ENDL; | ||
268 | while (num_to_cull-- > 0) | ||
269 | { | ||
270 | mSortedQueue.pop_back(); | ||
271 | } | ||
272 | } | ||
273 | } | ||
274 | } | ||
275 | |||
276 | // static | ||
277 | bool LLMediaDataClient::compareRequests(const request_ptr_t &o1, const request_ptr_t &o2) | ||
278 | { | ||
279 | if (o2.isNull()) return true; | ||
280 | if (o1.isNull()) return false; | ||
281 | return ( o1->getScore() > o2->getScore() ); | ||
282 | } | ||
283 | |||
284 | void LLMediaDataClient::serviceQueue() | ||
285 | { | ||
286 | request_queue_t *queue_p = getCurrentQueue(); | ||
287 | |||
288 | // quick retry loop for cases where we shouldn't wait for the next timer tick | ||
289 | while(true) | ||
290 | { | ||
291 | if (queue_p->empty()) | ||
292 | { | ||
293 | LL_DEBUGS("LLMediaDataClient") << "queue empty: " << (*queue_p) << LL_ENDL; | ||
294 | break; | ||
295 | } | ||
296 | |||
297 | // Peel one off of the items from the queue, and execute request | ||
298 | request_ptr_t request = queue_p->front(); | ||
299 | llassert(!request.isNull()); | ||
300 | const LLMediaDataClientObject *object = (request.isNull()) ? NULL : request->getObject(); | ||
301 | llassert(NULL != object); | ||
302 | |||
303 | // Check for conditions that would make us just pop and rapidly loop through | ||
304 | // the queue. | ||
305 | if(request.isNull() || | ||
306 | request->isMarkedSent() || | ||
307 | NULL == object || | ||
308 | object->isDead() || | ||
309 | !object->hasMedia()) | ||
310 | { | ||
311 | if (request.isNull()) | ||
312 | { | ||
313 | LL_WARNS("LLMediaDataClient") << "Skipping NULL request" << LL_ENDL; | ||
314 | } | ||
315 | else { | ||
316 | LL_INFOS("LLMediaDataClient") << "Skipping : " << *request << " " | ||
317 | << ((request->isMarkedSent()) ? " request is marked sent" : | ||
318 | ((NULL == object) ? " object is NULL " : | ||
319 | ((object->isDead()) ? "object is dead" : | ||
320 | ((!object->hasMedia()) ? "object has no media!" : "BADNESS!")))) << LL_ENDL; | ||
321 | } | ||
322 | queue_p->pop_front(); | ||
323 | continue; // jump back to the start of the quick retry loop | ||
324 | } | ||
325 | |||
326 | // Next, ask if this is "interesting enough" to fetch. If not, just stop | ||
327 | // and wait for the next timer go-round. Only do this for the sorted | ||
328 | // queue. | ||
329 | if (mCurrentQueueIsTheSortedQueue && !object->isInterestingEnough()) | ||
330 | { | ||
331 | LL_DEBUGS("LLMediaDataClient") << "Not fetching " << *request << ": not interesting enough" << LL_ENDL; | ||
332 | break; | ||
333 | } | ||
334 | |||
335 | // Finally, try to send the HTTP message to the cap url | ||
336 | std::string url = request->getCapability(); | ||
337 | bool maybe_retry = false; | ||
338 | if (!url.empty()) | ||
339 | { | ||
340 | const LLSD &sd_payload = request->getPayload(); | ||
341 | LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL; | ||
342 | |||
343 | // Call the subclass for creating the responder | ||
344 | LLHTTPClient::post(url, sd_payload, createResponder(request)); | ||
345 | } | ||
346 | else { | ||
347 | LL_INFOS("LLMediaDataClient") << "NOT Sending request for " << *request << ": empty cap url!" << LL_ENDL; | ||
348 | maybe_retry = true; | ||
349 | } | ||
350 | |||
351 | bool exceeded_retries = request->getRetryCount() > mMaxNumRetries; | ||
352 | if (maybe_retry && ! exceeded_retries) // Try N times before giving up | ||
353 | { | ||
354 | // We got an empty cap, but in that case we will retry again next | ||
355 | // timer fire. | ||
356 | request->incRetryCount(); | ||
357 | } | ||
358 | else { | ||
359 | if (exceeded_retries) | ||
360 | { | ||
361 | LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " | ||
362 | << mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL; | ||
363 | // XXX Should we bring up a warning dialog?? | ||
364 | } | ||
365 | |||
366 | queue_p->pop_front(); | ||
367 | |||
368 | if (! mCurrentQueueIsTheSortedQueue) { | ||
369 | // Round robin | ||
370 | request->markSent(true); | ||
371 | mRoundRobinQueue.push_back(request); | ||
372 | } | ||
373 | } | ||
374 | |||
375 | // end of quick loop -- any cases where we want to loop will use 'continue' to jump back to the start. | ||
376 | break; | ||
377 | } | ||
378 | |||
379 | swapCurrentQueue(); | ||
380 | } | ||
381 | |||
382 | void LLMediaDataClient::swapCurrentQueue() | ||
383 | { | ||
384 | // Swap | ||
385 | mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; | ||
386 | // If its empty, swap back | ||
387 | if (getCurrentQueue()->empty()) | ||
388 | { | ||
389 | mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; | ||
390 | } | ||
391 | } | ||
392 | |||
393 | LLMediaDataClient::request_queue_t *LLMediaDataClient::getCurrentQueue() | ||
394 | { | ||
395 | return (mCurrentQueueIsTheSortedQueue) ? &mSortedQueue : &mRoundRobinQueue; | ||
396 | } | ||
397 | |||
398 | // dump the queue | ||
399 | std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q) | ||
400 | { | ||
401 | int i = 0; | ||
402 | LLMediaDataClient::request_queue_t::const_iterator iter = q.begin(); | ||
403 | LLMediaDataClient::request_queue_t::const_iterator end = q.end(); | ||
404 | while (iter != end) | ||
405 | { | ||
406 | s << "\t" << i << "]: " << (*iter)->getObject()->getID().asString() << "(" << (*iter)->getObject()->getMediaInterest() << ")"; | ||
407 | iter++; | ||
408 | i++; | ||
409 | } | ||
410 | return s; | ||
411 | } | ||
412 | |||
413 | ////////////////////////////////////////////////////////////////////////////////////// | ||
414 | // | ||
415 | // LLMediaDataClient::QueueTimer | ||
416 | // Queue of LLMediaDataClientObject smart pointers to request media for. | ||
417 | // | ||
418 | ////////////////////////////////////////////////////////////////////////////////////// | ||
419 | |||
420 | LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc) | ||
421 | : LLEventTimer(time), mMDC(mdc) | ||
422 | { | ||
423 | mMDC->setIsRunning(true); | ||
424 | } | ||
425 | |||
426 | LLMediaDataClient::QueueTimer::~QueueTimer() | ||
427 | { | ||
428 | LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL; | ||
429 | mMDC->setIsRunning(false); | ||
430 | mMDC = NULL; | ||
431 | } | ||
432 | |||
433 | // virtual | ||
434 | BOOL LLMediaDataClient::QueueTimer::tick() | ||
435 | { | ||
436 | if (mMDC.isNull()) return TRUE; | ||
437 | return mMDC->processQueueTimer(); | ||
438 | } | ||
439 | |||
440 | |||
441 | ////////////////////////////////////////////////////////////////////////////////////// | ||
442 | // | ||
443 | // LLMediaDataClient::Responder::RetryTimer | ||
444 | // | ||
445 | ////////////////////////////////////////////////////////////////////////////////////// | ||
446 | |||
447 | LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr) | ||
448 | : LLEventTimer(time), mResponder(mdr) | ||
449 | { | ||
450 | } | ||
451 | |||
452 | // virtual | ||
453 | LLMediaDataClient::Responder::RetryTimer::~RetryTimer() | ||
454 | { | ||
455 | LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL; | ||
456 | |||
457 | // XXX This is weird: Instead of doing the work in tick() (which re-schedules | ||
458 | // a timer, which might be risky), do it here, in the destructor. Yes, it is very odd. | ||
459 | // Instead of retrying, we just put the request back onto the queue | ||
460 | LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *(mResponder->getRequest()) << " retrying" << LL_ENDL; | ||
461 | mResponder->getRequest()->reEnqueue(); | ||
462 | |||
463 | // Release the ref to the responder. | ||
464 | mResponder = NULL; | ||
465 | } | ||
466 | |||
467 | // virtual | ||
468 | BOOL LLMediaDataClient::Responder::RetryTimer::tick() | ||
469 | { | ||
470 | // Don't fire again | ||
471 | return TRUE; | ||
472 | } | ||
473 | |||
474 | |||
475 | ////////////////////////////////////////////////////////////////////////////////////// | ||
476 | // | ||
477 | // LLMediaDataClient::Request | ||
478 | // | ||
479 | ////////////////////////////////////////////////////////////////////////////////////// | ||
480 | /*static*/U32 LLMediaDataClient::Request::sNum = 0; | ||
481 | |||
482 | LLMediaDataClient::Request::Request(const char *cap_name, | ||
483 | const LLSD& sd_payload, | ||
484 | LLMediaDataClientObject *obj, | ||
485 | LLMediaDataClient *mdc) | ||
486 | : mCapName(cap_name), | ||
487 | mPayload(sd_payload), | ||
488 | mObject(obj), | ||
489 | mNum(++sNum), | ||
490 | mRetryCount(0), | ||
491 | mMDC(mdc), | ||
492 | mMarkedSent(false), | ||
493 | mScore((F64)0.0) | ||
494 | { | ||
495 | } | ||
496 | |||
497 | LLMediaDataClient::Request::~Request() | ||
498 | { | ||
499 | LL_DEBUGS("LLMediaDataClient") << "~Request" << (*this) << LL_ENDL; | ||
500 | mMDC = NULL; | ||
501 | mObject = NULL; | ||
502 | } | ||
503 | |||
504 | |||
505 | std::string LLMediaDataClient::Request::getCapability() const | ||
506 | { | ||
507 | return getObject()->getCapabilityUrl(getCapName()); | ||
508 | } | ||
509 | |||
510 | // Helper function to get the "type" of request, which just pokes around to | ||
511 | // discover it. | ||
512 | LLMediaDataClient::Request::Type LLMediaDataClient::Request::getType() const | ||
513 | { | ||
514 | if (0 == strcmp(mCapName, "ObjectMediaNavigate")) | ||
515 | { | ||
516 | return NAVIGATE; | ||
517 | } | ||
518 | else if (0 == strcmp(mCapName, "ObjectMedia")) | ||
519 | { | ||
520 | const std::string &verb = mPayload["verb"]; | ||
521 | if (verb == "GET") | ||
522 | { | ||
523 | return GET; | ||
524 | } | ||
525 | else if (verb == "UPDATE") | ||
526 | { | ||
527 | return UPDATE; | ||
528 | } | ||
529 | } | ||
530 | llassert(false); | ||
531 | return GET; | ||
532 | } | ||
533 | |||
534 | const char *LLMediaDataClient::Request::getTypeAsString() const | ||
535 | { | ||
536 | Type t = getType(); | ||
537 | switch (t) | ||
538 | { | ||
539 | case GET: | ||
540 | return "GET"; | ||
541 | break; | ||
542 | case UPDATE: | ||
543 | return "UPDATE"; | ||
544 | break; | ||
545 | case NAVIGATE: | ||
546 | return "NAVIGATE"; | ||
547 | break; | ||
548 | case ANY: | ||
549 | return "ANY"; | ||
550 | break; | ||
551 | } | ||
552 | return ""; | ||
553 | } | ||
554 | |||
555 | |||
556 | void LLMediaDataClient::Request::reEnqueue() const | ||
557 | { | ||
558 | // I sure hope this doesn't deref a bad pointer: | ||
559 | mMDC->enqueue(this); | ||
560 | } | ||
561 | |||
562 | F32 LLMediaDataClient::Request::getRetryTimerDelay() const | ||
563 | { | ||
564 | return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY : | ||
565 | mMDC->mRetryTimerDelay; | ||
566 | } | ||
567 | |||
568 | U32 LLMediaDataClient::Request::getMaxNumRetries() const | ||
569 | { | ||
570 | return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries; | ||
571 | } | ||
572 | |||
573 | void LLMediaDataClient::Request::markSent(bool flag) | ||
574 | { | ||
575 | if (mMarkedSent != flag) | ||
576 | { | ||
577 | mMarkedSent = flag; | ||
578 | if (!mMarkedSent) | ||
579 | { | ||
580 | mNum = ++sNum; | ||
581 | } | ||
582 | } | ||
583 | } | ||
584 | |||
585 | void LLMediaDataClient::Request::updateScore() | ||
586 | { | ||
587 | F64 tmp = mObject->getMediaInterest(); | ||
588 | if (tmp != mScore) | ||
589 | { | ||
590 | LL_DEBUGS("LLMediaDataClient") << "Score for " << mObject->getID() << " changed from " << mScore << " to " << tmp << LL_ENDL; | ||
591 | mScore = tmp; | ||
592 | } | ||
593 | } | ||
594 | |||
595 | std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r) | ||
596 | { | ||
597 | s << "request: num=" << r.getNum() | ||
598 | << " type=" << r.getTypeAsString() | ||
599 | << " ID=" << r.getObject()->getID() | ||
600 | << " #retries=" << r.getRetryCount(); | ||
601 | return s; | ||
602 | } | ||
603 | |||
604 | |||
605 | ////////////////////////////////////////////////////////////////////////////////////// | ||
606 | // | ||
607 | // LLMediaDataClient::Responder | ||
608 | // | ||
609 | ////////////////////////////////////////////////////////////////////////////////////// | ||
610 | |||
611 | LLMediaDataClient::Responder::Responder(const request_ptr_t &request) | ||
612 | : mRequest(request) | ||
613 | { | ||
614 | } | ||
615 | |||
616 | LLMediaDataClient::Responder::~Responder() | ||
617 | { | ||
618 | LL_DEBUGS("LLMediaDataClient") << "~Responder" << *(getRequest()) << LL_ENDL; | ||
619 | mRequest = NULL; | ||
620 | } | ||
621 | |||
622 | /*virtual*/ | ||
623 | void LLMediaDataClient::Responder::error(U32 status, const std::string& reason) | ||
624 | { | ||
625 | if (status == HTTP_SERVICE_UNAVAILABLE) | ||
626 | { | ||
627 | F32 retry_timeout = mRequest->getRetryTimerDelay(); | ||
628 | |||
629 | mRequest->incRetryCount(); | ||
630 | |||
631 | if (mRequest->getRetryCount() < mRequest->getMaxNumRetries()) | ||
632 | { | ||
633 | LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; | ||
634 | |||
635 | // Start timer (instances are automagically tracked by | ||
636 | // InstanceTracker<> and LLEventTimer) | ||
637 | new RetryTimer(F32(retry_timeout/*secs*/), this); | ||
638 | } | ||
639 | else { | ||
640 | LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count " | ||
641 | << mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL; | ||
642 | } | ||
643 | } | ||
644 | else { | ||
645 | std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason; | ||
646 | LL_WARNS("LLMediaDataClient") << *mRequest << " http error(" << msg << ")" << LL_ENDL; | ||
647 | } | ||
648 | } | ||
649 | |||
650 | /*virtual*/ | ||
651 | void LLMediaDataClient::Responder::result(const LLSD& content) | ||
652 | { | ||
653 | LL_DEBUGS("LLMediaDataClientResponse") << *mRequest << " result : " << ll_print_sd(content) << LL_ENDL; | ||
654 | } | ||
655 | |||
656 | ////////////////////////////////////////////////////////////////////////////////////// | ||
657 | // | ||
658 | // LLObjectMediaDataClient | ||
659 | // Subclass of LLMediaDataClient for the ObjectMedia cap | ||
660 | // | ||
661 | ////////////////////////////////////////////////////////////////////////////////////// | ||
662 | |||
663 | LLMediaDataClient::Responder *LLObjectMediaDataClient::createResponder(const request_ptr_t &request) const | ||
664 | { | ||
665 | return new LLObjectMediaDataClient::Responder(request); | ||
666 | } | ||
667 | |||
668 | const char *LLObjectMediaDataClient::getCapabilityName() const | ||
669 | { | ||
670 | return "ObjectMedia"; | ||
671 | } | ||
672 | |||
673 | void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object) | ||
674 | { | ||
675 | LLSD sd_payload; | ||
676 | sd_payload["verb"] = "GET"; | ||
677 | sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); | ||
678 | request(object, sd_payload); | ||
679 | } | ||
680 | |||
681 | void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object) | ||
682 | { | ||
683 | LLSD sd_payload; | ||
684 | sd_payload["verb"] = "UPDATE"; | ||
685 | sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); | ||
686 | LLSD object_media_data; | ||
687 | int i = 0; | ||
688 | int end = object->getMediaDataCount(); | ||
689 | for ( ; i < end ; ++i) | ||
690 | { | ||
691 | object_media_data.append(object->getMediaDataLLSD(i)); | ||
692 | } | ||
693 | sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; | ||
694 | |||
695 | LL_DEBUGS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_print_sd(sd_payload) << LL_ENDL; | ||
696 | |||
697 | request(object, sd_payload); | ||
698 | } | ||
699 | |||
700 | /*virtual*/ | ||
701 | void LLObjectMediaDataClient::Responder::result(const LLSD& content) | ||
702 | { | ||
703 | const LLMediaDataClient::Request::Type type = getRequest()->getType(); | ||
704 | llassert(type == LLMediaDataClient::Request::GET || type == LLMediaDataClient::Request::UPDATE) | ||
705 | if (type == LLMediaDataClient::Request::GET) | ||
706 | { | ||
707 | LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " GET returned: " << ll_print_sd(content) << LL_ENDL; | ||
708 | |||
709 | // Look for an error | ||
710 | if (content.has("error")) | ||
711 | { | ||
712 | const LLSD &error = content["error"]; | ||
713 | LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" << | ||
714 | error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; | ||
715 | |||
716 | // XXX Warn user? | ||
717 | } | ||
718 | else { | ||
719 | // Check the data | ||
720 | const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY]; | ||
721 | if (object_id != getRequest()->getObject()->getID()) | ||
722 | { | ||
723 | // NOT good, wrong object id!! | ||
724 | LL_WARNS("LLMediaDataClient") << *(getRequest()) << " DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL; | ||
725 | return; | ||
726 | } | ||
727 | |||
728 | // Otherwise, update with object media data | ||
729 | getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY], | ||
730 | content[LLTextureEntry::MEDIA_VERSION_KEY]); | ||
731 | } | ||
732 | } | ||
733 | else if (type == LLMediaDataClient::Request::UPDATE) | ||
734 | { | ||
735 | // just do what our superclass does | ||
736 | LLMediaDataClient::Responder::result(content); | ||
737 | } | ||
738 | } | ||
739 | |||
740 | ////////////////////////////////////////////////////////////////////////////////////// | ||
741 | // | ||
742 | // LLObjectMediaNavigateClient | ||
743 | // Subclass of LLMediaDataClient for the ObjectMediaNavigate cap | ||
744 | // | ||
745 | ////////////////////////////////////////////////////////////////////////////////////// | ||
746 | LLMediaDataClient::Responder *LLObjectMediaNavigateClient::createResponder(const request_ptr_t &request) const | ||
747 | { | ||
748 | return new LLObjectMediaNavigateClient::Responder(request); | ||
749 | } | ||
750 | |||
751 | const char *LLObjectMediaNavigateClient::getCapabilityName() const | ||
752 | { | ||
753 | return "ObjectMediaNavigate"; | ||
754 | } | ||
755 | |||
756 | void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url) | ||
757 | { | ||
758 | LLSD sd_payload; | ||
759 | sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); | ||
760 | sd_payload[LLMediaEntry::CURRENT_URL_KEY] = url; | ||
761 | sd_payload[LLTextureEntry::TEXTURE_INDEX_KEY] = (LLSD::Integer)texture_index; | ||
762 | |||
763 | LL_INFOS("LLMediaDataClient") << "navigate() initiated: " << ll_print_sd(sd_payload) << LL_ENDL; | ||
764 | |||
765 | request(object, sd_payload); | ||
766 | } | ||
767 | |||
768 | /*virtual*/ | ||
769 | void LLObjectMediaNavigateClient::Responder::error(U32 status, const std::string& reason) | ||
770 | { | ||
771 | // Bounce back (unless HTTP_SERVICE_UNAVAILABLE, in which case call base | ||
772 | // class | ||
773 | if (status == HTTP_SERVICE_UNAVAILABLE) | ||
774 | { | ||
775 | LLMediaDataClient::Responder::error(status, reason); | ||
776 | } | ||
777 | else { | ||
778 | // bounce the face back | ||
779 | LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: http code=" << status << LL_ENDL; | ||
780 | const LLSD &payload = getRequest()->getPayload(); | ||
781 | // bounce the face back | ||
782 | getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); | ||
783 | } | ||
784 | } | ||
785 | |||
786 | /*virtual*/ | ||
787 | void LLObjectMediaNavigateClient::Responder::result(const LLSD& content) | ||
788 | { | ||
789 | LL_INFOS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << ll_print_sd(content) << LL_ENDL; | ||
790 | |||
791 | if (content.has("error")) | ||
792 | { | ||
793 | const LLSD &error = content["error"]; | ||
794 | int error_code = error["code"]; | ||
795 | |||
796 | if (ERROR_PERMISSION_DENIED_CODE == error_code) | ||
797 | { | ||
798 | LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL; | ||
799 | const LLSD &payload = getRequest()->getPayload(); | ||
800 | // bounce the face back | ||
801 | getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); | ||
802 | } | ||
803 | else { | ||
804 | LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" << | ||
805 | error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; | ||
806 | } | ||
807 | // XXX Warn user? | ||
808 | } | ||
809 | else { | ||
810 | // just do what our superclass does | ||
811 | LLMediaDataClient::Responder::result(content); | ||
812 | } | ||
813 | } | ||