diff options
author | Josh Bell <josh@lindenlab.com> | 2008-02-28 18:15:01 +0000 |
---|---|---|
committer | Josh Bell <josh@lindenlab.com> | 2008-02-28 18:15:01 +0000 |
commit | 2fdd7c35f33d1d98091547b8e96ad9ebf99dee47 (patch) | |
tree | 247ba5247139531ac636bb8b9db7ddf3d5ce6203 /indra/newview | |
parent | 42bc4ba02abebced9fc3e7f91317ae293cbd20dd (diff) |
svn merge -r 80357:80990 svn+ssh://svn.lindenlab.com/svn/linden/branches/Branch_1-19-1-Server --> release
Merge patches from 1.19.1 Server branch:
* QAR-293 Fix for hardcoded TTLs in web dataservices
* DEV-10826 fix for busted binary and notation parse if handed an unlimited parse size
* Bounce apache2 processes before starting backbone/dataserver/simulator
* Changing web-ds TTL in a way that any query that has 0 < ttl <=60 will have ttl = 61.
* Partial reversion of multiagent-chat to 1.19.0 to address fast memory leak
* Fixed minor, non user facing bug in multiagentchat
* set-classified-stats: Rewrote to use new MDB2 query reformatting syntax
* Fixed possible bad conversion of vivox data
* DEV-550, caching changes to DirClassifieds Query
* QAR-240 (DEV-8488) Prevent residents from purging stuff that isn't trash on the backend
* More mem leak fixes for multiagent-chat
* QAR-274 Fetch inventory descendents over TCP (via HTTP cap) instead of UDP
* DEV-10151: Sometimes group IMs appear to be person to person IMs
* QAR-321 Changes to crash_reporter
* DEV-11004 Speed up people search query using FORCE INDEX (PRIMARY) on the username table if the first-name query fragment is >= 3 chars
* DEV-11004 Speed up people search query using FORCE INDEX (PRIMARY). Web service version of this, must use two named queries because we need to change the query based on input string length.
Diffstat (limited to 'indra/newview')
-rw-r--r-- | indra/newview/llappviewer.cpp | 17 | ||||
-rw-r--r-- | indra/newview/llinventorymodel.cpp | 301 | ||||
-rw-r--r-- | indra/newview/llinventorymodel.h | 28 | ||||
-rw-r--r-- | indra/newview/llviewerinventory.cpp | 48 | ||||
-rw-r--r-- | indra/newview/llviewerinventory.h | 1 | ||||
-rw-r--r-- | indra/newview/llviewerregion.cpp | 1 | ||||
-rw-r--r-- | indra/newview/llxmlrpctransaction.cpp | 152 |
7 files changed, 435 insertions, 113 deletions
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 666fcd1301..61699d21c8 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -51,6 +51,7 @@ #include "llstartup.h" #include "llfocusmgr.h" #include "llviewerjoystick.h" +#include "llares.h" #include "llcurl.h" #include "llfloatersnapshot.h" #include "llviewerwindow.h" @@ -1338,7 +1339,7 @@ bool LLAppViewer::mainLoop() // Create IO Pump to use for HTTP Requests. gServicePump = new LLPumpIO(gAPRPoolp); LLHTTPClient::setPump(*gServicePump); - LLHTTPClient::setCABundle(gDirUtilp->getCAFile()); + LLCurl::setCAFile(gDirUtilp->getCAFile()); // initialize voice stuff here gLocalSpeakerMgr = new LLLocalSpeakerMgr(); @@ -1398,10 +1399,14 @@ bool LLAppViewer::mainLoop() { LLFastTimer t3(LLFastTimer::FTM_IDLE); idle(); - LLCurl::process(); - // this pump is necessary to make the login screen show up - gServicePump->pump(); - gServicePump->callback(); + + { + LLFastTimer t4(LLFastTimer::FTM_PUMP); + gAres->process(); + // this pump is necessary to make the login screen show up + gServicePump->pump(); + gServicePump->callback(); + } } if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED)) @@ -1811,7 +1816,7 @@ bool LLAppViewer::cleanup() end_messaging_system(); // *NOTE:Mani - The following call is not thread safe. - LLCurl::cleanup(); + LLCurl::cleanupClass(); // If we're exiting to launch an URL, do that here so the screen // is at the right resolution before we launch IE. diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 7b27a830c4..17a8b84472 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -47,6 +47,7 @@ #include "llviewerinventory.h" #include "llviewermessage.h" #include "llviewerwindow.h" +#include "llviewerregion.h" #include "llappviewer.h" #include "lldbstrings.h" #include "llviewerstats.h" @@ -54,6 +55,8 @@ #include "llnotify.h" #include "llcallbacklist.h" #include "llpreview.h" +#include "llviewercontrol.h" +#include "llsdutil.h" #include <deque> //#define DIFF_INVENTORY_FILES @@ -69,6 +72,8 @@ F32 LLInventoryModel::sMinTimeBetweenFetches = 0.3f; F32 LLInventoryModel::sMaxTimeBetweenFetches = 10.f; BOOL LLInventoryModel::sTimelyFetchPending = FALSE; LLFrameTimer LLInventoryModel::sFetchTimer; +LLInventoryModel::cat_map_t LLInventoryModel::sBulkFetchMap; +S16 LLInventoryModel::sBulkFetchCount = 0; // RN: for some reason, using std::queue in the header file confuses the compiler which things it's an xmlrpc_queue static std::deque<LLUUID> sFetchQueue; @@ -1002,6 +1007,286 @@ void LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) } } +//Initialize statics. +LLAlertDialog* LLInventoryModel::fetchDescendentsResponder::sRetryDialog=NULL; +LLSD LLInventoryModel::fetchDescendentsResponder::sRetrySD; + +bool LLInventoryModel::isBulkFetchProcessingComplete() +{ + return ( (sFetchQueue.empty() + && sBulkFetchMap.empty() + && sBulkFetchCount==0) ? TRUE : FALSE ) ; +} + +//If we get back a normal response, handle it here +void LLInventoryModel::fetchDescendentsResponder::result(const LLSD& content) +{ + if (content.has("folders")) + { + for(LLSD::array_const_iterator folder_it = content["folders"].beginArray(); + folder_it != content["folders"].endArray(); + ++folder_it) + { + LLSD folder_sd = *folder_it; + + + LLUUID agent_id = folder_sd["agent-id"]; + + if(agent_id != gAgent.getID()) //This should never happen. + { + llwarns << "Got a UpdateInventoryItem for the wrong agent." + << llendl; + break; + } + LLUUID parent_id = folder_sd["folder-id"]; + LLUUID owner_id = folder_sd["owner-id"]; + S32 version = (S32)folder_sd["version"].asInteger(); + S32 descendents = (S32)folder_sd["descendents"].asInteger(); + LLPointer<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id); + for(LLSD::array_const_iterator category_it = folder_sd["categories"].beginArray(); + category_it != folder_sd["categories"].endArray(); + ++category_it) + { + LLSD category = *category_it; + tcategory->fromLLSD(category); + + if (sFullFetchStarted) + { + sFetchQueue.push_back(tcategory->getUUID()); + } + else if ( !gInventory.isCategoryComplete(tcategory->getUUID()) ) + { + gInventory.updateCategory(tcategory); + } + + } + LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; + for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray(); + item_it != folder_sd["items"].endArray(); + ++item_it) + { + LLSD item = *item_it; + titem->unpackMessage(item); + + gInventory.updateItem(titem); + } + + // set version and descendentcount according to message. + LLViewerInventoryCategory* cat = gInventory.getCategory(parent_id); + if(cat) + { + cat->setVersion(version); + cat->setDescendentCount(descendents); + } + + } + } + + if (content.has("bad-folders")) + { + for(LLSD::array_const_iterator folder_it = content["bad-folders"].beginArray(); + folder_it != content["bad-folders"].endArray(); + ++folder_it) + { + LLSD folder_sd = *folder_it; + + //These folders failed on the dataserver. We probably don't want to retry them. + llinfos << "Folder " << folder_sd["folder-id"].asString() + << "Error: " << folder_sd["error"].asString() << llendl; + } + } + + LLInventoryModel::incrBulkFetch(-1); + + if (isBulkFetchProcessingComplete()) + { + llinfos << "Inventory fetch completed" << llendl; + if (sFullFetchStarted) + { + sAllFoldersFetched = TRUE; + } + stopBackgroundFetch(); + } + + gInventory.notifyObservers(); +} + +//If we get back an error (not found, etc...), handle it here +void LLInventoryModel::fetchDescendentsResponder::error(U32 status, const std::string& reason) +{ + llinfos << "fetchDescendentsResponder::error " + << status << ": " << reason << llendl; + + LLInventoryModel::incrBulkFetch(-1); + + if (status==499) //timed out. Let's be awesome! + { + for(LLSD::array_const_iterator folder_it = mRequestSD["folders"].beginArray(); + folder_it != mRequestSD["folders"].endArray(); + ++folder_it) + { + LLSD folder_sd = *folder_it; + sRetrySD["folders"].append(folder_sd); + } + sMinTimeBetweenFetches = 10.0f; //Add 10 seconds for every time out in this sequence. + + if (!sRetryDialog) //The dialog isn't up. Prompt the resident. + { + sRetryDialog = gViewerWindow->alertXml("RetryFetchInventoryDescendents", onClickRetry, this); + } + } + else + { + if (isBulkFetchProcessingComplete()) + { + if (sFullFetchStarted) + { + sAllFoldersFetched = TRUE; + } + stopBackgroundFetch(); + } + } + gInventory.notifyObservers(); +} + +void LLInventoryModel::fetchDescendentsResponder::onClickRetry(S32 option, void* userdata) +{ + if (option == 0) + { + std::string url = gAgent.getRegion()->getCapability("FetchInventoryDescendents"); + + if (!url.empty()) //Capability found. Build up LLSD and use it. + { + LLSD body = sRetrySD; + LLInventoryModel::incrBulkFetch(1); + LLHTTPClient::post(url, body, new LLInventoryModel::fetchDescendentsResponder(body),300); + } + } + else + { + if (isBulkFetchProcessingComplete()) + { + if (sFullFetchStarted) + { + sAllFoldersFetched = TRUE; + } + stopBackgroundFetch(); + } + } + sRetryDialog=NULL; + sRetrySD.clear(); +} + +//static Bundle up a bunch of requests to send all at once. +void LLInventoryModel::bulkFetch(std::string url) +{ + //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped. + //If there are items in sFetchQueue, we want to check the time since the last bulkFetch was + //sent. If it exceeds our retry time, go ahead and fire off another batch. + //Stopbackgroundfetch will be run from the Responder instead of here. + + S16 max_concurrent_fetches=8; + F32 new_min_time = 0.5f; //HACK! Clean this up when old code goes away entirely. + if (sMinTimeBetweenFetches <= new_min_time) sMinTimeBetweenFetches=new_min_time; //HACK! See above. + + if(gDisconnected + || sBulkFetchCount > max_concurrent_fetches + || sFetchTimer.getElapsedTimeF32() < sMinTimeBetweenFetches) + { + return; // just bail if we are disconnected. + } + + //HACK. This is inelegant. We're shuffling a dequeue to a map to get rid of + //redundant requests. When we get rid of the old code entirely, we can change + //the dequeue to a map. In the new model, there is no benefit to queue order. + U32 folder_count=0; + U32 max_batch_size=10; + while( !(sFetchQueue.empty() ) ) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front()); + + if (cat) + { + if ( !gInventory.isCategoryComplete(cat->getUUID()) ) //grab this folder. + { + sBulkFetchMap[(cat->getUUID())] = cat; + } + else if (sFullFetchStarted) + { //Already have this folder but append child folders to list. + // add all children to queue + parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID()); + if (cat_it != gInventory.mParentChildCategoryTree.end()) + { + cat_array_t* child_categories = cat_it->second; + + for (S32 child_num = 0; child_num < child_categories->count(); child_num++) + { + sFetchQueue.push_back(child_categories->get(child_num)->getUUID()); + } + } + + } + } + sFetchQueue.pop_front(); + } + + + if (!sBulkFetchMap.empty()) //There's stuff to fetch. + { + U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1; + + LLSD body; + + cat_map_t::iterator iter=sBulkFetchMap.begin(); + while( iter!=sBulkFetchMap.end() && (folder_count < max_batch_size) ) + { + LLViewerInventoryCategory* cat = iter->second; + + if (cat && !gInventory.isCategoryComplete(cat->getUUID()) ) //Category exists + { + BOOL fetchItems=TRUE; + if ( sFullFetchStarted + && gInventory.isCategoryComplete(cat->getUUID()) ) + { + fetchItems=FALSE; + } + + LLSD folder_sd; + folder_sd["folder-id"] = cat->getUUID(); + folder_sd["owner-id"] = cat->getOwnerID(); + folder_sd["sort-order"] = (LLSD::Integer)sort_order; + folder_sd["fetch-folders"] = (LLSD::Boolean)sFullFetchStarted; + folder_sd["fetch-items"] = (LLSD::Boolean)fetchItems; + body["folders"].append(folder_sd); + + folder_count++; + } + sBulkFetchMap.erase(iter); + iter=sBulkFetchMap.begin(); + } + + if (iter == sBulkFetchMap.end()) sBulkFetchMap.clear(); + + if (folder_count > 0) + { + sBulkFetchCount++; + + LLHTTPClient::post(url, body, new LLInventoryModel::fetchDescendentsResponder(body)); + sFetchTimer.reset(); + } + + } + + if (isBulkFetchProcessingComplete()) + { + if (sFullFetchStarted) + { + sAllFoldersFetched = TRUE; + } + stopBackgroundFetch(); + } +} + // static bool LLInventoryModel::isEverythingFetched() { @@ -1049,6 +1334,9 @@ void LLInventoryModel::stopBackgroundFetch() { sBackgroundFetchActive = FALSE; gIdleCallbacks.deleteFunction(&LLInventoryModel::backgroundFetch, NULL); + sBulkFetchCount=0; + sMinTimeBetweenFetches=0.0f; +// sFullFetchStarted=FALSE; } } @@ -1057,6 +1345,15 @@ void LLInventoryModel::backgroundFetch(void*) { if (sBackgroundFetchActive) { + //If we'll be using the capability, we'll be sending batches and the background thing isn't as important. + std::string url = gAgent.getRegion()->getCapability("FetchInventoryDescendents"); + if (!url.empty()) + { + bulkFetch(url); + return; + } + + //DEPRECATED OLD CODE FOLLOWS. // no more categories to fetch, stop fetch process if (sFetchQueue.empty()) { @@ -3063,8 +3360,8 @@ void LLInventoryFetchDescendentsObserver::fetchDescendents( if(!cat) continue; if(!isComplete(cat)) { - cat->fetchDescendents(); - mIncompleteFolders.push_back(*it); + cat->fetchDescendents(); //blindly fetch it without seeing if anything else is fetching it. + mIncompleteFolders.push_back(*it); //Add to list of things being downloaded for this observer. } else { diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index b560aea8b2..79a35f78ea 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -91,6 +91,7 @@ class LLViewerInventoryItem; class LLViewerInventoryCategory; class LLMessageSystem; class LLInventoryCollectFunctor; +class LLAlertDialog; class LLInventoryModel { @@ -105,11 +106,26 @@ public: // These are used a lot... typedef LLDynamicArray<LLPointer<LLViewerInventoryCategory> > cat_array_t; typedef LLDynamicArray<LLPointer<LLViewerInventoryItem> > item_array_t; - // construction & destruction LLInventoryModel(); ~LLInventoryModel(); + class fetchDescendentsResponder: public LLHTTPClient::Responder + { + public: + fetchDescendentsResponder(const LLSD& request_sd) : mRequestSD(request_sd) {}; + void result(const LLSD& content); + void error(U32 status, const std::string& reason); + static void onClickRetry(S32 option, void* userdata); + static void appendRetryList(LLSD retry_sd); + public: + typedef std::vector<LLViewerInventoryCategory*> folder_ref_t; + protected: + LLSD mRequestSD; + static LLSD sRetrySD; + static LLAlertDialog *sRetryDialog; + }; + // // Accessors // @@ -263,6 +279,9 @@ public: // make sure we have the descendents in the structure. void fetchDescendentsOf(const LLUUID& folder_id); + + // Add categories to a list to be fetched in bulk. + static void bulkFetch(std::string url); // call this method to request the inventory. //void requestFromServer(const LLUUID& agent_id); @@ -348,7 +367,7 @@ public: static BOOL backgroundFetchActive(); static bool isEverythingFetched(); static void backgroundFetch(void*); // background fetch idle function - + static void incrBulkFetch(S16 fetching) { sBulkFetchCount+=fetching; if (sBulkFetchCount<0) sBulkFetchCount=0; } protected: // Internal methods which add inventory and make sure that all of @@ -395,7 +414,8 @@ protected: static void processInventoryDescendents(LLMessageSystem* msg, void**); static void processMoveInventoryItem(LLMessageSystem* msg, void**); static void processFetchInventoryReply(LLMessageSystem* msg, void**); - + static bool isBulkFetchProcessingComplete(); + bool messageUpdateCore(LLMessageSystem* msg, bool do_accounting); protected: @@ -430,6 +450,7 @@ protected: observer_list_t mObservers; // completing the fetch once per session should be sufficient + static cat_map_t sBulkFetchMap; static BOOL sBackgroundFetchActive; static BOOL sTimelyFetchPending; static BOOL sAllFoldersFetched; @@ -438,6 +459,7 @@ protected: static LLFrameTimer sFetchTimer; static F32 sMinTimeBetweenFetches; static F32 sMaxTimeBetweenFetches; + static S16 sBulkFetchCount; // This flag is used to handle an invalid inventory state. bool mIsAgentInvUsable; diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 981605d1fa..01feff9b3c 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -48,6 +48,8 @@ #include "llviewerregion.h" #include "llviewerobjectlist.h" #include "llpreviewgesture.h" +#include "llviewerwindow.h" + ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- @@ -213,6 +215,14 @@ void LLViewerInventoryItem::fetchFromServer(void) const } // virtual +BOOL LLViewerInventoryItem::unpackMessage(LLSD item) +{ + BOOL rv = LLInventoryItem::fromLLSD(item); + mIsComplete = TRUE; + return rv; +} + +// virtual BOOL LLViewerInventoryItem::unpackMessage( LLMessageSystem* msg, const char* block, S32 block_num) { @@ -420,30 +430,42 @@ void LLViewerInventoryCategory::removeFromServer( void ) bool LLViewerInventoryCategory::fetchDescendents() { if((VERSION_UNKNOWN == mVersion) - && mDescendentsRequested.hasExpired()) + && mDescendentsRequested.hasExpired()) //Expired check prevents multiple downloads. { const F32 FETCH_TIMER_EXPIRY = 10.0f; mDescendentsRequested.reset(); mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY); - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("FetchInventoryDescendents"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("InventoryData"); - msg->addUUID("FolderID", mUUID); - msg->addUUID("OwnerID", mOwnerID); // bitfield // 1 = by date // 2 = folders by date // Need to mask off anything but the first bit. // This comes from LLInventoryFilter from llfolderview.h U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1; - msg->addS32("SortOrder", sort_order); - msg->addBOOL("FetchFolders", FALSE); - msg->addBOOL("FetchItems", TRUE); - gAgent.sendReliableMessage(); + + std::string url = gAgent.getRegion()->getCapability("FetchInventoryDescendents"); + + if (!url.empty()) //Capability found. Build up LLSD and use it. + { + LLInventoryModel::startBackgroundFetch(mUUID); + } + else + { //Deprecated, but if we don't have a capability, use the old system. + llinfos << "FetchInventoryDescendents capability not found. Using deprecated UDP message." << llendl; + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("FetchInventoryDescendents"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->nextBlock("InventoryData"); + msg->addUUID("FolderID", mUUID); + msg->addUUID("OwnerID", mOwnerID); + + msg->addS32("SortOrder", sort_order); + msg->addBOOL("FetchFolders", FALSE); + msg->addBOOL("FetchItems", TRUE); + gAgent.sendReliableMessage(); + } return true; } return false; diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index fd6928243b..bf49a1604f 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -99,6 +99,7 @@ public: //virtual void packMessage(LLMessageSystem* msg) const; virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); + virtual BOOL unpackMessage(LLSD item); virtual BOOL importFile(FILE* fp); virtual BOOL importLegacyStream(std::istream& input_stream); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 3f0f5bee98..42654e250b 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1378,6 +1378,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("DispatchRegionInfo"); capabilityNames.append("EstateChangeInfo"); capabilityNames.append("EventQueueGet"); + capabilityNames.append("FetchInventoryDescendents"); capabilityNames.append("GroupProposalBallot"); capabilityNames.append("MapLayer"); capabilityNames.append("MapLayerGod"); diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 3df2073c6a..9dc92efa81 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -33,10 +33,10 @@ #include "llxmlrpctransaction.h" +#include "llcurl.h" #include "llviewercontrol.h" // Have to include these last to avoid queue redefinition! -#include <curl/curl.h> #include <xmlrpc-epi/xmlrpc.h> #include "llappviewer.h" @@ -150,51 +150,48 @@ class LLXMLRPCTransaction::Impl { public: typedef LLXMLRPCTransaction::Status Status; - - CURL* mCurl; - CURLM* mCurlMulti; + + LLCurlEasyRequest* mCurlRequest; Status mStatus; CURLcode mCurlCode; std::string mStatusMessage; std::string mStatusURI; + LLCurl::TransferInfo mTransferInfo; - char mCurlErrorBuffer[CURL_ERROR_SIZE]; /* Flawfinder: ignore */ - std::string mURI; char* mRequestText; int mRequestTextSize; std::string mProxyAddress; - struct curl_slist* mHeaders; std::string mResponseText; XMLRPC_REQUEST mResponse; Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip); Impl(const std::string& uri, - const std::string& method, LLXMLRPCValue params, bool useGzip); + const std::string& method, LLXMLRPCValue params, bool useGzip); ~Impl(); bool process(); void setStatus(Status code, - const std::string& message = "", const std::string& uri = ""); + const std::string& message = "", const std::string& uri = ""); void setCurlStatus(CURLcode); private: void init(XMLRPC_REQUEST request, bool useGzip); static size_t curlDownloadCallback( - void* data, size_t size, size_t nmemb, void* user_data); + char* data, size_t size, size_t nmemb, void* user_data); }; LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip) - : mCurl(0), mCurlMulti(0), + : mCurlRequest(0), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), - mRequestText(0), mHeaders(0), + mRequestText(0), mResponse(0) { init(request, useGzip); @@ -203,10 +200,10 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, LLXMLRPCTransaction::Impl::Impl(const std::string& uri, const std::string& method, LLXMLRPCValue params, bool useGzip) - : mCurl(0), mCurlMulti(0), + : mCurlRequest(0), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), - mRequestText(0), mHeaders(0), + mRequestText(0), mResponse(0) { XMLRPC_REQUEST request = XMLRPC_RequestNew(); @@ -222,55 +219,53 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { - mCurl = curl_easy_init(); - + if (!mCurlRequest) + { + mCurlRequest = new LLCurlEasyRequest(); + } + if (gSavedSettings.getBOOL("BrowserProxyEnabled")) { mProxyAddress = gSavedSettings.getString("BrowserProxyAddress"); S32 port = gSavedSettings.getS32 ( "BrowserProxyPort" ); // tell curl about the settings - curl_easy_setopt(mCurl, CURLOPT_PROXY, mProxyAddress.c_str()); - curl_easy_setopt(mCurl, CURLOPT_PROXYPORT, (long) port); - curl_easy_setopt(mCurl, CURLOPT_PROXYTYPE, (long) CURLPROXY_HTTP); - }; - -// curl_easy_setopt(mCurl, CURLOPT_VERBOSE, 1L); // usefull for debugging - curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &curlDownloadCallback); - curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, this); - curl_easy_setopt(mCurl, CURLOPT_ERRORBUFFER, &mCurlErrorBuffer); - curl_easy_setopt(mCurl, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str()); - curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, (long) gVerifySSLCert); - curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYHOST, gVerifySSLCert? 2L : 0L); + mCurlRequest->setoptString(CURLOPT_PROXY, mProxyAddress); + mCurlRequest->setopt(CURLOPT_PROXYPORT, port); + mCurlRequest->setopt(CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } + +// mCurlRequest->setopt(CURLOPT_VERBOSE, 1); // usefull for debugging + mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); + mCurlRequest->setWriteCallback(&curlDownloadCallback, (void*)this); + mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, gVerifySSLCert); + mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, gVerifySSLCert? 2 : 0); // Be a little impatient about establishing connections. - curl_easy_setopt(mCurl, CURLOPT_CONNECTTIMEOUT, 40L); + mCurlRequest->setopt(CURLOPT_CONNECTTIMEOUT, 40L); /* Setting the DNS cache timeout to -1 disables it completely. This might help with bug #503 */ - curl_easy_setopt(mCurl, CURLOPT_DNS_CACHE_TIMEOUT, -1L); + mCurlRequest->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1); + + mCurlRequest->slist_append("Content-Type: text/xml"); - mHeaders = curl_slist_append(mHeaders, "Content-Type: text/xml"); - curl_easy_setopt(mCurl, CURLOPT_URL, mURI.c_str()); - curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mHeaders); if (useGzip) { - curl_easy_setopt(mCurl, CURLOPT_ENCODING, ""); + mCurlRequest->setoptString(CURLOPT_ENCODING, ""); } mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize); if (mRequestText) { - curl_easy_setopt(mCurl, CURLOPT_POSTFIELDS, mRequestText); - curl_easy_setopt(mCurl, CURLOPT_POSTFIELDSIZE, (long) mRequestTextSize); + mCurlRequest->setoptString(CURLOPT_POSTFIELDS, mRequestText); + mCurlRequest->setopt(CURLOPT_POSTFIELDSIZE, mRequestTextSize); } else { setStatus(StatusOtherError); } - - mCurlMulti = curl_multi_init(); - curl_multi_add_handle(mCurlMulti, mCurl); + + mCurlRequest->sendRequest(mURI); } @@ -281,30 +276,12 @@ LLXMLRPCTransaction::Impl::~Impl() XMLRPC_RequestFree(mResponse, 1); } - if (mHeaders) - { - curl_slist_free_all(mHeaders); - } - if (mRequestText) { XMLRPC_Free(mRequestText); } - if (mCurl) - { - if (mCurlMulti) - { - curl_multi_remove_handle(mCurlMulti, mCurl); - } - curl_easy_cleanup(mCurl); - } - - if (mCurlMulti) - { - curl_multi_cleanup(mCurlMulti); - } - + delete mCurlRequest; } bool LLXMLRPCTransaction::Impl::process() @@ -333,27 +310,28 @@ bool LLXMLRPCTransaction::Impl::process() const F32 MAX_PROCESSING_TIME = 0.05f; LLTimer timer; - int count; - - while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(mCurlMulti, &count)) + + while (mCurlRequest->perform() > 0) { if (timer.getElapsedTimeF32() >= MAX_PROCESSING_TIME) { return false; } } - - while(CURLMsg* curl_msg = curl_multi_info_read(mCurlMulti, &count)) + + while(1) { - if (CURLMSG_DONE == curl_msg->msg) + CURLcode result; + bool newmsg = mCurlRequest->getResult(&result, &mTransferInfo); + if (newmsg) { - if (curl_msg->data.result != CURLE_OK) + if (result != CURLE_OK) { - setCurlStatus(curl_msg->data.result); + setCurlStatus(result); llwarns << "LLXMLRPCTransaction CURL error " - << mCurlCode << ": " << mCurlErrorBuffer << llendl; + << mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; llwarns << "LLXMLRPCTransaction request URI: " - << mURI << llendl; + << mURI << llendl; return true; } @@ -361,7 +339,7 @@ bool LLXMLRPCTransaction::Impl::process() setStatus(LLXMLRPCTransaction::StatusComplete); mResponse = XMLRPC_REQUEST_FromXML( - mResponseText.data(), mResponseText.size(), NULL); + mResponseText.data(), mResponseText.size(), NULL); bool hasError = false; bool hasFault = false; @@ -387,15 +365,19 @@ bool LLXMLRPCTransaction::Impl::process() setStatus(LLXMLRPCTransaction::StatusXMLRPCError); llwarns << "LLXMLRPCTransaction XMLRPC " - << (hasError ? "error " : "fault ") - << faultCode << ": " - << faultString << llendl; + << (hasError ? "error " : "fault ") + << faultCode << ": " + << faultString << llendl; llwarns << "LLXMLRPCTransaction request URI: " - << mURI << llendl; + << mURI << llendl; } return true; } + else + { + break; // done + } } return false; @@ -504,13 +486,13 @@ void LLXMLRPCTransaction::Impl::setCurlStatus(CURLcode code) } size_t LLXMLRPCTransaction::Impl::curlDownloadCallback( - void* data, size_t size, size_t nmemb, void* user_data) + char* data, size_t size, size_t nmemb, void* user_data) { Impl& impl(*(Impl*)user_data); size_t n = size * nmemb; - impl.mResponseText.append((const char*)data, n); + impl.mResponseText.append(data, n); if (impl.mStatus == LLXMLRPCTransaction::StatusStarted) { @@ -579,25 +561,17 @@ LLXMLRPCValue LLXMLRPCTransaction::responseValue() F64 LLXMLRPCTransaction::transferRate() { - if (!impl.mCurl || impl.mStatus != StatusComplete) + if (impl.mStatus != StatusComplete) { return 0.0L; } - double size_bytes = 0.0; - double time_seconds = 0.0; - double rate_bytes_per_sec = 0.0; - - curl_easy_getinfo(impl.mCurl, CURLINFO_SIZE_DOWNLOAD, &size_bytes); - curl_easy_getinfo(impl.mCurl, CURLINFO_TOTAL_TIME, &time_seconds); - curl_easy_getinfo(impl.mCurl, CURLINFO_SPEED_DOWNLOAD, &rate_bytes_per_sec); - - double rate_bits_per_sec = rate_bytes_per_sec * 8.0; + double rate_bits_per_sec = impl.mTransferInfo.mSpeedDownload * 8.0; llinfos << "Buffer size: " << impl.mResponseText.size() << " B" << llendl; - llinfos << "Transfer size: " << size_bytes << " B" << llendl; - llinfos << "Transfer time: " << time_seconds << " s" << llendl; - llinfos << "Transfer rate: " << rate_bits_per_sec/1000.0 << " Kb/s" << llendl; + llinfos << "Transfer size: " << impl.mTransferInfo.mSizeDownload << " B" << llendl; + llinfos << "Transfer time: " << impl.mTransferInfo.mTotalTime << " s" << llendl; + llinfos << "Transfer rate: " << rate_bits_per_sec / 1000.0 << " Kb/s" << llendl; return rate_bits_per_sec; } |