llhttpassetstorage.cpp

Go to the documentation of this file.
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 // Try for 30 minutes for now.
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 // LLTempAssetData
00067 // An asset not stored on central asset store, but on a simulator node somewhere.
00069 struct LLTempAssetData
00070 {
00071         LLUUID  mAssetID;
00072         LLUUID  mAgentID;
00073         std::string     mHostName;
00074 };
00075 
00077 // LLHTTPAssetRequest
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]; /*Flawfinder: ignore*/
00141         if (mURLBuffer)
00142         {
00143             strcpy(mURLBuffer, url);    /*Flawfinder: ignore*/
00144         }
00145 }
00146 
00147 LLHTTPAssetRequest::~LLHTTPAssetRequest()
00148 {
00149         // Cleanup/cancel the request
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 // virtual
00165 LLSD LLHTTPAssetRequest::getTerseDetails() const
00166 {
00167         LLSD sd = LLAssetRequest::getTerseDetails();
00168 
00169         sd["url"] = mURLBuffer;
00170 
00171         return sd;
00172 }
00173 
00174 // virtual
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                 // only do this on downloads, as uploads 
00251                 // to some apache configs (like our test grids)
00252                 // mistakenly claim the response is gzip'd if the resource
00253                 // name ends in .gz, even though in a PUT, the response is
00254                 // just plain HTML saying "created"
00255         }
00256         /* Remove the Pragma: no-cache header that libcurl inserts by default;
00257            we want the cached version, if possible. */
00258         if (mZInitialized)
00259         {
00260                 curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, "");
00261                         // disable use of proxy, which can't handle chunked transfers
00262         }
00263         mHTTPHeaders = curl_slist_append(mHTTPHeaders, "Pragma:");
00264         // resist the temptation to explicitly add the Transfer-Encoding: chunked
00265         // header here - invokes a libCURL bug
00266         curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mHTTPHeaders);
00267         if (mAssetStoragep)
00268         {
00269                 // Set the appropriate pending upload or download flag
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                 // Terminating a request.  Thus upload or download is no longer pending.
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,                      // compression level
00303                         Z_DEFLATED,     // the only method defined
00304                         15 + 16,        // the default windowBits + gzip header flag
00305                         8,                      // the default memLevel
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         llassert(mZInitialized);
00338 
00339         mZStream.next_out = (Bytef*)data;
00340         mZStream.avail_out = size;
00341 
00342         while (mZStream.avail_out > 0)
00343         {
00344                 if (mZStream.avail_in == 0 && !mZInputExhausted)
00345                 {
00346                         S32 to_read = llmin(COMPRESSED_INPUT_BUFFER_SIZE,
00347                                                         (S32)(mVFile->getSize() - mVFile->tell()));
00348                         
00349                         if ( to_read > 0 )
00350                         {
00351                                 mVFile->read((U8*)mZInputBuffer, to_read); /*Flawfinder: ignore*/
00352                                 mZStream.next_in = (Bytef*)mZInputBuffer;
00353                                 mZStream.avail_in = mVFile->getLastBytesRead();
00354                         }
00355 
00356                         mZInputExhausted = mZStream.avail_in == 0;
00357                 }
00358 
00359                 int r = deflate(&mZStream,
00360                                         mZInputExhausted ? Z_FINISH : Z_NO_FLUSH);
00361 
00362                 if (r == Z_STREAM_END || r < 0 || mZInputExhausted)
00363                 {
00364                         if (r < 0)
00365                         {
00366                                 llwarns << "LLHTTPAssetRequest::readCompressedData: deflate returned error code " 
00367                                                 << (S32) r << llendl;
00368                         }
00369                         break;
00370                 }
00371         }
00372 
00373         return size - mZStream.avail_out;
00374 }
00375 
00376 //static
00377 size_t LLHTTPAssetRequest::curlCompressedUploadCallback(
00378                 void *data, size_t size, size_t nmemb, void *user_data)
00379 {
00380         size_t num_read = 0;
00381 
00382         if (gAssetStorage)
00383         {
00384                 CURL *curl_handle = (CURL *)user_data;
00385                 LLHTTPAssetRequest *req = NULL;
00386                 curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
00387                 if (req)
00388                 {
00389                         num_read = req->readCompressedData(data, size * nmemb);
00390                 }
00391         }
00392 
00393         return num_read;
00394 }
00395 
00397 // LLHTTPAssetStorage
00399 
00400 
00401 LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
00402                                                                          LLVFS *vfs, const LLHost &upstream_host,
00403                                                                          const char *web_host,
00404                                                                          const char *local_web_host,
00405                                                                          const char *host_name)
00406         : LLAssetStorage(msg, xfer, vfs, upstream_host)
00407 {
00408         _init(web_host, local_web_host, host_name);
00409 }
00410 
00411 LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
00412                                                                            LLVFS *vfs,
00413                                                                            const char *web_host,
00414                                                                            const char *local_web_host,
00415                                                                            const char *host_name)
00416         : LLAssetStorage(msg, xfer, vfs)
00417 {
00418         _init(web_host, local_web_host, host_name);
00419 }
00420 
00421 void LLHTTPAssetStorage::_init(const char *web_host, const char *local_web_host, const char* host_name)
00422 {
00423         mBaseURL = web_host;
00424         mLocalBaseURL = local_web_host;
00425         mHostName = host_name;
00426 
00427         // curl_global_init moved to LLCurl::initClass()
00428         
00429         mCurlMultiHandle = curl_multi_init();
00430 }
00431 
00432 LLHTTPAssetStorage::~LLHTTPAssetStorage()
00433 {
00434         curl_multi_cleanup(mCurlMultiHandle);
00435         mCurlMultiHandle = NULL;
00436         
00437         // curl_global_cleanup moved to LLCurl::initClass()
00438 }
00439 
00440 // storing data is simpler than getting it, so we just overload the whole method
00441 void LLHTTPAssetStorage::storeAssetData(
00442         const LLUUID& uuid,
00443         LLAssetType::EType type,
00444         LLAssetStorage::LLStoreAssetCallback callback,
00445         void* user_data,
00446         bool temp_file,
00447         bool is_priority,
00448         bool store_local,
00449         const LLUUID& requesting_agent_id,
00450         bool user_waiting,
00451         F64 timeout)
00452 {
00453         if (mVFS->getExists(uuid, type)) // VFS treats nonexistant and zero-length identically
00454         {
00455                 LLAssetRequest *req = new LLAssetRequest(uuid, type);
00456                 req->mUpCallback    = callback;
00457                 req->mUserData      = user_data;
00458                 req->mRequestingAgentID = requesting_agent_id;
00459                 req->mIsUserWaiting = user_waiting;
00460                 req->mTimeout       = timeout;
00461 
00462                 // LLAssetStorage metric: Successful Request
00463                 S32 size = mVFS->getSize(uuid, type);
00464                 const char *message;
00465                 if( store_local )
00466                 {
00467                         message = "Added to local upload queue";
00468                 }
00469                 else
00470                 {
00471                         message = "Added to upload queue";
00472                 }
00473                 reportMetric( uuid, type, NULL, requesting_agent_id, size, MR_OKAY, __FILE__, __LINE__, message );
00474 
00475                 // this will get picked up and transmitted in checkForTimeouts
00476                 if(store_local)
00477                 {
00478                         mPendingLocalUploads.push_back(req);
00479                 }
00480                 else if(is_priority)
00481                 {
00482                         mPendingUploads.push_front(req);
00483                 }
00484                 else
00485                 {
00486                         mPendingUploads.push_back(req);
00487                 }
00488         }
00489         else
00490         {
00491                 llwarns << "AssetStorage: attempt to upload non-existent vfile " << uuid << ":" << LLAssetType::lookup(type) << llendl;
00492                 if (callback)
00493                 {
00494                         // LLAssetStorage metric: Zero size VFS
00495                         reportMetric( uuid, type, NULL, requesting_agent_id, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" );
00496                         callback(uuid, user_data,  LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_NONEXISTENT_FILE);
00497                 }
00498         }
00499 }
00500 
00501 // virtual
00502 void LLHTTPAssetStorage::storeAssetData(
00503         const char* filename,
00504         const LLUUID& asset_id,
00505         LLAssetType::EType asset_type,
00506         LLStoreAssetCallback callback,
00507         void* user_data,
00508         bool temp_file,
00509         bool is_priority,
00510         bool user_waiting,
00511         F64 timeout)
00512 {
00513         llinfos << "LLAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
00514 
00515         LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
00516 
00517         legacy->mUpCallback = callback;
00518         legacy->mUserData = user_data;
00519 
00520         FILE *fp = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
00521         S32 size = 0;
00522         if (fp)
00523         {
00524                 fseek(fp, 0, SEEK_END);
00525                 size = ftell(fp);
00526                 fseek(fp, 0, SEEK_SET);
00527         }
00528 
00529         if( size )
00530         {
00531                 LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE);
00532 
00533                 file.setMaxSize(size);
00534 
00535                 const S32 buf_size = 65536;
00536                 U8 copy_buf[buf_size];
00537                 while ((size = (S32)fread(copy_buf, 1, buf_size, fp)))
00538                 {
00539                         file.write(copy_buf, size);
00540                 }
00541                 fclose(fp);
00542 
00543                 // if this upload fails, the caller needs to setup a new tempfile for us
00544                 if (temp_file)
00545                 {
00546                         LLFile::remove(filename);
00547                 }
00548                 
00549                 // LLAssetStorage metric: Success not needed; handled in the overloaded method here:
00550                 storeAssetData(
00551                         asset_id,
00552                         asset_type,
00553                         legacyStoreDataCallback,
00554                         (void**)legacy,
00555                         temp_file,
00556                         is_priority,
00557                         false,
00558                         LLUUID::null,
00559                         user_waiting,
00560                         timeout);
00561         }
00562         else // !size
00563         {
00564                 if( fp )
00565                 {
00566                         // LLAssetStorage metric: Zero size
00567                         reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file was zero length" );
00568                         fclose( fp );
00569                 }
00570                 else
00571                 {
00572                         // LLAssetStorage metric: Missing File
00573                         reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_FILE_NONEXIST, __FILE__, __LINE__, "The file didn't exist" );
00574                 }
00575                 if (callback)
00576                 {
00577                         callback(LLUUID::null, user_data, LL_ERR_CANNOT_OPEN_FILE, LL_EXSTAT_BLOCKED_FILE);
00578                 }
00579                 delete legacy;
00580         }
00581 }
00582 
00583 // virtual
00584 LLSD LLHTTPAssetStorage::getPendingDetails(LLAssetStorage::ERequestType rt,
00585                                                                                 LLAssetType::EType asset_type,
00586                                                                                 const std::string& detail_prefix) const
00587 {
00588         LLSD sd = LLAssetStorage::getPendingDetails(rt, asset_type, detail_prefix);
00589         const request_list_t* running = getRunningList(rt);
00590         if (running)
00591         {
00592                 // Loop through the pending requests sd, and add extra info about its running status.
00593                 S32 num_pending = sd["requests"].size();
00594                 S32 i;
00595                 for (i = 0; i < num_pending; ++i)
00596                 {
00597                         LLSD& pending = sd["requests"][i];
00598                         // See if this pending request is running.
00599                         const LLAssetRequest* req = findRequest(running, 
00600                                                                         LLAssetType::lookup(pending["type"].asString().c_str()),
00601                                                                         pending["asset_id"]);
00602                         if (req)
00603                         {
00604                                 // Keep the detail_url so we don't have to rebuild it.
00605                                 LLURI detail_url = pending["detail"];
00606                                 pending = req->getTerseDetails();
00607                                 pending["detail"] = detail_url;
00608                                 pending["is_running"] = true;
00609                         }
00610                         else
00611                         {
00612                                 pending["is_running"] = false;
00613                         }
00614                 }
00615         }
00616         return sd;
00617 }
00618 
00619 // virtual
00620 LLSD LLHTTPAssetStorage::getPendingRequest(LLAssetStorage::ERequestType rt,
00621                                                                                 LLAssetType::EType asset_type,
00622                                                                                 const LLUUID& asset_id) const
00623 {
00624         // Look for this asset in the running list first.
00625         const request_list_t* running = getRunningList(rt);
00626         if (running)
00627         {
00628                 LLSD sd = LLAssetStorage::getPendingRequest(running, asset_type, asset_id);
00629                 if (sd)
00630                 {
00631                         sd["is_running"] = true;
00632                         return sd;
00633                 }
00634         }
00635         LLSD sd = LLAssetStorage::getPendingRequest(rt, asset_type, asset_id);
00636         if (sd)
00637         {
00638                 sd["is_running"] = false;
00639         }
00640         return sd;
00641 }
00642 
00643 // virtual
00644 bool LLHTTPAssetStorage::deletePendingRequest(LLAssetStorage::ERequestType rt,
00645                                                                                         LLAssetType::EType asset_type,
00646                                                                                         const LLUUID& asset_id)
00647 {
00648         // Try removing this from the running list first.
00649         request_list_t* running = getRunningList(rt);
00650         if (running)
00651         {
00652                 LLAssetRequest* req = findRequest(running, asset_type, asset_id);
00653                 if (req)
00654                 {
00655                         // Remove this request from the running list to get it out of curl.
00656                         running->remove(req);
00657                         
00658                         // Find this request in the pending list, so we can move it to the end of the line.
00659                         request_list_t* pending = getRequestList(rt);
00660                         if (pending)
00661                         {
00662                                 request_list_t::iterator result = std::find_if(pending->begin(), pending->end(),
00663                                                                                 std::bind2nd(ll_asset_request_equal<LLAssetRequest*>(), req));
00664                                 if (pending->end() != result)
00665                                 {
00666                                         // This request was found in the pending list.  Move it to the end!
00667                                         LLAssetRequest* pending_req = *result;
00668                                         pending->remove(pending_req);
00669 
00670                                         if (!pending_req->mIsUserWaiting)                               //A user is waiting on this request.  Toss it.
00671                                         {
00672                                                 pending->push_back(pending_req);
00673                                         }
00674                                         else
00675                                         {
00676                                                 if (pending_req->mUpCallback)   //Clean up here rather than _callUploadCallbacks because this request is already cleared the req.
00677                                                 {
00678                                                         pending_req->mUpCallback(pending_req->getUUID(), pending_req->mUserData, -1, LL_EXSTAT_REQUEST_DROPPED);
00679                                                 }
00680 
00681                                         }
00682 
00683                                         llinfos << "Asset " << getRequestName(rt) << " request for "
00684                                                         << asset_id << "." << LLAssetType::lookup(asset_type)
00685                                                         << " removed from curl and placed at the end of the pending queue."
00686                                                         << llendl;
00687                                 }
00688                                 else
00689                                 {
00690                                         llwarns << "Unable to find pending " << getRequestName(rt) << " request for "
00691                                                         << asset_id << "." << LLAssetType::lookup(asset_type) << llendl;
00692                                 }
00693                         }
00694                         delete req;
00695 
00696                         return true;
00697                 }
00698         }
00699         return LLAssetStorage::deletePendingRequest(rt, asset_type, asset_id);
00700 }
00701 
00702 // internal requester, used by getAssetData in superclass
00703 void LLHTTPAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType type,
00704                                                                                   void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat),
00705                                                                                   void *user_data, BOOL duplicate,
00706                                                                                    BOOL is_priority)
00707 {
00708         // stash the callback info so we can find it after we get the response message
00709         LLAssetRequest *req = new LLAssetRequest(uuid, type);
00710         req->mDownCallback = callback;
00711         req->mUserData = user_data;
00712         req->mIsPriority = is_priority;
00713 
00714         // this will get picked up and downloaded in checkForTimeouts
00715 
00716         //
00717         // HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACK!  Asset requests were taking too long and timing out.
00718         // Since texture requests are the LEAST sensitive (on the simulator) to being delayed, add
00719         // non-texture requests to the front, and add texture requests to the back.  The theory is
00720         // that we always want them first, even if they're out of order.
00721         //
00722         
00723         if (req->getType() == LLAssetType::AT_TEXTURE)
00724         {
00725                 mPendingDownloads.push_back(req);
00726         }
00727         else
00728         {
00729                 mPendingDownloads.push_front(req);
00730         }
00731 }
00732 
00733 LLAssetRequest* LLHTTPAssetStorage::findNextRequest(LLAssetStorage::request_list_t& pending, 
00734                                                                                                         LLAssetStorage::request_list_t& running)
00735 {
00736         // Early exit if the running list is full, or we don't have more pending than running.
00737         if (running.size() >= MAX_RUNNING_REQUESTS
00738                 || pending.size() <= running.size()) return NULL;
00739 
00740         // Look for the first pending request that is not already running.
00741         request_list_t::iterator running_begin = running.begin();
00742         request_list_t::iterator running_end   = running.end();
00743 
00744         request_list_t::iterator pending_iter = pending.begin();
00745         request_list_t::iterator pending_end  = pending.end();
00746         // Loop over all pending requests until we miss finding it in the running list.
00747         for (; pending_iter != pending.end(); ++pending_iter)
00748         {
00749                 LLAssetRequest* req = *pending_iter;
00750                 // Look for this pending request in the running list.
00751                 if (running_end == std::find_if(running_begin, running_end, 
00752                                                 std::bind2nd(ll_asset_request_equal<LLAssetRequest*>(), req)))
00753                 {
00754                         // It isn't running!  Return it.
00755                         return req;
00756                 }
00757         }
00758         return NULL;
00759 }
00760 
00761 // overloaded to additionally move data to/from the webserver
00762 void LLHTTPAssetStorage::checkForTimeouts()
00763 {
00764         CURLMcode mcode;
00765         LLAssetRequest *req;
00766         while ( (req = findNextRequest(mPendingDownloads, mRunningDownloads)) )
00767         {
00768                 // Setup this curl download request
00769                 // We need to generate a new request here
00770                 // since the one in the list could go away
00771                 char tmp_url[MAX_STRING]; /*Flawfinder: ignore*/
00772                 char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
00773                 req->getUUID().toString(uuid_str);
00774                 std::string base_url = getBaseURL(req->getUUID(), req->getType());
00775                 snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", base_url.c_str() , uuid_str, LLAssetType::lookup(req->getType()));     /* Flawfinder: ignore */
00776 
00777                 LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), 
00778                                                                                 req->getType(), RT_DOWNLOAD, tmp_url, mCurlMultiHandle);
00779                 new_req->mTmpUUID.generate();
00780 
00781                 // Sets pending download flag internally
00782                 new_req->setupCurlHandle();
00783                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
00784                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &curlDownCallback);
00785                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEDATA, new_req->mCurlHandle);
00786         
00787                 mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
00788                 if (mcode > CURLM_OK)
00789                 {
00790                         // Failure.  Deleting the pending request will remove it from the running
00791                         // queue, and push it to the end of the pending queue.
00792                         new_req->cleanupCurlHandle();
00793                         deletePendingRequest(RT_DOWNLOAD, new_req->getType(), new_req->getUUID());
00794                         break;
00795                 }
00796                 else
00797                 {
00798                         llinfos << "Requesting " << new_req->mURLBuffer << llendl;
00799                 }
00800         }
00801 
00802         while ( (req = findNextRequest(mPendingUploads, mRunningUploads)) )
00803         {
00804                 // setup this curl upload request
00805 
00806                 bool do_compress = req->getType() == LLAssetType::AT_OBJECT;
00807 
00808                 char tmp_url[MAX_STRING];/*Flawfinder: ignore*/
00809                 char uuid_str[UUID_STR_LENGTH];/*Flawfinder: ignore*/
00810                 req->getUUID().toString(uuid_str);
00811                 snprintf(tmp_url, sizeof(tmp_url),                                      /* Flawfinder: ignore */
00812                                 do_compress ? "%s/%s.%s.gz" : "%s/%s.%s",
00813                                 mBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType())); 
00814 
00815                 LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), 
00816                                                                         req->getType(), RT_UPLOAD, tmp_url, mCurlMultiHandle);
00817 
00818                 if (req->mIsUserWaiting) //If a user is waiting on a realtime response, we want to perserve information across upload attempts.
00819                 {
00820                         new_req->mTime          = req->mTime;
00821                         new_req->mTimeout       = req->mTimeout;
00822                         new_req->mIsUserWaiting = req->mIsUserWaiting;
00823                 }
00824 
00825                 if (do_compress)
00826                 {
00827                         new_req->prepareCompressedUpload();
00828                 }
00829 
00830                 // Sets pending upload flag internally
00831                 new_req->setupCurlHandle();
00832                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_UPLOAD, 1);
00833                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
00834 
00835                 if (do_compress)
00836                 {
00837                         curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
00838                                         &LLHTTPAssetRequest::curlCompressedUploadCallback);
00839                 }
00840                 else
00841                 {
00842                         LLVFile file(mVFS, req->getUUID(), req->getType());
00843                         curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
00844                         curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
00845                                         &curlUpCallback);
00846                 }
00847                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
00848         
00849                 mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
00850                 if (mcode > CURLM_OK)
00851                 {
00852                         // Failure.  Deleting the pending request will remove it from the running
00853                         // queue, and push it to the end of the pending queue.
00854                         new_req->cleanupCurlHandle();
00855                         deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID());
00856                         break;
00857                 }
00858                 else
00859                 {
00860                         // Get the uncompressed file size.
00861                         LLVFile file(mVFS,new_req->getUUID(),new_req->getType());
00862                         S32 size = file.getSize();
00863                         llinfos << "Requesting PUT " << new_req->mURLBuffer << ", asset size: " << size << " bytes" << llendl;
00864                         if (size == 0)
00865                         {
00866                                 llwarns << "Rejecting zero size PUT request!" << llendl;
00867                                 new_req->cleanupCurlHandle();
00868                                 deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID());                                
00869                         }
00870                 }
00871                 // Pending upload will have been flagged by the request
00872         }
00873 
00874         while ( (req = findNextRequest(mPendingLocalUploads, mRunningLocalUploads)) )
00875         {
00876                 // setup this curl upload request
00877                 LLVFile file(mVFS, req->getUUID(), req->getType());
00878 
00879                 char tmp_url[MAX_STRING]; /*Flawfinder: ignore*/
00880                 char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
00881                 req->getUUID().toString(uuid_str);
00882                 
00883                 // KLW - All temporary uploads are saved locally "http://localhost:12041/asset"
00884                 snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", mLocalBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType()));         /* Flawfinder: ignore */
00885 
00886                 LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), 
00887                                                                                 req->getType(), RT_LOCALUPLOAD, tmp_url, mCurlMultiHandle);
00888                 new_req->mRequestingAgentID = req->mRequestingAgentID;
00889 
00890                 // Sets pending upload flag internally
00891                 new_req->setupCurlHandle();
00892                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_PUT, 1);
00893                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
00894                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
00895                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &curlUpCallback);
00896                 curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
00897         
00898                 mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
00899                 if (mcode > CURLM_OK)
00900                 {
00901                         // Failure.  Deleting the pending request will remove it from the running
00902                         // queue, and push it to the end of the pending queue.
00903                         new_req->cleanupCurlHandle();
00904                         deletePendingRequest(RT_LOCALUPLOAD, new_req->getType(), new_req->getUUID());
00905                         break;
00906                 }
00907                 else
00908                 {
00909                         // Get the uncompressed file size.
00910                         S32 size = file.getSize();
00911 
00912                         llinfos << "TAT: LLHTTPAssetStorage::checkForTimeouts() : pending local!"
00913                                 << " Requesting PUT " << new_req->mURLBuffer << ", asset size: " << size << " bytes" << llendl;
00914                         if (size == 0)
00915                         {
00916                                 
00917                                 llwarns << "Rejecting zero size PUT request!" << llendl;
00918                                 new_req->cleanupCurlHandle();
00919                                 deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID());                                
00920                         }
00921 
00922                 }
00923                 // Pending upload will have been flagged by the request
00924         }
00925         S32 count = 0;
00926         int queue_length;
00927         do
00928         {
00929                 mcode = curl_multi_perform(mCurlMultiHandle, &queue_length);
00930                 count++;
00931         } while (mcode == CURLM_CALL_MULTI_PERFORM && (count < 5));
00932 
00933         CURLMsg *curl_msg;
00934         do
00935         {
00936                 curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
00937                 if (curl_msg && curl_msg->msg == CURLMSG_DONE)
00938                 {
00939                         long curl_result = 0;
00940                         S32 xfer_result = LL_ERR_NOERR;
00941 
00942                         LLHTTPAssetRequest *req = NULL;
00943                         curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, &req);
00944                                                                 
00945                         // TODO: Throw curl_result at all callbacks.
00946                         curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
00947                         if (RT_UPLOAD == req->mRequestType || RT_LOCALUPLOAD == req->mRequestType)
00948                         {
00949                                 if (curl_msg->data.result == CURLE_OK && 
00950                                         (   curl_result == HTTP_OK 
00951                                          || curl_result == HTTP_PUT_OK 
00952                                          || curl_result == HTTP_NO_CONTENT))
00953                                 {
00954                                         llinfos << "Success uploading " << req->getUUID() << " to " << req->mURLBuffer << llendl;
00955                                         if (RT_LOCALUPLOAD == req->mRequestType)
00956                                         {
00957                                                 addTempAssetData(req->getUUID(), req->mRequestingAgentID, mHostName);
00958                                         }
00959                                 }
00960                                 else if (curl_msg->data.result == CURLE_COULDNT_CONNECT ||
00961                                                 curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
00962                                                 curl_result == HTTP_SERVER_BAD_GATEWAY ||
00963                                                 curl_result == HTTP_SERVER_TEMP_UNAVAILABLE)
00964                                 {
00965                                         llwarns << "Re-requesting upload for " << req->getUUID() << ".  Received upload error to " << req->mURLBuffer <<
00966                                                 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
00967 
00969                                         //if (req->mIsUserWaiting)
00970                                         //{
00971                                         //      deletePendingRequest(RT_UPLOAD, req->getType(), req->getUUID());
00972                                         //}
00973                                 }
00974                                 else
00975                                 {
00976                                         llwarns << "Failure uploading " << req->getUUID() << " to " << req->mURLBuffer <<
00977                                                 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
00978 
00979                                         xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
00980                                 }
00981 
00982                                 if (!(curl_msg->data.result == CURLE_COULDNT_CONNECT ||
00983                                                 curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
00984                                                 curl_result == HTTP_SERVER_BAD_GATEWAY ||
00985                                                 curl_result == HTTP_SERVER_TEMP_UNAVAILABLE))
00986                                 {
00987                                         // shared upload finished callback
00988                                         // in the base class, this is called from processUploadComplete
00989                                         _callUploadCallbacks(req->getUUID(), req->getType(), (xfer_result == 0), LL_EXSTAT_CURL_RESULT | curl_result);
00990                                         // Pending upload flag will get cleared when the request is deleted
00991                                 }
00992                         }
00993                         else if (RT_DOWNLOAD == req->mRequestType)
00994                         {
00995                                 if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
00996                                 {
00997                                         if (req->mVFile && req->mVFile->getSize() > 0)
00998                                         {                                       
00999                                                 llinfos << "Success downloading " << req->mURLBuffer << ", size " << req->mVFile->getSize() << llendl;
01000 
01001                                                 req->mVFile->rename(req->getUUID(), req->getType());
01002                                         }
01003                                         else
01004                                         {
01005                                                 // *TODO: if this actually indicates a bad asset on the server
01006                                                 // (not certain at this point), then delete it
01007                                                 llwarns << "Found " << req->mURLBuffer << " to be zero size" << llendl;
01008                                                 xfer_result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
01009                                         }
01010                                 }
01011                                 else
01012                                 {
01013                                         // KLW - TAT See if an avatar owns this texture, and if so request re-upload.
01014                                         llwarns << "Failure downloading " << req->mURLBuffer << 
01015                                                 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
01016 
01017                                         xfer_result = (curl_result == HTTP_MISSING) ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
01018 
01019                                         if (req->mVFile)
01020                                         {
01021                                                 req->mVFile->remove();
01022                                         }
01023                                 }
01024 
01025                                 // call the static callback for transfer completion
01026                                 // this will cleanup all requests for this asset, including ours
01027                                 downloadCompleteCallback(
01028                                         xfer_result,
01029                                         req->getUUID(),
01030                                         req->getType(),
01031                                         (void *)req,
01032                                         LL_EXSTAT_CURL_RESULT | curl_result);
01033                                 // Pending download flag will get cleared when the request is deleted
01034                         }
01035                         else
01036                         {
01037                                 // nothing, just axe this request
01038                                 // currently this can only mean an asset delete
01039                         }
01040 
01041                         // Deleting clears the pending upload/download flag if it's set and the request is transferring
01042                         delete req;
01043                         req = NULL;
01044                 }
01045 
01046         } while (curl_msg && queue_length > 0);
01047         
01048 
01049         // Cleanup 
01050         // We want to bump to the back of the line any running uploads that have timed out.
01051         bumpTimedOutUploads();
01052 
01053         LLAssetStorage::checkForTimeouts();
01054 }
01055 
01056 void LLHTTPAssetStorage::bumpTimedOutUploads()
01057 {
01058         bool user_waiting=FALSE;
01059 
01060         F64 mt_secs = LLMessageSystem::getMessageTimeSeconds();
01061 
01062         if (mPendingUploads.size())
01063         {
01064                 request_list_t::iterator it = mPendingUploads.begin();
01065                 LLAssetRequest* req = *it;
01066                 user_waiting=req->mIsUserWaiting;
01067         }
01068 
01069         // No point bumping currently running uploads if there are no others in line.
01070         if (!(mPendingUploads.size() > mRunningUploads.size()) && !user_waiting) 
01071         {
01072                 return;
01073         }
01074 
01075         // deletePendingRequest will modify the mRunningUploads list so we don't want to iterate over it.
01076         request_list_t temp_running = mRunningUploads;
01077 
01078         request_list_t::iterator it = temp_running.begin();
01079         request_list_t::iterator end = temp_running.end();
01080         for ( ; it != end; ++it)
01081         {
01082                 //request_list_t::iterator curiter = iter++;
01083                 LLAssetRequest* req = *it;
01084 
01085                 if ( req->mTimeout < (mt_secs - req->mTime) )
01086                 {
01087                         llwarns << "Asset upload request timed out for "
01088                                         << req->getUUID() << "."
01089                                         << LLAssetType::lookup(req->getType()) 
01090                                         << ", bumping to the back of the line!" << llendl;
01091 
01092                         deletePendingRequest(RT_UPLOAD, req->getType(), req->getUUID());
01093                 }
01094         }
01095 }
01096 
01097 // static
01098 size_t LLHTTPAssetStorage::curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
01099 {
01100         if (!gAssetStorage)
01101         {
01102                 llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
01103                 return 0;
01104         }
01105         S32 bytes = (S32)(size * nmemb);
01106         CURL *curl_handle = (CURL *)user_data;
01107         LLHTTPAssetRequest *req = NULL;
01108         curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
01109 
01110         if (! req->mVFile)
01111         {
01112                 req->mVFile = new LLVFile(gAssetStorage->mVFS, req->mTmpUUID, LLAssetType::AT_NONE, LLVFile::APPEND);
01113         }
01114 
01115         double content_length = 0.0;
01116         curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length);
01117 
01118         // sanitize content_length, reconcile w/ actual data
01119         S32 file_length = llmax(0, (S32)llmin(content_length, 20000000.0), bytes + req->mVFile->getSize());
01120 
01121         req->mVFile->setMaxSize(file_length);
01122         req->mVFile->write((U8*)data, bytes);
01123 
01124         return nmemb;
01125 }
01126 
01127 // static 
01128 size_t LLHTTPAssetStorage::curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data)
01129 {
01130         if (!gAssetStorage)
01131         {
01132                 llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
01133                 return 0;
01134         }
01135         CURL *curl_handle = (CURL *)user_data;
01136         LLHTTPAssetRequest *req = NULL;
01137         curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
01138 
01139         if (! req->mVFile)
01140         {
01141                 req->mVFile = new LLVFile(gAssetStorage->mVFS, req->getUUID(), req->getType(), LLVFile::READ);
01142         }
01143 
01144         S32 bytes = llmin((S32)(size * nmemb), (S32)(req->mVFile->getSize() - req->mVFile->tell()));
01145 
01146         req->mVFile->read((U8*)data, bytes);/*Flawfinder: ignore*/
01147 
01148         return req->mVFile->getLastBytesRead();
01149 }
01150 
01151 // static
01152 size_t LLHTTPAssetStorage::nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data)
01153 {
01154         // do nothing, this is here to soak up script output so it doesn't end up on stdout
01155 
01156         return nmemb;
01157 }
01158 
01159 
01160 
01161 // blocking asset fetch which bypasses the VFS
01162 // this is a very limited function for use by the simstate loader and other one-offs
01163 S32 LLHTTPAssetStorage::getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const LLString &url, const char *filename, progress_callback callback, void *userdata)
01164 {
01165         // *NOTE: There is no guarantee that the uuid and the asset_type match
01166         // - not that it matters. - Doug
01167         lldebugs << "LLHTTPAssetStorage::getURLToFile() - " << url << llendl;
01168 
01169         FILE *fp = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/
01170         if (! fp)
01171         {
01172                 llwarns << "Failed to open " << filename << " for writing" << llendl;
01173                 return LL_ERR_ASSET_REQUEST_FAILED;
01174         }
01175 
01176         // make sure we use the normal curl setup, even though we don't really need a request object
01177         LLHTTPAssetRequest req(this, uuid, asset_type, RT_DOWNLOAD, url.c_str(), mCurlMultiHandle);
01178         req.mFP = fp;
01179         
01180         req.setupCurlHandle();
01181         curl_easy_setopt(req.mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
01182         curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEFUNCTION, &curlFileDownCallback);
01183         curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEDATA, req.mCurlHandle);
01184 
01185         curl_multi_add_handle(mCurlMultiHandle, req.mCurlHandle);
01186         llinfos << "Requesting as file " << req.mURLBuffer << llendl;
01187 
01188         // braindead curl loop
01189         int queue_length;
01190         CURLMsg *curl_msg;
01191         LLTimer timeout;
01192         timeout.setTimerExpirySec(GET_URL_TO_FILE_TIMEOUT);
01193         bool success = false;
01194         S32 xfer_result = 0;
01195         do
01196         {
01197                 curl_multi_perform(mCurlMultiHandle, &queue_length);
01198                 curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
01199 
01200                 if (callback)
01201                 {
01202                         callback(userdata);
01203                 }
01204 
01205                 if ( curl_msg && (CURLMSG_DONE == curl_msg->msg) )
01206                 {
01207                         success = true;
01208                 }
01209                 else if (timeout.hasExpired())
01210                 {
01211                         llwarns << "Request for " << url << " has timed out." << llendl;
01212                         success = false;
01213                         xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
01214                         break;
01215                 }
01216         } while (!success);
01217 
01218         if (success)
01219         {
01220                 long curl_result = 0;
01221                 curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
01222                 
01223                 if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
01224                 {
01225                         S32 size = ftell(req.mFP);
01226                         if (size > 0)
01227                         {
01228                                 // everything seems to be in order
01229                                 llinfos << "Success downloading " << req.mURLBuffer << " to file, size " << size << llendl;
01230                         }
01231                         else
01232                         {
01233                                 llwarns << "Found " << req.mURLBuffer << " to be zero size" << llendl;
01234                                 xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
01235                         }
01236                 }
01237                 else
01238                 {
01239                         xfer_result = curl_result == HTTP_MISSING ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
01240                         llinfos << "Failure downloading " << req.mURLBuffer << 
01241                                 " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
01242                 }
01243         }
01244 
01245         fclose(fp);
01246         if (xfer_result)
01247         {
01248                 LLFile::remove(filename);
01249         }
01250         return xfer_result;
01251 }
01252 
01253 
01254 // static
01255 size_t LLHTTPAssetStorage::curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
01256 {       
01257         CURL *curl_handle = (CURL *)user_data;
01258         LLHTTPAssetRequest *req = NULL;
01259         curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
01260 
01261         if (! req->mFP)
01262         {
01263                 llwarns << "Missing mFP, aborting curl file download callback!" << llendl;
01264                 return 0;
01265         }
01266 
01267         return fwrite(data, size, nmemb, req->mFP);
01268 }
01269 
01270 LLAssetStorage::request_list_t* LLHTTPAssetStorage::getRunningList(LLAssetStorage::ERequestType rt)
01271 {
01272         switch (rt)
01273         {
01274         case RT_DOWNLOAD:
01275                 return &mRunningDownloads;
01276         case RT_UPLOAD:
01277                 return &mRunningUploads;
01278         case RT_LOCALUPLOAD:
01279                 return &mRunningLocalUploads;
01280         default:
01281                 return NULL;
01282         }
01283 }
01284 
01285 const LLAssetStorage::request_list_t* LLHTTPAssetStorage::getRunningList(LLAssetStorage::ERequestType rt) const
01286 {
01287         switch (rt)
01288         {
01289         case RT_DOWNLOAD:
01290                 return &mRunningDownloads;
01291         case RT_UPLOAD:
01292                 return &mRunningUploads;
01293         case RT_LOCALUPLOAD:
01294                 return &mRunningLocalUploads;
01295         default:
01296                 return NULL;
01297         }
01298 }
01299 
01300 
01301 void LLHTTPAssetStorage::addRunningRequest(ERequestType rt, LLHTTPAssetRequest* request)
01302 {
01303         request_list_t* requests = getRunningList(rt);
01304         if (requests)
01305         {
01306                 requests->push_back(request);
01307         }
01308         else
01309         {
01310                 llerrs << "LLHTTPAssetStorage::addRunningRequest - Request is not an upload OR download, this is bad!" << llendl;
01311         }
01312 }
01313 
01314 void LLHTTPAssetStorage::removeRunningRequest(ERequestType rt, LLHTTPAssetRequest* request)
01315 {
01316         request_list_t* requests = getRunningList(rt);
01317         if (requests)
01318         {
01319                 requests->remove(request);
01320         }
01321         else
01322         {
01323                 llerrs << "LLHTTPAssetStorage::removeRunningRequest - Destroyed request is not an upload OR download, this is bad!" << llendl;
01324         }
01325 }
01326 
01327 // virtual 
01328 void LLHTTPAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name)
01329 {
01330         if (agent_id.isNull() || asset_id.isNull())
01331         {
01332                 llwarns << "TAT: addTempAssetData bad id's asset_id: " << asset_id << "  agent_id: " << agent_id << llendl;
01333                 return;
01334         }
01335 
01336         LLTempAssetData temp_asset_data;
01337         temp_asset_data.mAssetID = asset_id;
01338         temp_asset_data.mAgentID = agent_id;
01339         temp_asset_data.mHostName = host_name;
01340 
01341         mTempAssets[asset_id] = temp_asset_data;
01342 }
01343 
01344 // virtual
01345 BOOL LLHTTPAssetStorage::hasTempAssetData(const LLUUID& texture_id) const
01346 {
01347         uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
01348         BOOL found = (citer != mTempAssets.end());
01349         return found;
01350 }
01351 
01352 // virtual
01353 std::string LLHTTPAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const
01354 {
01355         uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
01356         if (citer != mTempAssets.end())
01357         {
01358                 return citer->second.mHostName;
01359         }
01360         else
01361         {
01362                 return std::string();
01363         }
01364 }
01365 
01366 // virtual 
01367 LLUUID LLHTTPAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const
01368 {
01369         uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
01370         if (citer != mTempAssets.end())
01371         {
01372                 return citer->second.mAgentID;
01373         }
01374         else
01375         {
01376                 return LLUUID::null;
01377         }
01378 }
01379 
01380 // virtual 
01381 void LLHTTPAssetStorage::removeTempAssetData(const LLUUID& asset_id)
01382 {
01383         mTempAssets.erase(asset_id);
01384 }
01385 
01386 // virtual 
01387 void LLHTTPAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id)
01388 {
01389         uuid_tempdata_map::iterator it = mTempAssets.begin();
01390         uuid_tempdata_map::iterator end = mTempAssets.end();
01391 
01392         while (it != end)
01393         {
01394                 const LLTempAssetData& asset_data = it->second;
01395                 if (asset_data.mAgentID == agent_id)
01396                 {
01397                         mTempAssets.erase(it++);
01398                 }
01399                 else
01400                 {
01401                         ++it;
01402                 }
01403         }
01404 }
01405 
01406 std::string LLHTTPAssetStorage::getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type)
01407 {
01408         if (LLAssetType::AT_TEXTURE == asset_type)
01409         {
01410                 uuid_tempdata_map::const_iterator citer = mTempAssets.find(asset_id);
01411                 if (citer != mTempAssets.end())
01412                 {
01413                         const std::string& host_name = citer->second.mHostName;
01414                         std::string url = llformat(LOCAL_ASSET_URL_FORMAT, host_name.c_str());
01415                         return url;
01416                 }
01417         }
01418 
01419         return mBaseURL;
01420 }
01421 
01422 void LLHTTPAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const
01423 {
01424         uuid_tempdata_map::const_iterator it = mTempAssets.begin();
01425         uuid_tempdata_map::const_iterator end = mTempAssets.end();
01426         S32 count = 0;
01427         for ( ; it != end; ++it)
01428         {
01429                 const LLTempAssetData& temp_asset_data = it->second;
01430                 if (avatar_id.isNull()
01431                         || avatar_id == temp_asset_data.mAgentID)
01432                 {
01433                         llinfos << "TAT: dump agent " << temp_asset_data.mAgentID
01434                                 << " texture " << temp_asset_data.mAssetID
01435                                 << " host " << temp_asset_data.mHostName
01436                                 << llendl;
01437                         count++;
01438                 }
01439         }
01440 
01441         if (avatar_id.isNull())
01442         {
01443                 llinfos << "TAT: dumped " << count << " entries for all avatars" << llendl;
01444         }
01445         else
01446         {
01447                 llinfos << "TAT: dumped " << count << " entries for avatar " << avatar_id << llendl;
01448         }
01449 }
01450 
01451 void LLHTTPAssetStorage::clearTempAssetData()
01452 {
01453         llinfos << "TAT: Clearing temp asset data map" << llendl;
01454         mTempAssets.clear();
01455 }

Generated on Fri May 16 08:32:25 2008 for SecondLife by  doxygen 1.5.5