diff options
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;  }  | 
