00001
00033 #include "linden_common.h"
00034
00035 #include "llhttpassetstorage.h"
00036
00037 #include <sys/stat.h>
00038
00039 #include "indra_constants.h"
00040 #include "message.h"
00041 #include "llvfile.h"
00042 #include "llvfs.h"
00043
00044 #ifdef LL_STANDALONE
00045 # include <zlib.h>
00046 #else
00047 # include "zlib/zlib.h"
00048 #endif
00049
00050 const U32 MAX_RUNNING_REQUESTS = 1;
00051 const F32 MAX_PROCESSING_TIME = 0.005f;
00052 const S32 CURL_XFER_BUFFER_SIZE = 65536;
00053
00054 const F32 GET_URL_TO_FILE_TIMEOUT = 1800.0f;
00055
00056 const S32 COMPRESSED_INPUT_BUFFER_SIZE = 4096;
00057
00058 const S32 HTTP_OK = 200;
00059 const S32 HTTP_PUT_OK = 201;
00060 const S32 HTTP_NO_CONTENT = 204;
00061 const S32 HTTP_MISSING = 404;
00062 const S32 HTTP_SERVER_BAD_GATEWAY = 502;
00063 const S32 HTTP_SERVER_TEMP_UNAVAILABLE = 503;
00064
00066
00067
00069 struct LLTempAssetData
00070 {
00071 LLUUID mAssetID;
00072 LLUUID mAgentID;
00073 std::string mHostName;
00074 };
00075
00077
00079
00080 class LLHTTPAssetRequest : public LLAssetRequest
00081 {
00082 public:
00083 LLHTTPAssetRequest(LLHTTPAssetStorage *asp, const LLUUID &uuid,
00084 LLAssetType::EType type, LLAssetStorage::ERequestType rt,
00085 const char *url, CURLM *curl_multi);
00086 virtual ~LLHTTPAssetRequest();
00087
00088 void setupCurlHandle();
00089 void cleanupCurlHandle();
00090
00091 void prepareCompressedUpload();
00092 void finishCompressedUpload();
00093 size_t readCompressedData(void* data, size_t size);
00094
00095 static size_t curlCompressedUploadCallback(
00096 void *data, size_t size, size_t nmemb, void *user_data);
00097
00098 virtual LLSD getTerseDetails() const;
00099 virtual LLSD getFullDetails() const;
00100
00101 public:
00102 LLHTTPAssetStorage *mAssetStoragep;
00103
00104 CURL *mCurlHandle;
00105 CURLM *mCurlMultiHandle;
00106 char *mURLBuffer;
00107 struct curl_slist *mHTTPHeaders;
00108 LLVFile *mVFile;
00109 LLUUID mTmpUUID;
00110 LLAssetStorage::ERequestType mRequestType;
00111
00112 bool mZInitialized;
00113 z_stream mZStream;
00114 char* mZInputBuffer;
00115 bool mZInputExhausted;
00116
00117 FILE *mFP;
00118 };
00119
00120
00121 LLHTTPAssetRequest::LLHTTPAssetRequest(LLHTTPAssetStorage *asp,
00122 const LLUUID &uuid,
00123 LLAssetType::EType type,
00124 LLAssetStorage::ERequestType rt,
00125 const char *url,
00126 CURLM *curl_multi)
00127 : LLAssetRequest(uuid, type),
00128 mZInitialized(false)
00129 {
00130 mAssetStoragep = asp;
00131 mCurlHandle = NULL;
00132 mCurlMultiHandle = curl_multi;
00133 mVFile = NULL;
00134 mRequestType = rt;
00135 mHTTPHeaders = NULL;
00136 mFP = NULL;
00137 mZInputBuffer = NULL;
00138 mZInputExhausted = false;
00139
00140 mURLBuffer = new char[strlen(url) + 1];
00141 if (mURLBuffer)
00142 {
00143 strcpy(mURLBuffer, url);
00144 }
00145 }
00146
00147 LLHTTPAssetRequest::~LLHTTPAssetRequest()
00148 {
00149
00150 if (mCurlHandle)
00151 {
00152 curl_multi_remove_handle(mCurlMultiHandle, mCurlHandle);
00153 cleanupCurlHandle();
00154 }
00155 if (mHTTPHeaders)
00156 {
00157 curl_slist_free_all(mHTTPHeaders);
00158 }
00159 delete[] mURLBuffer;
00160 delete mVFile;
00161 finishCompressedUpload();
00162 }
00163
00164
00165 LLSD LLHTTPAssetRequest::getTerseDetails() const
00166 {
00167 LLSD sd = LLAssetRequest::getTerseDetails();
00168
00169 sd["url"] = mURLBuffer;
00170
00171 return sd;
00172 }
00173
00174
00175 LLSD LLHTTPAssetRequest::getFullDetails() const
00176 {
00177 LLSD sd = LLAssetRequest::getFullDetails();
00178
00179 if (mCurlHandle)
00180 {
00181 long curl_response = -1;
00182 long curl_connect = -1;
00183 double curl_total_time = -1.0f;
00184 double curl_size_upload = -1.0f;
00185 double curl_size_download = -1.0f;
00186 long curl_content_length_upload = -1;
00187 long curl_content_length_download = -1;
00188 long curl_request_size = -1;
00189 const char* curl_content_type = NULL;
00190
00191 curl_easy_getinfo(mCurlHandle, CURLINFO_HTTP_CODE, &curl_response);
00192 curl_easy_getinfo(mCurlHandle, CURLINFO_HTTP_CONNECTCODE, &curl_connect);
00193 curl_easy_getinfo(mCurlHandle, CURLINFO_TOTAL_TIME, &curl_total_time);
00194 curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_UPLOAD, &curl_size_upload);
00195 curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_DOWNLOAD, &curl_size_download);
00196 curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_LENGTH_UPLOAD, &curl_content_length_upload);
00197 curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curl_content_length_download);
00198 curl_easy_getinfo(mCurlHandle, CURLINFO_REQUEST_SIZE, &curl_request_size);
00199 curl_easy_getinfo(mCurlHandle, CURLINFO_CONTENT_TYPE, &curl_content_type);
00200
00201 sd["curl_response_code"] = (int) curl_response;
00202 sd["curl_http_connect_code"] = (int) curl_connect;
00203 sd["curl_total_time"] = curl_total_time;
00204 sd["curl_size_upload"] = curl_size_upload;
00205 sd["curl_size_download"] = curl_size_download;
00206 sd["curl_content_length_upload"] = (int) curl_content_length_upload;
00207 sd["curl_content_length_download"] = (int) curl_content_length_download;
00208 sd["curl_request_size"] = (int) curl_request_size;
00209 if (curl_content_type)
00210 {
00211 sd["curl_content_type"] = curl_content_type;
00212 }
00213 else
00214 {
00215 sd["curl_content_type"] = "";
00216 }
00217 }
00218
00219 sd["temp_id"] = mTmpUUID;
00220 sd["request_type"] = LLAssetStorage::getRequestName(mRequestType);
00221 sd["z_initialized"] = mZInitialized;
00222 sd["z_input_exhausted"] = mZInputExhausted;
00223
00224 S32 file_size = -1;
00225 if (mFP)
00226 {
00227 struct stat file_stat;
00228 int file_desc = fileno(mFP);
00229 if ( fstat(file_desc, &file_stat) == 0)
00230 {
00231 file_size = file_stat.st_size;
00232 }
00233 }
00234 sd["file_size"] = file_size;
00235
00236 return sd;
00237 }
00238
00239
00240 void LLHTTPAssetRequest::setupCurlHandle()
00241 {
00242 mCurlHandle = curl_easy_init();
00243 curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
00244 curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
00245 curl_easy_setopt(mCurlHandle, CURLOPT_URL, mURLBuffer);
00246 curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
00247 if (LLAssetStorage::RT_DOWNLOAD == mRequestType)
00248 {
00249 curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
00250
00251
00252
00253
00254
00255 }
00256
00257
00258 if (mZInitialized)
00259 {
00260 curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, "");
00261
00262 }
00263 mHTTPHeaders = curl_slist_append(mHTTPHeaders, "Pragma:");
00264
00265
00266 curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mHTTPHeaders);
00267 if (mAssetStoragep)
00268 {
00269
00270 mAssetStoragep->addRunningRequest(mRequestType, this);
00271 }
00272 else
00273 {
00274 llerrs << "LLHTTPAssetRequest::setupCurlHandle - No asset storage associated with this request!" << llendl;
00275 }
00276 }
00277
00278 void LLHTTPAssetRequest::cleanupCurlHandle()
00279 {
00280 curl_easy_cleanup(mCurlHandle);
00281 if (mAssetStoragep)
00282 {
00283
00284 mAssetStoragep->removeRunningRequest(mRequestType, this);
00285 }
00286 else
00287 {
00288 llerrs << "LLHTTPAssetRequest::~LLHTTPAssetRequest - No asset storage associated with this request!" << llendl;
00289 }
00290 mCurlHandle = NULL;
00291 }
00292
00293 void LLHTTPAssetRequest::prepareCompressedUpload()
00294 {
00295 mZStream.next_in = Z_NULL;
00296 mZStream.avail_in = 0;
00297 mZStream.zalloc = Z_NULL;
00298 mZStream.zfree = Z_NULL;
00299 mZStream.opaque = Z_NULL;
00300
00301 int r = deflateInit2(&mZStream,
00302 1,
00303 Z_DEFLATED,
00304 15 + 16,
00305 8,
00306 Z_DEFAULT_STRATEGY);
00307
00308 if (r != Z_OK)
00309 {
00310 llerrs << "LLHTTPAssetRequest::prepareCompressedUpload defalateInit2() failed" << llendl;
00311 }
00312
00313 mZInitialized = true;
00314 mZInputBuffer = new char[COMPRESSED_INPUT_BUFFER_SIZE];
00315 mZInputExhausted = false;
00316
00317 mVFile = new LLVFile(gAssetStorage->mVFS,
00318 getUUID(), getType(), LLVFile::READ);
00319 }
00320
00321 void LLHTTPAssetRequest::finishCompressedUpload()
00322 {
00323 if (mZInitialized)
00324 {
00325 llinfos << "LLHTTPAssetRequest::finishCompressedUpload: "
00326 << "read " << mZStream.total_in << " byte asset file, "
00327 << "uploaded " << mZStream.total_out << " byte compressed asset"
00328 << llendl;
00329
00330 deflateEnd(&mZStream);
00331 delete[] mZInputBuffer;
00332 }
00333 }
00334
00335 size_t LLHTTPAssetRequest::readCompressedData(void* data, size_t size)
00336 {
00337 mZStream.next_out = (Bytef*)data;
00338 mZStream.avail_out = size;
00339
00340 while (mZStream.avail_out > 0)
00341 {
00342 if (mZStream.avail_in == 0 && !mZInputExhausted)
00343 {
00344 S32 to_read = llmin(COMPRESSED_INPUT_BUFFER_SIZE,
00345 (S32)(mVFile->getSize() - mVFile->tell()));
00346
00347 if ( to_read > 0 )
00348 {
00349 mVFile->read((U8*)mZInputBuffer, to_read);
00350 mZStream.next_in = (Bytef*)mZInputBuffer;
00351 mZStream.avail_in = mVFile->getLastBytesRead();
00352 }
00353
00354 mZInputExhausted = mZStream.avail_in == 0;
00355 }
00356
00357 int r = deflate(&mZStream,
00358 mZInputExhausted ? Z_FINISH : Z_NO_FLUSH);
00359
00360 if (r == Z_STREAM_END || r < 0 || mZInputExhausted)
00361 {
00362 if (r < 0)
00363 {
00364 llwarns << "LLHTTPAssetRequest::readCompressedData: deflate returned error code "
00365 << (S32) r << llendl;
00366 }
00367 break;
00368 }
00369 }
00370
00371 return size - mZStream.avail_out;
00372 }
00373
00374
00375 size_t LLHTTPAssetRequest::curlCompressedUploadCallback(
00376 void *data, size_t size, size_t nmemb, void *user_data)
00377 {
00378 size_t num_read = 0;
00379
00380 if (gAssetStorage)
00381 {
00382 CURL *curl_handle = (CURL *)user_data;
00383 LLHTTPAssetRequest *req = NULL;
00384 curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
00385 if (req)
00386 {
00387 num_read = req->readCompressedData(data, size * nmemb);
00388 }
00389 }
00390
00391 return num_read;
00392 }
00393
00395
00397
00398
00399 LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
00400 LLVFS *vfs, const LLHost &upstream_host,
00401 const char *web_host,
00402 const char *local_web_host,
00403 const char *host_name)
00404 : LLAssetStorage(msg, xfer, vfs, upstream_host)
00405 {
00406 _init(web_host, local_web_host, host_name);
00407 }
00408
00409 LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
00410 LLVFS *vfs,
00411 const char *web_host,
00412 const char *local_web_host,
00413 const char *host_name)
00414 : LLAssetStorage(msg, xfer, vfs)
00415 {
00416 _init(web_host, local_web_host, host_name);
00417 }
00418
00419 void LLHTTPAssetStorage::_init(const char *web_host, const char *local_web_host, const char* host_name)
00420 {
00421 mBaseURL = web_host;
00422 mLocalBaseURL = local_web_host;
00423 mHostName = host_name;
00424
00425
00426
00427
00428 curl_global_init(CURL_GLOBAL_ALL);
00429
00430 mCurlMultiHandle = curl_multi_init();
00431 }
00432
00433 LLHTTPAssetStorage::~LLHTTPAssetStorage()
00434 {
00435 curl_multi_cleanup(mCurlMultiHandle);
00436 mCurlMultiHandle = NULL;
00437
00438 curl_global_cleanup();
00439 }
00440
00441
00442 void LLHTTPAssetStorage::storeAssetData(
00443 const LLUUID& uuid,
00444 LLAssetType::EType type,
00445 LLAssetStorage::LLStoreAssetCallback callback,
00446 void* user_data,
00447 bool temp_file,
00448 bool is_priority,
00449 bool store_local,
00450 const LLUUID& requesting_agent_id,
00451 bool user_waiting,
00452 F64 timeout)
00453 {
00454 if (mVFS->getExists(uuid, type))
00455 {
00456 LLAssetRequest *req = new LLAssetRequest(uuid, type);
00457 req->mUpCallback = callback;
00458 req->mUserData = user_data;
00459 req->mRequestingAgentID = requesting_agent_id;
00460 req->mIsUserWaiting = user_waiting;
00461 req->mTimeout = timeout;
00462
00463
00464 if(store_local)
00465 {
00466 mPendingLocalUploads.push_back(req);
00467 }
00468 else if(is_priority)
00469 {
00470 mPendingUploads.push_front(req);
00471 }
00472 else
00473 {
00474 mPendingUploads.push_back(req);
00475 }
00476 }
00477 else
00478 {
00479 llwarns << "AssetStorage: attempt to upload non-existent vfile " << uuid << ":" << LLAssetType::lookup(type) << llendl;
00480 if (callback)
00481 {
00482 callback(uuid, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_NONEXISTENT_FILE);
00483 }
00484 }
00485 }
00486
00487
00488 void LLHTTPAssetStorage::storeAssetData(
00489 const char* filename,
00490 const LLUUID& asset_id,
00491 LLAssetType::EType asset_type,
00492 LLStoreAssetCallback callback,
00493 void* user_data,
00494 bool temp_file,
00495 bool is_priority,
00496 bool user_waiting,
00497 F64 timeout)
00498 {
00499 llinfos << "LLAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
00500
00501 LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
00502
00503 legacy->mUpCallback = callback;
00504 legacy->mUserData = user_data;
00505
00506 FILE *fp = LLFile::fopen(filename, "rb");
00507 if (fp)
00508 {
00509 LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE);
00510
00511 fseek(fp, 0, SEEK_END);
00512 S32 size = ftell(fp);
00513 fseek(fp, 0, SEEK_SET);
00514
00515 file.setMaxSize(size);
00516
00517 const S32 buf_size = 65536;
00518 U8 copy_buf[buf_size];
00519 while ((size = (S32)fread(copy_buf, 1, buf_size, fp)))
00520 {
00521 file.write(copy_buf, size);
00522 }
00523 fclose(fp);
00524
00525
00526 if (temp_file)
00527 {
00528 LLFile::remove(filename);
00529 }
00530
00531 storeAssetData(
00532 asset_id,
00533 asset_type,
00534 legacyStoreDataCallback,
00535 (void**)legacy,
00536 temp_file,
00537 is_priority,
00538 false,
00539 LLUUID::null,
00540 user_waiting,
00541 timeout);
00542 }
00543 else
00544 {
00545 if (callback)
00546 {
00547 callback(LLUUID::null, user_data, LL_ERR_CANNOT_OPEN_FILE, LL_EXSTAT_BLOCKED_FILE);
00548 }
00549 delete legacy;
00550 }
00551 }
00552
00553
00554 LLSD LLHTTPAssetStorage::getPendingDetails(LLAssetStorage::ERequestType rt,
00555 LLAssetType::EType asset_type,
00556 const std::string& detail_prefix) const
00557 {
00558 LLSD sd = LLAssetStorage::getPendingDetails(rt, asset_type, detail_prefix);
00559 const request_list_t* running = getRunningList(rt);
00560 if (running)
00561 {
00562
00563 S32 num_pending = sd["requests"].size();
00564 S32 i;
00565 for (i = 0; i < num_pending; ++i)
00566 {
00567 LLSD& pending = sd["requests"][i];
00568
00569 const LLAssetRequest* req = findRequest(running,
00570 LLAssetType::lookup(pending["type"].asString().c_str()),
00571 pending["asset_id"]);
00572 if (req)
00573 {
00574
00575 LLURI detail_url = pending["detail"];
00576 pending = req->getTerseDetails();
00577 pending["detail"] = detail_url;
00578 pending["is_running"] = true;
00579 }
00580 else
00581 {
00582 pending["is_running"] = false;
00583 }
00584 }
00585 }
00586 return sd;
00587 }
00588
00589
00590 LLSD LLHTTPAssetStorage::getPendingRequest(LLAssetStorage::ERequestType rt,
00591 LLAssetType::EType asset_type,
00592 const LLUUID& asset_id) const
00593 {
00594
00595 const request_list_t* running = getRunningList(rt);
00596 if (running)
00597 {
00598 LLSD sd = LLAssetStorage::getPendingRequest(running, asset_type, asset_id);
00599 if (sd)
00600 {
00601 sd["is_running"] = true;
00602 return sd;
00603 }
00604 }
00605 LLSD sd = LLAssetStorage::getPendingRequest(rt, asset_type, asset_id);
00606 if (sd)
00607 {
00608 sd["is_running"] = false;
00609 }
00610 return sd;
00611 }
00612
00613
00614 bool LLHTTPAssetStorage::deletePendingRequest(LLAssetStorage::ERequestType rt,
00615 LLAssetType::EType asset_type,
00616 const LLUUID& asset_id)
00617 {
00618
00619 request_list_t* running = getRunningList(rt);
00620 if (running)
00621 {
00622 LLAssetRequest* req = findRequest(running, asset_type, asset_id);
00623 if (req)
00624 {
00625
00626 running->remove(req);
00627
00628
00629 request_list_t* pending = getRequestList(rt);
00630 if (pending)
00631 {
00632 request_list_t::iterator result = std::find_if(pending->begin(), pending->end(),
00633 std::bind2nd(ll_asset_request_equal<LLAssetRequest*>(), req));
00634 if (pending->end() != result)
00635 {
00636
00637 LLAssetRequest* pending_req = *result;
00638 pending->remove(pending_req);
00639
00640 if (!pending_req->mIsUserWaiting)
00641 {
00642 pending->push_back(pending_req);
00643 }
00644 else
00645 {
00646 if (pending_req->mUpCallback)
00647 {
00648 pending_req->mUpCallback(pending_req->getUUID(), pending_req->mUserData, -1, LL_EXSTAT_REQUEST_DROPPED);
00649 }
00650
00651 }
00652
00653 llinfos << "Asset " << getRequestName(rt) << " request for "
00654 << asset_id << "." << LLAssetType::lookup(asset_type)
00655 << " removed from curl and placed at the end of the pending queue."
00656 << llendl;
00657 }
00658 else
00659 {
00660 llwarns << "Unable to find pending " << getRequestName(rt) << " request for "
00661 << asset_id << "." << LLAssetType::lookup(asset_type) << llendl;
00662 }
00663 }
00664 delete req;
00665
00666 return true;
00667 }
00668 }
00669 return LLAssetStorage::deletePendingRequest(rt, asset_type, asset_id);
00670 }
00671
00672
00673 void LLHTTPAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType type,
00674 void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat),
00675 void *user_data, BOOL duplicate,
00676 BOOL is_priority)
00677 {
00678
00679 LLAssetRequest *req = new LLAssetRequest(uuid, type);
00680 req->mDownCallback = callback;
00681 req->mUserData = user_data;
00682 req->mIsPriority = is_priority;
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693 if (req->getType() == LLAssetType::AT_TEXTURE)
00694 {
00695 mPendingDownloads.push_back(req);
00696 }
00697 else
00698 {
00699 mPendingDownloads.push_front(req);
00700 }
00701 }
00702
00703 LLAssetRequest* LLHTTPAssetStorage::findNextRequest(LLAssetStorage::request_list_t& pending,
00704 LLAssetStorage::request_list_t& running)
00705 {
00706
00707 if (running.size() >= MAX_RUNNING_REQUESTS
00708 || pending.size() <= running.size()) return NULL;
00709
00710
00711 request_list_t::iterator running_begin = running.begin();
00712 request_list_t::iterator running_end = running.end();
00713
00714 request_list_t::iterator pending_iter = pending.begin();
00715 request_list_t::iterator pending_end = pending.end();
00716
00717 for (; pending_iter != pending.end(); ++pending_iter)
00718 {
00719 LLAssetRequest* req = *pending_iter;
00720
00721 if (running_end == std::find_if(running_begin, running_end,
00722 std::bind2nd(ll_asset_request_equal<LLAssetRequest*>(), req)))
00723 {
00724
00725 return req;
00726 }
00727 }
00728 return NULL;
00729 }
00730
00731
00732 void LLHTTPAssetStorage::checkForTimeouts()
00733 {
00734 CURLMcode mcode;
00735 LLAssetRequest *req;
00736 while ( (req = findNextRequest(mPendingDownloads, mRunningDownloads)) )
00737 {
00738
00739
00740
00741 char tmp_url[MAX_STRING];
00742 char uuid_str[UUID_STR_LENGTH];
00743 req->getUUID().toString(uuid_str);
00744 std::string base_url = getBaseURL(req->getUUID(), req->getType());
00745 snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", base_url.c_str() , uuid_str, LLAssetType::lookup(req->getType()));
00746
00747 LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(),
00748 req->getType(), RT_DOWNLOAD, tmp_url, mCurlMultiHandle);
00749 new_req->mTmpUUID.generate();
00750
00751
00752 new_req->setupCurlHandle();
00753 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
00754 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &curlDownCallback);
00755 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEDATA, new_req->mCurlHandle);
00756
00757 mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
00758 if (mcode > CURLM_OK)
00759 {
00760
00761
00762 new_req->cleanupCurlHandle();
00763 deletePendingRequest(RT_DOWNLOAD, new_req->getType(), new_req->getUUID());
00764 break;
00765 }
00766 else
00767 {
00768 llinfos << "Requesting " << new_req->mURLBuffer << llendl;
00769 }
00770 }
00771
00772 while ( (req = findNextRequest(mPendingUploads, mRunningUploads)) )
00773 {
00774
00775
00776 bool do_compress = req->getType() == LLAssetType::AT_OBJECT;
00777
00778 char tmp_url[MAX_STRING];
00779 char uuid_str[UUID_STR_LENGTH];
00780 req->getUUID().toString(uuid_str);
00781 snprintf(tmp_url, sizeof(tmp_url),
00782 do_compress ? "%s/%s.%s.gz" : "%s/%s.%s",
00783 mBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType()));
00784
00785 LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(),
00786 req->getType(), RT_UPLOAD, tmp_url, mCurlMultiHandle);
00787
00788 if (req->mIsUserWaiting)
00789 {
00790 new_req->mTime = req->mTime;
00791 new_req->mTimeout = req->mTimeout;
00792 new_req->mIsUserWaiting = req->mIsUserWaiting;
00793 }
00794
00795 if (do_compress)
00796 {
00797 new_req->prepareCompressedUpload();
00798 }
00799
00800
00801 new_req->setupCurlHandle();
00802 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_UPLOAD, 1);
00803 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
00804
00805 if (do_compress)
00806 {
00807 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
00808 &LLHTTPAssetRequest::curlCompressedUploadCallback);
00809 }
00810 else
00811 {
00812 LLVFile file(mVFS, req->getUUID(), req->getType());
00813 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
00814 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
00815 &curlUpCallback);
00816 }
00817 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
00818
00819 mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
00820 if (mcode > CURLM_OK)
00821 {
00822
00823
00824 new_req->cleanupCurlHandle();
00825 deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID());
00826 break;
00827 }
00828 else
00829 {
00830 llinfos << "Requesting PUT " << new_req->mURLBuffer << llendl;
00831 }
00832
00833 }
00834
00835 while ( (req = findNextRequest(mPendingLocalUploads, mRunningLocalUploads)) )
00836 {
00837
00838 LLVFile file(mVFS, req->getUUID(), req->getType());
00839
00840 char tmp_url[MAX_STRING];
00841 char uuid_str[UUID_STR_LENGTH];
00842 req->getUUID().toString(uuid_str);
00843
00844
00845 snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", mLocalBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType()));
00846
00847 LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(),
00848 req->getType(), RT_LOCALUPLOAD, tmp_url, mCurlMultiHandle);
00849 new_req->mRequestingAgentID = req->mRequestingAgentID;
00850
00851
00852 new_req->setupCurlHandle();
00853 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_PUT, 1);
00854 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
00855 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
00856 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &curlUpCallback);
00857 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
00858
00859 mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
00860 if (mcode > CURLM_OK)
00861 {
00862
00863
00864 new_req->cleanupCurlHandle();
00865 deletePendingRequest(RT_LOCALUPLOAD, new_req->getType(), new_req->getUUID());
00866 break;
00867 }
00868 else
00869 {
00870 llinfos << "TAT: LLHTTPAssetStorage::checkForTimeouts() : pending local!"
00871 << " Requesting PUT " << new_req->mURLBuffer << llendl;
00872 }
00873
00874 }
00875 S32 count = 0;
00876 int queue_length;
00877 do
00878 {
00879 mcode = curl_multi_perform(mCurlMultiHandle, &queue_length);
00880 count++;
00881 } while (mcode == CURLM_CALL_MULTI_PERFORM && (count < 5));
00882
00883 CURLMsg *curl_msg;
00884 do
00885 {
00886 curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
00887 if (curl_msg && curl_msg->msg == CURLMSG_DONE)
00888 {
00889 long curl_result = 0;
00890 S32 xfer_result = LL_ERR_NOERR;
00891
00892 LLHTTPAssetRequest *req = NULL;
00893 curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, &req);
00894
00895
00896 curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
00897 if (RT_UPLOAD == req->mRequestType || RT_LOCALUPLOAD == req->mRequestType)
00898 {
00899 if (curl_msg->data.result == CURLE_OK &&
00900 ( curl_result == HTTP_OK
00901 || curl_result == HTTP_PUT_OK
00902 || curl_result == HTTP_NO_CONTENT))
00903 {
00904 llinfos << "Success uploading " << req->getUUID() << " to " << req->mURLBuffer << llendl;
00905 if (RT_LOCALUPLOAD == req->mRequestType)
00906 {
00907 addTempAssetData(req->getUUID(), req->mRequestingAgentID, mHostName);
00908 }
00909 }
00910 else if (curl_msg->data.result == CURLE_COULDNT_CONNECT ||
00911 curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
00912 curl_result == HTTP_SERVER_BAD_GATEWAY ||
00913 curl_result == HTTP_SERVER_TEMP_UNAVAILABLE)
00914 {
00915 llwarns << "Re-requesting upload for " << req->getUUID() << ". Received upload error to " << req->mURLBuffer <<
00916 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
00917
00919
00920
00921
00922
00923 }
00924 else
00925 {
00926 llwarns << "Failure uploading " << req->getUUID() << " to " << req->mURLBuffer <<
00927 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
00928
00929 xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
00930 }
00931
00932 if (!(curl_msg->data.result == CURLE_COULDNT_CONNECT ||
00933 curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
00934 curl_result == HTTP_SERVER_BAD_GATEWAY ||
00935 curl_result == HTTP_SERVER_TEMP_UNAVAILABLE))
00936 {
00937
00938
00939 _callUploadCallbacks(req->getUUID(), req->getType(), (xfer_result == 0), LL_EXSTAT_CURL_RESULT | curl_result);
00940
00941 }
00942 }
00943 else if (RT_DOWNLOAD == req->mRequestType)
00944 {
00945 if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
00946 {
00947 if (req->mVFile && req->mVFile->getSize() > 0)
00948 {
00949 llinfos << "Success downloading " << req->mURLBuffer << ", size " << req->mVFile->getSize() << llendl;
00950
00951 req->mVFile->rename(req->getUUID(), req->getType());
00952 }
00953 else
00954 {
00955
00956
00957 llwarns << "Found " << req->mURLBuffer << " to be zero size" << llendl;
00958 xfer_result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
00959 }
00960 }
00961 else
00962 {
00963
00964 llwarns << "Failure downloading " << req->mURLBuffer <<
00965 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
00966
00967 xfer_result = (curl_result == HTTP_MISSING) ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
00968
00969 if (req->mVFile)
00970 {
00971 req->mVFile->remove();
00972 }
00973 }
00974
00975
00976
00977 downloadCompleteCallback(
00978 xfer_result,
00979 req->getUUID(),
00980 req->getType(),
00981 (void *)req,
00982 LL_EXSTAT_CURL_RESULT | curl_result);
00983
00984 }
00985 else
00986 {
00987
00988
00989 }
00990
00991
00992 delete req;
00993 req = NULL;
00994 }
00995
00996 } while (curl_msg && queue_length > 0);
00997
00998
00999
01000
01001 bumpTimedOutUploads();
01002
01003 LLAssetStorage::checkForTimeouts();
01004 }
01005
01006 void LLHTTPAssetStorage::bumpTimedOutUploads()
01007 {
01008 bool user_waiting=FALSE;
01009
01010 F64 mt_secs = LLMessageSystem::getMessageTimeSeconds();
01011
01012 if (mPendingUploads.size())
01013 {
01014 request_list_t::iterator it = mPendingUploads.begin();
01015 LLAssetRequest* req = *it;
01016 user_waiting=req->mIsUserWaiting;
01017 }
01018
01019
01020 if (!(mPendingUploads.size() > mRunningUploads.size()) && !user_waiting)
01021 {
01022 return;
01023 }
01024
01025
01026 request_list_t temp_running = mRunningUploads;
01027
01028 request_list_t::iterator it = temp_running.begin();
01029 request_list_t::iterator end = temp_running.end();
01030 for ( ; it != end; ++it)
01031 {
01032
01033 LLAssetRequest* req = *it;
01034
01035 if ( req->mTimeout < (mt_secs - req->mTime) )
01036 {
01037 llwarns << "Asset upload request timed out for "
01038 << req->getUUID() << "."
01039 << LLAssetType::lookup(req->getType())
01040 << ", bumping to the back of the line!" << llendl;
01041
01042 deletePendingRequest(RT_UPLOAD, req->getType(), req->getUUID());
01043 }
01044 }
01045 }
01046
01047
01048 size_t LLHTTPAssetStorage::curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
01049 {
01050 if (!gAssetStorage)
01051 {
01052 llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
01053 return 0;
01054 }
01055 S32 bytes = (S32)(size * nmemb);
01056 CURL *curl_handle = (CURL *)user_data;
01057 LLHTTPAssetRequest *req = NULL;
01058 curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
01059
01060 if (! req->mVFile)
01061 {
01062 req->mVFile = new LLVFile(gAssetStorage->mVFS, req->mTmpUUID, LLAssetType::AT_NONE, LLVFile::APPEND);
01063 }
01064
01065 double content_length = 0.0;
01066 curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length);
01067
01068
01069 S32 file_length = llmax(0, (S32)llmin(content_length, 20000000.0), bytes + req->mVFile->getSize());
01070
01071 req->mVFile->setMaxSize(file_length);
01072 req->mVFile->write((U8*)data, bytes);
01073
01074 return nmemb;
01075 }
01076
01077
01078 size_t LLHTTPAssetStorage::curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data)
01079 {
01080 if (!gAssetStorage)
01081 {
01082 llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
01083 return 0;
01084 }
01085 CURL *curl_handle = (CURL *)user_data;
01086 LLHTTPAssetRequest *req = NULL;
01087 curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
01088
01089 if (! req->mVFile)
01090 {
01091 req->mVFile = new LLVFile(gAssetStorage->mVFS, req->getUUID(), req->getType(), LLVFile::READ);
01092 }
01093
01094 S32 bytes = llmin((S32)(size * nmemb), (S32)(req->mVFile->getSize() - req->mVFile->tell()));
01095
01096 req->mVFile->read((U8*)data, bytes);
01097
01098 return req->mVFile->getLastBytesRead();
01099 }
01100
01101
01102 size_t LLHTTPAssetStorage::nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data)
01103 {
01104
01105
01106 return nmemb;
01107 }
01108
01109
01110
01111
01112
01113 S32 LLHTTPAssetStorage::getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const LLString &url, const char *filename, progress_callback callback, void *userdata)
01114 {
01115
01116
01117 lldebugs << "LLHTTPAssetStorage::getURLToFile() - " << url << llendl;
01118
01119 FILE *fp = LLFile::fopen(filename, "wb");
01120 if (! fp)
01121 {
01122 llwarns << "Failed to open " << filename << " for writing" << llendl;
01123 return LL_ERR_ASSET_REQUEST_FAILED;
01124 }
01125
01126
01127 LLHTTPAssetRequest req(this, uuid, asset_type, RT_DOWNLOAD, url.c_str(), mCurlMultiHandle);
01128 req.mFP = fp;
01129
01130 req.setupCurlHandle();
01131 curl_easy_setopt(req.mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
01132 curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEFUNCTION, &curlFileDownCallback);
01133 curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEDATA, req.mCurlHandle);
01134
01135 curl_multi_add_handle(mCurlMultiHandle, req.mCurlHandle);
01136 llinfos << "Requesting as file " << req.mURLBuffer << llendl;
01137
01138
01139 int queue_length;
01140 CURLMsg *curl_msg;
01141 LLTimer timeout;
01142 timeout.setTimerExpirySec(GET_URL_TO_FILE_TIMEOUT);
01143 bool success = false;
01144 S32 xfer_result = 0;
01145 do
01146 {
01147 curl_multi_perform(mCurlMultiHandle, &queue_length);
01148 curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
01149
01150 if (callback)
01151 {
01152 callback(userdata);
01153 }
01154
01155 if ( curl_msg && (CURLMSG_DONE == curl_msg->msg) )
01156 {
01157 success = true;
01158 }
01159 else if (timeout.hasExpired())
01160 {
01161 llwarns << "Request for " << url << " has timed out." << llendl;
01162 success = false;
01163 xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
01164 break;
01165 }
01166 } while (!success);
01167
01168 if (success)
01169 {
01170 long curl_result = 0;
01171 curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
01172
01173 if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
01174 {
01175 S32 size = ftell(req.mFP);
01176 if (size > 0)
01177 {
01178
01179 llinfos << "Success downloading " << req.mURLBuffer << " to file, size " << size << llendl;
01180 }
01181 else
01182 {
01183 llwarns << "Found " << req.mURLBuffer << " to be zero size" << llendl;
01184 xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
01185 }
01186 }
01187 else
01188 {
01189 xfer_result = curl_result == HTTP_MISSING ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
01190 llinfos << "Failure downloading " << req.mURLBuffer <<
01191 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
01192 }
01193 }
01194
01195 fclose(fp);
01196 if (xfer_result)
01197 {
01198 LLFile::remove(filename);
01199 }
01200 return xfer_result;
01201 }
01202
01203
01204
01205 size_t LLHTTPAssetStorage::curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
01206 {
01207 CURL *curl_handle = (CURL *)user_data;
01208 LLHTTPAssetRequest *req = NULL;
01209 curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
01210
01211 if (! req->mFP)
01212 {
01213 llwarns << "Missing mFP, aborting curl file download callback!" << llendl;
01214 return 0;
01215 }
01216
01217 return fwrite(data, size, nmemb, req->mFP);
01218 }
01219
01220 LLAssetStorage::request_list_t* LLHTTPAssetStorage::getRunningList(LLAssetStorage::ERequestType rt)
01221 {
01222 switch (rt)
01223 {
01224 case RT_DOWNLOAD:
01225 return &mRunningDownloads;
01226 case RT_UPLOAD:
01227 return &mRunningUploads;
01228 case RT_LOCALUPLOAD:
01229 return &mRunningLocalUploads;
01230 default:
01231 return NULL;
01232 }
01233 }
01234
01235 const LLAssetStorage::request_list_t* LLHTTPAssetStorage::getRunningList(LLAssetStorage::ERequestType rt) const
01236 {
01237 switch (rt)
01238 {
01239 case RT_DOWNLOAD:
01240 return &mRunningDownloads;
01241 case RT_UPLOAD:
01242 return &mRunningUploads;
01243 case RT_LOCALUPLOAD:
01244 return &mRunningLocalUploads;
01245 default:
01246 return NULL;
01247 }
01248 }
01249
01250
01251 void LLHTTPAssetStorage::addRunningRequest(ERequestType rt, LLHTTPAssetRequest* request)
01252 {
01253 request_list_t* requests = getRunningList(rt);
01254 if (requests)
01255 {
01256 requests->push_back(request);
01257 }
01258 else
01259 {
01260 llerrs << "LLHTTPAssetStorage::addRunningRequest - Request is not an upload OR download, this is bad!" << llendl;
01261 }
01262 }
01263
01264 void LLHTTPAssetStorage::removeRunningRequest(ERequestType rt, LLHTTPAssetRequest* request)
01265 {
01266 request_list_t* requests = getRunningList(rt);
01267 if (requests)
01268 {
01269 requests->remove(request);
01270 }
01271 else
01272 {
01273 llerrs << "LLHTTPAssetStorage::removeRunningRequest - Destroyed request is not an upload OR download, this is bad!" << llendl;
01274 }
01275 }
01276
01277
01278 void LLHTTPAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name)
01279 {
01280 if (agent_id.isNull() || asset_id.isNull())
01281 {
01282 llwarns << "TAT: addTempAssetData bad id's asset_id: " << asset_id << " agent_id: " << agent_id << llendl;
01283 return;
01284 }
01285
01286 LLTempAssetData temp_asset_data;
01287 temp_asset_data.mAssetID = asset_id;
01288 temp_asset_data.mAgentID = agent_id;
01289 temp_asset_data.mHostName = host_name;
01290
01291 mTempAssets[asset_id] = temp_asset_data;
01292 }
01293
01294
01295 BOOL LLHTTPAssetStorage::hasTempAssetData(const LLUUID& texture_id) const
01296 {
01297 uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
01298 BOOL found = (citer != mTempAssets.end());
01299 return found;
01300 }
01301
01302
01303 std::string LLHTTPAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const
01304 {
01305 uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
01306 if (citer != mTempAssets.end())
01307 {
01308 return citer->second.mHostName;
01309 }
01310 else
01311 {
01312 return std::string();
01313 }
01314 }
01315
01316
01317 LLUUID LLHTTPAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const
01318 {
01319 uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
01320 if (citer != mTempAssets.end())
01321 {
01322 return citer->second.mAgentID;
01323 }
01324 else
01325 {
01326 return LLUUID::null;
01327 }
01328 }
01329
01330
01331 void LLHTTPAssetStorage::removeTempAssetData(const LLUUID& asset_id)
01332 {
01333 mTempAssets.erase(asset_id);
01334 }
01335
01336
01337 void LLHTTPAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id)
01338 {
01339 uuid_tempdata_map::iterator it = mTempAssets.begin();
01340 uuid_tempdata_map::iterator end = mTempAssets.end();
01341
01342 while (it != end)
01343 {
01344 const LLTempAssetData& asset_data = it->second;
01345 if (asset_data.mAgentID == agent_id)
01346 {
01347 mTempAssets.erase(it++);
01348 }
01349 else
01350 {
01351 ++it;
01352 }
01353 }
01354 }
01355
01356 std::string LLHTTPAssetStorage::getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type)
01357 {
01358 if (LLAssetType::AT_TEXTURE == asset_type)
01359 {
01360 uuid_tempdata_map::const_iterator citer = mTempAssets.find(asset_id);
01361 if (citer != mTempAssets.end())
01362 {
01363 const std::string& host_name = citer->second.mHostName;
01364 std::string url = llformat(LOCAL_ASSET_URL_FORMAT, host_name.c_str());
01365 return url;
01366 }
01367 }
01368
01369 return mBaseURL;
01370 }
01371
01372 void LLHTTPAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const
01373 {
01374 uuid_tempdata_map::const_iterator it = mTempAssets.begin();
01375 uuid_tempdata_map::const_iterator end = mTempAssets.end();
01376 S32 count = 0;
01377 for ( ; it != end; ++it)
01378 {
01379 const LLTempAssetData& temp_asset_data = it->second;
01380 if (avatar_id.isNull()
01381 || avatar_id == temp_asset_data.mAgentID)
01382 {
01383 llinfos << "TAT: dump agent " << temp_asset_data.mAgentID
01384 << " texture " << temp_asset_data.mAssetID
01385 << " host " << temp_asset_data.mHostName
01386 << llendl;
01387 count++;
01388 }
01389 }
01390
01391 if (avatar_id.isNull())
01392 {
01393 llinfos << "TAT: dumped " << count << " entries for all avatars" << llendl;
01394 }
01395 else
01396 {
01397 llinfos << "TAT: dumped " << count << " entries for avatar " << avatar_id << llendl;
01398 }
01399 }
01400
01401 void LLHTTPAssetStorage::clearTempAssetData()
01402 {
01403 llinfos << "TAT: Clearing temp asset data map" << llendl;
01404 mTempAssets.clear();
01405 }
01406
01407