aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llmediadataclient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/newview/llmediadataclient.cpp')
-rw-r--r--linden/indra/newview/llmediadataclient.cpp813
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//
65const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s)
66const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 10.0; // secs
67const U32 LLMediaDataClient::MAX_RETRIES = 10;
68const U32 LLMediaDataClient::MAX_SORTED_QUEUE_SIZE = 10000;
69const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000;
70
71// << operators
72std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q);
73std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &q);
74
75//////////////////////////////////////////////////////////////////////////////////////
76//
77// LLMediaDataClient
78//
79//////////////////////////////////////////////////////////////////////////////////////
80
81LLMediaDataClient::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
96LLMediaDataClient::~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
108bool LLMediaDataClient::isEmpty() const
109{
110 return mSortedQueue.empty() && mRoundRobinQueue.empty();
111}
112
113bool 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
119bool 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
127LLMediaDataClient::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
145void 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
153void 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
204void 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
217void LLMediaDataClient::stopQueueTimer()
218{
219 mQueueTimerIsRunning = false;
220}
221
222bool 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
244void 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
277bool 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
284void 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
382void 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
393LLMediaDataClient::request_queue_t *LLMediaDataClient::getCurrentQueue()
394{
395 return (mCurrentQueueIsTheSortedQueue) ? &mSortedQueue : &mRoundRobinQueue;
396}
397
398// dump the queue
399std::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
420LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc)
421: LLEventTimer(time), mMDC(mdc)
422{
423 mMDC->setIsRunning(true);
424}
425
426LLMediaDataClient::QueueTimer::~QueueTimer()
427{
428 LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL;
429 mMDC->setIsRunning(false);
430 mMDC = NULL;
431}
432
433// virtual
434BOOL 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
447LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr)
448: LLEventTimer(time), mResponder(mdr)
449{
450}
451
452// virtual
453LLMediaDataClient::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
468BOOL 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
482LLMediaDataClient::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
497LLMediaDataClient::Request::~Request()
498{
499 LL_DEBUGS("LLMediaDataClient") << "~Request" << (*this) << LL_ENDL;
500 mMDC = NULL;
501 mObject = NULL;
502}
503
504
505std::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.
512LLMediaDataClient::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
534const 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
556void LLMediaDataClient::Request::reEnqueue() const
557{
558 // I sure hope this doesn't deref a bad pointer:
559 mMDC->enqueue(this);
560}
561
562F32 LLMediaDataClient::Request::getRetryTimerDelay() const
563{
564 return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY :
565 mMDC->mRetryTimerDelay;
566}
567
568U32 LLMediaDataClient::Request::getMaxNumRetries() const
569{
570 return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries;
571}
572
573void 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
585void 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
595std::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
611LLMediaDataClient::Responder::Responder(const request_ptr_t &request)
612: mRequest(request)
613{
614}
615
616LLMediaDataClient::Responder::~Responder()
617{
618 LL_DEBUGS("LLMediaDataClient") << "~Responder" << *(getRequest()) << LL_ENDL;
619 mRequest = NULL;
620}
621
622/*virtual*/
623void 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*/
651void 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
663LLMediaDataClient::Responder *LLObjectMediaDataClient::createResponder(const request_ptr_t &request) const
664{
665 return new LLObjectMediaDataClient::Responder(request);
666}
667
668const char *LLObjectMediaDataClient::getCapabilityName() const
669{
670 return "ObjectMedia";
671}
672
673void 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
681void 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*/
701void 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//////////////////////////////////////////////////////////////////////////////////////
746LLMediaDataClient::Responder *LLObjectMediaNavigateClient::createResponder(const request_ptr_t &request) const
747{
748 return new LLObjectMediaNavigateClient::Responder(request);
749}
750
751const char *LLObjectMediaNavigateClient::getCapabilityName() const
752{
753 return "ObjectMediaNavigate";
754}
755
756void 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*/
769void 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*/
787void 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}