void LLHTTPAssetStorage::bumpTimedOutUploads() { // No point bumping currently running uploads if there are no others in line. if (!(mPendingUploads.size() > mRunningUploads.size())) { return; } F64 mt_secs = LLMessageSystem::getMessageTimeSeconds(); // deletePendingRequest will modify the mRunningUploads list so we don't want to iterate over it. request_list_t temp_running = mRunningUploads; request_list_t::iterator it = temp_running.begin(); request_list_t::iterator end = temp_running.end(); for ( ; it != end; ++it) { //request_list_t::iterator curiter = iter++; LLAssetRequest* req = *it; if ( LL_ASSET_STORAGE_TIMEOUT < (mt_secs - req->mTime) ) { llwarns << "Asset upload request timed out for " << req->getUUID() << "." << LLAssetType::lookup(req->getType()) << ", bumping to the back of the line!" << llendl; deletePendingRequest(RT_UPLOAD, req->getType(), req->getUUID()); } } }
// virtual bool LLAssetStorage::deletePendingRequest(LLAssetStorage::ERequestType rt, LLAssetType::EType asset_type, const LLUUID& asset_id) { request_list_t* requests = getRequestList(rt); if (deletePendingRequest(requests, asset_type, asset_id)) { llinfos << "Asset " << getRequestName(rt) << " request for " << asset_id << "." << LLAssetType::lookup(asset_type) << " removed from pending queue." << llendl; return true; } return false; }
// overloaded to additionally move data to/from the webserver void LLHTTPAssetStorage::checkForTimeouts() { CURLMcode mcode; LLAssetRequest *req; while ( (req = findNextRequest(mPendingDownloads, mRunningDownloads)) ) { // Setup this curl download request // We need to generate a new request here // since the one in the list could go away char tmp_url[MAX_STRING]; /*Flawfinder: ignore*/ char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/ req->getUUID().toString(uuid_str); std::string base_url = getBaseURL(req->getUUID(), req->getType()); snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", base_url.c_str() , uuid_str, LLAssetType::lookup(req->getType())); /* Flawfinder: ignore */ LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), RT_DOWNLOAD, tmp_url, mCurlMultiHandle); new_req->mTmpUUID.generate(); // Sets pending download flag internally new_req->setupCurlHandle(); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &curlDownCallback); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEDATA, new_req->mCurlHandle); mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle); if (mcode > CURLM_OK) { // Failure. Deleting the pending request will remove it from the running // queue, and push it to the end of the pending queue. new_req->cleanupCurlHandle(); deletePendingRequest(RT_DOWNLOAD, new_req->getType(), new_req->getUUID()); break; } else { llinfos << "Requesting " << new_req->mURLBuffer << llendl; } } while ( (req = findNextRequest(mPendingUploads, mRunningUploads)) ) { // setup this curl upload request bool do_compress = req->getType() == LLAssetType::AT_OBJECT; char tmp_url[MAX_STRING];/*Flawfinder: ignore*/ char uuid_str[UUID_STR_LENGTH];/*Flawfinder: ignore*/ req->getUUID().toString(uuid_str); snprintf(tmp_url, sizeof(tmp_url), /* Flawfinder: ignore */ do_compress ? "%s/%s.%s.gz" : "%s/%s.%s", mBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType())); LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), RT_UPLOAD, tmp_url, mCurlMultiHandle); if (do_compress) { new_req->prepareCompressedUpload(); } // Sets pending upload flag internally new_req->setupCurlHandle(); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_UPLOAD, 1); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback); if (do_compress) { curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &LLHTTPAssetRequest::curlCompressedUploadCallback); } else { LLVFile file(mVFS, req->getUUID(), req->getType()); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize()); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &curlUpCallback); } curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle); mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle); if (mcode > CURLM_OK) { // Failure. Deleting the pending request will remove it from the running // queue, and push it to the end of the pending queue. new_req->cleanupCurlHandle(); deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID()); break; } else { llinfos << "Requesting PUT " << new_req->mURLBuffer << llendl; } // Pending upload will have been flagged by the request } while ( (req = findNextRequest(mPendingLocalUploads, mRunningLocalUploads)) ) { // setup this curl upload request LLVFile file(mVFS, req->getUUID(), req->getType()); char tmp_url[MAX_STRING]; /*Flawfinder: ignore*/ char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/ req->getUUID().toString(uuid_str); // KLW - All temporary uploads are saved locally "http://localhost:12041/asset" snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", mLocalBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType())); /* Flawfinder: ignore */ LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), RT_LOCALUPLOAD, tmp_url, mCurlMultiHandle); new_req->mRequestingAgentID = req->mRequestingAgentID; // Sets pending upload flag internally new_req->setupCurlHandle(); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_PUT, 1); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize()); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &curlUpCallback); curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle); mcode = curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle); if (mcode > CURLM_OK) { // Failure. Deleting the pending request will remove it from the running // queue, and push it to the end of the pending queue. new_req->cleanupCurlHandle(); deletePendingRequest(RT_LOCALUPLOAD, new_req->getType(), new_req->getUUID()); break; } else { llinfos << "TAT: LLHTTPAssetStorage::checkForTimeouts() : pending local!" << " Requesting PUT " << new_req->mURLBuffer << llendl; } // Pending upload will have been flagged by the request } S32 count = 0; int queue_length; do { mcode = curl_multi_perform(mCurlMultiHandle, &queue_length); count++; } while (mcode == CURLM_CALL_MULTI_PERFORM && (count < 5)); CURLMsg *curl_msg; do { curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length); if (curl_msg && curl_msg->msg == CURLMSG_DONE) { long curl_result = 0; S32 xfer_result = 0; LLHTTPAssetRequest *req = NULL; curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, &req); curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result); if (RT_UPLOAD == req->mRequestType || RT_LOCALUPLOAD == req->mRequestType) { if (curl_msg->data.result == CURLE_OK && ( curl_result == HTTP_OK || curl_result == HTTP_PUT_OK || curl_result == HTTP_NO_CONTENT)) { llinfos << "Success uploading " << req->getUUID() << " to " << req->mURLBuffer << llendl; if (RT_LOCALUPLOAD == req->mRequestType) { addTempAssetData(req->getUUID(), req->mRequestingAgentID, mHostName); } } else if (curl_msg->data.result == CURLE_COULDNT_CONNECT || curl_msg->data.result == CURLE_OPERATION_TIMEOUTED || curl_result == HTTP_SERVER_BAD_GATEWAY || curl_result == HTTP_SERVER_TEMP_UNAVAILABLE) { llwarns << "Re-requesting upload for " << req->getUUID() << ". Received upload error to " << req->mURLBuffer << " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl; } else { llwarns << "Failure uploading " << req->getUUID() << " to " << req->mURLBuffer << " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl; xfer_result = LL_ERR_ASSET_REQUEST_FAILED; } if (!(curl_msg->data.result == CURLE_COULDNT_CONNECT || curl_msg->data.result == CURLE_OPERATION_TIMEOUTED || curl_result == HTTP_SERVER_BAD_GATEWAY || curl_result == HTTP_SERVER_TEMP_UNAVAILABLE)) { // shared upload finished callback // in the base class, this is called from processUploadComplete _callUploadCallbacks(req->getUUID(), req->getType(), (xfer_result == 0)); // Pending upload flag will get cleared when the request is deleted } } else if (RT_DOWNLOAD == req->mRequestType) { if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK) { if (req->mVFile && req->mVFile->getSize() > 0) { llinfos << "Success downloading " << req->mURLBuffer << ", size " << req->mVFile->getSize() << llendl; req->mVFile->rename(req->getUUID(), req->getType()); } else { // *TODO: if this actually indicates a bad asset on the server // (not certain at this point), then delete it llwarns << "Found " << req->mURLBuffer << " to be zero size" << llendl; xfer_result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; } } else { // KLW - TAT See if an avatar owns this texture, and if so request re-upload. llwarns << "Failure downloading " << req->mURLBuffer << " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl; xfer_result = (curl_result == HTTP_MISSING) ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED; if (req->mVFile) { req->mVFile->remove(); } } // call the static callback for transfer completion // this will cleanup all requests for this asset, including ours downloadCompleteCallback( xfer_result, req->getUUID(), req->getType(), (void *)req); // Pending download flag will get cleared when the request is deleted } else { // nothing, just axe this request // currently this can only mean an asset delete } // Deleting clears the pending upload/download flag if it's set and the request is transferring delete req; req = NULL; } } while (curl_msg && queue_length > 0); // Cleanup // We want to bump to the back of the line any running uploads that have timed out. bumpTimedOutUploads(); LLAssetStorage::checkForTimeouts(); }