From 56d23a03cf3a9655c098de31c4457671667e7c59 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 28 Feb 2019 13:46:26 -0500 Subject: [PATCH] Add ViewerAsset over HTTP support Has fallbacks for grids without HTTP, of course. Though some assets might be dropped initially, as it is untested, but better than nothing. --- indra/newview/llviewerassetstorage.cpp | 183 ++++++++++++++++++++++--- indra/newview/llviewerassetstorage.h | 14 +- 2 files changed, 178 insertions(+), 19 deletions(-) diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index bfd9c083b..393bc56fc 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -33,9 +33,12 @@ #include "message.h" #include "llagent.h" +#include "llviewerregion.h" + #include "lltransfersourceasset.h" #include "lltransfertargetvfile.h" #include "llviewerassetstats.h" +#include "llworld.h" ///---------------------------------------------------------------------------- /// LLViewerAssetRequest @@ -138,7 +141,6 @@ void LLViewerAssetStorage::storeAssetData( LLVFile vfile(mVFS, asset_id, asset_type, LLVFile::READ); S32 asset_size = vfile.getSize(); - LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type); req->mUpCallback = callback; req->mUserData = user_data; @@ -324,6 +326,7 @@ void LLViewerAssetStorage::storeAssetData( } } + /** * @brief Allocate and queue an asset fetch request for the viewer * @@ -360,9 +363,13 @@ void LLViewerAssetStorage::queueRequestUDP( BOOL duplicate, BOOL is_priority) { + LL_DEBUGS("ViewerAsset") << "Request asset via HTTP " << uuid << " type " << LLAssetType::lookup(atype) << LL_ENDL; + if (mUpstreamHost.isOk()) { - bool with_http = false; + const auto region = gAgent.getRegion(); + // Fallback on UDP if we have no cap or haven't received caps. This means missing some UDP-only region assets before caps received, but that's better for HTTP only. + bool with_http = !region || !region->capabilitiesReceived() || region->getViewerAssetUrl().empty(); LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype, with_http); req->mDownCallback = callback; req->mUserData = user_data; @@ -373,28 +380,33 @@ void LLViewerAssetStorage::queueRequestUDP( // are piggy-backing and will artificially lower averages. req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); } - mPendingDownloads.push_back(req); + // This is the same as the current UDP logic - don't re-request a duplicate. if (!duplicate) { - // send request message to our upstream data provider - // Create a new asset transfer. - LLTransferSourceParamsAsset spa; - spa.setAsset(uuid, atype); - - // Set our destination file, and the completion callback. - LLTransferTargetParamsVFile tpvf; - tpvf.setAsset(uuid, atype); - tpvf.setCallback(downloadCompleteCallback, *req); - - LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid << LL_ENDL; - LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET); - ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f)); - - bool with_http = false; bool is_temp = false; LLViewerAssetStatsFF::record_enqueue_main(atype, with_http, is_temp); + if (!with_http) // maintain this code for older grids + { + // send request message to our upstream data provider + // Create a new asset transfer. + LLTransferSourceParamsAsset spa; + spa.setAsset(uuid, atype); + + // Set our destination file, and the completion callback. + LLTransferTargetParamsVFile tpvf; + tpvf.setAsset(uuid, atype); + tpvf.setCallback(downloadCompleteCallback, *req); + + LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid << LL_ENDL; + LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET); + ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f)); + } + else + { + LLViewerAssetStorage::assetRequestCoro(req, uuid, atype, callback, user_data); + } } } else @@ -408,3 +420,138 @@ void LLViewerAssetStorage::queueRequestUDP( } } +extern AIHTTPTimeoutPolicy HTTPGetResponder_timeout; +class LLViewerAssetResponder : public LLHTTPClient::ResponderWithCompleted +{ +public: + LLViewerAssetResponder(const LLUUID& id, LLAssetType::EType type) : LLHTTPClient::ResponderWithCompleted() + , uuid(id), atype(type) + {} +private: + LLUUID uuid; + LLAssetType::EType atype; + + void completedRaw(LLChannelDescriptors const& channels, buffer_ptr_t const& buffer) override + { + if (LLApp::isQuitting()) + { + // Bail out if result arrives after shutdown has been started. + return; + } + + LL_DEBUGS("ViewerAsset") << "request succeeded, url " << mURL << LL_ENDL; + + S32 result_code = LL_ERR_NOERR; + LLExtStat ext_status = LL_EXSTAT_NONE; + + if (!isGoodStatus(mStatus)) + { + LL_DEBUGS("ViewerAsset") << "request failed, status " << mStatus << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_NONE; + } + else + { + std::string raw; + decode_raw_body(channels, buffer, raw); + + S32 size = raw.size(); + if (size > 0) + { + // This create-then-rename flow is modeled on + // LLTransferTargetVFile, which is what was used in the UDP + // case. + LLUUID temp_id; + temp_id.generate(); + LLVFile vf(gAssetStorage->mVFS, temp_id, atype, LLVFile::WRITE); + vf.setMaxSize(size); + if (!vf.write((const U8*)raw.data(), size)) + { + // TODO asset-http: handle error + LL_WARNS("ViewerAsset") << "Failure in vf.write()" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_VFS_CORRUPT; + } + else if (!vf.rename(uuid, atype)) + { + LL_WARNS("ViewerAsset") << "rename failed" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_VFS_CORRUPT; + } + } + else + { + // TODO asset-http: handle invalid size case + LL_WARNS("ViewerAsset") << "bad size" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_NONE; + } + } + + // Clean up pending downloads and trigger callbacks + gAssetStorage->removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); + } + AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy() const override { return HTTPGetResponder_timeout; } + char const* getName() const override { return "assetRequestCoro"; } +}; + +void LLViewerAssetStorage::capsRecvForRegion(const LLUUID& uuid, LLAssetType::EType atype, const LLUUID& region_id) +{ + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(region_id); + if (!regionp) + { + LL_WARNS("ViewerAsset") << "region not found for region_id " << region_id << LL_ENDL; + } + else + { + mViewerAssetUrl = regionp->getViewerAssetUrl(); + } + + LL_WARNS_ONCE("ViewerAsset") << "capsRecv got event" << LL_ENDL; + LL_WARNS_ONCE("ViewerAsset") << "region " << gAgent.getRegion() << " mViewerAssetUrl " << mViewerAssetUrl << LL_ENDL; + if (mViewerAssetUrl.empty()) + { + LL_WARNS_ONCE("ViewerAsset") << "asset request fails: caps received but no viewer asset cap found" << LL_ENDL; + auto result_code = LL_ERR_ASSET_REQUEST_FAILED; + auto ext_status = LL_EXSTAT_NONE; + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); + return; + } + std::string url = getAssetURL(mViewerAssetUrl, uuid,atype); + LL_DEBUGS("ViewerAsset") << "request url: " << url << LL_ENDL; + + LLHTTPClient::get(url, new LLViewerAssetResponder(uuid, atype)); +} + +void LLViewerAssetStorage::assetRequestCoro( + LLViewerAssetRequest *req, + const LLUUID uuid, + LLAssetType::EType atype, + LLGetAssetCallback callback, + void *user_data) +{ + if (!gAgent.getRegion()) + { + LL_WARNS_ONCE("ViewerAsset") << "Asset request fails: no region set" << LL_ENDL; + auto result_code = LL_ERR_ASSET_REQUEST_FAILED; + auto ext_status = LL_EXSTAT_NONE; + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); + return; + } + else if (!gAgent.getRegion()->capabilitiesReceived()) + { + LL_WARNS_ONCE("ViewerAsset") << "Waiting for capabilities" << LL_ENDL; + + gAgent.getRegion()->setCapabilitiesReceivedCallback( + boost::bind(&LLViewerAssetStorage::capsRecvForRegion, this, uuid, atype, _1)); + } + else capsRecvForRegion(uuid, atype, gAgent.getRegion()->getRegionID()); +} + +std::string LLViewerAssetStorage::getAssetURL(const std::string& cap_url, const LLUUID& uuid, LLAssetType::EType atype) +{ + std::string type_name = LLAssetType::lookup(atype); + std::string url = cap_url + "/?" + type_name + "_id=" + uuid.asString(); + return url; +} + diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index c71a68527..e1e110187 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -32,6 +32,8 @@ class LLVFile; +class LLViewerAssetRequest; + class LLViewerAssetStorage : public LLAssetStorage { public: @@ -73,7 +75,6 @@ protected: BOOL duplicate, BOOL is_priority); - void queueRequestUDP(const LLUUID& uuid, LLAssetType::EType type, void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), @@ -81,6 +82,17 @@ protected: BOOL duplicate, BOOL is_priority); + void capsRecvForRegion(const LLUUID& uuid, LLAssetType::EType atype, const LLUUID& region_id); + + void assetRequestCoro(LLViewerAssetRequest *req, + const LLUUID uuid, + LLAssetType::EType atype, + void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), + void *user_data); + + std::string getAssetURL(const std::string& cap_url, const LLUUID& uuid, LLAssetType::EType atype); + + std::string mViewerAssetUrl; }; #endif