llviewerassetstorage.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /**
  2. * @file llviewerassetstorage.cpp
  3. * @brief Subclass capable of loading asset data to/from an external source.
  4. *
  5. * $LicenseInfo:firstyear=2003&license=viewergpl$
  6. *
  7. * Copyright (c) 2003-2009, 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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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. #include "llviewerprecompiledheaders.h"
  33. #include "llviewerassetstorage.h"
  34. #include "llapp.h"
  35. #include "llcoproceduremanager.h"
  36. #include "llfilesystem.h"
  37. #include "lltransfersourceasset.h"
  38. #include "lltransfertargetvfile.h"
  39. #include "llmessage.h"
  40. #include "llagent.h"
  41. #include "llappviewer.h" // For gAppViewerp and LLAppCoreHttp
  42. #include "llgridmanager.h" // For gIsInSecondLife
  43. #include "llviewercontrol.h"
  44. #include "llviewerregion.h"
  45. #include "llvlcomposition.h" // For LLTerrain::isAsset()
  46. LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem* msg,
  47. LLXferManager* xfer)
  48. : LLAssetStorage(msg, xfer)
  49. {
  50. LLCoprocedureManager::getInstance()->initializePool("AssetStorage");
  51. LLAppCoreHttp& app_core_http = gAppViewerp->getAppCoreHttp();
  52. mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_ASSETS);
  53. }
  54. //virtual
  55. LLViewerAssetStorage::~LLViewerAssetStorage()
  56. {
  57. while (mCoroWaitList.size())
  58. {
  59. CoroWaitList& request = mCoroWaitList.front();
  60. // Clean up pending downloads, delete request and trigger callbacks
  61. removeAndCallbackPendingDownloads(request.mId, request.mType,
  62. request.mId, request.mType,
  63. LL_ERR_NOERR, LLExtStat::NONE);
  64. mCoroWaitList.pop_front();
  65. }
  66. }
  67. //virtual
  68. void LLViewerAssetStorage::storeAssetData(const LLTransactionID& tid,
  69. LLAssetType::EType asset_type,
  70. LLStoreAssetCallback callback,
  71. void* user_data,
  72. bool temp_file,
  73. bool is_priority,
  74. bool store_local,
  75. bool user_waiting,
  76. F64 timeout)
  77. {
  78. LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
  79. llinfos << "Legacy call for " << tid << "."
  80. << LLAssetType::lookup(asset_type) << " - Asset ID: " << asset_id
  81. << llendl;
  82. if (mUpstreamHost.isOk())
  83. {
  84. if (LLFileSystem::getExists(asset_id))
  85. {
  86. // Pack data into this packet if we can fit it.
  87. U8 buffer[MTUBYTES];
  88. buffer[0] = 0;
  89. LLFileSystem vfile(asset_id);
  90. S32 asset_size = vfile.getSize();
  91. LLAssetRequest* req = new LLAssetRequest(asset_id, asset_type);
  92. req->mUpCallback = callback;
  93. req->mUserData = user_data;
  94. if (asset_size < 1)
  95. {
  96. // This can happen if there's a bug in our code or if the cache
  97. // has been corrupted.
  98. llwarns << "Data for asset " << asset_id << "."
  99. << LLAssetType::lookup(asset_type)
  100. << "_should_ already be in the cache, but it is not !"
  101. << llendl;
  102. delete req;
  103. if (callback)
  104. {
  105. callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_FAILED,
  106. LLExtStat::CACHE_CORRUPT);
  107. }
  108. return;
  109. }
  110. else
  111. {
  112. if (is_priority)
  113. {
  114. mPendingUploads.push_front(req);
  115. }
  116. else
  117. {
  118. mPendingUploads.push_back(req);
  119. }
  120. }
  121. // Read the data from the cache if it will fit in this packet.
  122. if (asset_size + 100 < MTUBYTES)
  123. {
  124. bool res = vfile.read(buffer, asset_size);
  125. S32 bytes_read = res ? vfile.getLastBytesRead() : 0;
  126. if (bytes_read == asset_size)
  127. {
  128. req->mDataSentInFirstPacket = true;
  129. }
  130. else
  131. {
  132. llwarns << "Probable corruption in cached file, aborting store asset data."
  133. << llendl;
  134. if (callback)
  135. {
  136. callback(asset_id, user_data,
  137. LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE,
  138. LLExtStat::CACHE_CORRUPT);
  139. }
  140. return;
  141. }
  142. }
  143. else
  144. {
  145. // Too big, do an xfer
  146. buffer[0] = 0;
  147. asset_size = 0;
  148. }
  149. mMessageSys->newMessageFast(_PREHASH_AssetUploadRequest);
  150. mMessageSys->nextBlockFast(_PREHASH_AssetBlock);
  151. mMessageSys->addUUIDFast(_PREHASH_TransactionID, tid);
  152. mMessageSys->addS8Fast(_PREHASH_Type, (S8)asset_type);
  153. mMessageSys->addBoolFast(_PREHASH_Tempfile, temp_file);
  154. mMessageSys->addBoolFast(_PREHASH_StoreLocal, store_local);
  155. mMessageSys->addBinaryDataFast(_PREHASH_AssetData, buffer, asset_size);
  156. mMessageSys->sendReliable(mUpstreamHost);
  157. }
  158. else
  159. {
  160. llwarns << "AssetStorage: attempt to upload non-existent vfile "
  161. << asset_id << "." << LLAssetType::lookup(asset_type)
  162. << llendl;
  163. if (callback)
  164. {
  165. callback(asset_id, user_data,
  166. LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE,
  167. LLExtStat::NONEXISTENT_FILE);
  168. }
  169. }
  170. }
  171. else
  172. {
  173. llwarns << "Attempt to move asset store request upstream without valid upstream provider"
  174. << llendl;
  175. if (callback)
  176. {
  177. callback(asset_id, user_data, LL_ERR_CIRCUIT_GONE,
  178. LLExtStat::NO_UPSTREAM);
  179. }
  180. }
  181. }
  182. void LLViewerAssetStorage::storeAssetData(const std::string& filename,
  183. const LLTransactionID& tid,
  184. LLAssetType::EType asset_type,
  185. LLStoreAssetCallback callback,
  186. void* user_data,
  187. bool temp_file,
  188. bool is_priority,
  189. bool user_waiting,
  190. F64 timeout)
  191. {
  192. if (filename.empty())
  193. {
  194. llerrs << "No filename specified" << llendl;
  195. return;
  196. }
  197. LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
  198. llinfos << "Legacy storeAssetData call for asset" << asset_id << "."
  199. << LLAssetType::lookup(asset_type) << llendl;
  200. S32 size = 0;
  201. LLFILE* fp = LLFile::open(filename, "rb");
  202. if (fp)
  203. {
  204. fseek(fp, 0, SEEK_END);
  205. size = ftell(fp);
  206. fseek(fp, 0, SEEK_SET);
  207. }
  208. if (size)
  209. {
  210. LLLegacyAssetRequest* legacy = new LLLegacyAssetRequest;
  211. legacy->mUpCallback = callback;
  212. legacy->mUserData = user_data;
  213. LLFileSystem file(asset_id, LLFileSystem::APPEND);
  214. constexpr S32 buf_size = 65536;
  215. U8 copy_buf[buf_size];
  216. while ((size = (S32)fread(copy_buf, 1, buf_size, fp)))
  217. {
  218. file.write(copy_buf, size);
  219. }
  220. LLFile::close(fp);
  221. // if this upload fails, the caller needs to setup a new tempfile for
  222. // us
  223. if (temp_file)
  224. {
  225. LLFile::remove(filename);
  226. }
  227. // LLAssetStorage metric: Success not needed; handled in the
  228. // overloaded method here:
  229. LLViewerAssetStorage::storeAssetData(tid, asset_type,
  230. legacyStoreDataCallback,
  231. (void**)legacy, temp_file,
  232. is_priority);
  233. }
  234. else // size == 0 (but previous block changes size)
  235. {
  236. if (fp)
  237. {
  238. LLFile::close(fp);
  239. }
  240. if (callback)
  241. {
  242. callback(asset_id, user_data, LL_ERR_CANNOT_OPEN_FILE,
  243. LLExtStat::BLOCKED_FILE);
  244. }
  245. }
  246. }
  247. //virtual
  248. void LLViewerAssetStorage::checkForTimeouts()
  249. {
  250. // If no agent region: wait for it to come up (required for "terrain
  251. // assets" in the agent region on login). HB
  252. if (!gAgent.getRegion())
  253. {
  254. return;
  255. }
  256. LLAssetStorage::checkForTimeouts();
  257. // Restore requests
  258. LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
  259. while (mCoroWaitList.size())
  260. {
  261. CoroWaitList& req = mCoroWaitList.front();
  262. LLUUID id =
  263. cpmgr->enqueueCoprocedure("AssetStorage",
  264. "LLViewerAssetStorage::assetRequestCoro",
  265. boost::bind(&LLViewerAssetStorage::assetRequestCoro,
  266. this, req.mUrl, req.mRequest,
  267. req.mId, req.mType, req.mCallback,
  268. req.mUserData));
  269. if (id.isNull()) // Failed to enqueue...
  270. {
  271. llinfos << "Will retry: " << req.mId << llendl;
  272. break;
  273. }
  274. mCoroWaitList.pop_front();
  275. }
  276. }
  277. //virtual
  278. void LLViewerAssetStorage::queueDataRequest(const LLUUID& uuid,
  279. LLAssetType::EType atype,
  280. LLGetAssetCallback callback,
  281. void* user_data, bool duplicate,
  282. bool is_priority)
  283. {
  284. static LLCachedControl<bool> use_viewerasset(gSavedSettings,
  285. "UseViewerAssetCap");
  286. if (gIsInSecondLife ||
  287. (use_viewerasset && gAgent.hasRegionCapability("ViewerAsset")))
  288. {
  289. queueHttpRequest(uuid, atype, callback, user_data, duplicate,
  290. is_priority);
  291. return;
  292. }
  293. // Legacy, UDP fetch, for OpenSim
  294. queueUdpRequest(uuid, atype, callback, user_data, duplicate, is_priority);
  295. }
  296. void LLViewerAssetStorage::queueUdpRequest(const LLUUID& uuid,
  297. LLAssetType::EType atype,
  298. LLGetAssetCallback callback,
  299. void* user_data, bool duplicate,
  300. bool is_priority)
  301. {
  302. if (mUpstreamHost.isOk())
  303. {
  304. // Stash the callback info so we can find it after we get the response
  305. // message
  306. LLAssetRequest* req = new LLAssetRequest(uuid, atype);
  307. req->mDownCallback = callback;
  308. req->mUserData = user_data;
  309. req->mIsPriority = is_priority;
  310. mPendingDownloads.push_back(req);
  311. if (!duplicate)
  312. {
  313. // Send request message to our upstream data provider.
  314. // Create a new asset transfer.
  315. LLTransferSourceParamsAsset spa;
  316. spa.setAsset(uuid, atype);
  317. // Set our destination file, and the completion callback.
  318. LLTransferTargetParamsVFile tpvf;
  319. tpvf.setAsset(uuid, atype);
  320. tpvf.setCallback(downloadCompleteCallback, *req);
  321. LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid
  322. << LL_ENDL;
  323. LLTransferTargetChannel* ttcp =
  324. gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET);
  325. if (ttcp)
  326. {
  327. ttcp->requestTransfer(spa, tpvf,
  328. 100.f + (is_priority ? 1.f : 0.f));
  329. }
  330. else
  331. {
  332. llwarns << "Cannot find transfer manager channel for upstream host: "
  333. << mUpstreamHost.getIPandPort() << ". Aborted."
  334. << llendl;
  335. }
  336. }
  337. }
  338. else
  339. {
  340. // Uh-oh, we should not have gotten here
  341. llwarns << "Attempt to move asset data request upstream without valid upstream provider"
  342. << llendl;
  343. if (callback)
  344. {
  345. callback(uuid, atype, user_data, LL_ERR_CIRCUIT_GONE,
  346. LLExtStat::NO_UPSTREAM);
  347. }
  348. }
  349. }
  350. void LLViewerAssetStorage::queueHttpRequest(const LLUUID& asset_id,
  351. LLAssetType::EType atype,
  352. LLGetAssetCallback callback,
  353. void* user_data, bool duplicate,
  354. bool is_priority)
  355. {
  356. LLAssetRequest* req = new LLAssetRequest(asset_id, atype);
  357. req->mDownCallback = callback;
  358. req->mUserData = user_data;
  359. req->mIsPriority = is_priority;
  360. mPendingDownloads.push_back(req);
  361. if (duplicate)
  362. {
  363. return;
  364. }
  365. std::string query = "?";
  366. query += LLAssetType::lookup(atype);
  367. query += "_id=" + asset_id.asString();
  368. // If no agent region: wait for it to come up (required for "terrain
  369. // assets" in the agent region on login). HB
  370. if (!gAgent.getRegion())
  371. {
  372. LL_DEBUGS("AssetStorage") << "Agent region not yet set. Delaying: "
  373. << asset_id << LL_ENDL;
  374. mCoroWaitList.emplace_back(req, query, asset_id, atype, callback,
  375. user_data);
  376. return;
  377. }
  378. LLCoprocedureManager* cpmgr = LLCoprocedureManager::getInstance();
  379. LLUUID id =
  380. cpmgr->enqueueCoprocedure("AssetStorage",
  381. "LLViewerAssetStorage::assetRequestCoro",
  382. boost::bind(&LLViewerAssetStorage::assetRequestCoro,
  383. this, query, req, asset_id, atype,
  384. callback, user_data));
  385. if (id.isNull()) // Failed to enqueue...
  386. {
  387. mCoroWaitList.emplace_back(req, query, asset_id, atype, callback,
  388. user_data);
  389. llinfos << "Will retry: " << asset_id << llendl;
  390. }
  391. }
  392. static void cap_received_for_region(std::string pump_name)
  393. {
  394. gEventPumps.obtain(pump_name).post(LLSD());
  395. }
  396. void LLViewerAssetStorage::assetRequestCoro(std::string query,
  397. LLAssetRequest* req, LLUUID uuid,
  398. LLAssetType::EType atype,
  399. void (*callback)(const LLUUID&,
  400. LLAssetType::EType,
  401. void*, S32,
  402. LLExtStat),
  403. void* user_data)
  404. {
  405. if (!gAssetStoragep)
  406. {
  407. llwarns << "Asset storage no longer exists. Failed to fetch asset: "
  408. << uuid << llendl;
  409. return;
  410. }
  411. S32 result_code = LL_ERR_NOERR;
  412. LLExtStat ext_status = LLExtStat::NONE;
  413. LLViewerRegion* regionp = gAgent.getRegion();
  414. if (!regionp)
  415. {
  416. llwarns << "No agent region ! Failed to fetch asset: " << uuid
  417. << llendl;
  418. result_code = LL_ERR_ASSET_REQUEST_FAILED;
  419. ext_status = LLExtStat::NONE;
  420. removeAndCallbackPendingDownloads(uuid, atype, uuid, atype,
  421. result_code, ext_status);
  422. return;
  423. }
  424. if (!regionp->capabilitiesReceived())
  425. {
  426. llinfos_once << "Waiting for capabilities in region: "
  427. << regionp->getName() << llendl;
  428. LLEventStream caps_recv("waitForCaps", true);
  429. regionp->setCapsReceivedCB(boost::bind(&cap_received_for_region,
  430. caps_recv.getName()));
  431. llcoro::suspendUntilEventOn(caps_recv);
  432. }
  433. if (LLApp::isExiting() || !gAssetStoragep)
  434. {
  435. // Bail out if capabilities arrive after shutdown has been started.
  436. return;
  437. }
  438. const std::string& cap = gAgent.getRegionCapability("ViewerAsset");
  439. if (cap.empty())
  440. {
  441. if (regionp != gAgent.getRegion())
  442. {
  443. llwarns << "Region gone. Failed to fetch asset: " << uuid
  444. << llendl;
  445. }
  446. else
  447. {
  448. llwarns << "Capabilities received but no ViewerAsset cap found. Failed to fetch asset: "
  449. << uuid << llendl;
  450. }
  451. result_code = LL_ERR_ASSET_REQUEST_FAILED;
  452. ext_status = LLExtStat::NONE;
  453. removeAndCallbackPendingDownloads(uuid, atype, uuid, atype,
  454. result_code, ext_status);
  455. return;
  456. }
  457. LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid
  458. << " - Request URL: " << cap + query << LL_ENDL;
  459. LLCoreHttpUtil::HttpCoroutineAdapter adapter("assetRequestCoro",
  460. mHttpPolicyClass);
  461. LLSD result = adapter.getRawAndSuspend(cap + query);
  462. if (LLApp::isExiting() || !gAssetStoragep)
  463. {
  464. // Bail out if result arrives after shutdown has been started.
  465. return;
  466. }
  467. LLCore::HttpStatus status =
  468. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  469. if (status)
  470. {
  471. const LLSD::Binary& raw =
  472. result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary();
  473. S32 size = raw.size();
  474. if (size > 0)
  475. {
  476. LLFileSystem vf(uuid, LLFileSystem::OVERWRITE);
  477. if (!vf.write(raw.data(), size))
  478. {
  479. // *TODO asset-http: handle error
  480. llwarns << "Failure to write data in cache for asset: " << uuid
  481. << llendl;
  482. result_code = LL_ERR_ASSET_REQUEST_FAILED;
  483. ext_status = LLExtStat::CACHE_CORRUPT;
  484. }
  485. else
  486. {
  487. LL_DEBUGS("AssetStorage") << "Transfer successful for " << uuid
  488. << LL_ENDL;
  489. }
  490. }
  491. else
  492. {
  493. llwarns << "Bad size (" << size
  494. << ") in response to fetch request for asset: " << uuid
  495. << llendl;
  496. result_code = LL_ERR_ASSET_REQUEST_FAILED;
  497. ext_status = LLExtStat::NONE;
  498. // *TODO: implement UDP fallback path ?
  499. }
  500. }
  501. else
  502. {
  503. // A failure is "normal" if that asset Id was actually a terrain
  504. // texture Id and not a PBR material, so do not log an error. HB
  505. if (!LLTerrain::isAsset(uuid))
  506. {
  507. llwarns << "Request failed for asset: " << uuid << " - Reason: "
  508. << status.toString() << llendl;
  509. }
  510. result_code = LL_ERR_ASSET_REQUEST_FAILED;
  511. ext_status = LLExtStat::NONE;
  512. // *TODO: implement UDP fallback path (for OpenSIM only now) ?
  513. }
  514. removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code,
  515. ext_status);
  516. }