llhttpassetstorage.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:42k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llhttpassetstorage.cpp
  3.  * @brief Subclass capable of loading asset data to/from an external
  4.  * source. Currently, a web server accessed via curl
  5.  *
  6.  * $LicenseInfo:firstyear=2003&license=viewergpl$
  7.  * 
  8.  * Copyright (c) 2003-2010, Linden Research, Inc.
  9.  * 
  10.  * Second Life Viewer Source Code
  11.  * The source code in this file ("Source Code") is provided by Linden Lab
  12.  * to you under the terms of the GNU General Public License, version 2.0
  13.  * ("GPL"), unless you have obtained a separate licensing agreement
  14.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  15.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17.  * 
  18.  * There are special exceptions to the terms and conditions of the GPL as
  19.  * it is applied to this Source Code. View the full text of the exception
  20.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  21.  * online at
  22.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23.  * 
  24.  * By copying, modifying or distributing this software, you acknowledge
  25.  * that you have read and understood your obligations described above,
  26.  * and agree to abide by those obligations.
  27.  * 
  28.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30.  * COMPLETENESS OR PERFORMANCE.
  31.  * $/LicenseInfo$
  32.  */
  33. #include "linden_common.h"
  34. #include "llhttpassetstorage.h"
  35. #include <sys/stat.h>
  36. #include "indra_constants.h"
  37. #include "message.h"
  38. #include "llvfile.h"
  39. #include "llvfs.h"
  40. #ifdef LL_STANDALONE
  41. # include <zlib.h>
  42. #else
  43. # include "zlib/zlib.h"
  44. #endif
  45. const U32 MAX_RUNNING_REQUESTS = 1;
  46. const F32 MAX_PROCESSING_TIME = 0.005f;
  47. const S32 CURL_XFER_BUFFER_SIZE = 65536;
  48. // Try for 30 minutes for now.
  49. const F32 GET_URL_TO_FILE_TIMEOUT = 1800.0f;
  50. const S32 COMPRESSED_INPUT_BUFFER_SIZE = 4096;
  51. const S32 HTTP_OK = 200;
  52. const S32 HTTP_PUT_OK = 201;
  53. const S32 HTTP_NO_CONTENT = 204;
  54. const S32 HTTP_MISSING = 404;
  55. const S32 HTTP_SERVER_BAD_GATEWAY = 502;
  56. const S32 HTTP_SERVER_TEMP_UNAVAILABLE = 503;
  57. /////////////////////////////////////////////////////////////////////////////////
  58. // LLTempAssetData
  59. // An asset not stored on central asset store, but on a simulator node somewhere.
  60. /////////////////////////////////////////////////////////////////////////////////
  61. struct LLTempAssetData
  62. {
  63. LLUUID mAssetID;
  64. LLUUID mAgentID;
  65. std::string mHostName;
  66. };
  67. /////////////////////////////////////////////////////////////////////////////////
  68. // LLHTTPAssetRequest
  69. /////////////////////////////////////////////////////////////////////////////////
  70. class LLHTTPAssetRequest : public LLAssetRequest
  71. {
  72. public:
  73. LLHTTPAssetRequest(LLHTTPAssetStorage *asp, const LLUUID &uuid, 
  74.    LLAssetType::EType type, LLAssetStorage::ERequestType rt,
  75.    const std::string& url, CURLM *curl_multi);
  76. virtual ~LLHTTPAssetRequest();
  77. void setupCurlHandle();
  78. void cleanupCurlHandle();
  79. void    prepareCompressedUpload();
  80. void finishCompressedUpload();
  81. size_t readCompressedData(void* data, size_t size);
  82. static size_t curlCompressedUploadCallback(
  83. void *data, size_t size, size_t nmemb, void *user_data);
  84. virtual LLSD getTerseDetails() const;
  85. virtual LLSD getFullDetails() const;
  86. public:
  87. LLHTTPAssetStorage *mAssetStoragep;
  88. CURL  *mCurlHandle;
  89. CURLM *mCurlMultiHandle;
  90. std::string mURLBuffer;
  91. struct curl_slist *mHTTPHeaders;
  92. LLVFile *mVFile;
  93. LLUUID  mTmpUUID;
  94. LLAssetStorage::ERequestType mRequestType;
  95. bool mZInitialized;
  96. z_stream mZStream;
  97. char* mZInputBuffer;
  98. bool mZInputExhausted;
  99. FILE *mFP;
  100. };
  101. LLHTTPAssetRequest::LLHTTPAssetRequest(LLHTTPAssetStorage *asp, 
  102. const LLUUID &uuid, 
  103. LLAssetType::EType type, 
  104. LLAssetStorage::ERequestType rt,
  105. const std::string& url, 
  106. CURLM *curl_multi)
  107. : LLAssetRequest(uuid, type),
  108.   mZInitialized(false)
  109. {
  110. memset(&mZStream, 0, sizeof(mZStream)); // we'll initialize this later, but for now zero the whole C-style struct to avoid debug/coverity noise
  111. mAssetStoragep = asp;
  112. mCurlHandle = NULL;
  113. mCurlMultiHandle = curl_multi;
  114. mVFile = NULL;
  115. mRequestType = rt;
  116. mHTTPHeaders = NULL;
  117. mFP = NULL;
  118. mZInputBuffer = NULL;
  119. mZInputExhausted = false;
  120. mURLBuffer = url;
  121. }
  122. LLHTTPAssetRequest::~LLHTTPAssetRequest()
  123. {
  124. // Cleanup/cancel the request
  125. if (mCurlHandle)
  126. {
  127. curl_multi_remove_handle(mCurlMultiHandle, mCurlHandle);
  128. cleanupCurlHandle();
  129. }
  130. if (mHTTPHeaders)
  131. {
  132. curl_slist_free_all(mHTTPHeaders);
  133. }
  134. delete   mVFile;
  135. finishCompressedUpload();
  136. }
  137. // virtual
  138. LLSD LLHTTPAssetRequest::getTerseDetails() const
  139. {
  140. LLSD sd = LLAssetRequest::getTerseDetails();
  141. sd["url"] = mURLBuffer;
  142. return sd;
  143. }
  144. // virtual
  145. LLSD LLHTTPAssetRequest::getFullDetails() const
  146. {
  147. LLSD sd = LLAssetRequest::getFullDetails();
  148. if (mCurlHandle)
  149. {
  150. long curl_response = -1;
  151. long curl_connect = -1;
  152. double curl_total_time = -1.0f;
  153. double curl_size_upload = -1.0f;
  154. double curl_size_download = -1.0f;
  155. long curl_content_length_upload = -1;
  156. long curl_content_length_download = -1;
  157. long curl_request_size = -1;
  158. const char* curl_content_type = NULL;
  159. curl_easy_getinfo(mCurlHandle, CURLINFO_HTTP_CODE, &curl_response);
  160. curl_easy_getinfo(mCurlHandle, CURLINFO_HTTP_CONNECTCODE, &curl_connect);
  161. curl_easy_getinfo(mCurlHandle, CURLINFO_TOTAL_TIME, &curl_total_time);
  162. curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_UPLOAD,  &curl_size_upload);
  163. curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_DOWNLOAD, &curl_size_download);
  164. curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_LENGTH_UPLOAD,   &curl_content_length_upload);
  165. curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curl_content_length_download);
  166. curl_easy_getinfo(mCurlHandle, CURLINFO_REQUEST_SIZE, &curl_request_size);
  167. curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_TYPE, &curl_content_type);
  168. sd["curl_response_code"] = (int) curl_response;
  169. sd["curl_http_connect_code"] = (int) curl_connect;
  170. sd["curl_total_time"] = curl_total_time;
  171. sd["curl_size_upload"]   = curl_size_upload;
  172. sd["curl_size_download"] = curl_size_download;
  173. sd["curl_content_length_upload"]   = (int) curl_content_length_upload;
  174. sd["curl_content_length_download"] = (int) curl_content_length_download;
  175. sd["curl_request_size"] = (int) curl_request_size;
  176. if (curl_content_type)
  177. {
  178. sd["curl_content_type"] = curl_content_type;
  179. }
  180. else
  181. {
  182. sd["curl_content_type"] = "";
  183. }
  184. }
  185. sd["temp_id"] = mTmpUUID;
  186. sd["request_type"] = LLAssetStorage::getRequestName(mRequestType);
  187. sd["z_initialized"] = mZInitialized;
  188. sd["z_input_exhausted"] = mZInputExhausted;
  189. S32 file_size = -1;
  190. if (mFP)
  191. {
  192. struct stat file_stat;
  193. int file_desc = fileno(mFP);
  194. if ( fstat(file_desc, &file_stat) == 0)
  195. {
  196. file_size = file_stat.st_size;
  197. }
  198. }
  199. sd["file_size"] = file_size;
  200. return sd;
  201. }
  202. void LLHTTPAssetRequest::setupCurlHandle()
  203. {
  204. // *NOTE: Similar code exists in mapserver/llcurlutil.cpp  JC
  205. mCurlHandle = curl_easy_init();
  206. curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
  207. curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
  208. curl_easy_setopt(mCurlHandle, CURLOPT_URL, mURLBuffer.c_str());
  209. curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
  210. if (LLAssetStorage::RT_DOWNLOAD == mRequestType)
  211. {
  212. curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
  213. // only do this on downloads, as uploads 
  214. // to some apache configs (like our test grids)
  215. // mistakenly claim the response is gzip'd if the resource
  216. // name ends in .gz, even though in a PUT, the response is
  217. // just plain HTML saying "created"
  218. }
  219. /* Remove the Pragma: no-cache header that libcurl inserts by default;
  220.    we want the cached version, if possible. */
  221. if (mZInitialized)
  222. {
  223. curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, "");
  224. // disable use of proxy, which can't handle chunked transfers
  225. }
  226. mHTTPHeaders = curl_slist_append(mHTTPHeaders, "Pragma:");
  227. // bug in curl causes DNS to be cached for too long a time, 0 sets it to never cache DNS results internally (to curl)
  228. curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
  229. // resist the temptation to explicitly add the Transfer-Encoding: chunked
  230. // header here - invokes a libCURL bug
  231. curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mHTTPHeaders);
  232. if (mAssetStoragep)
  233. {
  234. // Set the appropriate pending upload or download flag
  235. mAssetStoragep->addRunningRequest(mRequestType, this);
  236. }
  237. else
  238. {
  239. llerrs << "LLHTTPAssetRequest::setupCurlHandle - No asset storage associated with this request!" << llendl;
  240. }
  241. }
  242. void LLHTTPAssetRequest::cleanupCurlHandle()
  243. {
  244. curl_easy_cleanup(mCurlHandle);
  245. if (mAssetStoragep)
  246. {
  247. // Terminating a request.  Thus upload or download is no longer pending.
  248. mAssetStoragep->removeRunningRequest(mRequestType, this);
  249. }
  250. else
  251. {
  252. llerrs << "LLHTTPAssetRequest::~LLHTTPAssetRequest - No asset storage associated with this request!" << llendl;
  253. }
  254. mCurlHandle = NULL;
  255. }
  256. void LLHTTPAssetRequest::prepareCompressedUpload()
  257. {
  258. mZStream.next_in = Z_NULL;
  259. mZStream.avail_in = 0;
  260. mZStream.zalloc = Z_NULL;
  261. mZStream.zfree = Z_NULL;
  262. mZStream.opaque = Z_NULL;
  263. int r = deflateInit2(&mZStream,
  264. 1, // compression level
  265. Z_DEFLATED, // the only method defined
  266. 15 + 16, // the default windowBits + gzip header flag
  267. 8, // the default memLevel
  268. Z_DEFAULT_STRATEGY);
  269. if (r != Z_OK)
  270. {
  271. llerrs << "LLHTTPAssetRequest::prepareCompressedUpload defalateInit2() failed" << llendl;
  272. }
  273. mZInitialized = true;
  274. mZInputBuffer = new char[COMPRESSED_INPUT_BUFFER_SIZE];
  275. mZInputExhausted = false;
  276. mVFile = new LLVFile(gAssetStorage->mVFS,
  277. getUUID(), getType(), LLVFile::READ);
  278. }
  279. void LLHTTPAssetRequest::finishCompressedUpload()
  280. {
  281. if (mZInitialized)
  282. {
  283. llinfos << "LLHTTPAssetRequest::finishCompressedUpload: "
  284. << "read " << mZStream.total_in << " byte asset file, "
  285. << "uploaded " << mZStream.total_out << " byte compressed asset"
  286. << llendl;
  287. deflateEnd(&mZStream);
  288. delete[] mZInputBuffer;
  289. }
  290. }
  291. size_t LLHTTPAssetRequest::readCompressedData(void* data, size_t size)
  292. {
  293. llassert(mZInitialized);
  294. mZStream.next_out = (Bytef*)data;
  295. mZStream.avail_out = size;
  296. while (mZStream.avail_out > 0)
  297. {
  298. if (mZStream.avail_in == 0 && !mZInputExhausted)
  299. {
  300. S32 to_read = llmin(COMPRESSED_INPUT_BUFFER_SIZE,
  301. (S32)(mVFile->getSize() - mVFile->tell()));
  302. if ( to_read > 0 )
  303. {
  304. mVFile->read((U8*)mZInputBuffer, to_read); /*Flawfinder: ignore*/
  305. mZStream.next_in = (Bytef*)mZInputBuffer;
  306. mZStream.avail_in = mVFile->getLastBytesRead();
  307. }
  308. mZInputExhausted = mZStream.avail_in == 0;
  309. }
  310. int r = deflate(&mZStream,
  311. mZInputExhausted ? Z_FINISH : Z_NO_FLUSH);
  312. if (r == Z_STREAM_END || r < 0 || mZInputExhausted)
  313. {
  314. if (r < 0)
  315. {
  316. llwarns << "LLHTTPAssetRequest::readCompressedData: deflate returned error code " 
  317. << (S32) r << llendl;
  318. }
  319. break;
  320. }
  321. }
  322. return size - mZStream.avail_out;
  323. }
  324. //static
  325. size_t LLHTTPAssetRequest::curlCompressedUploadCallback(
  326. void *data, size_t size, size_t nmemb, void *user_data)
  327. {
  328. size_t num_read = 0;
  329. if (gAssetStorage)
  330. {
  331. CURL *curl_handle = (CURL *)user_data;
  332. LLHTTPAssetRequest *req = NULL;
  333. curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
  334. if (req)
  335. {
  336. num_read = req->readCompressedData(data, size * nmemb);
  337. }
  338. }
  339. return num_read;
  340. }
  341. /////////////////////////////////////////////////////////////////////////////////
  342. // LLHTTPAssetStorage
  343. /////////////////////////////////////////////////////////////////////////////////
  344. LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
  345.  LLVFS *vfs, const LLHost &upstream_host,
  346.  const std::string& web_host,
  347.  const std::string& local_web_host,
  348.  const std::string& host_name)
  349. : LLAssetStorage(msg, xfer, vfs, upstream_host)
  350. {
  351. _init(web_host, local_web_host, host_name);
  352. }
  353. LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
  354.    LLVFS *vfs,
  355.    const std::string& web_host,
  356.    const std::string& local_web_host,
  357.    const std::string& host_name)
  358. : LLAssetStorage(msg, xfer, vfs)
  359. {
  360. _init(web_host, local_web_host, host_name);
  361. }
  362. void LLHTTPAssetStorage::_init(const std::string& web_host, const std::string& local_web_host, const std::string& host_name)
  363. {
  364. mBaseURL = web_host;
  365. mLocalBaseURL = local_web_host;
  366. mHostName = host_name;
  367. // curl_global_init moved to LLCurl::initClass()
  368. mCurlMultiHandle = curl_multi_init();
  369. }
  370. LLHTTPAssetStorage::~LLHTTPAssetStorage()
  371. {
  372. curl_multi_cleanup(mCurlMultiHandle);
  373. mCurlMultiHandle = NULL;
  374. // curl_global_cleanup moved to LLCurl::initClass()
  375. }
  376. // storing data is simpler than getting it, so we just overload the whole method
  377. void LLHTTPAssetStorage::storeAssetData(
  378. const LLUUID& uuid,
  379. LLAssetType::EType type,
  380. LLAssetStorage::LLStoreAssetCallback callback,
  381. void* user_data,
  382. bool temp_file,
  383. bool is_priority,
  384. bool store_local,
  385. const LLUUID& requesting_agent_id,
  386. bool user_waiting,
  387. F64 timeout)
  388. {
  389. if (mVFS->getExists(uuid, type)) // VFS treats nonexistant and zero-length identically
  390. {
  391. LLAssetRequest *req = new LLAssetRequest(uuid, type);
  392. req->mUpCallback    = callback;
  393. req->mUserData      = user_data;
  394. req->mRequestingAgentID = requesting_agent_id;
  395. req->mIsUserWaiting = user_waiting;
  396. req->mTimeout       = timeout;
  397. // LLAssetStorage metric: Successful Request
  398. S32 size = mVFS->getSize(uuid, type);
  399. const char *message;
  400. if( store_local )
  401. {
  402. message = "Added to local upload queue";
  403. }
  404. else
  405. {
  406. message = "Added to upload queue";
  407. }
  408. reportMetric( uuid, type, LLStringUtil::null, requesting_agent_id, size, MR_OKAY, __FILE__, __LINE__, message );
  409. // this will get picked up and transmitted in checkForTimeouts
  410. if(store_local)
  411. {
  412. mPendingLocalUploads.push_back(req);
  413. }
  414. else if(is_priority)
  415. {
  416. mPendingUploads.push_front(req);
  417. }
  418. else
  419. {
  420. mPendingUploads.push_back(req);
  421. }
  422. }
  423. else
  424. {
  425. llwarns << "AssetStorage: attempt to upload non-existent vfile " << uuid << ":" << LLAssetType::lookup(type) << llendl;
  426. if (callback)
  427. {
  428. // LLAssetStorage metric: Zero size VFS
  429. reportMetric( uuid, type, LLStringUtil::null, requesting_agent_id, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" );
  430. callback(uuid, user_data,  LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_NONEXISTENT_FILE);
  431. }
  432. }
  433. }
  434. // virtual
  435. void LLHTTPAssetStorage::storeAssetData(
  436. const std::string& filename,
  437. const LLUUID& asset_id,
  438. LLAssetType::EType asset_type,
  439. LLStoreAssetCallback callback,
  440. void* user_data,
  441. bool temp_file,
  442. bool is_priority,
  443. bool user_waiting,
  444. F64 timeout)
  445. {
  446. llinfos << "LLAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
  447. LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
  448. legacy->mUpCallback = callback;
  449. legacy->mUserData = user_data;
  450. FILE *fp = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
  451. S32 size = 0;
  452. if (fp)
  453. {
  454. fseek(fp, 0, SEEK_END);
  455. size = ftell(fp);
  456. fseek(fp, 0, SEEK_SET);
  457. }
  458. if( size )
  459. {
  460. LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE);
  461. file.setMaxSize(size);
  462. const S32 buf_size = 65536;
  463. U8 copy_buf[buf_size];
  464. while ((size = (S32)fread(copy_buf, 1, buf_size, fp)))
  465. {
  466. file.write(copy_buf, size);
  467. }
  468. fclose(fp);
  469. // if this upload fails, the caller needs to setup a new tempfile for us
  470. if (temp_file)
  471. {
  472. LLFile::remove(filename);
  473. }
  474. // LLAssetStorage metric: Success not needed; handled in the overloaded method here:
  475. storeAssetData(
  476. asset_id,
  477. asset_type,
  478. legacyStoreDataCallback,
  479. (void**)legacy,
  480. temp_file,
  481. is_priority,
  482. false,
  483. LLUUID::null,
  484. user_waiting,
  485. timeout);
  486. }
  487. else // !size
  488. {
  489. if( fp )
  490. {
  491. // LLAssetStorage metric: Zero size
  492. reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file was zero length" );
  493. fclose( fp );
  494. }
  495. else
  496. {
  497. // LLAssetStorage metric: Missing File
  498. reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_FILE_NONEXIST, __FILE__, __LINE__, "The file didn't exist" );
  499. }
  500. if (callback)
  501. {
  502. callback(LLUUID::null, user_data, LL_ERR_CANNOT_OPEN_FILE, LL_EXSTAT_BLOCKED_FILE);
  503. }
  504. delete legacy;
  505. }
  506. }
  507. // virtual
  508. LLSD LLHTTPAssetStorage::getPendingDetails(LLAssetStorage::ERequestType rt,
  509. LLAssetType::EType asset_type,
  510. const std::string& detail_prefix) const
  511. {
  512. LLSD sd = LLAssetStorage::getPendingDetails(rt, asset_type, detail_prefix);
  513. const request_list_t* running = getRunningList(rt);
  514. if (running)
  515. {
  516. // Loop through the pending requests sd, and add extra info about its running status.
  517. S32 num_pending = sd["requests"].size();
  518. S32 i;
  519. for (i = 0; i < num_pending; ++i)
  520. {
  521. LLSD& pending = sd["requests"][i];
  522. // See if this pending request is running.
  523. const LLAssetRequest* req = findRequest(running, 
  524. LLAssetType::lookup(pending["type"].asString()),
  525. pending["asset_id"]);
  526. if (req)
  527. {
  528. // Keep the detail_url so we don't have to rebuild it.
  529. LLURI detail_url = pending["detail"];
  530. pending = req->getTerseDetails();
  531. pending["detail"] = detail_url;
  532. pending["is_running"] = true;
  533. }
  534. else
  535. {
  536. pending["is_running"] = false;
  537. }
  538. }
  539. }
  540. return sd;
  541. }
  542. // virtual
  543. LLSD LLHTTPAssetStorage::getPendingRequest(LLAssetStorage::ERequestType rt,
  544. LLAssetType::EType asset_type,
  545. const LLUUID& asset_id) const
  546. {
  547. // Look for this asset in the running list first.
  548. const request_list_t* running = getRunningList(rt);
  549. if (running)
  550. {
  551. LLSD sd = LLAssetStorage::getPendingRequestImpl(running, asset_type, asset_id);
  552. if (sd)
  553. {
  554. sd["is_running"] = true;
  555. return sd;
  556. }
  557. }
  558. LLSD sd = LLAssetStorage::getPendingRequest(rt, asset_type, asset_id);
  559. if (sd)
  560. {
  561. sd["is_running"] = false;
  562. }
  563. return sd;
  564. }
  565. // virtual
  566. bool LLHTTPAssetStorage::deletePendingRequest(LLAssetStorage::ERequestType rt,
  567. LLAssetType::EType asset_type,
  568. const LLUUID& asset_id)
  569. {
  570. // Try removing this from the running list first.
  571. request_list_t* running = getRunningList(rt);
  572. if (running)
  573. {
  574. LLAssetRequest* req = findRequest(running, asset_type, asset_id);
  575. if (req)
  576. {
  577. // Remove this request from the running list to get it out of curl.
  578. running->remove(req);
  579. // Find this request in the pending list, so we can move it to the end of the line.
  580. request_list_t* pending = getRequestList(rt);
  581. if (pending)
  582. {
  583. request_list_t::iterator result = std::find_if(pending->begin(), pending->end(),
  584. std::bind2nd(ll_asset_request_equal<LLAssetRequest*>(), req));
  585. if (pending->end() != result)
  586. {
  587. // This request was found in the pending list.  Move it to the end!
  588. LLAssetRequest* pending_req = *result;
  589. pending->remove(pending_req);
  590. if (!pending_req->mIsUserWaiting) //A user is waiting on this request.  Toss it.
  591. {
  592. pending->push_back(pending_req);
  593. }
  594. else
  595. {
  596. if (pending_req->mUpCallback) //Clean up here rather than _callUploadCallbacks because this request is already cleared the req.
  597. {
  598. pending_req->mUpCallback(pending_req->getUUID(), pending_req->mUserData, -1, LL_EXSTAT_REQUEST_DROPPED);
  599. }
  600. }
  601. llinfos << "Asset " << getRequestName(rt) << " request for "
  602. << asset_id << "." << LLAssetType::lookup(asset_type)
  603. << " removed from curl and placed at the end of the pending queue."
  604. << llendl;
  605. }
  606. else
  607. {
  608. llwarns << "Unable to find pending " << getRequestName(rt) << " request for "
  609. << asset_id << "." << LLAssetType::lookup(asset_type) << llendl;
  610. }
  611. }
  612. delete req;
  613. return true;
  614. }
  615. }
  616. return LLAssetStorage::deletePendingRequest(rt, asset_type, asset_id);
  617. }
  618. // internal requester, used by getAssetData in superclass
  619. void LLHTTPAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType type,
  620.   void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat),
  621.   void *user_data, BOOL duplicate,
  622.    BOOL is_priority)
  623. {
  624. // stash the callback info so we can find it after we get the response message
  625. LLAssetRequest *req = new LLAssetRequest(uuid, type);
  626. req->mDownCallback = callback;
  627. req->mUserData = user_data;
  628. req->mIsPriority = is_priority;
  629. // this will get picked up and downloaded in checkForTimeouts
  630. //
  631. // HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACK!  Asset requests were taking too long and timing out.
  632. // Since texture requests are the LEAST sensitive (on the simulator) to being delayed, add
  633. // non-texture requests to the front, and add texture requests to the back.  The theory is
  634. // that we always want them first, even if they're out of order.
  635. //
  636. if (req->getType() == LLAssetType::AT_TEXTURE)
  637. {
  638. mPendingDownloads.push_back(req);
  639. }
  640. else
  641. {
  642. mPendingDownloads.push_front(req);
  643. }
  644. }
  645. LLAssetRequest* LLHTTPAssetStorage::findNextRequest(LLAssetStorage::request_list_t& pending, 
  646. LLAssetStorage::request_list_t& running)
  647. {
  648. // Early exit if the running list is full, or we don't have more pending than running.
  649. if (running.size() >= MAX_RUNNING_REQUESTS
  650. || pending.size() <= running.size()) return NULL;
  651. // Look for the first pending request that is not already running.
  652. request_list_t::iterator running_begin = running.begin();
  653. request_list_t::iterator running_end   = running.end();
  654. request_list_t::iterator pending_iter = pending.begin();
  655. request_list_t::iterator pending_end  = pending.end();
  656. // Loop over all pending requests until we miss finding it in the running list.
  657. for (; pending_iter != pending.end(); ++pending_iter)
  658. {
  659. LLAssetRequest* req = *pending_iter;
  660. // Look for this pending request in the running list.
  661. if (running_end == std::find_if(running_begin, running_end, 
  662. std::bind2nd(ll_asset_request_equal<LLAssetRequest*>(), req)))
  663. {
  664. // It isn't running!  Return it.
  665. return req;
  666. }
  667. }
  668. return NULL;
  669. }
  670. // overloaded to additionally move data to/from the webserver
  671. void LLHTTPAssetStorage::checkForTimeouts()
  672. {
  673. CURLMcode mcode;
  674. LLAssetRequest *req;
  675. while ( (req = findNextRequest(mPendingDownloads, mRunningDownloads)) )
  676. {
  677. // Setup this curl download request
  678. // We need to generate a new request here
  679. // since the one in the list could go away
  680. std::string tmp_url;
  681. std::string uuid_str;
  682. req->getUUID().toString(uuid_str);
  683. std::string base_url = getBaseURL(req->getUUID(), req->getType());
  684. tmp_url = llformat("%s/%36s.%s", base_url.c_str() , uuid_str.c_str(), LLAssetType::lookup(req->getType()));
  685. LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), 
  686. req->getType(), RT_DOWNLOAD, tmp_url, mCurlMultiHandle);
  687. new_req->mTmpUUID.generate();
  688. // Sets pending download flag internally
  689. new_req->setupCurlHandle();
  690. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
  691. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &curlDownCallback);
  692. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEDATA, new_req->mCurlHandle);
  693. mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
  694. if (mcode > CURLM_OK)
  695. {
  696. // Failure.  Deleting the pending request will remove it from the running
  697. // queue, and push it to the end of the pending queue.
  698. new_req->cleanupCurlHandle();
  699. deletePendingRequest(RT_DOWNLOAD, new_req->getType(), new_req->getUUID());
  700. break;
  701. }
  702. else
  703. {
  704. llinfos << "Requesting " << new_req->mURLBuffer << llendl;
  705. }
  706. }
  707. while ( (req = findNextRequest(mPendingUploads, mRunningUploads)) )
  708. {
  709. // setup this curl upload request
  710. bool do_compress = req->getType() == LLAssetType::AT_OBJECT;
  711. std::string tmp_url;
  712. std::string uuid_str;
  713. req->getUUID().toString(uuid_str);
  714. tmp_url = mBaseURL + "/" + uuid_str + "." + LLAssetType::lookup(req->getType());
  715. if (do_compress) tmp_url += ".gz";
  716. LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), 
  717. req->getType(), RT_UPLOAD, tmp_url, mCurlMultiHandle);
  718. if (req->mIsUserWaiting) //If a user is waiting on a realtime response, we want to perserve information across upload attempts.
  719. {
  720. new_req->mTime          = req->mTime;
  721. new_req->mTimeout       = req->mTimeout;
  722. new_req->mIsUserWaiting = req->mIsUserWaiting;
  723. }
  724. if (do_compress)
  725. {
  726. new_req->prepareCompressedUpload();
  727. }
  728. // Sets pending upload flag internally
  729. new_req->setupCurlHandle();
  730. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_UPLOAD, 1);
  731. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
  732. if (do_compress)
  733. {
  734. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
  735. &LLHTTPAssetRequest::curlCompressedUploadCallback);
  736. }
  737. else
  738. {
  739. LLVFile file(mVFS, req->getUUID(), req->getType());
  740. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
  741. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
  742. &curlUpCallback);
  743. }
  744. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
  745. mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
  746. if (mcode > CURLM_OK)
  747. {
  748. // Failure.  Deleting the pending request will remove it from the running
  749. // queue, and push it to the end of the pending queue.
  750. new_req->cleanupCurlHandle();
  751. deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID());
  752. break;
  753. }
  754. else
  755. {
  756. // Get the uncompressed file size.
  757. LLVFile file(mVFS,new_req->getUUID(),new_req->getType());
  758. S32 size = file.getSize();
  759. llinfos << "Requesting PUT " << new_req->mURLBuffer << ", asset size: " << size << " bytes" << llendl;
  760. if (size == 0)
  761. {
  762. llwarns << "Rejecting zero size PUT request!" << llendl;
  763. new_req->cleanupCurlHandle();
  764. deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID());
  765. }
  766. }
  767. // Pending upload will have been flagged by the request
  768. }
  769. while ( (req = findNextRequest(mPendingLocalUploads, mRunningLocalUploads)) )
  770. {
  771. // setup this curl upload request
  772. LLVFile file(mVFS, req->getUUID(), req->getType());
  773. std::string tmp_url;
  774. std::string uuid_str;
  775. req->getUUID().toString(uuid_str);
  776. // KLW - All temporary uploads are saved locally "http://localhost:12041/asset"
  777. tmp_url = llformat("%s/%36s.%s", mLocalBaseURL.c_str(), uuid_str.c_str(), LLAssetType::lookup(req->getType()));
  778. LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), 
  779. req->getType(), RT_LOCALUPLOAD, tmp_url, mCurlMultiHandle);
  780. new_req->mRequestingAgentID = req->mRequestingAgentID;
  781. // Sets pending upload flag internally
  782. new_req->setupCurlHandle();
  783. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_PUT, 1);
  784. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
  785. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
  786. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &curlUpCallback);
  787. curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
  788. mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
  789. if (mcode > CURLM_OK)
  790. {
  791. // Failure.  Deleting the pending request will remove it from the running
  792. // queue, and push it to the end of the pending queue.
  793. new_req->cleanupCurlHandle();
  794. deletePendingRequest(RT_LOCALUPLOAD, new_req->getType(), new_req->getUUID());
  795. break;
  796. }
  797. else
  798. {
  799. // Get the uncompressed file size.
  800. S32 size = file.getSize();
  801. llinfos << "TAT: LLHTTPAssetStorage::checkForTimeouts() : pending local!"
  802. << " Requesting PUT " << new_req->mURLBuffer << ", asset size: " << size << " bytes" << llendl;
  803. if (size == 0)
  804. {
  805. llwarns << "Rejecting zero size PUT request!" << llendl;
  806. new_req->cleanupCurlHandle();
  807. deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID());
  808. }
  809. }
  810. // Pending upload will have been flagged by the request
  811. }
  812. S32 count = 0;
  813. int queue_length;
  814. do
  815. {
  816. mcode = curl_multi_perform(mCurlMultiHandle, &queue_length);
  817. count++;
  818. } while (mcode == CURLM_CALL_MULTI_PERFORM && (count < 5));
  819. CURLMsg *curl_msg;
  820. do
  821. {
  822. curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
  823. if (curl_msg && curl_msg->msg == CURLMSG_DONE)
  824. {
  825. long curl_result = 0;
  826. S32 xfer_result = LL_ERR_NOERR;
  827. LLHTTPAssetRequest *req = NULL;
  828. curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, &req);
  829. // TODO: Throw curl_result at all callbacks.
  830. curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
  831. if (RT_UPLOAD == req->mRequestType || RT_LOCALUPLOAD == req->mRequestType)
  832. {
  833. if (curl_msg->data.result == CURLE_OK && 
  834. (   curl_result == HTTP_OK 
  835.  || curl_result == HTTP_PUT_OK 
  836.  || curl_result == HTTP_NO_CONTENT))
  837. {
  838. llinfos << "Success uploading " << req->getUUID() << " to " << req->mURLBuffer << llendl;
  839. if (RT_LOCALUPLOAD == req->mRequestType)
  840. {
  841. addTempAssetData(req->getUUID(), req->mRequestingAgentID, mHostName);
  842. }
  843. }
  844. else if (curl_msg->data.result == CURLE_COULDNT_CONNECT ||
  845. curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
  846. curl_result == HTTP_SERVER_BAD_GATEWAY ||
  847. curl_result == HTTP_SERVER_TEMP_UNAVAILABLE)
  848. {
  849. llwarns << "Re-requesting upload for " << req->getUUID() << ".  Received upload error to " << req->mURLBuffer <<
  850. " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
  851. ////HACK (probably) I am sick of this getting requeued and driving me mad.
  852. //if (req->mIsUserWaiting)
  853. //{
  854. // deletePendingRequest(RT_UPLOAD, req->getType(), req->getUUID());
  855. //}
  856. }
  857. else
  858. {
  859. llwarns << "Failure uploading " << req->getUUID() << " to " << req->mURLBuffer <<
  860. " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
  861. xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
  862. }
  863. if (!(curl_msg->data.result == CURLE_COULDNT_CONNECT ||
  864. curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
  865. curl_result == HTTP_SERVER_BAD_GATEWAY ||
  866. curl_result == HTTP_SERVER_TEMP_UNAVAILABLE))
  867. {
  868. // shared upload finished callback
  869. // in the base class, this is called from processUploadComplete
  870. _callUploadCallbacks(req->getUUID(), req->getType(), (xfer_result == 0), LL_EXSTAT_CURL_RESULT | curl_result);
  871. // Pending upload flag will get cleared when the request is deleted
  872. }
  873. }
  874. else if (RT_DOWNLOAD == req->mRequestType)
  875. {
  876. if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
  877. {
  878. if (req->mVFile && req->mVFile->getSize() > 0)
  879. {
  880. llinfos << "Success downloading " << req->mURLBuffer << ", size " << req->mVFile->getSize() << llendl;
  881. req->mVFile->rename(req->getUUID(), req->getType());
  882. }
  883. else
  884. {
  885. // *TODO: if this actually indicates a bad asset on the server
  886. // (not certain at this point), then delete it
  887. llwarns << "Found " << req->mURLBuffer << " to be zero size" << llendl;
  888. xfer_result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
  889. }
  890. }
  891. else
  892. {
  893. // KLW - TAT See if an avatar owns this texture, and if so request re-upload.
  894. llwarns << "Failure downloading " << req->mURLBuffer << 
  895. " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
  896. xfer_result = (curl_result == HTTP_MISSING) ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
  897. if (req->mVFile)
  898. {
  899. req->mVFile->remove();
  900. }
  901. }
  902. // call the static callback for transfer completion
  903. // this will cleanup all requests for this asset, including ours
  904. downloadCompleteCallback(
  905. xfer_result,
  906. req->getUUID(),
  907. req->getType(),
  908. (void *)req,
  909. LL_EXSTAT_CURL_RESULT | curl_result);
  910. // Pending download flag will get cleared when the request is deleted
  911. }
  912. else
  913. {
  914. // nothing, just axe this request
  915. // currently this can only mean an asset delete
  916. }
  917. // Deleting clears the pending upload/download flag if it's set and the request is transferring
  918. delete req;
  919. req = NULL;
  920. }
  921. } while (curl_msg && queue_length > 0);
  922. // Cleanup 
  923. // We want to bump to the back of the line any running uploads that have timed out.
  924. bumpTimedOutUploads();
  925. LLAssetStorage::checkForTimeouts();
  926. }
  927. void LLHTTPAssetStorage::bumpTimedOutUploads()
  928. {
  929. bool user_waiting=FALSE;
  930. F64 mt_secs = LLMessageSystem::getMessageTimeSeconds();
  931. if (mPendingUploads.size())
  932. {
  933. request_list_t::iterator it = mPendingUploads.begin();
  934. LLAssetRequest* req = *it;
  935. user_waiting=req->mIsUserWaiting;
  936. }
  937. // No point bumping currently running uploads if there are no others in line.
  938. if (!(mPendingUploads.size() > mRunningUploads.size()) && !user_waiting) 
  939. {
  940. return;
  941. }
  942. // deletePendingRequest will modify the mRunningUploads list so we don't want to iterate over it.
  943. request_list_t temp_running = mRunningUploads;
  944. request_list_t::iterator it = temp_running.begin();
  945. request_list_t::iterator end = temp_running.end();
  946. for ( ; it != end; ++it)
  947. {
  948. //request_list_t::iterator curiter = iter++;
  949. LLAssetRequest* req = *it;
  950. if ( req->mTimeout < (mt_secs - req->mTime) )
  951. {
  952. llwarns << "Asset upload request timed out for "
  953. << req->getUUID() << "."
  954. << LLAssetType::lookup(req->getType()) 
  955. << ", bumping to the back of the line!" << llendl;
  956. deletePendingRequest(RT_UPLOAD, req->getType(), req->getUUID());
  957. }
  958. }
  959. }
  960. // static
  961. size_t LLHTTPAssetStorage::curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
  962. {
  963. if (!gAssetStorage)
  964. {
  965. llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
  966. return 0;
  967. }
  968. S32 bytes = (S32)(size * nmemb);
  969. CURL *curl_handle = (CURL *)user_data;
  970. LLHTTPAssetRequest *req = NULL;
  971. curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
  972. if (! req->mVFile)
  973. {
  974. req->mVFile = new LLVFile(gAssetStorage->mVFS, req->mTmpUUID, LLAssetType::AT_NONE, LLVFile::APPEND);
  975. }
  976. double content_length = 0.0;
  977. curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length);
  978. // sanitize content_length, reconcile w/ actual data
  979. S32 file_length = llmax(0, (S32)llmin(content_length, 20000000.0), bytes + req->mVFile->getSize());
  980. req->mVFile->setMaxSize(file_length);
  981. req->mVFile->write((U8*)data, bytes);
  982. return nmemb;
  983. }
  984. // static 
  985. size_t LLHTTPAssetStorage::curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data)
  986. {
  987. if (!gAssetStorage)
  988. {
  989. llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
  990. return 0;
  991. }
  992. CURL *curl_handle = (CURL *)user_data;
  993. LLHTTPAssetRequest *req = NULL;
  994. curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
  995. if (! req->mVFile)
  996. {
  997. req->mVFile = new LLVFile(gAssetStorage->mVFS, req->getUUID(), req->getType(), LLVFile::READ);
  998. }
  999. S32 bytes = llmin((S32)(size * nmemb), (S32)(req->mVFile->getSize() - req->mVFile->tell()));
  1000. req->mVFile->read((U8*)data, bytes);/*Flawfinder: ignore*/
  1001. return req->mVFile->getLastBytesRead();
  1002. }
  1003. // static
  1004. size_t LLHTTPAssetStorage::nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data)
  1005. {
  1006. // do nothing, this is here to soak up script output so it doesn't end up on stdout
  1007. return nmemb;
  1008. }
  1009. // blocking asset fetch which bypasses the VFS
  1010. // this is a very limited function for use by the simstate loader and other one-offs
  1011. S32 LLHTTPAssetStorage::getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const std::string &url, const std::string& filename, progress_callback callback, void *userdata)
  1012. {
  1013. // *NOTE: There is no guarantee that the uuid and the asset_type match
  1014. // - not that it matters. - Doug
  1015. lldebugs << "LLHTTPAssetStorage::getURLToFile() - " << url << llendl;
  1016. FILE *fp = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/
  1017. if (! fp)
  1018. {
  1019. llwarns << "Failed to open " << filename << " for writing" << llendl;
  1020. return LL_ERR_ASSET_REQUEST_FAILED;
  1021. }
  1022. // make sure we use the normal curl setup, even though we don't really need a request object
  1023. LLHTTPAssetRequest req(this, uuid, asset_type, RT_DOWNLOAD, url, mCurlMultiHandle);
  1024. req.mFP = fp;
  1025. req.setupCurlHandle();
  1026. curl_easy_setopt(req.mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
  1027. curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEFUNCTION, &curlFileDownCallback);
  1028. curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEDATA, req.mCurlHandle);
  1029. curl_multi_add_handle(mCurlMultiHandle, req.mCurlHandle);
  1030. llinfos << "Requesting as file " << req.mURLBuffer << llendl;
  1031. // braindead curl loop
  1032. int queue_length;
  1033. CURLMsg *curl_msg;
  1034. LLTimer timeout;
  1035. timeout.setTimerExpirySec(GET_URL_TO_FILE_TIMEOUT);
  1036. bool success = false;
  1037. S32 xfer_result = 0;
  1038. do
  1039. {
  1040. curl_multi_perform(mCurlMultiHandle, &queue_length);
  1041. curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
  1042. if (callback)
  1043. {
  1044. callback(userdata);
  1045. }
  1046. if ( curl_msg && (CURLMSG_DONE == curl_msg->msg) )
  1047. {
  1048. success = true;
  1049. }
  1050. else if (timeout.hasExpired())
  1051. {
  1052. llwarns << "Request for " << url << " has timed out." << llendl;
  1053. success = false;
  1054. xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
  1055. break;
  1056. }
  1057. } while (!success);
  1058. if (success)
  1059. {
  1060. long curl_result = 0;
  1061. curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
  1062. if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
  1063. {
  1064. S32 size = ftell(req.mFP);
  1065. if (size > 0)
  1066. {
  1067. // everything seems to be in order
  1068. llinfos << "Success downloading " << req.mURLBuffer << " to file, size " << size << llendl;
  1069. }
  1070. else
  1071. {
  1072. llwarns << "Found " << req.mURLBuffer << " to be zero size" << llendl;
  1073. xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
  1074. }
  1075. }
  1076. else
  1077. {
  1078. xfer_result = curl_result == HTTP_MISSING ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
  1079. llinfos << "Failure downloading " << req.mURLBuffer << 
  1080. " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
  1081. }
  1082. }
  1083. fclose(fp);
  1084. if (xfer_result)
  1085. {
  1086. LLFile::remove(filename);
  1087. }
  1088. return xfer_result;
  1089. }
  1090. // static
  1091. size_t LLHTTPAssetStorage::curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
  1092. {
  1093. CURL *curl_handle = (CURL *)user_data;
  1094. LLHTTPAssetRequest *req = NULL;
  1095. curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
  1096. if (! req->mFP)
  1097. {
  1098. llwarns << "Missing mFP, aborting curl file download callback!" << llendl;
  1099. return 0;
  1100. }
  1101. return fwrite(data, size, nmemb, req->mFP);
  1102. }
  1103. LLAssetStorage::request_list_t* LLHTTPAssetStorage::getRunningList(LLAssetStorage::ERequestType rt)
  1104. {
  1105. switch (rt)
  1106. {
  1107. case RT_DOWNLOAD:
  1108. return &mRunningDownloads;
  1109. case RT_UPLOAD:
  1110. return &mRunningUploads;
  1111. case RT_LOCALUPLOAD:
  1112. return &mRunningLocalUploads;
  1113. default:
  1114. return NULL;
  1115. }
  1116. }
  1117. const LLAssetStorage::request_list_t* LLHTTPAssetStorage::getRunningList(LLAssetStorage::ERequestType rt) const
  1118. {
  1119. switch (rt)
  1120. {
  1121. case RT_DOWNLOAD:
  1122. return &mRunningDownloads;
  1123. case RT_UPLOAD:
  1124. return &mRunningUploads;
  1125. case RT_LOCALUPLOAD:
  1126. return &mRunningLocalUploads;
  1127. default:
  1128. return NULL;
  1129. }
  1130. }
  1131. void LLHTTPAssetStorage::addRunningRequest(ERequestType rt, LLHTTPAssetRequest* request)
  1132. {
  1133. request_list_t* requests = getRunningList(rt);
  1134. if (requests)
  1135. {
  1136. requests->push_back(request);
  1137. }
  1138. else
  1139. {
  1140. llerrs << "LLHTTPAssetStorage::addRunningRequest - Request is not an upload OR download, this is bad!" << llendl;
  1141. }
  1142. }
  1143. void LLHTTPAssetStorage::removeRunningRequest(ERequestType rt, LLHTTPAssetRequest* request)
  1144. {
  1145. request_list_t* requests = getRunningList(rt);
  1146. if (requests)
  1147. {
  1148. requests->remove(request);
  1149. }
  1150. else
  1151. {
  1152. llerrs << "LLHTTPAssetStorage::removeRunningRequest - Destroyed request is not an upload OR download, this is bad!" << llendl;
  1153. }
  1154. }
  1155. // virtual 
  1156. void LLHTTPAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name)
  1157. {
  1158. if (agent_id.isNull() || asset_id.isNull())
  1159. {
  1160. llwarns << "TAT: addTempAssetData bad id's asset_id: " << asset_id << "  agent_id: " << agent_id << llendl;
  1161. return;
  1162. }
  1163. LLTempAssetData temp_asset_data;
  1164. temp_asset_data.mAssetID = asset_id;
  1165. temp_asset_data.mAgentID = agent_id;
  1166. temp_asset_data.mHostName = host_name;
  1167. mTempAssets[asset_id] = temp_asset_data;
  1168. }
  1169. // virtual
  1170. BOOL LLHTTPAssetStorage::hasTempAssetData(const LLUUID& texture_id) const
  1171. {
  1172. uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
  1173. BOOL found = (citer != mTempAssets.end());
  1174. return found;
  1175. }
  1176. // virtual
  1177. std::string LLHTTPAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const
  1178. {
  1179. uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
  1180. if (citer != mTempAssets.end())
  1181. {
  1182. return citer->second.mHostName;
  1183. }
  1184. else
  1185. {
  1186. return std::string();
  1187. }
  1188. }
  1189. // virtual 
  1190. LLUUID LLHTTPAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const
  1191. {
  1192. uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
  1193. if (citer != mTempAssets.end())
  1194. {
  1195. return citer->second.mAgentID;
  1196. }
  1197. else
  1198. {
  1199. return LLUUID::null;
  1200. }
  1201. }
  1202. // virtual 
  1203. void LLHTTPAssetStorage::removeTempAssetData(const LLUUID& asset_id)
  1204. {
  1205. mTempAssets.erase(asset_id);
  1206. }
  1207. // virtual 
  1208. void LLHTTPAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id)
  1209. {
  1210. uuid_tempdata_map::iterator it = mTempAssets.begin();
  1211. uuid_tempdata_map::iterator end = mTempAssets.end();
  1212. while (it != end)
  1213. {
  1214. const LLTempAssetData& asset_data = it->second;
  1215. if (asset_data.mAgentID == agent_id)
  1216. {
  1217. mTempAssets.erase(it++);
  1218. }
  1219. else
  1220. {
  1221. ++it;
  1222. }
  1223. }
  1224. }
  1225. std::string LLHTTPAssetStorage::getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type)
  1226. {
  1227. if (LLAssetType::AT_TEXTURE == asset_type)
  1228. {
  1229. uuid_tempdata_map::const_iterator citer = mTempAssets.find(asset_id);
  1230. if (citer != mTempAssets.end())
  1231. {
  1232. const std::string& host_name = citer->second.mHostName;
  1233. std::string url = llformat(LOCAL_ASSET_URL_FORMAT, host_name.c_str());
  1234. return url;
  1235. }
  1236. }
  1237. return mBaseURL;
  1238. }
  1239. void LLHTTPAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const
  1240. {
  1241. uuid_tempdata_map::const_iterator it = mTempAssets.begin();
  1242. uuid_tempdata_map::const_iterator end = mTempAssets.end();
  1243. S32 count = 0;
  1244. for ( ; it != end; ++it)
  1245. {
  1246. const LLTempAssetData& temp_asset_data = it->second;
  1247. if (avatar_id.isNull()
  1248. || avatar_id == temp_asset_data.mAgentID)
  1249. {
  1250. llinfos << "TAT: dump agent " << temp_asset_data.mAgentID
  1251. << " texture " << temp_asset_data.mAssetID
  1252. << " host " << temp_asset_data.mHostName
  1253. << llendl;
  1254. count++;
  1255. }
  1256. }
  1257. if (avatar_id.isNull())
  1258. {
  1259. llinfos << "TAT: dumped " << count << " entries for all avatars" << llendl;
  1260. }
  1261. else
  1262. {
  1263. llinfos << "TAT: dumped " << count << " entries for avatar " << avatar_id << llendl;
  1264. }
  1265. }
  1266. void LLHTTPAssetStorage::clearTempAssetData()
  1267. {
  1268. llinfos << "TAT: Clearing temp asset data map" << llendl;
  1269. mTempAssets.clear();
  1270. }