12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064 |
- /**
- * @file llexperiencecache.cpp
- * @brief Caches information relating to experience keys
- *
- * $LicenseInfo:firstyear=2012&license=viewergpl$
- *
- * Copyright (c) 2012, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "boost/concept_check.hpp"
- #include "boost/tokenizer.hpp"
- #include "llexperiencecache.h"
- #include "llcoproceduremanager.h"
- #include "llsdserialize.h"
- #include "lluri.h"
- const std::string LLExperienceCache::PRIVATE_KEY = "private_id";
- const std::string LLExperienceCache::MISSING = "DoesNotExist";
- const std::string LLExperienceCache::AGENT_ID = "agent_id";
- const std::string LLExperienceCache::GROUP_ID = "group_id";
- const std::string LLExperienceCache::EXPERIENCE_ID = "public_id";
- const std::string LLExperienceCache::NAME = "name";
- const std::string LLExperienceCache::PROPERTIES = "properties";
- const std::string LLExperienceCache::EXPIRES = "expiration";
- const std::string LLExperienceCache::DESCRIPTION = "description";
- const std::string LLExperienceCache::QUOTA = "quota";
- const std::string LLExperienceCache::MATURITY = "maturity";
- const std::string LLExperienceCache::METADATA = "extended_metadata";
- const std::string LLExperienceCache::SLURL = "slurl";
- static const std::string MAX_AGE = "max-age";
- const boost::char_separator<char> EQUALS_SEPARATOR("=");
- const boost::char_separator<char> COMMA_SEPARATOR(",");
- //static
- std::string LLExperienceCache::sLookupURL;
- bool LLExperienceCache::sShutdown = false;
- LLExperienceCache::LLExperienceCache()
- : // NOTE: by using these instead of omitting the corresponding
- // xxxAndSuspend() parameters, we avoid seeing such classes constructed
- // and destroyed each time...
- mHttpOptions(new LLCore::HttpOptions),
- mHttpHeaders(new LLCore::HttpHeaders)
- {
- }
- LLExperienceCache::~LLExperienceCache()
- {
- mHttpOptions.reset();
- mHttpHeaders.reset();
- }
- //virtual
- void LLExperienceCache::initSingleton()
- {
- LLCoprocedureManager::getInstance()->initializePool("ExpCache");
- gCoros.launch("LLExperienceCache::idleCoro",
- boost::bind(&LLExperienceCache::idleCoro, this));
- }
- void LLExperienceCache::cleanup()
- {
- sShutdown = true;
- }
- void LLExperienceCache::importFile(std::istream& istr)
- {
- LLSD data;
- S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr);
- if (parse_count < 1) return;
- LLSD experiences = data["experiences"];
- LLUUID public_key;
- for (LLSD::map_const_iterator it = experiences.beginMap(),
- end = experiences.endMap();
- it != end; ++it)
- {
- public_key.set(it->first);
- mCache[public_key] = it->second;
- }
- LL_DEBUGS("ExperienceCache") << "Loaded " << mCache.size()
- << " experiences." << LL_ENDL;
- }
- void LLExperienceCache::exportFile(std::ostream& ostr) const
- {
- LLSD experiences;
- for (cache_t::const_iterator it = mCache.begin(), end = mCache.end();
- it != end; ++it)
- {
- if (!it->second.has(EXPERIENCE_ID) ||
- it->second[EXPERIENCE_ID].asUUID().isNull() ||
- it->second.has("DoesNotExist") ||
- (it->second.has(PROPERTIES) &&
- (it->second[PROPERTIES].asInteger() & PROPERTY_INVALID)))
- {
- continue;
- }
- experiences[it->first.asString()] = it->second;
- }
- LLSD data;
- data["experiences"] = experiences;
- LLSDSerialize::toPrettyXML(data, ostr);
- }
- #if 0 // Not used
- LLExperienceCache::key_map_t LLExperienceCache::sPrivateToPublicKeyMap;
- void LLExperienceCache::bootstrap(const LLSD& legacy_keys,
- S32 initial_expiration)
- {
- mapKeys(legacy_keys);
- for (LLSD::array_const_iterator it = legacy_keys.beginArray(),
- end = legacy_keys.endArray();
- it != end; ++it)
- {
- LLSD experience = *it;
- if (experience.has(EXPERIENCE_ID))
- {
- if (!experience.has(EXPIRES))
- {
- experience[EXPIRES] = initial_expiration;
- }
- processExperience(experience[EXPERIENCE_ID].asUUID(), experience);
- }
- else
- {
- llwarns << "Skipping bootstrap entry which is missing "
- << EXPERIENCE_ID << llendl;
- }
- }
- }
- LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key,
- bool null_if_not_found)
- {
- if (private_key.isNull())
- {
- return LLUUID::null;
- }
- key_map_t::const_iterator it = sPrivateToPublicKeyMap.find(private_key);
- if (it == sPrivateToPublicKeyMap.end())
- {
- return null_if_not_found ? LLUUID::null : private_key;
- }
- llwarns << "Converted private key " << private_key << " to experience_id "
- << it->second << llendl;
- return it->second;
- }
- void LLExperienceCache::mapKeys(const LLSD& legacy_keys)
- {
- for (LLSD::array_const_iterator exp = legacy_keys.beginArray(),
- end = legacy_keys.endArray();
- exp != end; ++exp)
- {
- if (exp->has(EXPERIENCE_ID) && exp->has(PRIVATE_KEY))
- {
- sPrivateToPublicKeyMap[(*exp)[PRIVATE_KEY].asUUID()] =
- (*exp)[EXPERIENCE_ID].asUUID();
- }
- }
- }
- #endif
- void LLExperienceCache::processExperience(const LLUUID& public_key,
- const LLSD& experience)
- {
- LL_DEBUGS("ExperienceCache") << "Processing experience: "
- << experience[NAME] << " - Key: "
- << public_key.asString() << LL_ENDL;
- mCache[public_key] = experience;
- LLSD& row = mCache[public_key];
- if (row.has(EXPIRES))
- {
- row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds();
- }
- if (row.has(EXPERIENCE_ID))
- {
- mPendingQueue.erase(row[EXPERIENCE_ID].asUUID());
- }
- // signal
- signal_map_t::iterator sig_it = mSignalMap.find(public_key);
- if (sig_it != mSignalMap.end())
- {
- signal_ptr signal = sig_it->second;
- if (signal)
- {
- (*signal)(experience);
- }
- mSignalMap.erase(public_key);
- }
- }
- void LLExperienceCache::requestExperiencesCoro(adapter_ptr_t& adapter,
- std::string url,
- uuid_list_t requests)
- {
- LLSD result = adapter->getAndSuspend(url, mHttpOptions, mHttpHeaders);
-
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (!status)
- {
- F64 now = LLFrameTimer::getTotalSeconds();
- // Compute the retry delay, depending on the HTTP error or header
- S32 hstatus = status.getType();
- const LLSD& http_results =
- result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
- const LLSD& headers =
- http_results[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS];
- F64 retry_after = getErrorRetryDeltaTime(hstatus, headers);
- // build dummy entries for the failed requests
- for (uuid_list_t::const_iterator it = requests.begin(),
- end = requests.end();
- it != end; ++it)
- {
- const LLUUID& id = *it;
- if (id.notNull())
- {
- LLSD exp = get(id);
- if (exp.isUndefined())
- {
- // Leave the properties alone if we already have a cache entry
- // for this experience
- exp[PROPERTIES] = PROPERTY_INVALID;
- }
- exp[EXPIRES] = now + retry_after;
- exp[EXPERIENCE_ID] = id;
- exp["key_type"] = EXPERIENCE_ID;
- exp["uuid"] = id;
- exp["error"] = (LLSD::Integer)hstatus;
- exp[QUOTA] = DEFAULT_QUOTA;
- processExperience(id, exp);
- }
- }
- return;
- }
- const LLSD& experiences = result["experience_keys"];
- for (LLSD::array_const_iterator it = experiences.beginArray(),
- end = experiences.endArray();
- it != end; ++it)
- {
- const LLSD& row = *it;
- LLUUID public_key = row[EXPERIENCE_ID].asUUID();
- LL_DEBUGS("ExperienceCache") << "Received result for " << public_key
- << " display '" << row[NAME].asString()
- << "'" << LL_ENDL ;
- processExperience(public_key, row);
- }
- const LLSD& error_ids = result["error_ids"];
- for (LLSD::array_const_iterator it = error_ids.beginArray(),
- end = error_ids.endArray();
- it != end; ++it)
- {
- LLUUID id = it->asUUID();
- if (id.notNull())
- {
- LLSD exp;
- exp[EXPIRES] = DEFAULT_EXPIRATION;
- exp[EXPERIENCE_ID] = id;
- exp[PROPERTIES] = PROPERTY_INVALID;
- exp[MISSING] = true;
- exp[QUOTA] = DEFAULT_QUOTA;
- processExperience(id, exp);
- llwarns << "Error result for " << id << llendl;
- }
- }
- }
- void LLExperienceCache::requestExperiences()
- {
- if (sLookupURL.empty())
- {
- return;
- }
- F64 now = LLFrameTimer::getTotalSeconds();
- constexpr U32 EXP_URL_SEND_THRESHOLD = 3000;
- // Note: "PAGE_SIZE" is #defined somewhere in macOS headers... So we cannot
- // use it.
- constexpr U32 PAGE_SIZE_ = EXP_URL_SEND_THRESHOLD / UUID_STR_LENGTH;
- const std::string page_size = llformat("?page_size=%d", PAGE_SIZE_);
- const std::string key_query = "&" + EXPERIENCE_ID + "=";
- std::string uri = sLookupURL + page_size;
- LLUUID key;
- uuid_list_t requests;
- while (!mRequestQueue.empty() && !sShutdown)
- {
- uuid_list_t::iterator it = mRequestQueue.begin();
- key = *it;
- mRequestQueue.hset_erase(it);
- if (key.isNull()) continue;
- requests.emplace(key);
- uri += key_query + key.asString();
- mPendingQueue[key] = now;
- if (mRequestQueue.empty() || uri.size() > EXP_URL_SEND_THRESHOLD)
- {
- LL_DEBUGS("ExperienceCache") << "Query: " << uri << LL_ENDL;
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "RequestExperiences",
- boost::bind(&LLExperienceCache::requestExperiencesCoro,
- this, _1, uri, requests));
- uri = sLookupURL + page_size;
- requests.clear();
- }
- }
- }
- bool LLExperienceCache::isRequestPending(const LLUUID& public_key)
- {
- bool is_pending = false;
- constexpr F64 PENDING_TIMEOUT_SECS = 300.0;
- pending_map_t::const_iterator it = mPendingQueue.find(public_key);
- if (it != mPendingQueue.end())
- {
- F64 expire_time = LLFrameTimer::getTotalSeconds() -
- PENDING_TIMEOUT_SECS;
- is_pending = it->second > expire_time;
- }
- return is_pending;
- }
- void LLExperienceCache::idleCoro()
- {
- constexpr F32 SECS_BETWEEN_REQUESTS = 0.5f;
- constexpr F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds
- llinfos << "Launching Experience cache idle coro." << llendl;
- do
- {
- if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT))
- {
- eraseExpired();
- }
- if (!mRequestQueue.empty())
- {
- requestExperiences();
- }
- llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS);
- }
- while (!sShutdown);
- llinfos << "Experience cache idle coroutine exited." << llendl;
- }
- void LLExperienceCache::erase(const LLUUID& key)
- {
- cache_t::iterator it = mCache.find(key);
- if (it != mCache.end())
- {
- mCache.hmap_erase(it);
- }
- }
- void LLExperienceCache::eraseExpired()
- {
- F64 now = LLFrameTimer::getTotalSeconds();
- cache_t::iterator it = mCache.begin();
- while (it != mCache.end())
- {
- cache_t::iterator cur = it++;
- const LLSD& exp = cur->second;
- if (exp.has(EXPIRES) && exp[EXPIRES].asReal() < now)
- {
- if (!exp.has(EXPERIENCE_ID))
- {
- llwarns << "Removing an experience with no id" << llendl;
- mCache.erase(cur);
- }
- else
- {
- LLUUID id = exp[EXPERIENCE_ID].asUUID();
- LLUUID private_key;
- if (exp.has(PRIVATE_KEY))
- {
- private_key = exp[PRIVATE_KEY].asUUID();
- }
- if (private_key.notNull() || !exp.has("DoesNotExist"))
- {
- fetch(id, true);
- }
- else
- {
- llwarns << "Removing invalid experience: " << id << llendl;
- mCache.erase(cur);
- }
- }
- }
- }
- }
- bool LLExperienceCache::fetch(const LLUUID& key, bool refresh)
- {
- if (!key.isNull() && !isRequestPending(key) &&
- (refresh || mCache.count(key) == 0))
- {
- LL_DEBUGS("ExperienceCache") << "Queue request for " << EXPERIENCE_ID
- << " " << key << LL_ENDL;
- mRequestQueue.emplace(key);
- return true;
- }
- return false;
- }
- void LLExperienceCache::insert(const LLSD& exp_data)
- {
- if (exp_data.has(EXPERIENCE_ID))
- {
- processExperience(exp_data[EXPERIENCE_ID].asUUID(), exp_data);
- }
- else
- {
- llwarns << "Ignoring cache insert of experience which is missing "
- << EXPERIENCE_ID << llendl;
- }
- }
- const LLSD& LLExperienceCache::get(const LLUUID& key)
- {
- static const LLSD empty;
- if (key.isNull())
- {
- return empty;
- }
- cache_t::const_iterator it = mCache.find(key);
- if (it != mCache.end())
- {
- return it->second;
- }
- fetch(key);
- return empty;
- }
- void LLExperienceCache::get(const LLUUID& key, experience_get_fn_t slot)
- {
- if (key.isNull()) return;
- cache_t::const_iterator it = mCache.find(key);
- if (it != mCache.end())
- {
- // ...name already exists in cache, fire callback now
- callback_signal_t signal;
- signal.connect(slot);
- signal(it->second);
- return;
- }
- fetch(key);
- signal_ptr signal = signal_ptr(new callback_signal_t());
- std::pair<signal_map_t::iterator, bool> result =
- mSignalMap.emplace(key, signal);
- if (!result.second)
- {
- signal = (*result.first).second;
- }
- if (signal)
- {
- signal->connect(slot);
- }
- }
- void LLExperienceCache::fetchAssociatedExperience(const LLUUID& object_id,
- const LLUUID& item_id,
- const std::string& cap_url,
- experience_get_fn_t fn)
- {
- if (mCapability.empty())
- {
- llwarns << "Capability query method not set." << llendl;
- return;
- }
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "Fetch Associated",
- boost::bind(&LLExperienceCache::fetchAssociatedExperienceCoro,
- this, _1, object_id, item_id,
- cap_url, fn));
- }
- void LLExperienceCache::fetchAssociatedExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t& adapter,
- LLUUID object_id,
- LLUUID item_id,
- std::string url,
- experience_get_fn_t fn)
- {
- if (url.empty())
- {
- url = mCapability("GetMetadata");
- if (url.empty())
- {
- llwarns << "No GetMetadata capability." << llendl;
- return;
- }
- }
- LLSD fields;
- fields.append("experience");
- LLSD data;
- data["object-id"] = object_id;
- data["item-id"] = item_id;
- data["fields"] = fields;
- LLSD result = adapter->postAndSuspend(url, data, mHttpOptions,
- mHttpHeaders);
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (!status || !result.has("experience"))
- {
- LLSD failure;
- if (!status)
- {
- failure["error"] = (LLSD::Integer)status.getType();
- failure["message"] = status.getMessage();
- }
- else
- {
- failure["error"] = -1;
- failure["message"] = "no experience";
- }
- if (fn && !fn.empty())
- {
- fn(failure);
- }
- }
- else
- {
- LLUUID exp_id = result["experience"].asUUID();
- get(exp_id, fn);
- }
- }
- void LLExperienceCache::findExperienceByName(std::string text, int page,
- experience_get_fn_t fn)
- {
- if (mCapability.empty())
- {
- llwarns << "Capability query method not set." << llendl;
- return;
- }
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "Search Name",
- boost::bind(&LLExperienceCache::findExperienceByNameCoro,
- this, _1, text, page, fn));
- }
- void LLExperienceCache::findExperienceByNameCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t& adapter,
- std::string text, int page,
- experience_get_fn_t fn)
- {
- std::ostringstream url;
- url << mCapability("FindExperienceByName") << "?page=" << page
- << "&page_size=" << SEARCH_PAGE_SIZE << "&query="
- << LLURI::escape(text);
- LLSD result = adapter->getAndSuspend(url.str(), mHttpOptions,
- mHttpHeaders);
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (!status)
- {
- fn(LLSD());
- return;
- }
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- const LLSD& experiences = result["experience_keys"];
- for (LLSD::array_const_iterator it = experiences.beginArray(),
- end = experiences.endArray();
- it != end; ++it)
- {
- insert(*it);
- }
- fn(result);
- }
- void LLExperienceCache::getGroupExperiences(const LLUUID& group_id,
- experience_get_fn_t fn)
- {
- if (mCapability.empty())
- {
- llwarns << "Capability query method not set." << llendl;
- return;
- }
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "Group Experiences",
- boost::bind(&LLExperienceCache::getGroupExperiencesCoro,
- this, _1, group_id, fn));
- }
- void LLExperienceCache::getGroupExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t& adapter,
- LLUUID group_id,
- experience_get_fn_t fn)
- {
- // search for experiences owned by the current group
- std::string url = mCapability("GroupExperiences");
- if (url.empty())
- {
- llwarns << "No GroupExperiences capability" << llendl;
- return;
- }
- url += "?" + group_id.asString();
- LLSD result = adapter->getAndSuspend(url, mHttpOptions, mHttpHeaders);
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (!status)
- {
- fn(LLSD());
- return;
- }
- const LLSD& exp_ids = result["experience_ids"];
- fn(exp_ids);
- }
- void LLExperienceCache::getRegionExperiences(cap_query_fn_t regioncaps,
- experience_get_fn_t fn)
- {
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "Region Experiences",
- boost::bind(&LLExperienceCache::regionExperiencesCoro,
- this, _1, regioncaps, false, LLSD(),
- fn));
- }
- void LLExperienceCache::setRegionExperiences(cap_query_fn_t regioncaps,
- const LLSD& experiences,
- experience_get_fn_t fn)
- {
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "Region Experiences",
- boost::bind(&LLExperienceCache::regionExperiencesCoro,
- this, _1, regioncaps, true,
- experiences, fn));
- }
- void LLExperienceCache::regionExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t& adapter,
- cap_query_fn_t regioncaps,
- bool update, LLSD experiences,
- experience_get_fn_t fn)
- {
- // Search for experiences owned by the current group
- const std::string& url = regioncaps("RegionExperiences");
- if (url.empty())
- {
- llwarns << "No RegionExperiences capability" << llendl;
- return;
- }
- LLSD result;
- if (update)
- {
- result = adapter->postAndSuspend(url, experiences, mHttpOptions,
- mHttpHeaders);
- }
- else
- {
- result = adapter->getAndSuspend(url, mHttpOptions, mHttpHeaders);
- }
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (!status)
- {
- #if 0
- fn(LLSD());
- #endif
- return;
- }
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- fn(result);
- }
- #define COROCAST(T) static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(const std::string&, const LLSD&, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>(T)
- #define COROCAST2(T) static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(const std::string&, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)>(T)
- void LLExperienceCache::getExperiencePermission(const LLUUID& exp_id,
- experience_get_fn_t fn)
- {
- if (mCapability.empty())
- {
- llwarns << "Capability query method not set." << llendl;
- return;
- }
- std::string url = mCapability("ExperiencePreferences") + "?" +
- exp_id.asString();
- permissionInvoker_fn invoker(boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend),
- // _1 -> adapter
- // _2 -> url
- _1, _2,
- LLCore::HttpOptions::ptr_t(),
- LLCore::HttpHeaders::ptr_t()));
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "Preferences Set",
- boost::bind(&LLExperienceCache::experiencePermissionCoro,
- this, _1, invoker, url, fn));
- }
- void LLExperienceCache::setExperiencePermission(const LLUUID& exp_id,
- const std::string& perm,
- experience_get_fn_t fn)
- {
- if (mCapability.empty())
- {
- llwarns << "Capability query method not set." << llendl;
- return;
- }
- std::string url = mCapability("ExperiencePreferences");
- if (url.empty())
- {
- llwarns << "No ExperiencePreferences capability" << llendl;
- return;
- }
- LLSD perm_data;
- perm_data["permission"] = perm;
- LLSD data;
- data[exp_id.asString()] = perm_data;
- permissionInvoker_fn invoker(boost::bind(COROCAST(&LLCoreHttpUtil::HttpCoroutineAdapter::putAndSuspend),
- // _1 -> adapter
- // _2 -> url
- _1, _2, data,
- LLCore::HttpOptions::ptr_t(),
- LLCore::HttpHeaders::ptr_t()));
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "Preferences Set",
- boost::bind(&LLExperienceCache::experiencePermissionCoro,
- this, _1, invoker, url, fn));
- }
- void LLExperienceCache::forgetExperiencePermission(const LLUUID& exp_id,
- experience_get_fn_t fn)
- {
- if (mCapability.empty())
- {
- llwarns << "Capability query method not set." << llendl;
- return;
- }
- std::string url = mCapability("ExperiencePreferences") + "?" + exp_id.asString();
- permissionInvoker_fn invoker(boost::bind(COROCAST2(&LLCoreHttpUtil::HttpCoroutineAdapter::deleteAndSuspend),
- // _1 -> adapter
- // _2 -> url
- _1, _2,
- LLCore::HttpOptions::ptr_t(),
- LLCore::HttpHeaders::ptr_t()));
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "Preferences Set",
- boost::bind(&LLExperienceCache::experiencePermissionCoro,
- this, _1, invoker, url, fn));
- }
- void LLExperienceCache::experiencePermissionCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t& adapter,
- permissionInvoker_fn invokerfn,
- std::string url,
- experience_get_fn_t fn)
- {
- // search for experiences owned by the current group
- LLSD result = invokerfn(adapter, url);
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (status)
- {
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- fn(result);
- }
- }
- void LLExperienceCache::getExperienceAdmin(const LLUUID& exp_id,
- experience_get_fn_t fn)
- {
- if (mCapability.empty())
- {
- llwarns << "Capability query method not set." << llendl;
- return;
- }
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "IsAdmin",
- boost::bind(&LLExperienceCache::getExperienceAdminCoro,
- this, _1, exp_id, fn));
- }
- void LLExperienceCache::getExperienceAdminCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t& adapter,
- LLUUID exp_id,
- experience_get_fn_t fn)
- {
- std::string url = mCapability("IsExperienceAdmin");
- if (url.empty())
- {
- llwarns << "No Region Experiences capability" << llendl;
- return;
- }
- url += "?experience_id=" + exp_id.asString();
- LLSD result = adapter->getAndSuspend(url, mHttpOptions, mHttpHeaders);
- #if 0
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- #endif
- fn(result);
- }
- void LLExperienceCache::updateExperience(LLSD upd_data, experience_get_fn_t fn)
- {
- if (mCapability.empty())
- {
- llwarns << "Capability query method not set." << llendl;
- return;
- }
- LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
- cpmgr->enqueueCoprocedure("ExpCache", "IsAdmin",
- boost::bind(&LLExperienceCache::updateExperienceCoro,
- this, _1, upd_data, fn));
- }
- void LLExperienceCache::updateExperienceCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t& adapter,
- LLSD upd_data,
- experience_get_fn_t fn)
- {
- std::string url = mCapability("UpdateExperience");
- if (url.empty())
- {
- llwarns << "No UpdateExperience capability" << llendl;
- return;
- }
- upd_data.erase(QUOTA);
- upd_data.erase(EXPIRES);
- upd_data.erase(AGENT_ID);
- LLSD result = adapter->postAndSuspend(url, upd_data, mHttpOptions,
- mHttpHeaders);
- #if 0
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- #endif
- fn(result);
- }
- // Returns time to retry a request that generated an error, based on error type
- // and headers. Return value is seconds-since-epoch.
- //static
- F64 LLExperienceCache::getErrorRetryDeltaTime(S32 status, LLSD headers)
- {
- // Retry-After takes priority
- LLSD retry_after = headers["retry-after"];
- if (retry_after.isDefined())
- {
- // We only support the delta-seconds type
- S32 delta_seconds = retry_after.asInteger();
- if (delta_seconds > 0)
- {
- // ...valid delta-seconds
- return (F64)delta_seconds;
- }
- }
- // If no Retry-After, look for Cache-Control max-age
- // Allow the header to override the default
- LLSD cache_control_header = headers["cache-control"];
- if (cache_control_header.isDefined())
- {
- S32 max_age = 0;
- std::string cache_control = cache_control_header.asString();
- if (maxAgeFromCacheControl(cache_control, &max_age))
- {
- llwarns << "Got EXPIRES from headers, max_age = " << max_age
- << llendl;
- return (F64)max_age;
- }
- }
- // No information in header, make a guess
- if (status == 503)
- {
- // ...service unavailable, retry soon
- constexpr F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min
- return SERVICE_UNAVAILABLE_DELAY;
- }
- else if (status == 499)
- {
- // ...we were probably too busy, retry quickly
- constexpr F64 BUSY_DELAY = 10.0; // 10 seconds
- return BUSY_DELAY;
- }
- else
- {
- // ...other unexpected error
- constexpr F64 DEFAULT_DELAY = 3600.0; // 1 hour
- return DEFAULT_DELAY;
- }
- }
- bool LLExperienceCache::maxAgeFromCacheControl(const std::string& cache_control,
- S32* max_age)
- {
- // Split the string on "," to get a list of directives
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- tokenizer directives(cache_control, COMMA_SEPARATOR);
- for (tokenizer::iterator token_it = directives.begin();
- token_it != directives.end(); ++token_it)
- {
- // Tokens may have leading or trailing whitespace
- std::string token = *token_it;
- LLStringUtil::trim(token);
- if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
- {
- // this token starts with max-age, so let's chop it up by "="
- tokenizer subtokens(token, EQUALS_SEPARATOR);
- tokenizer::iterator subtoken_it = subtokens.begin();
- // Must have a token
- if (subtoken_it == subtokens.end()) return false;
- std::string subtoken = *subtoken_it;
- // Must exactly equal "max-age"
- LLStringUtil::trim(subtoken);
- if (subtoken != MAX_AGE) return false;
- // Must have another token
- if (++subtoken_it == subtokens.end()) return false;
- subtoken = *subtoken_it;
- // Must be a valid integer
- // *NOTE: atoi() returns 0 for invalid values, so we have to check
- // the string first.
- // *TODO: Do servers ever send "0000" for zero ? We don't handle
- // it
- LLStringUtil::trim(subtoken);
- if (subtoken == "0")
- {
- *max_age = 0;
- return true;
- }
- S32 val = atoi(subtoken.c_str());
- if (val > 0 && val < S32_MAX)
- {
- *max_age = val;
- return true;
- }
- return false;
- }
- }
- return false;
- }
- //static
- void LLExperienceCache::setLookupURL(const std::string& lookup_url)
- {
- sLookupURL = lookup_url;
- if (!sLookupURL.empty())
- {
- sLookupURL += "id/";
- }
- }
- //static
- bool LLExperienceCache::FilterWithProperty(const LLSD& experience, S32 prop)
- {
- return (experience[PROPERTIES].asInteger() & prop) != 0;
- }
- //static
- bool LLExperienceCache::FilterWithoutProperties(const LLSD& experience, S32 prop)
- {
- return (experience[PROPERTIES].asInteger() & prop) == prop;
- }
- //static
- bool LLExperienceCache::FilterWithoutProperty(const LLSD& experience, S32 prop)
- {
- return (experience[PROPERTIES].asInteger() & prop) == 0;
- }
- //static
- bool LLExperienceCache::FilterMatching(const LLSD& experience,
- const LLUUID& id)
- {
- if (experience.isUUID())
- {
- return experience.asUUID() == id;
- }
- return experience[EXPERIENCE_ID].asUUID() == id;
- }
|