diff options
| author | simon <none@none> | 2014-11-03 18:02:49 -0800 | 
|---|---|---|
| committer | simon <none@none> | 2014-11-03 18:02:49 -0800 | 
| commit | 3b50cdd74c5870d3268ed30e342f13cf188a7841 (patch) | |
| tree | 9cae9119e97248beaacf4b19ff8056a2a4167142 | |
| parent | 0ffecdcfb203852a8f4a392331a5d177f8480bdd (diff) | |
| parent | fa7610fea599a98cc6e79d3835a14f8e5ca2b6b1 (diff) | |
Merge downstream code and become 3.7.20
38 files changed, 2811 insertions, 1157 deletions
| @@ -491,3 +491,4 @@ a7872554f3665588f1e8347d472cec3a299254b3 3.7.14-release  562e7dace7465060ac9adb2e8eca800b699ff024 3.7.16-release  bcc2770e21c125e0bab59141c51db9145aec068d 3.7.17-release  2729c1daf0257d68a40bdbc4acf1a16184974bbd 3.7.18-release +82973b38a6c9a457333e3519e4f2b16bb5eedf47 3.7.19-release diff --git a/autobuild.xml b/autobuild.xml index 6f4617e8c3..5bc45604b8 100755 --- a/autobuild.xml +++ b/autobuild.xml @@ -282,9 +282,9 @@              <key>archive</key>              <map>                <key>hash</key> -              <string>f5a699c93beb1a854d0b51382b5cecc8</string> +              <string>40b1c6b3727ebedafc2f1a172797ccd1</string>                <key>url</key> -              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3pl_3p-curl-update/rev/290664/arch/Darwin/installer/curl-7.37.0-darwin-20140605.tar.bz2</string> +              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3pl_3p-curl-update/rev/295367/arch/Darwin/installer/curl-7.38.0-darwin-20141010.tar.bz2</string>              </map>              <key>name</key>              <string>darwin</string> @@ -294,9 +294,9 @@              <key>archive</key>              <map>                <key>hash</key> -              <string>2bc285edffd0e55e0cd6290f39854a89</string> +              <string>06149da3d7a34adf40853f813ae55328</string>                <key>url</key> -              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3pl_3p-curl-update/rev/290664/arch/Linux/installer/curl-7.37.0-linux-20140605.tar.bz2</string> +              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3pl_3p-curl-update/rev/295367/arch/Linux/installer/curl-7.38.0-linux-20141010.tar.bz2</string>              </map>              <key>name</key>              <string>linux</string> @@ -306,9 +306,9 @@              <key>archive</key>              <map>                <key>hash</key> -              <string>8d3b197d7a114d2b688d2831a0a59757</string> +              <string>e4280eae792a5f13bc9d01d8cfb7c557</string>                <key>url</key> -              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3pl_3p-curl-update/rev/290664/arch/CYGWIN/installer/curl-7.37.0-windows-20140605.tar.bz2</string> +              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3pl_3p-curl-update/rev/295367/arch/CYGWIN/installer/curl-7.38.0-windows-20141010.tar.bz2</string>              </map>              <key>name</key>              <string>windows</string> diff --git a/indra/cmake/00-COMPILE-LINK-RUN.txt b/indra/cmake/00-COMPILE-LINK-RUN.txt index d08cc2dc0c..49b899c50d 100644 --- a/indra/cmake/00-COMPILE-LINK-RUN.txt +++ b/indra/cmake/00-COMPILE-LINK-RUN.txt @@ -115,12 +115,12 @@ Compilation      ----------------------------------------------------------------------------      Notes: -    1.  We’re also building dylibs in a somewhat unusual way.  They’re +    1.  We're also building dylibs in a somewhat unusual way.  They're      currently being generated with a link path of -    ‘@executable_path/../Resources/<library>’.  If we were to follow -    the recommendations in dyld’s man page, we’d instead reference -    ‘@loader_path/<library>’, use -rpath on the executable link -    (pointing to the ‘Resources’ subdir of the main executable), and +    '@executable_path/../Resources/<library>'.  If we were to follow +    the recommendations in dyld's man page, we’d instead reference +	'@loader_path/<library>', use -rpath on the executable link +    (pointing to the 'Resources' subdir of the main executable), and      be able to avoid some symlinking in the .app tree.      2.  Use the -headerpad_max_install_names link option on all .dylibs. @@ -184,7 +184,7 @@ Linking      second, incompatible version of the library.  Switching colladadom      to a static library ended the re-export problem. -    *  Preventing re-export is not sufficient.  other libraries will +    *  Preventing re-export is not sufficient.  Other libraries will      still be shipped as shared and they can still have Singleton and      Fragile Base Class issues.  A DLL may be built with a static      archive of a library that has global data.  That same static diff --git a/indra/llcorehttp/README.Linden b/indra/llcorehttp/README.Linden index eb6ccab3bc..c3aaa9158d 100644 --- a/indra/llcorehttp/README.Linden +++ b/indra/llcorehttp/README.Linden @@ -529,6 +529,14 @@ HttpOperation::addAsReply: TRACE, ToReplyQueue, Handle:  086D3148          data = NULL; +    There are now helper functions in llmessage/llcorehttputil.h to +    assist with LLSD usage.  requestPostWithLLSD(...) provides a +    requestPost()-like interface that takes an LLSD object rather than +    a BufferArray.  And responseToLLSD(...) attempts to convert a +    BufferArray received from a server into an LLSD object.  You can +    find examples in llmeshrepository.cpp, llinventorymodel.cpp, +    llinventorymodelbackgroundfetch.cpp and lltexturefetch.cpp. +      LLSD will often go hand-in-hand with BufferArray and data      transport.  But you can also do all the streaming I/O you'd expect      of a std::iostream object: diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index f80d7f60f5..a2a60ca056 100755 --- a/indra/llcorehttp/_httpinternal.h +++ b/indra/llcorehttp/_httpinternal.h @@ -145,8 +145,11 @@ const int HTTP_CONNECTION_LIMIT_DEFAULT = 8;  const int HTTP_CONNECTION_LIMIT_MIN = 1;  const int HTTP_CONNECTION_LIMIT_MAX = 256; -// Miscellaneous defaults +// Pipelining limits  const long HTTP_PIPELINING_DEFAULT = 0L; +const long HTTP_PIPELINING_MAX = 20L; + +// Miscellaneous defaults  const bool HTTP_USE_RETRY_AFTER_DEFAULT = true;  const long HTTP_THROTTLE_RATE_DEFAULT = 0L; diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index e56bc84174..81b44ab90b 100755 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -33,6 +33,17 @@  #include "llhttpconstants.h" +namespace +{ + +// Error testing and reporting for libcurl status codes +void check_curl_multi_code(CURLMcode code); +void check_curl_multi_code(CURLMcode code, int curl_setopt_option); + +static const char * const LOG_CORE("CoreHttp"); + +} // end anonymous namespace +  namespace LLCore  { @@ -40,16 +51,18 @@ namespace LLCore  HttpLibcurl::HttpLibcurl(HttpService * service)  	: mService(service), +	  mHandleCache(),  	  mPolicyCount(0),  	  mMultiHandles(NULL), -	  mActiveHandles(NULL) +	  mActiveHandles(NULL), +	  mDirtyPolicy(NULL)  {}  HttpLibcurl::~HttpLibcurl()  {  	shutdown(); -	 +  	mService = NULL;  } @@ -81,6 +94,9 @@ void HttpLibcurl::shutdown()  		delete [] mActiveHandles;  		mActiveHandles = NULL; + +		delete [] mDirtyPolicy; +		mDirtyPolicy = NULL;  	}  	mPolicyCount = 0; @@ -95,11 +111,18 @@ void HttpLibcurl::start(int policy_count)  	mPolicyCount = policy_count;  	mMultiHandles = new CURLM * [mPolicyCount];  	mActiveHandles = new int [mPolicyCount]; +	mDirtyPolicy = new bool [mPolicyCount];  	for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)  	{ -		mMultiHandles[policy_class] = curl_multi_init(); +		if (NULL == (mMultiHandles[policy_class] = curl_multi_init())) +		{ +			LL_ERRS(LOG_CORE) << "Failed to allocate multi handle in libcurl." +							  << LL_ENDL; +		}  		mActiveHandles[policy_class] = 0; +		mDirtyPolicy[policy_class] = false; +		policyUpdated(policy_class);  	}  } @@ -117,8 +140,19 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()  	// Give libcurl some cycles to do I/O & callbacks  	for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)  	{ -		if (! mActiveHandles[policy_class] || ! mMultiHandles[policy_class]) +		if (! mMultiHandles[policy_class]) +		{ +			// No handle, nothing to do. +			continue; +		} +		if (! mActiveHandles[policy_class])  		{ +			// If we've gone quiet and there's a dirty update, apply it, +			// otherwise we're done. +			if (mDirtyPolicy[policy_class]) +			{ +				policyUpdated(policy_class); +			}  			continue;  		} @@ -153,9 +187,9 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()  			}  			else  			{ -				LL_WARNS_ONCE("CoreHttp") << "Unexpected message from libcurl.  Msg code:  " -										  << msg->msg -										  << LL_ENDL; +				LL_WARNS_ONCE(LOG_CORE) << "Unexpected message from libcurl.  Msg code:  " +										<< msg->msg +										<< LL_ENDL;  			}  			msgs_in_queue = 0;  		} @@ -184,23 +218,28 @@ void HttpLibcurl::addOp(HttpOpRequest * op)  	}  	// Make the request live -	curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); +	CURLMcode code; +	code = curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); +	if (CURLM_OK != code) +	{ +		// *TODO:  Better cleanup and recovery but not much we can do here. +		check_curl_multi_code(code); +		return; +	}  	op->mCurlActive = true; +	mActiveOps.insert(op); +	++mActiveHandles[op->mReqPolicy];  	if (op->mTracing > HTTP_TRACE_OFF)  	{  		HttpPolicy & policy(mService->getPolicy()); -		LL_INFOS("CoreHttp") << "TRACE, ToActiveQueue, Handle:  " -							 << static_cast<HttpHandle>(op) -							 << ", Actives:  " << mActiveOps.size() -							 << ", Readies:  " << policy.getReadyCount(op->mReqPolicy) -							 << LL_ENDL; +		LL_INFOS(LOG_CORE) << "TRACE, ToActiveQueue, Handle:  " +						   << static_cast<HttpHandle>(op) +						   << ", Actives:  " << mActiveOps.size() +						   << ", Readies:  " << policy.getReadyCount(op->mReqPolicy) +						   << LL_ENDL;  	} -	 -	// On success, make operation active -	mActiveOps.insert(op); -	++mActiveHandles[op->mReqPolicy];  } @@ -241,16 +280,16 @@ void HttpLibcurl::cancelRequest(HttpOpRequest * op)  	// Detach from multi and recycle handle  	curl_multi_remove_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); -	curl_easy_cleanup(op->mCurlHandle); +	mHandleCache.freeHandle(op->mCurlHandle);  	op->mCurlHandle = NULL;  	// Tracing  	if (op->mTracing > HTTP_TRACE_OFF)  	{ -		LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle:  " -							 << static_cast<HttpHandle>(op) -							 << ", Status:  " << op->mStatus.toTerseString() -							 << LL_ENDL; +		LL_INFOS(LOG_CORE) << "TRACE, RequestCanceled, Handle:  " +						   << static_cast<HttpHandle>(op) +						   << ", Status:  " << op->mStatus.toTerseString() +						   << LL_ENDL;  	}  	// Cancel op and deliver for notification @@ -267,18 +306,18 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode  	if (handle != op->mCurlHandle || ! op->mCurlActive)  	{ -		LL_WARNS("CoreHttp") << "libcurl handle and HttpOpRequest handle in disagreement or inactive request." -							 << "  Handle:  " << static_cast<HttpHandle>(handle) -							 << LL_ENDL; +		LL_WARNS(LOG_CORE) << "libcurl handle and HttpOpRequest handle in disagreement or inactive request." +						   << "  Handle:  " << static_cast<HttpHandle>(handle) +						   << LL_ENDL;  		return false;  	}  	active_set_t::iterator it(mActiveOps.find(op));  	if (mActiveOps.end() == it)  	{ -		LL_WARNS("CoreHttp") << "libcurl completion for request not on active list.  Continuing." -							 << "  Handle:  " << static_cast<HttpHandle>(handle) -							 << LL_ENDL; +		LL_WARNS(LOG_CORE) << "libcurl completion for request not on active list.  Continuing." +						   << "  Handle:  " << static_cast<HttpHandle>(handle) +						   << LL_ENDL;  		return false;  	} @@ -309,25 +348,25 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode  		}  		else  		{ -			LL_WARNS("CoreHttp") << "Invalid HTTP response code (" -								 << http_status << ") received from server." -								 << LL_ENDL; +			LL_WARNS(LOG_CORE) << "Invalid HTTP response code (" +							   << http_status << ") received from server." +							   << LL_ENDL;  			op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS);  		}  	}  	// Detach from multi and recycle handle  	curl_multi_remove_handle(multi_handle, handle); -	curl_easy_cleanup(handle); +	mHandleCache.freeHandle(op->mCurlHandle);  	op->mCurlHandle = NULL;  	// Tracing  	if (op->mTracing > HTTP_TRACE_OFF)  	{ -		LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle:  " -							 << static_cast<HttpHandle>(op) -							 << ", Status:  " << op->mStatus.toTerseString() -							 << LL_ENDL; +		LL_INFOS(LOG_CORE) << "TRACE, RequestComplete, Handle:  " +						   << static_cast<HttpHandle>(op) +						   << ", Status:  " << op->mStatus.toTerseString() +						   << LL_ENDL;  	}  	// Dispatch to next stage @@ -351,6 +390,164 @@ int HttpLibcurl::getActiveCountInClass(int policy_class) const  	return mActiveHandles ? mActiveHandles[policy_class] : 0;  } +void HttpLibcurl::policyUpdated(int policy_class) +{ +	if (policy_class < 0 || policy_class >= mPolicyCount || ! mMultiHandles) +	{ +		return; +	} +	 +	HttpPolicy & policy(mService->getPolicy()); +	 +	if (! mActiveHandles[policy_class]) +	{ +		// Clear to set options.  As of libcurl 7.37.0, if a pipelining +		// multi handle has active requests and you try to set the +		// multi handle to non-pipelining, the library gets very angry +		// and goes off the rails corrupting memory.  A clue that you're +		// about to crash is that you'll get a missing server response +		// error (curl code 9).  So, if options are to be set, we let +		// the multi handle run out of requests, then set options, and +		// re-enable request processing. +		// +		// All of this stall mechanism exists for this reason.  If +		// libcurl becomes more resilient later, it should be possible +		// to remove all of this.  The connection limit settings are fine, +		// it's just that pipelined-to-non-pipelined transition that +		// is fatal at the moment. +		 +		HttpPolicyClass & options(policy.getClassOptions(policy_class)); +		CURLM * multi_handle(mMultiHandles[policy_class]); +		CURLMcode code; + +		// Enable policy if stalled +		policy.stallPolicy(policy_class, false); +		mDirtyPolicy[policy_class] = false; +		 +		if (options.mPipelining > 1) +		{ +			// We'll try to do pipelining on this multihandle +			code = curl_multi_setopt(multi_handle, +									 CURLMOPT_PIPELINING, +									 1L); +			check_curl_multi_code(code, CURLMOPT_PIPELINING); +			code = curl_multi_setopt(multi_handle, +									 CURLMOPT_MAX_PIPELINE_LENGTH, +									 long(options.mPipelining)); +			check_curl_multi_code(code, CURLMOPT_MAX_PIPELINE_LENGTH); +			code = curl_multi_setopt(multi_handle, +									 CURLMOPT_MAX_HOST_CONNECTIONS, +									 long(options.mPerHostConnectionLimit)); +			check_curl_multi_code(code, CURLMOPT_MAX_HOST_CONNECTIONS); +			code = curl_multi_setopt(multi_handle, +									 CURLMOPT_MAX_TOTAL_CONNECTIONS, +									 long(options.mConnectionLimit)); +			check_curl_multi_code(code, CURLMOPT_MAX_TOTAL_CONNECTIONS); +		} +		else +		{ +			code = curl_multi_setopt(multi_handle, +									 CURLMOPT_PIPELINING, +									 0L); +			check_curl_multi_code(code, CURLMOPT_PIPELINING); +			code = curl_multi_setopt(multi_handle, +									 CURLMOPT_MAX_HOST_CONNECTIONS, +									 0L); +			check_curl_multi_code(code, CURLMOPT_MAX_HOST_CONNECTIONS); +			code = curl_multi_setopt(multi_handle, +									 CURLMOPT_MAX_TOTAL_CONNECTIONS, +									 long(options.mConnectionLimit)); +			check_curl_multi_code(code, CURLMOPT_MAX_TOTAL_CONNECTIONS); +		} +	} +	else if (! mDirtyPolicy[policy_class]) +	{ +		// Mark policy dirty and request a stall in the policy. +		// When policy goes idle, we'll re-invoke this method +		// and perform the change.  Don't allow this thread to +		// sleep while we're waiting for quiescence, we'll just +		// stop processing. +		mDirtyPolicy[policy_class] = true; +		policy.stallPolicy(policy_class, true); +	} +} + +// --------------------------------------- +// HttpLibcurl::HandleCache +// --------------------------------------- + +HttpLibcurl::HandleCache::HandleCache() +	: mHandleTemplate(NULL) +{ +	mCache.reserve(50); +} + + +HttpLibcurl::HandleCache::~HandleCache() +{ +	if (mHandleTemplate) +	{ +		curl_easy_cleanup(mHandleTemplate); +		mHandleTemplate = NULL; +	} + +	for (handle_cache_t::iterator it(mCache.begin()); mCache.end() != it; ++it) +	{ +		curl_easy_cleanup(*it); +	} +	mCache.clear(); +} + + +CURL * HttpLibcurl::HandleCache::getHandle() +{ +	CURL * ret(NULL); +	 +	if (! mCache.empty()) +	{ +		// Fastest path to handle +		ret = mCache.back(); +		mCache.pop_back(); +	} +	else if (mHandleTemplate) +	{ +		// Still fast path +		ret = curl_easy_duphandle(mHandleTemplate); +	} +	else +	{ +		// When all else fails +		ret = curl_easy_init(); +	} + +	return ret; +} + + +void HttpLibcurl::HandleCache::freeHandle(CURL * handle) +{ +	if (! handle) +	{ +		return; +	} + +	curl_easy_reset(handle); +	if (! mHandleTemplate) +	{ +		// Save the first freed handle as a template. +		mHandleTemplate = handle; +	} +	else +	{ +		// Otherwise add it to the cache +		if (mCache.size() >= mCache.capacity()) +		{ +			mCache.reserve(mCache.capacity() + 50); +		} +		mCache.push_back(handle); +	} +} +  // ---------------------------------------  // Free functions @@ -376,3 +573,29 @@ struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct  }  // end namespace LLCore + + +namespace  +{ +	 +void check_curl_multi_code(CURLMcode code, int curl_setopt_option) +{ +	if (CURLM_OK != code) +	{ +		LL_WARNS(LOG_CORE) << "libcurl multi error detected:  " << curl_multi_strerror(code) +						   << ", curl_multi_setopt option:  " << curl_setopt_option +						   << LL_ENDL; +	} +} + + +void check_curl_multi_code(CURLMcode code) +{ +	if (CURLM_OK != code) +	{ +		LL_WARNS(LOG_CORE) << "libcurl multi error detected:  " << curl_multi_strerror(code) +						   << LL_ENDL; +	} +} + +}  // end anonymous namespace diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 67f98dd4f0..ffc24c63a8 100755 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -116,6 +116,31 @@ public:  	/// Threading:  called by worker thread.  	bool cancel(HttpHandle handle); +	/// Informs transport that a particular policy class has had +	/// options changed and so should effect any transport state +	/// change necessary to effect those changes.  Used mainly for +	/// initialization and dynamic option setting. +	/// +	/// Threading:  called by worker thread. +	void policyUpdated(int policy_class); + +	/// Allocate a curl handle for caller.  May be freed using +	/// either the freeHandle() method or calling curl_easy_cleanup() +	/// directly. +	/// +	/// @return			Libcurl handle (CURL *) or NULL on allocation +	///					problem.  Handle will be in curl_easy_reset() +	///					condition. +	/// +	/// Threading:  callable by worker thread. +	/// +	/// Deprecation:  Expect this to go away after _httpoprequest is +	/// refactored bringing code into this class. +	CURL * getHandle() +		{ +			return mHandleCache.getHandle(); +		} +  protected:  	/// Invoked when libcurl has indicated a request has been processed  	/// to completion and we need to move the request to a new state. @@ -127,13 +152,68 @@ protected:  protected:  	typedef std::set<HttpOpRequest *> active_set_t; + +	/// Simple request handle cache for libcurl. +	/// +	/// Handle creation is somewhat slow and chunky in libcurl and there's +	/// a pretty good speedup to be had from handle re-use.  So, a simple +	/// vector is kept of 'freed' handles to be reused as needed.  When +	/// that is empty, the first freed handle is kept as a template for +	/// handle duplication.  This is still faster than creation from nothing. +	/// And when that fails, we init fresh from curl_easy_init(). +	/// +	/// Handles allocated with getHandle() may be freed with either +	/// freeHandle() or curl_easy_cleanup().  Choice may be dictated +	/// by thread constraints. +	/// +	/// Threading:  Single-threaded.  May only be used by a single thread, +	/// typically the worker thread.  If freeing requests' handles in an +	/// unknown threading context, use curl_easy_cleanup() for safety. + +	class HandleCache +	{ +	public: +		HandleCache(); +		~HandleCache(); + +	private: +		HandleCache(const HandleCache &);				// Not defined +		void operator=(const HandleCache &);			// Not defined + +	public: +		/// Allocate a curl handle for caller.  May be freed using +		/// either the freeHandle() method or calling curl_easy_cleanup() +		/// directly. +		/// +		/// @return			Libcurl handle (CURL *) or NULL on allocation +		///					problem. +		/// +		/// Threading:  Single-thread (worker) only. +		CURL * getHandle(); + +		/// Free a libcurl handle acquired by whatever means.  Thread +		/// safety is left to the caller. +		/// +		/// Threading:  Single-thread (worker) only. +		void freeHandle(CURL * handle); + +	protected: +		typedef std::vector<CURL *> handle_cache_t; +	 +	protected: +		CURL *				mHandleTemplate;		// Template for duplicating new handles +		handle_cache_t		mCache;					// Cache of old handles +	}; // end class HandleCache  protected: -	HttpService *		mService;				// Simple reference, not owner +	HttpService *		mService;			// Simple reference, not owner +	HandleCache			mHandleCache;		// Handle allocator, owner  	active_set_t		mActiveOps;  	int					mPolicyCount; -	CURLM **			mMultiHandles;			// One handle per policy class -	int *				mActiveHandles;			// Active count per policy class +	CURLM **			mMultiHandles;		// One handle per policy class +	int *				mActiveHandles;		// Active count per policy class +	bool *				mDirtyPolicy;		// Dirty policy update waiting for stall (per pc) +	  }; // end class HttpLibcurl  }  // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index 5bb0654652..fefe561f80 100755 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -38,6 +38,14 @@  #include "lltimer.h" +namespace +{ + +static const char * const LOG_CORE("CoreHttp"); + +} // end anonymous namespace + +  namespace LLCore  { @@ -94,8 +102,8 @@ void HttpOperation::stageFromRequest(HttpService *)  	// Default implementation should never be called.  This  	// indicates an operation making a transition that isn't  	// defined. -	LL_ERRS("CoreHttp") << "Default stageFromRequest method may not be called." -						<< LL_ENDL; +	LL_ERRS(LOG_CORE) << "Default stageFromRequest method may not be called." +					  << LL_ENDL;  } @@ -104,8 +112,8 @@ void HttpOperation::stageFromReady(HttpService *)  	// Default implementation should never be called.  This  	// indicates an operation making a transition that isn't  	// defined. -	LL_ERRS("CoreHttp") << "Default stageFromReady method may not be called." -						<< LL_ENDL; +	LL_ERRS(LOG_CORE) << "Default stageFromReady method may not be called." +					  << LL_ENDL;  } @@ -114,8 +122,8 @@ void HttpOperation::stageFromActive(HttpService *)  	// Default implementation should never be called.  This  	// indicates an operation making a transition that isn't  	// defined. -	LL_ERRS("CoreHttp") << "Default stageFromActive method may not be called." -						<< LL_ENDL; +	LL_ERRS(LOG_CORE) << "Default stageFromActive method may not be called." +					  << LL_ENDL;  } @@ -145,9 +153,9 @@ void HttpOperation::addAsReply()  {  	if (mTracing > HTTP_TRACE_OFF)  	{ -		LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle:  " -							 << static_cast<HttpHandle>(this) -							 << LL_ENDL; +		LL_INFOS(LOG_CORE) << "TRACE, ToReplyQueue, Handle:  " +						   << static_cast<HttpHandle>(this) +						   << LL_ENDL;  	}  	if (mReplyQueue) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 43dd069bc6..fbbb1614fb 100755 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -47,6 +47,19 @@  #include "llhttpconstants.h"  #include "llproxy.h" +// *DEBUG:  "[curl:bugs] #1420" problem and testing. +// +// A pipelining problem, https://sourceforge.net/p/curl/bugs/1420/, +// was a source of Core_9 failures.  Code related to this can be +// identified and tested by: +// * Looking for '[curl:bugs]' strings in source and following +//   instructions there. +// * Set 'QAModeHttpTrace' to 2 or 3 in settings.xml and look for +//   'timed out' events in the log. +// * Enable the HttpRangeRequestsDisable debug setting which causes +//   full asset fetches.  These slow the pipelines down a bit. +// +  namespace  { @@ -94,6 +107,8 @@ void os_strlower(char * str);  void check_curl_easy_code(CURLcode code);  void check_curl_easy_code(CURLcode code, int curl_setopt_option); +static const char * const LOG_CORE("CoreHttp"); +  } // end anonymous namespace @@ -155,6 +170,8 @@ HttpOpRequest::~HttpOpRequest()  	if (mCurlHandle)  	{ +		// Uncertain of thread context so free using +		// safest method.  		curl_easy_cleanup(mCurlHandle);  		mCurlHandle = NULL;  	} @@ -376,6 +393,7 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,  // Junk may be left around from a failed request and that  // needs to be cleaned out.  // +// *TODO:  Move this to _httplibcurl where it belongs.  HttpStatus HttpOpRequest::prepareRequest(HttpService * service)  {  	CURLcode code; @@ -409,17 +427,19 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)  	// *FIXME:  better error handling later  	HttpStatus status; -	// Get global policy options -	HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions()); +	// Get global and class policy options +	HttpPolicyGlobal & gpolicy(service->getPolicy().getGlobalOptions()); +	HttpPolicyClass & cpolicy(service->getPolicy().getClassOptions(mReqPolicy)); -	mCurlHandle = LLCurl::createStandardCurlHandle(); +	mCurlHandle = service->getTransport().getHandle();  	if (! mCurlHandle)  	{  		// We're in trouble.  We'll continue but it won't go well. -		LL_WARNS("CoreHttp") << "Failed to allocate libcurl easy handle.  Continuing." -							 << LL_ENDL; +		LL_WARNS(LOG_CORE) << "Failed to allocate libcurl easy handle.  Continuing." +						   << LL_ENDL;  		return HttpStatus(HttpStatus::LLCORE, HE_BAD_ALLOC);  	} +  	code = curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);  	check_curl_easy_code(code, CURLOPT_IPRESOLVE);  	code = curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); @@ -460,30 +480,30 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)  	code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);  	check_curl_easy_code(code, CURLOPT_SSL_VERIFYHOST); -	if (policy.mUseLLProxy) +	if (gpolicy.mUseLLProxy)  	{  		// Use the viewer-based thread-safe API which has a  		// fast/safe check for proxy enable.  Would like to  		// encapsulate this someway...  		LLProxy::getInstance()->applyProxySettings(mCurlHandle);  	} -	else if (policy.mHttpProxy.size()) +	else if (gpolicy.mHttpProxy.size())  	{  		// *TODO:  This is fine for now but get fuller socks5/  		// authentication thing going later.... -		code = curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, policy.mHttpProxy.c_str()); +		code = curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, gpolicy.mHttpProxy.c_str());  		check_curl_easy_code(code, CURLOPT_PROXY);  		code = curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);  		check_curl_easy_code(code, CURLOPT_PROXYTYPE);  	} -	if (policy.mCAPath.size()) +	if (gpolicy.mCAPath.size())  	{ -		code = curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, policy.mCAPath.c_str()); +		code = curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, gpolicy.mCAPath.c_str());  		check_curl_easy_code(code, CURLOPT_CAPATH);  	} -	if (policy.mCAFile.size()) +	if (gpolicy.mCAFile.size())  	{ -		code = curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, policy.mCAFile.c_str()); +		code = curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, gpolicy.mCAFile.c_str());  		check_curl_easy_code(code, CURLOPT_CAINFO);  	} @@ -538,9 +558,9 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)  		break;  	default: -		LL_ERRS("CoreHttp") << "Invalid HTTP method in request:  " -							<< int(mReqMethod)  << ".  Can't recover." -							<< LL_ENDL; +		LL_ERRS(LOG_CORE) << "Invalid HTTP method in request:  " +						  << int(mReqMethod)  << ".  Can't recover." +						  << LL_ENDL;  		break;  	} @@ -592,6 +612,22 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)  	{  		xfer_timeout = timeout;  	} +	if (cpolicy.mPipelining > 1L) +	{ +		// Pipelining affects both connection and transfer timeout values. +		// Requests that are added to a pipeling immediately have completed +		// their connection so the connection delay tends to be less than +		// the non-pipelined value.  Transfers are the opposite.  Transfer +		// timeout starts once the connection is established and completion +		// can be delayed due to the pipelined requests ahead.  So, it's +		// a handwave but bump the transfer timeout up by the pipelining +		// depth to give some room. +		// +		// *TODO:  Find a better scheme than timeouts to guarantee liveness. +		xfer_timeout *= cpolicy.mPipelining; +	} +	// *DEBUG:  Enable following override for timeout handling and "[curl:bugs] #1420" tests +	// xfer_timeout = 1L;  	code = curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, xfer_timeout);  	check_curl_easy_code(code, CURLOPT_TIMEOUT);  	code = curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout); @@ -652,8 +688,8 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void  		{  			// Warn but continue if the read position moves beyond end-of-body  			// for some reason. -			LL_WARNS("CoreHttp") << "Request body position beyond body size.  Truncating request body." -								 << LL_ENDL; +			LL_WARNS(LOG_CORE) << "Request body position beyond body size.  Truncating request body." +							   << LL_ENDL;  		}  		return 0;  	} @@ -790,10 +826,10 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi  		else  		{  			// Ignore the unparsable. -			LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header:  '" -									  << std::string(hdr_data, wanted_hdr_size) -									  << "'.  Ignoring." -									  << LL_ENDL; +			LL_INFOS_ONCE(LOG_CORE) << "Problem parsing odd Content-Range header:  '" +									<< std::string(hdr_data, wanted_hdr_size) +									<< "'.  Ignoring." +									<< LL_ENDL;  		}  	} @@ -895,11 +931,11 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe  	if (logit)  	{ -		LL_INFOS("CoreHttp") << "TRACE, LibcurlDebug, Handle:  " -							 << static_cast<HttpHandle>(op) -							 << ", Type:  " << tag -							 << ", Data:  " << safe_line -							 << LL_ENDL; +		LL_INFOS(LOG_CORE) << "TRACE, LibcurlDebug, Handle:  " +						   << static_cast<HttpHandle>(op) +						   << ", Type:  " << tag +						   << ", Data:  " << safe_line +						   << LL_ENDL;  	}  	return 0; @@ -1094,9 +1130,9 @@ void check_curl_easy_code(CURLcode code, int curl_setopt_option)  		//  		// linux appears to throw a curl error once per session for a bad initialization  		// at a pretty random time (when enabling cookies). -		LL_WARNS("CoreHttp") << "libcurl error detected:  " << curl_easy_strerror(code) -							 << ", curl_easy_setopt option:  " << curl_setopt_option -							 << LL_ENDL; +		LL_WARNS(LOG_CORE) << "libcurl error detected:  " << curl_easy_strerror(code) +						   << ", curl_easy_setopt option:  " << curl_setopt_option +						   << LL_ENDL;  	}  } @@ -1109,8 +1145,8 @@ void check_curl_easy_code(CURLcode code)  		//  		// linux appears to throw a curl error once per session for a bad initialization  		// at a pretty random time (when enabling cookies). -		LL_WARNS("CoreHttp") << "libcurl error detected:  " << curl_easy_strerror(code) -							 << LL_ENDL; +		LL_WARNS(LOG_CORE) << "libcurl error detected:  " << curl_easy_strerror(code) +						   << LL_ENDL;  	}  } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 5cf44f8a82..e5d6321401 100755 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -35,6 +35,13 @@  #include "lltimer.h" +namespace +{ + +static const char * const LOG_CORE("CoreHttp"); + +} // end anonymous namespace +  namespace LLCore  { @@ -51,7 +58,8 @@ public:  	ClassState()  		: mThrottleEnd(0),  		  mThrottleLeft(0L), -		  mRequestCount(0L) +		  mRequestCount(0L), +		  mStallStaging(false)  		{}  	HttpReadyQueue		mReadyQueue; @@ -61,6 +69,7 @@ public:  	HttpTime			mThrottleEnd;  	long				mThrottleLeft;  	long				mRequestCount; +	bool				mStallStaging;  }; @@ -128,7 +137,8 @@ void HttpPolicy::shutdown()  void HttpPolicy::start() -{} +{ +}  void HttpPolicy::addOp(HttpOpRequest * op) @@ -170,19 +180,19 @@ void HttpPolicy::retryOp(HttpOpRequest * op)  	{  		++op->mPolicy503Retries;  	} -	LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op) -						  << " retry " << op->mPolicyRetries -						  << " scheduled in " << (delta / HttpTime(1000)) -						  << " mS (" << (external_delta ? "external" : "internal") -						  << ").  Status:  " << op->mStatus.toTerseString() -						  << LL_ENDL; +	LL_DEBUGS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op) +						<< " retry " << op->mPolicyRetries +						<< " scheduled in " << (delta / HttpTime(1000)) +						<< " mS (" << (external_delta ? "external" : "internal") +						<< ").  Status:  " << op->mStatus.toTerseString() +						<< LL_ENDL;  	if (op->mTracing > HTTP_TRACE_OFF)  	{ -		LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle:  " -							 << static_cast<HttpHandle>(op) -							 << ", Delta:  " << (delta / HttpTime(1000)) -							 << ", Retries:  " << op->mPolicyRetries -							 << LL_ENDL; +		LL_INFOS(LOG_CORE) << "TRACE, ToRetryQueue, Handle:  " +						   << static_cast<HttpHandle>(op) +						   << ", Delta:  " << (delta / HttpTime(1000)) +						   << ", Retries:  " << op->mPolicyRetries +						   << LL_ENDL;  	}  	mClasses[policy_class]->mRetryQueue.push(op);  } @@ -218,6 +228,15 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()  		HttpRetryQueue & retryq(state.mRetryQueue);  		HttpReadyQueue & readyq(state.mReadyQueue); +		if (state.mStallStaging) +		{ +			// Stalling but don't sleep.  Need to complete operations +			// and get back to servicing queues.  Do this test before +			// the retryq/readyq test or you'll get stalls until you +			// click a setting or an asset request comes in. +			result = HttpService::NORMAL; +			continue; +		}  		if (retryq.empty() && readyq.empty())  		{  			continue; @@ -234,7 +253,11 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()  		}  		int active(transport.getActiveCountInClass(policy_class)); -		int needed(state.mOptions.mConnectionLimit - active);		// Expect negatives here +		int active_limit(state.mOptions.mPipelining > 1L +						 ? (state.mOptions.mPerHostConnectionLimit +							* state.mOptions.mPipelining) +						 : state.mOptions.mConnectionLimit); +		int needed(active_limit - active);		// Expect negatives here  		if (needed > 0)  		{ @@ -257,9 +280,9 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()  					if (now >= state.mThrottleEnd)  					{  						// Throttle expired, move to next window -						LL_DEBUGS("CoreHttp") << "Throttle expired with " << state.mThrottleLeft -											  << " requests to go and " << state.mRequestCount -											  << " requests issued." << LL_ENDL; +						LL_DEBUGS(LOG_CORE) << "Throttle expired with " << state.mThrottleLeft +											<< " requests to go and " << state.mRequestCount +											<< " requests issued." << LL_ENDL;  						state.mThrottleLeft = state.mOptions.mThrottleRate;  						state.mThrottleEnd = now + HttpTime(1000000);  					} @@ -286,9 +309,9 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()  					if (now >= state.mThrottleEnd)  					{  						// Throttle expired, move to next window -						LL_DEBUGS("CoreHttp") << "Throttle expired with " << state.mThrottleLeft -											  << " requests to go and " << state.mRequestCount -											  << " requests issued." << LL_ENDL; +						LL_DEBUGS(LOG_CORE) << "Throttle expired with " << state.mThrottleLeft +											<< " requests to go and " << state.mRequestCount +											<< " requests issued." << LL_ENDL;  						state.mThrottleLeft = state.mOptions.mThrottleRate;  						state.mThrottleEnd = now + HttpTime(1000000);  					} @@ -391,6 +414,18 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)  	// Retry or finalize  	if (! op->mStatus)  	{ +		// *DEBUG:  For "[curl:bugs] #1420" tests.  This will interfere +		// with unit tests due to allocation retention by logging code. +		// But you won't be checking this in enabled. +#if 0 +		if (op->mStatus == HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT)) +		{ +			LL_WARNS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op) +							   << " timed out." +							   << LL_ENDL; +		} +#endif +		  		// If this failed, we might want to retry.  		if (op->mPolicyRetries < op->mPolicyRetryLimit && op->mStatus.isRetryable())  		{ @@ -403,17 +438,17 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)  	// This op is done, finalize it delivering it to the reply queue...  	if (! op->mStatus)  	{ -		LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op) -							 << " failed after " << op->mPolicyRetries -							 << " retries.  Reason:  " << op->mStatus.toString() -							 << " (" << op->mStatus.toTerseString() << ")" -							 << LL_ENDL; +		LL_WARNS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op) +						   << " failed after " << op->mPolicyRetries +						   << " retries.  Reason:  " << op->mStatus.toString() +						   << " (" << op->mStatus.toTerseString() << ")" +						   << LL_ENDL;  	}  	else if (op->mPolicyRetries)  	{ -		LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op) -							  << " succeeded on retry " << op->mPolicyRetries << "." -							  << LL_ENDL; +		LL_DEBUGS(LOG_CORE) << "HTTP request " << static_cast<HttpHandle>(op) +							<< " succeeded on retry " << op->mPolicyRetries << "." +							<< LL_ENDL;  	}  	op->stageFromActive(mService); @@ -441,4 +476,17 @@ int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const  } +bool HttpPolicy::stallPolicy(HttpRequest::policy_t policy_class, bool stall) +{ +	bool ret(false); +	 +	if (policy_class < mClasses.size()) +	{ +		ret = mClasses[policy_class]->mStallStaging; +		mClasses[policy_class]->mStallStaging = stall; +	} +	return ret; +} + +  }  // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index bf1aa74267..11cd89bbd1 100755 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -158,6 +158,14 @@ public:  	/// Threading:  called by worker thread  	int getReadyCount(HttpRequest::policy_t policy_class) const; +	/// Stall (or unstall) a policy class preventing requests from +	/// transitioning to an active state.  Used to allow an HTTP +	/// request policy to empty prior to changing settings or state +	/// that isn't tolerant of changes when work is outstanding. +	/// +	/// Threading:  called by worker thread +	bool stallPolicy(HttpRequest::policy_t policy_class, bool stall); +	  protected:  	struct ClassState;  	typedef std::vector<ClassState *>	class_list_t; diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp index f34a8e9f1e..2c0f650155 100755 --- a/indra/llcorehttp/_httppolicyclass.cpp +++ b/indra/llcorehttp/_httppolicyclass.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -78,8 +78,8 @@ HttpStatus HttpPolicyClass::set(HttpRequest::EPolicyOption opt, long value)  		mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit);  		break; -	case HttpRequest::PO_ENABLE_PIPELINING: -		mPipelining = llclamp(value, 0L, 1L); +	case HttpRequest::PO_PIPELINING_DEPTH: +		mPipelining = llclamp(value, 0L, HTTP_PIPELINING_MAX);  		break;  	case HttpRequest::PO_THROTTLE_RATE: @@ -106,7 +106,7 @@ HttpStatus HttpPolicyClass::get(HttpRequest::EPolicyOption opt, long * value) co  		*value = mPerHostConnectionLimit;  		break; -	case HttpRequest::PO_ENABLE_PIPELINING: +	case HttpRequest::PO_PIPELINING_DEPTH:  		*value = mPipelining;  		break; diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index c94249dc2d..c673e1be1d 100755 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -40,6 +40,14 @@  #include "llthread.h" +namespace +{ + +static const char * const LOG_CORE("CoreHttp"); + +} // end anonymous namespace + +  namespace LLCore  { @@ -87,8 +95,8 @@ HttpService::~HttpService()  				// Failed to join, expect problems ahead so do a hard termination.  				mThread->cancel(); -				LL_WARNS("CoreHttp") << "Destroying HttpService with running thread.  Expect problems." -									 << LL_ENDL; +				LL_WARNS(LOG_CORE) << "Destroying HttpService with running thread.  Expect problems." +								   << LL_ENDL;  			}  		}  	} @@ -328,9 +336,9 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)  			if (op->mTracing > HTTP_TRACE_OFF)  			{ -				LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle:  " -									 << static_cast<HttpHandle>(op) -									 << LL_ENDL; +				LL_INFOS(LOG_CORE) << "TRACE, FromRequestQueue, Handle:  " +								   << static_cast<HttpHandle>(op) +								   << LL_ENDL;  			}  			// Stage @@ -437,9 +445,13 @@ HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ  		HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));  		status = opts.set(opt, value); -		if (status && ret_value) +		if (status)  		{ -			status = opts.get(opt, ret_value); +			mTransport->policyUpdated(pclass); +			if (ret_value) +			{ +				status = opts.get(opt, ret_value); +			}  		}  	} @@ -463,7 +475,7 @@ HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ  		return status;  	} -	// Only string values are global at this time +	// String values are always global (at this time).  	if (pclass == HttpRequest::GLOBAL_POLICY_ID)  	{  		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions()); diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 73c49687d7..b76c874557 100755 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *    * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -59,11 +59,13 @@ void usage(std::ostream & out);  // Default command line settings  static int concurrency_limit(40);  static int highwater(100); +static int pipeline_depth(0); +static int tracing(0);  static char url_format[1024] = "http://example.com/some/path?texture_id=%s.texture";  #if defined(WIN32) -#define	strncpy(_a, _b, _c)   strncpy_s(_a, _b, _c) +#define	strncpy(_a, _b, _c)			strncpy_s(_a, _b, _c)  #define strtok_r(_a, _b, _c)		strtok_s(_a, _b, _c)  int getopt(int argc, char * const argv[], const char *optstring); @@ -100,6 +102,7 @@ public:  public:  	bool						mVerbose;  	bool						mRandomRange; +	bool						mNoRange;  	int							mRequestLowWater;  	int							mRequestHighWater;  	handle_set_t				mHandles; @@ -160,10 +163,11 @@ int main(int argc, char** argv)  {  	LLCore::HttpStatus status;  	bool do_random(false); +	bool do_whole(false);  	bool do_verbose(false);  	int option(-1); -	while (-1 != (option = getopt(argc, argv, "u:c:h?RvH:"))) +	while (-1 != (option = getopt(argc, argv, "u:c:h?RwvH:p:t:")))  	{  		switch (option)  		{ @@ -193,7 +197,7 @@ int main(int argc, char** argv)  				char * end;  				value = strtoul(optarg, &end, 10); -				if (value < 1 || value > 100 || *end != '\0') +				if (value < 1 || value > 200 || *end != '\0')  				{  					usage(std::cerr);  					return 1; @@ -202,8 +206,44 @@ int main(int argc, char** argv)  			}  			break; +		case 'p': +		    { +				unsigned long value; +				char * end; + +				value = strtoul(optarg, &end, 10); +				if (value < 0 || value > 100 || *end != '\0') +				{ +					usage(std::cerr); +					return 1; +				} +				pipeline_depth = value; +			} +			break; + +		case '5': +		    { +				unsigned long value; +				char * end; + +				value = strtoul(optarg, &end, 10); +				if (value < 0 || value > 3 || *end != '\0') +				{ +					usage(std::cerr); +					return 1; +				} +				tracing = value; +			} +			break; +  		case 'R':  			do_random = true; +			do_whole = false; +			break; + +		case 'w': +			do_whole = true; +			do_random = false;  			break;  		case 'v': @@ -240,6 +280,24 @@ int main(int argc, char** argv)  											   LLCore::HttpRequest::DEFAULT_POLICY_ID,  											   concurrency_limit,  											   NULL); +	LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_PER_HOST_CONNECTION_LIMIT, +											   LLCore::HttpRequest::DEFAULT_POLICY_ID, +											   concurrency_limit, +											   NULL); +	if (pipeline_depth) +	{ +		LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_PIPELINING_DEPTH, +												   LLCore::HttpRequest::DEFAULT_POLICY_ID, +												   pipeline_depth, +												   NULL); +	} +	if (tracing) +	{ +		LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_TRACE, +												   LLCore::HttpRequest::DEFAULT_POLICY_ID, +												   tracing, +												   NULL); +	}  	LLCore::HttpRequest::startThread();  	// Get service point @@ -257,6 +315,7 @@ int main(int argc, char** argv)  	ws.mUrl = url_format;  	ws.loadAssetUuids(uuids);  	ws.mRandomRange = do_random; +	ws.mNoRange = do_whole;  	ws.mVerbose = do_verbose;  	ws.mRequestHighWater = highwater;  	ws.mRequestLowWater = ws.mRequestHighWater / 2; @@ -331,10 +390,15 @@ void usage(std::ostream & out)  		" -u <url_format>       printf-style format string for URL generation\n"  		"                       Default:  " << url_format << "\n"  		" -R                    Issue GETs with random Range: headers\n" +		" -w                    Issue GETs without Range: headers to get whole object\n"  		" -c <limit>            Maximum connection concurrency.  Range:  [1..100]\n"  		"                       Default:  " << concurrency_limit << "\n"  		" -H <limit>            HTTP request highwater (requests fed to llcorehttp).\n" -		"                       Range:  [1..100]  Default:  " << highwater << "\n" +		"                       Range:  [1..200]  Default:  " << highwater << "\n" +		" -p <depth>            If <depth> is positive, enables and sets pipelineing\n" +		"                       depth on HTTP requests.  Default:  " << pipeline_depth << "\n" +		" -t <level>            If <level> is positive ([1..3]), enables and sets HTTP\n" +		"                       tracing on HTTP requests.  Default:  " << tracing << "\n"  		" -v                    Verbose mode.  Issue some chatter while running\n"  		" -h                    print this help\n"  		"\n" @@ -346,6 +410,7 @@ WorkingSet::WorkingSet()  	: LLCore::HttpHandler(),  	  mVerbose(false),  	  mRandomRange(false), +	  mNoRange(false),  	  mRemaining(200),  	  mLimit(200),  	  mAt(0), @@ -395,8 +460,12 @@ bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt)  #else  		snprintf(buffer, sizeof(buffer), mUrl.c_str(), mAssets[mAt].mUuid.c_str());  #endif -		int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mAssets[mAt].mOffset); -		int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mAssets[mAt].mLength); +		int offset(mNoRange +				   ? 0 +				   : (mRandomRange ? ((unsigned long) rand()) % 1000000UL : mAssets[mAt].mOffset)); +		int length(mNoRange +				   ? 0 +				   : (mRandomRange ? ((unsigned long) rand()) % 1000000UL : mAssets[mAt].mLength));  		LLCore::HttpHandle handle;  		if (offset || length) diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index c2f15155ac..7907e958a4 100755 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -219,6 +219,13 @@ std::string HttpStatus::toTerseString() const  // Pass true on statuses that might actually be cleared by a  // retry.  Library failures, calling problems, etc. aren't  // going to be fixed by squirting bits all over the Net. +// +// HE_INVALID_HTTP_STATUS is special.  As of 7.37.0, there are +// some scenarios where response processing in libcurl appear +// to go wrong and response data is corrupted.  A side-effect +// of this is that the HTTP status is read as 0 from the library. +// See libcurl bug report 1420 (https://sourceforge.net/p/curl/bugs/1420/) +// for details.  bool HttpStatus::isRetryable() const  {  	static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); @@ -231,6 +238,11 @@ bool HttpStatus::isRetryable() const  	static const HttpStatus post_error(HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR);  	static const HttpStatus partial_file(HttpStatus::EXT_CURL_EASY, CURLE_PARTIAL_FILE);  	static const HttpStatus inv_cont_range(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); +	static const HttpStatus inv_status(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS); + +	// *DEBUG:  For "[curl:bugs] #1420" tests. +	// Disable the '*this == inv_status' test and look for 'Core_9' +	// failures in log files.  	return ((isHttpStatus() && mType >= 499 && mType <= 599) ||	// Include special 499 in retryables  			*this == cant_connect ||	// Connection reset/endpoint problems @@ -242,6 +254,8 @@ bool HttpStatus::isRetryable() const  			*this == op_timedout ||		// Timer expired  			*this == post_error ||		// Transport problem  			*this == partial_file ||	// Data inconsistency in response +			// *DEBUG:  Comment out 'inv_status' test for [curl:bugs] #1420 testing. +			*this == inv_status ||		// Inv status can reflect internal state problem in libcurl  			*this == inv_cont_range);	// Short data read disagrees with content-range  } diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 651654844a..7f23723b0b 100755 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -183,11 +183,38 @@ public:  		/// Global only  		PO_TRACE, -		/// Suitable requests are allowed to pipeline on their -		/// connections when they ask for it. +		/// If greater than 1, suitable requests are allowed to +		/// pipeline on their connections when they ask for it. +		/// Value gives the maximum number of outstanding requests +		/// on a connection. +		/// +		/// There is some interaction between PO_CONNECTION_LIMIT, +		/// PO_PER_HOST_CONNECTION_LIMIT, and PO_PIPELINING_DEPTH. +		/// When PIPELINING_DEPTH is 0 or 1 (no pipelining), this +		/// library manages connection lifecycle and honors the +		/// PO_CONNECTION_LIMIT setting as the maximum in-flight +		/// request limit.  Libcurl itself may be caching additional +		/// connections under its connection cache policy. +		/// +		/// When PIPELINING_DEPTH is 2 or more, libcurl performs +		/// connection management and both PO_CONNECTION_LIMIT and +		/// PO_PER_HOST_CONNECTION_LIMIT should be set and non-zero. +		/// In this case (as of libcurl 7.37.0), libcurl will +		/// open new connections in preference to pipelining, up +		/// to the above limits at which time pipelining begins. +		/// And as usual, an additional cache of open but inactive +		/// connections may still be maintained within libcurl. +		/// For SL, a good rule-of-thumb is to set +		/// PO_PER_HOST_CONNECTION_LIMIT to the user-visible +		/// concurrency value and PO_CONNECTION_LIMIT to twice +		/// that for baked texture loads and region crossings where +		/// additional connection load will be tolerated.  If +		/// either limit is 0, libcurl will prefer pipelining +		/// over connection creation, which is still interesting, +		/// but won't be pursued at this time.  		///  		/// Per-class only -		PO_ENABLE_PIPELINING, +		PO_PIPELINING_DEPTH,  		/// Controls whether client-side throttling should be  		/// performed on this policy class.  Positive values diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 8bd134dc84..40eddcb0ab 100755 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -6,6 +6,7 @@ include(00-Common)  include(GoogleMock)  include(LLAddBuildTest)  include(LLCommon) +include(LLCoreHttp)  include(LLMath)  include(LLMessage)  include(LLVFS) @@ -18,6 +19,7 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR})  include_directories(      ${LLCOMMON_INCLUDE_DIRS} +    ${LLCOREHTTP_INCLUDE_DIRS}      ${LLMATH_INCLUDE_DIRS}      ${LLMESSAGE_INCLUDE_DIRS}      ${LLVFS_INCLUDE_DIRS} @@ -36,6 +38,7 @@ set(llmessage_SOURCE_FILES      llchainio.cpp      llcircuit.cpp      llclassifiedflags.cpp +    llcorehttputil.cpp      llcurl.cpp      lldatapacker.cpp      lldispatcher.cpp @@ -124,6 +127,7 @@ set(llmessage_HEADER_FILES      llcipher.h      llcircuit.h      llclassifiedflags.h +    llcorehttputil.h      llcurl.h      lldatapacker.h      lldbstrings.h diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp new file mode 100644 index 0000000000..ee80b0fd94 --- /dev/null +++ b/indra/llmessage/llcorehttputil.cpp @@ -0,0 +1,139 @@ +/**  + * @file llcorehttputil.cpp + * @date 2014-08-25 + * @brief Implementation of adapter and utility classes expanding the llcorehttp interfaces. + * + * $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2014, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include <sstream> + +#include "llcorehttputil.h" +#include "llsdserialize.h" + + +using namespace LLCore; + + +namespace LLCoreHttpUtil +{ + +// *TODO:  Currently converts only from XML content.  A mode +// to convert using fromBinary() might be useful as well.  Mesh +// headers could use it. +bool responseToLLSD(HttpResponse * response, bool log, LLSD & out_llsd) +{ +	// Convert response to LLSD +	BufferArray * body(response->getBody()); +	if (! body || ! body->size()) +	{ +		return false; +	} + +	LLCore::BufferArrayStream bas(body); +	LLSD body_llsd; +	S32 parse_status(LLSDSerialize::fromXML(body_llsd, bas, log)); +	if (LLSDParser::PARSE_FAILURE == parse_status){ +		return false; +	} +	out_llsd = body_llsd; +	return true; +} + + +HttpHandle requestPostWithLLSD(HttpRequest * request, +							   HttpRequest::policy_t policy_id, +							   HttpRequest::priority_t priority, +							   const std::string & url, +							   const LLSD & body, +							   HttpOptions * options, +							   HttpHeaders * headers, +							   HttpHandler * handler) +{ +	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + +	BufferArray * ba = new BufferArray(); +	BufferArrayStream bas(ba); +	LLSDSerialize::toXML(body, bas); + +	handle = request->requestPost(policy_id, +								  priority, +								  url, +								  ba, +								  options, +								  headers, +								  handler); +	ba->release(); +	return handle; +} + + +std::string responseToString(LLCore::HttpResponse * response) +{ +	static const std::string empty("[Empty]"); + +	if (! response) +	{ +		return empty; +	} + +	BufferArray * body(response->getBody()); +	if (! body || ! body->size()) +	{ +		return empty; +	} + +	// Attempt to parse as LLSD regardless of content-type +	LLSD body_llsd; +	if (responseToLLSD(response, false, body_llsd)) +	{ +		std::ostringstream tmp; + +		LLSDSerialize::toPrettyNotation(body_llsd, tmp); +		std::size_t temp_len(tmp.tellp()); +		 +		if (temp_len) +		{ +			return tmp.str().substr(0, std::min(temp_len, std::size_t(1024))); +		} +	} +	else +	{ +		// *TODO:  More elaborate forms based on Content-Type as needed. +		char content[1024]; + +		size_t len(body->read(0, content, sizeof(content))); +		if (len) +		{ +			return std::string(content, 0, len); +		} +	} + +	// Default +	return empty; +} + + +} // end namespace LLCoreHttpUtil + diff --git a/indra/llmessage/llcorehttputil.h b/indra/llmessage/llcorehttputil.h new file mode 100644 index 0000000000..d40172bc7a --- /dev/null +++ b/indra/llmessage/llcorehttputil.h @@ -0,0 +1,115 @@ +/**  + * @file llcorehttputil.h + * @date 2014-08-25 + * @brief Adapter and utility classes expanding the llcorehttp interfaces. + * + * $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2014, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLCOREHTTPUTIL_H +#define LL_LLCOREHTTPUTIL_H + +#include <string> + +#include "httpcommon.h" +#include "httprequest.h" +#include "httpresponse.h" +#include "httpheaders.h" +#include "httpoptions.h" +#include "httphandler.h" +#include "bufferarray.h" +#include "bufferstream.h" +#include "llsd.h" + +/// +/// The base llcorehttp library implements many HTTP idioms +/// used in the viewer but not all.  That library intentionally +/// avoids the use of LLSD and its conventions which aren't +/// universally applicable.  This module, using namespace +/// LLCoreHttpUtil, provides the additional helper functions +/// that support idiomatic LLSD transport via the newer +/// llcorehttp library. +/// +namespace LLCoreHttpUtil +{ + +/// Attempt to convert a response object's contents to LLSD. +/// It is expected that the response body will be of non-zero +/// length on input but basic checks will be performed and +/// and error (false status) returned if there is no data. +/// If there is data but it cannot be successfully parsed, +/// an error is also returned.  If successfully parsed, +/// the output LLSD object, out_llsd, is written with the +/// result and true is returned. +/// +/// @arg	response	Response object as returned in +///						in an HttpHandler onCompleted() callback. +/// @arg	log			If true, LLSD parser will emit errors +///						as LL_INFOS-level messages as it parses. +///						Otherwise, it *should* be a quiet parse. +/// @arg	out_llsd	Output LLSD object written only upon +///						successful parse of the response object. +/// +/// @return				Returns true (and writes to out_llsd) if +///						parse was successful.  False otherwise. +/// +bool responseToLLSD(LLCore::HttpResponse * response, +					bool log, +					LLSD & out_llsd); + +/// Create a std::string representation of a response object +/// suitable for logging.  Mainly intended for logging of +/// failures and debug information.  This won't be fast, +/// just adequate. +std::string responseToString(LLCore::HttpResponse * response); + + +/// Issue a standard HttpRequest::requestPost() call but using +/// and LLSD object as the request body.  Conventions are the +/// same as with that method.  Caller is expected to provide +/// an HttpHeaders object with a correct 'Content-Type:' header. +/// One will not be provided by this call.  You might look after +/// the 'Accept:' header as well. +/// +/// @return				If request is successfully issued, the +///						HttpHandle representing the request. +///						On error, LLCORE_HTTP_HANDLE_INVALID +///						is returned and caller can fetch detailed +///						status with the getStatus() method on the +///						request object.  In case of error, no +///						request is queued and caller may need to +///						perform additional cleanup such as freeing +///						a now-useless HttpHandler object. +/// +LLCore::HttpHandle requestPostWithLLSD(LLCore::HttpRequest * request, +									   LLCore::HttpRequest::policy_t policy_id, +									   LLCore::HttpRequest::priority_t priority, +									   const std::string & url, +									   const LLSD & body, +									   LLCore::HttpOptions * options, +									   LLCore::HttpHeaders * headers, +									   LLCore::HttpHandler * handler); + +} // end namespace LLCoreHttpUtil + + +#endif // LL_LLCOREHTTPUTIL_H diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llmessage/llhttpconstants.cpp index 01f4a080b0..32f76f0d70 100755 --- a/indra/llmessage/llhttpconstants.cpp +++ b/indra/llmessage/llhttpconstants.cpp @@ -3,11 +3,8 @@   * @brief Implementation of the HTTP request / response constant lookups   *   * $LicenseInfo:firstyear=2013&license=viewerlgpl$ - *  - * Copyright (c) 2013, Linden Research, Inc. - *    * Second Life Viewer Source Code - * Copyright (C) 2013, Linden Research, Inc. + * Copyright (C) 2013-2014, Linden Research, Inc.   *    * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -107,6 +104,7 @@ const std::string HTTP_IN_HEADER_X_FORWARDED_FOR("x-forwarded-for");  const std::string HTTP_CONTENT_LLSD_XML("application/llsd+xml");  const std::string HTTP_CONTENT_OCTET_STREAM("application/octet-stream"); +const std::string HTTP_CONTENT_VND_LL_MESH("application/vnd.ll.mesh");  const std::string HTTP_CONTENT_XML("application/xml");  const std::string HTTP_CONTENT_JSON("application/json");  const std::string HTTP_CONTENT_TEXT_HTML("text/html"); diff --git a/indra/llmessage/llhttpconstants.h b/indra/llmessage/llhttpconstants.h index 4aa3cc6394..d6bcbd3c19 100755 --- a/indra/llmessage/llhttpconstants.h +++ b/indra/llmessage/llhttpconstants.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2001&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2001-2013, Linden Research, Inc. + * Copyright (C) 2001-2014, Linden Research, Inc.   *    * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -203,6 +203,7 @@ extern const std::string HTTP_IN_HEADER_X_FORWARDED_FOR;  extern const std::string HTTP_CONTENT_LLSD_XML;  extern const std::string HTTP_CONTENT_OCTET_STREAM; +extern const std::string HTTP_CONTENT_VND_LL_MESH;  extern const std::string HTTP_CONTENT_XML;  extern const std::string HTTP_CONTENT_JSON;  extern const std::string HTTP_CONTENT_TEXT_HTML; diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 7707ee0e29..82a60c0bb1 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -3.7.19 +3.7.20 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 466f52820e..e269b2dd09 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4467,6 +4467,28 @@        <key>Value</key>        <string />      </map> +    <key>HttpPipelining</key> +    <map> +      <key>Comment</key> +      <string>If true, viewer will attempt to pipeline HTTP requests.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>1</integer> +    </map> +    <key>HttpRangeRequestsDisable</key> +    <map> +      <key>Comment</key> +      <string>If true, viewer will not issue GET requests with 'Range:' headers for meshes and textures.  May resolve problems with certain ISPs and networking gear.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>0</integer> +    </map>      <key>IMShowTimestamps</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 70dcffefb2..f5f224b83e 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -40,69 +40,79 @@  // be open at a time.  const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0); +const long LLAppCoreHttp::PIPELINING_DEPTH(5L); + +//  Default and dynamic values for classes  static const struct  { -	LLAppCoreHttp::EAppPolicy	mPolicy;  	U32							mDefault;  	U32							mMin;  	U32							mMax;  	U32							mRate; +	bool						mPipelined;  	std::string					mKey;  	const char *				mUsage; -} init_data[] =					//  Default and dynamic values for classes +} init_data[LLAppCoreHttp::AP_COUNT] =  { -	{ -		LLAppCoreHttp::AP_DEFAULT,			8,		8,		8,		0, +	{ // AP_DEFAULT +		8,		8,		8,		0,		false,  		"",  		"other"  	}, -	{ -		LLAppCoreHttp::AP_TEXTURE,			8,		1,		12,		0, +	{ // AP_TEXTURE +		8,		1,		12,		0,		true,  		"TextureFetchConcurrency",  		"texture fetch"  	}, -	{ -		LLAppCoreHttp::AP_MESH1,			32,		1,		128,	100, +	{ // AP_MESH1 +		32,		1,		128,	0,		false,  		"MeshMaxConcurrentRequests",  		"mesh fetch"  	}, -	{ -		LLAppCoreHttp::AP_MESH2,			8,		1,		32,		100, +	{ // AP_MESH2 +		8,		1,		32,		0,		true,	  		"Mesh2MaxConcurrentRequests",  		"mesh2 fetch"  	}, -	{ -		LLAppCoreHttp::AP_LARGE_MESH,		2,		1,		8,		0, +	{ // AP_LARGE_MESH +		2,		1,		8,		0,		false,  		"",  		"large mesh fetch"  	}, -	{ -		LLAppCoreHttp::AP_UPLOADS,			2,		1,		8,		0, +	{ // AP_UPLOADS  +		2,		1,		8,		0,		false,  		"",  		"asset upload"  	}, -	{ -		LLAppCoreHttp::AP_LONG_POLL,		32,		32,		32,		0, +	{ // AP_LONG_POLL +		32,		32,		32,		0,		false,  		"",  		"long poll" +	}, +	{ // AP_INVENTORY +		4,		1,		4,		0,		false, +		"", +		"inventory"  	}  };  static void setting_changed(); +LLAppCoreHttp::HttpClass::HttpClass() +	: mPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID), +	  mConnLimit(0U), +	  mPipelined(false) +{} + +  LLAppCoreHttp::LLAppCoreHttp()  	: mRequest(NULL),  	  mStopHandle(LLCORE_HTTP_HANDLE_INVALID),  	  mStopRequested(0.0), -	  mStopped(false) -{ -	for (int i(0); i < LL_ARRAY_SIZE(mPolicies); ++i) -	{ -		mPolicies[i] = LLCore::HttpRequest::DEFAULT_POLICY_ID; -		mSettings[i] = 0U; -	} -} +	  mStopped(false), +	  mPipelined(true) +{}  LLAppCoreHttp::~LLAppCoreHttp() @@ -157,27 +167,28 @@ void LLAppCoreHttp::init()  	}  	// Setup default policy and constrain if directed to -	mPolicies[AP_DEFAULT] = LLCore::HttpRequest::DEFAULT_POLICY_ID; +	mHttpClasses[AP_DEFAULT].mPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID;  	// Setup additional policies based on table and some special rules +	llassert(LL_ARRAY_SIZE(init_data) == AP_COUNT);  	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)  	{ -		const EAppPolicy policy(init_data[i].mPolicy); +		const EAppPolicy app_policy(static_cast<EAppPolicy>(i)); -		if (AP_DEFAULT == policy) +		if (AP_DEFAULT == app_policy)  		{  			// Pre-created  			continue;  		} -		mPolicies[policy] = LLCore::HttpRequest::createPolicyClass(); -		if (! mPolicies[policy]) +		mHttpClasses[app_policy].mPolicy = LLCore::HttpRequest::createPolicyClass(); +		if (! mHttpClasses[app_policy].mPolicy)  		{  			// Use default policy (but don't accidentally modify default)  			LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage  							 << ".  Using default policy."  							 << LL_ENDL; -			mPolicies[policy] = mPolicies[AP_DEFAULT]; +			mHttpClasses[app_policy].mPolicy = mHttpClasses[AP_DEFAULT].mPolicy;  			continue;  		}  	} @@ -196,9 +207,27 @@ void LLAppCoreHttp::init()  						<< LL_ENDL;  	} +	// Signal for global pipelining preference from settings +	static const std::string http_pipelining("HttpPipelining"); +	if (gSavedSettings.controlExists(http_pipelining)) +	{ +		LLPointer<LLControlVariable> cntrl_ptr = gSavedSettings.getControl(http_pipelining); +		if (cntrl_ptr.isNull()) +		{ +			LL_WARNS("Init") << "Unable to set signal on global setting '" << http_pipelining +							 << "'" << LL_ENDL; +		} +		else +		{ +			mPipelinedSignal = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed)); +		} +	} +  	// Register signals for settings and state changes  	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)  	{ +		const EAppPolicy app_policy(static_cast<EAppPolicy>(i)); +  		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))  		{  			LLPointer<LLControlVariable> cntrl_ptr = gSavedSettings.getControl(init_data[i].mKey); @@ -209,7 +238,7 @@ void LLAppCoreHttp::init()  			}  			else  			{ -				mSettingsSignal[i] = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed)); +				mHttpClasses[app_policy].mSettingsSignal = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed));  			}  		}  	} @@ -261,10 +290,11 @@ void LLAppCoreHttp::cleanup()  		}  	} -	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) +	for (int i(0); i < LL_ARRAY_SIZE(mHttpClasses); ++i)  	{ -		mSettingsSignal[i].disconnect(); +		mHttpClasses[i].mSettingsSignal.disconnect();  	} +	mPipelinedSignal.disconnect();  	delete mRequest;  	mRequest = NULL; @@ -278,30 +308,84 @@ void LLAppCoreHttp::cleanup()  	}  } +  void LLAppCoreHttp::refreshSettings(bool initial)  {  	LLCore::HttpStatus status; + +	// Global pipelining setting +	bool pipeline_changed(false); +	static const std::string http_pipelining("HttpPipelining"); +	if (gSavedSettings.controlExists(http_pipelining)) +	{ +		// Default to true (in ctor) if absent. +		bool pipelined(gSavedSettings.getBOOL(http_pipelining)); +		if (pipelined != mPipelined) +		{ +			mPipelined = pipelined; +			pipeline_changed = true; +		} +	}  	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)  	{ -		const EAppPolicy policy(init_data[i].mPolicy); +		const EAppPolicy app_policy(static_cast<EAppPolicy>(i)); -		// Set any desired throttle -		if (initial && init_data[i].mRate) +		if (initial)  		{ -			// Init-time only, can use the static setters here -			status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_THROTTLE_RATE, -																mPolicies[policy], -																init_data[i].mRate, -																NULL); -			if (! status) +			// Init-time only settings, can use the static setters here + +			if (init_data[i].mRate)  			{ -				LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage -								 << " throttle rate.  Reason:  " << status.toString() -								 << LL_ENDL; +				// Set any desired throttle +				status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_THROTTLE_RATE, +																	mHttpClasses[app_policy].mPolicy, +																	init_data[i].mRate, +																	NULL); +				if (! status) +				{ +					LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage +									 << " throttle rate.  Reason:  " << status.toString() +									 << LL_ENDL; +				}  			} +  		} +		// Init- or run-time settings.  Must use the queued request API. + +		// Pipelining changes +		if (initial || pipeline_changed) +		{ +			const bool to_pipeline(mPipelined && init_data[i].mPipelined); +			if (to_pipeline != mHttpClasses[app_policy].mPipelined) +			{ +				// Pipeline election changing, set dynamic option via request + +				LLCore::HttpHandle handle; +				const long new_depth(to_pipeline ? PIPELINING_DEPTH : 0); +				 +				handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_PIPELINING_DEPTH, +												   mHttpClasses[app_policy].mPolicy, +												   new_depth, +												   NULL); +				if (LLCORE_HTTP_HANDLE_INVALID == handle) +				{ +					status = mRequest->getStatus(); +					LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage +									 << " pipelining.  Reason:  " << status.toString() +									 << LL_ENDL; +				} +				else +				{ +					LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage +									  << " pipelining.  New value:  " << new_depth +									  << LL_ENDL; +					mHttpClasses[app_policy].mPipelined = to_pipeline; +				} +			} +		} +		  		// Get target connection concurrency value  		U32 setting(init_data[i].mDefault);  		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) @@ -314,38 +398,61 @@ void LLAppCoreHttp::refreshSettings(bool initial)  			}  		} -		if (! initial && setting == mSettings[policy]) +		if (initial || setting != mHttpClasses[app_policy].mConnLimit || pipeline_changed)  		{ -			// Unchanged, try next setting -			continue; -		} -		 -		// Set it and report -		// *TODO:  These are intended to be per-host limits when we can -		// support that in llcorehttp/libcurl. -		LLCore::HttpHandle handle; -		handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT, -										   mPolicies[policy], -										   setting, NULL); -		if (LLCORE_HTTP_HANDLE_INVALID == handle) -		{ -			status = mRequest->getStatus(); -			LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage -							 << " concurrency.  Reason:  " << status.toString() -							 << LL_ENDL; -		} -		else -		{ -			LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage -							  << " concurrency.  New value:  " << setting -							  << LL_ENDL; -			mSettings[policy] = setting; -			if (initial && setting != init_data[i].mDefault) +			// Set it and report.  Strategies depend on pipelining: +			// +			// No Pipelining.  Llcorehttp manages connections itself based +			// on the PO_CONNECTION_LIMIT setting.  Set both limits to the +			// same value for logical consistency.  In the future, may +			// hand over connection management to libcurl after the +			// connection cache has been better vetted. +			// +			// Pipelining.  Libcurl is allowed to manage connections to a +			// great degree.  Steady state will connection limit based on +			// the per-host setting.  Transitions (region crossings, new +			// avatars, etc.) can request additional outbound connections +			// to other servers via 2X total connection limit. +			// +			LLCore::HttpHandle handle; +			handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT, +											   mHttpClasses[app_policy].mPolicy, +											   (mHttpClasses[app_policy].mPipelined ? 2 * setting : setting), +											   NULL); +			if (LLCORE_HTTP_HANDLE_INVALID == handle)  			{ -				LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage -								 << " concurrency.  New value:  " << setting +				status = mRequest->getStatus(); +				LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage +								 << " concurrency.  Reason:  " << status.toString()  								 << LL_ENDL;  			} +			else +			{ +				handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_PER_HOST_CONNECTION_LIMIT, +												   mHttpClasses[app_policy].mPolicy, +												   setting, +												   NULL); +				if (LLCORE_HTTP_HANDLE_INVALID == handle) +				{ +					status = mRequest->getStatus(); +					LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage +									 << " per-host concurrency.  Reason:  " << status.toString() +									 << LL_ENDL; +				} +				else +				{ +					LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage +									  << " concurrency.  New value:  " << setting +									  << LL_ENDL; +					mHttpClasses[app_policy].mConnLimit = setting; +					if (initial && setting != init_data[i].mDefault) +					{ +						LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage +										 << " concurrency.  New value:  " << setting +										 << LL_ENDL; +					} +				} +			}  		}  	}  } diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index 40e3042b84..37d7a737e7 100755 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012-2013, Linden Research, Inc. + * Copyright (C) 2012-2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -41,6 +41,8 @@  class LLAppCoreHttp : public LLCore::HttpHandler  {  public: +	static const long			PIPELINING_DEPTH; +  	typedef LLCore::HttpRequest::policy_t policy_t;  	enum EAppPolicy @@ -62,15 +64,15 @@ public:  		/// Texture fetching policy class.  Used to  		/// download textures via capability or SSA  		/// baking service.  Deep queueing of requests. -		/// Do not share. +		/// Do not share.  GET requests only.  		/// -		/// Destination:     simhost:12046 & bake-texture:80 +		/// Destination:     simhost:12046 & {bake-texture,cdn}:80  		/// Protocol:        http:  		/// Transfer size:   KB-MB  		/// Long poll:       no  		/// Concurrency:     high  		/// Request rate:    high -		/// Pipelined:       soon +		/// Pipelined:       yes  		AP_TEXTURE,  		/// Legacy mesh fetching policy class.  Used to @@ -90,15 +92,16 @@ public:  		/// download textures via 'GetMesh2' capability.  		/// Used when fetch request (typically one LOD)  		/// is 'small', currently defined as 2MB. -		/// Very deeply queued.  Do not share. +		/// Very deeply queued.  Do not share.  GET +		/// requests only.  		/// -		/// Destination:     simhost:12046 +		/// Destination:     simhost:12046 & cdn:80  		/// Protocol:        http:  		/// Transfer size:   KB-MB  		/// Long poll:       no  		/// Concurrency:     high  		/// Request rate:    high -		/// Pipelined:       soon +		/// Pipelined:       yes  		AP_MESH2,  		/// Large mesh fetching policy class.  Used to @@ -110,13 +113,13 @@ public:  		/// traffic that can wait for longish stalls  		/// (default timeout 600S).  		/// -		/// Destination:     simhost:12046 +		/// Destination:     simhost:12046 & cdn:80  		/// Protocol:        http:  		/// Transfer size:   MB  		/// Long poll:       no  		/// Concurrency:     low  		/// Request rate:    low -		/// Pipelined:       soon +		/// Pipelined:       no  		AP_LARGE_MESH,  		/// Asset upload policy class.  Used to store @@ -148,6 +151,20 @@ public:  		/// Pipelined:       no  		AP_LONG_POLL, +		/// Inventory operations (really Capabilities- +		/// related operations).  Mix of high-priority +		/// and low-priority operations. +		/// +		/// Destination:     simhost:12043 +		/// Protocol:        https: +		/// Transfer size:   KB-MB +		/// Long poll:       no +		/// Concurrency:     high +		/// Request rate:    high +		/// Pipelined:       no +		AP_INVENTORY, +		AP_REPORTING = AP_INVENTORY,	// Piggy-back on inventory +		  		AP_COUNT						// Must be last  	}; @@ -180,7 +197,13 @@ public:  	// application function.  	policy_t getPolicy(EAppPolicy policy) const  		{ -			return mPolicies[policy]; +			return mHttpClasses[policy].mPolicy; +		} + +	// Return whether a policy is using pipelined operations. +	bool isPipelined(EAppPolicy policy) const +		{ +			return mHttpClasses[policy].mPipelined;  		}  	// Apply initial or new settings from the environment. @@ -190,13 +213,27 @@ private:  	static const F64			MAX_THREAD_WAIT_TIME;  private: -	LLCore::HttpRequest *		mRequest;						// Request queue to issue shutdowns + +	// PODish container for per-class settings and state. +	struct HttpClass +	{ +	public: +		HttpClass(); + +	public: +		policy_t					mPolicy;			// Policy class id for the class +		U32							mConnLimit; +		bool						mPipelined; +		boost::signals2::connection mSettingsSignal;	// Signal to global setting that affect this class (if any) +	}; +		 +	LLCore::HttpRequest *		mRequest;				// Request queue to issue shutdowns  	LLCore::HttpHandle			mStopHandle;  	F64							mStopRequested;  	bool						mStopped; -	policy_t					mPolicies[AP_COUNT];			// Policy class id for each connection set -	U32							mSettings[AP_COUNT]; -	boost::signals2::connection mSettingsSignal[AP_COUNT];		// Signals to global settings that affect us +	HttpClass					mHttpClasses[AP_COUNT]; +	bool						mPipelined;				// Global setting +	boost::signals2::connection mPipelinedSignal;		// Signal for 'HttpPipelining' setting  }; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 14ca0095ae..f92332dea5 100755 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2002&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2014, Linden Research, Inc.   *    * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -25,6 +25,9 @@   */  #include "llviewerprecompiledheaders.h" + +#include <typeinfo> +  #include "llinventorymodel.h"  #include "llaisapi.h" @@ -50,7 +53,9 @@  #include "llvoavatarself.h"  #include "llgesturemgr.h"  #include "llsdutil.h" -#include <typeinfo> +#include "bufferarray.h" +#include "bufferstream.h" +#include "llcorehttputil.h"  //#define DIFF_INVENTORY_FILES  #ifdef DIFF_INVENTORY_FILES @@ -67,7 +72,8 @@ BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE;  ///----------------------------------------------------------------------------  //BOOL decompress_file(const char* src_filename, const char* dst_filename); -const char CACHE_FORMAT_STRING[] = "%s.inv";  +static const char CACHE_FORMAT_STRING[] = "%s.inv";  +static const char * const LOG_INV("Inventory");  struct InventoryIDPtrLess  { @@ -125,24 +131,32 @@ LLInventoryModel gInventory;  // Default constructor  LLInventoryModel::LLInventoryModel() -:	mModifyMask(LLInventoryObserver::ALL), -	mChangedItemIDs(), +:   // These are now ordered, keep them that way.  	mBacklinkMMap(), +	mIsAgentInvUsable(false), +	mRootFolderID(), +	mLibraryRootFolderID(), +	mLibraryOwnerID(),  	mCategoryMap(),  	mItemMap(), -	mCategoryLock(), -	mItemLock(), -	mLastItem(NULL),  	mParentChildCategoryTree(),  	mParentChildItemTree(), -	mObservers(), -	mRootFolderID(), -	mLibraryRootFolderID(), -	mLibraryOwnerID(), +	mLastItem(NULL),  	mIsNotifyObservers(FALSE), -	mIsAgentInvUsable(false) -{ -} +	mModifyMask(LLInventoryObserver::ALL), +	mChangedItemIDs(), +	mObservers(), +	mHttpRequestFG(NULL), +	mHttpRequestBG(NULL), +	mHttpOptions(NULL), +	mHttpHeaders(NULL), +	mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), +	mHttpPriorityFG(0), +	mHttpPriorityBG(0), +	mCategoryLock(), +	mItemLock() +{} +  // Destroys the object  LLInventoryModel::~LLInventoryModel() @@ -162,6 +176,22 @@ void LLInventoryModel::cleanupInventory()  		delete observer;  	}  	mObservers.clear(); + +	// Run down HTTP transport +	if (mHttpHeaders) +	{ +		mHttpHeaders->release(); +		mHttpHeaders = NULL; +	} +	if (mHttpOptions) +	{ +		mHttpOptions->release(); +		mHttpOptions = NULL; +	} +	delete mHttpRequestFG; +	mHttpRequestFG = NULL; +	delete mHttpRequestBG; +	mHttpRequestBG = NULL;  }  // This is a convenience function to check if one object has a parent @@ -253,7 +283,7 @@ bool LLInventoryModel::getObjectTopmostAncestor(const LLUUID& object_id, LLUUID&  		LLInventoryObject *parent_object = getObject(object->getParentUUID());  		if (!parent_object)  		{ -			LL_WARNS() << "unable to trace topmost ancestor, missing item for uuid " << object->getParentUUID() << LL_ENDL; +			LL_WARNS(LOG_INV) << "unable to trace topmost ancestor, missing item for uuid " << object->getParentUUID() << LL_ENDL;  			return false;  		}  		object = parent_object; @@ -508,7 +538,7 @@ public:  protected:  	virtual void httpFailure()  	{ -		LL_WARNS("InvAPI") << dumpResponse() << LL_ENDL; +		LL_WARNS(LOG_INV) << dumpResponse() << LL_ENDL;  	}  	virtual void httpSuccess() @@ -522,7 +552,7 @@ protected:  		}  		LLUUID category_id = content["folder_id"].asUUID(); -		LL_DEBUGS("Avatar") << ll_pretty_print_sd(content) << LL_ENDL; +		LL_DEBUGS(LOG_INV) << ll_pretty_print_sd(content) << LL_ENDL;  		// Add the category to the internal representation  		LLPointer<LLViewerInventoryCategory> cat =  		new LLViewerInventoryCategory( category_id,  @@ -560,13 +590,13 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,  	LLUUID id;  	if(!isInventoryUsable())  	{ -		LL_WARNS() << "Inventory is broken." << LL_ENDL; +		LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL;  		return id;  	}  	if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup())  	{ -		LL_DEBUGS() << "Attempt to create undefined category." << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "Attempt to create undefined category." << LL_ENDL;  		return id;  	} @@ -599,7 +629,7 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,  		request["message"] = "CreateInventoryCategory";  		request["payload"] = body; -		LL_DEBUGS("Avatar") << "create category request: " << ll_pretty_print_sd(request) << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "create category request: " << ll_pretty_print_sd(request) << LL_ENDL;  		//		viewer_region->getCapAPI().post(request);  		LLHTTPClient::post(  			url, @@ -820,7 +850,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask)  	if(!isInventoryUsable())  	{ -		LL_WARNS() << "Inventory is broken." << LL_ENDL; +		LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL;  		return mask;  	} @@ -883,7 +913,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask)  			}  			else  			{ -				LL_WARNS() << "Couldn't find parent-child item tree for " << new_item->getName() << LL_ENDL; +				LL_WARNS(LOG_INV) << "Couldn't find parent-child item tree for " << new_item->getName() << LL_ENDL;  			}  		}  		else @@ -912,8 +942,8 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask)  			else  			{  				// Whoops! No such parent, make one. -				LL_INFOS() << "Lost item: " << new_item->getUUID() << " - " -						<< new_item->getName() << LL_ENDL; +				LL_INFOS(LOG_INV) << "Lost item: " << new_item->getUUID() << " - " +								  << new_item->getName() << LL_ENDL;  				parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);  				new_item->setParent(parent_id);  				item_array = get_ptr_in_map(mParentChildItemTree, parent_id); @@ -926,7 +956,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask)  				}  				else  				{ -					LL_WARNS() << "Lost and found Not there!!" << LL_ENDL; +					LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL;  				}  			}  		} @@ -1000,7 +1030,7 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32  	if(!isInventoryUsable())  	{ -		LL_WARNS() << "Inventory is broken." << LL_ENDL; +		LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL;  		return;  	} @@ -1062,17 +1092,17 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32  void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id)  { -	LL_DEBUGS() << "LLInventoryModel::moveObject()" << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "LLInventoryModel::moveObject()" << LL_ENDL;  	if(!isInventoryUsable())  	{ -		LL_WARNS() << "Inventory is broken." << LL_ENDL; +		LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL;  		return;  	}  	if((object_id == cat_id) || !is_in_map(mCategoryMap, cat_id))  	{ -		LL_WARNS() << "Could not move inventory object " << object_id << " to " -				<< cat_id << LL_ENDL; +		LL_WARNS(LOG_INV) << "Could not move inventory object " << object_id << " to " +						  << cat_id << LL_ENDL;  		return;  	}  	LLPointer<LLViewerInventoryCategory> cat = getCategory(object_id); @@ -1108,14 +1138,14 @@ void LLInventoryModel::changeItemParent(LLViewerInventoryItem* item,  {  	if (item->getParentUUID() == new_parent_id)  	{ -		LL_DEBUGS("Inventory") << "'" << item->getName() << "' (" << item->getUUID() -							   << ") is already in folder " << new_parent_id << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "'" << item->getName() << "' (" << item->getUUID() +						   << ") is already in folder " << new_parent_id << LL_ENDL;  	}  	else  	{ -		LL_INFOS("Inventory") << "Moving '" << item->getName() << "' (" << item->getUUID() -							  << ") from " << item->getParentUUID() << " to folder " -							  << new_parent_id << LL_ENDL; +		LL_INFOS(LOG_INV) << "Moving '" << item->getName() << "' (" << item->getUUID() +						  << ") from " << item->getParentUUID() << " to folder " +						  << new_parent_id << LL_ENDL;  		LLInventoryModel::update_list_t update;  		LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);  		update.push_back(old_folder); @@ -1171,7 +1201,7 @@ void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLS  	AISUpdate ais_update(update); // parse update llsd into stuff to do.  	ais_update.doUpdate(); // execute the updates in the appropriate order. -	LL_INFOS() << "elapsed: " << timer.getElapsedTimeF32() << LL_ENDL; +	LL_INFOS(LOG_INV) << "elapsed: " << timer.getElapsedTimeF32() << LL_ENDL;  }  // Does not appear to be used currently. @@ -1180,7 +1210,7 @@ void LLInventoryModel::onItemUpdated(const LLUUID& item_id, const LLSD& updates,  	U32 mask = LLInventoryObserver::NONE;  	LLPointer<LLViewerInventoryItem> item = gInventory.getItem(item_id); -	LL_DEBUGS("Inventory") << "item_id: [" << item_id << "] name " << (item ? item->getName() : "(NOT FOUND)") << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (item ? item->getName() : "(NOT FOUND)") << LL_ENDL;  	if(item)  	{  		for (LLSD::map_const_iterator it = updates.beginMap(); @@ -1188,19 +1218,19 @@ void LLInventoryModel::onItemUpdated(const LLUUID& item_id, const LLSD& updates,  		{  			if (it->first == "name")  			{ -				LL_INFOS() << "Updating name from " << item->getName() << " to " << it->second.asString() << LL_ENDL; +				LL_INFOS(LOG_INV) << "Updating name from " << item->getName() << " to " << it->second.asString() << LL_ENDL;  				item->rename(it->second.asString());  				mask |= LLInventoryObserver::LABEL;  			}  			else if (it->first == "desc")  			{ -				LL_INFOS() << "Updating description from " << item->getActualDescription() -						<< " to " << it->second.asString() << LL_ENDL; +				LL_INFOS(LOG_INV) << "Updating description from " << item->getActualDescription() +								  << " to " << it->second.asString() << LL_ENDL;  				item->setDescription(it->second.asString());  			}  			else  			{ -				LL_ERRS() << "unhandled updates for field: " << it->first << LL_ENDL; +				LL_ERRS(LOG_INV) << "unhandled updates for field: " << it->first << LL_ENDL;  			}  		}  		mask |= LLInventoryObserver::INTERNAL; @@ -1211,7 +1241,7 @@ void LLInventoryModel::onItemUpdated(const LLUUID& item_id, const LLSD& updates,  			LLInventoryModel::LLCategoryUpdate up(item->getParentUUID(), 0);  			accountForUpdate(up);  		} -		gInventory.notifyObservers(); // do we want to be able to make this optional? +		notifyObservers(); // do we want to be able to make this optional?  	}  } @@ -1221,7 +1251,7 @@ void LLInventoryModel::onCategoryUpdated(const LLUUID& cat_id, const LLSD& updat  	U32 mask = LLInventoryObserver::NONE;  	LLPointer<LLViewerInventoryCategory> cat = gInventory.getCategory(cat_id); -	LL_DEBUGS("Inventory") << "cat_id: [" << cat_id << "] name " << (cat ? cat->getName() : "(NOT FOUND)") << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] name " << (cat ? cat->getName() : "(NOT FOUND)") << LL_ENDL;  	if(cat)  	{  		for (LLSD::map_const_iterator it = updates.beginMap(); @@ -1229,18 +1259,18 @@ void LLInventoryModel::onCategoryUpdated(const LLUUID& cat_id, const LLSD& updat  		{  			if (it->first == "name")  			{ -				LL_INFOS() << "Updating name from " << cat->getName() << " to " << it->second.asString() << LL_ENDL; +				LL_INFOS(LOG_INV) << "Updating name from " << cat->getName() << " to " << it->second.asString() << LL_ENDL;  				cat->rename(it->second.asString());  				mask |= LLInventoryObserver::LABEL;  			}  			else  			{ -				LL_ERRS() << "unhandled updates for field: " << it->first << LL_ENDL; +				LL_ERRS(LOG_INV) << "unhandled updates for field: " << it->first << LL_ENDL;  			}  		}  		mask |= LLInventoryObserver::INTERNAL;  		addChangedMask(mask, cat->getUUID()); -		gInventory.notifyObservers(); // do we want to be able to make this optional? +		notifyObservers(); // do we want to be able to make this optional?  	}  } @@ -1317,8 +1347,8 @@ void LLInventoryModel::onDescendentsPurgedFromServer(const LLUUID& object_id, bo  		while (deleted_count > 0);  		if (total_deleted_count != count)  		{ -			LL_WARNS() << "Unexpected count of categories deleted, got " -					<< total_deleted_count << " expected " << count << LL_ENDL; +			LL_WARNS(LOG_INV) << "Unexpected count of categories deleted, got " +							  << total_deleted_count << " expected " << count << LL_ENDL;  		}  		//gInventory.validate();  	} @@ -1355,15 +1385,15 @@ void LLInventoryModel::onObjectDeletedFromServer(const LLUUID& object_id, bool f  // Delete a particular inventory object by ID.  void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, bool do_notify_observers)  { -	LL_DEBUGS() << "LLInventoryModel::deleteObject()" << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "LLInventoryModel::deleteObject()" << LL_ENDL;  	LLPointer<LLInventoryObject> obj = getObject(id);  	if (!obj)   	{ -		LL_WARNS() << "Deleting non-existent object [ id: " << id << " ] " << LL_ENDL; +		LL_WARNS(LOG_INV) << "Deleting non-existent object [ id: " << id << " ] " << LL_ENDL;  		return;  	} -	LL_DEBUGS() << "Deleting inventory object " << id << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "Deleting inventory object " << id << LL_ENDL;  	mLastItem = NULL;  	LLUUID parent_id = obj->getParentUUID();  	mCategoryMap.erase(id); @@ -1386,7 +1416,7 @@ void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, boo  	{  		if (item_list->size())  		{ -			LL_WARNS() << "Deleting cat " << id << " while it still has child items" << LL_ENDL; +			LL_WARNS(LOG_INV) << "Deleting cat " << id << " while it still has child items" << LL_ENDL;  		}  		delete item_list;  		mParentChildItemTree.erase(id); @@ -1396,7 +1426,7 @@ void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, boo  	{  		if (cat_list->size())  		{ -			LL_WARNS() << "Deleting cat " << id << " while it still has child cats" << LL_ENDL; +			LL_WARNS(LOG_INV) << "Deleting cat " << id << " while it still has child cats" << LL_ENDL;  		}  		delete cat_list;  		mParentChildCategoryTree.erase(id); @@ -1431,7 +1461,7 @@ void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id)  	// everything else on the changelist will also get rebuilt.  	if (item_array.size() > 0)  	{ -		gInventory.notifyObservers(); +		notifyObservers();  		for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();  			iter != item_array.end();  			iter++) @@ -1441,7 +1471,7 @@ void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id)  			if (item_id == baseobj_id) continue;  			addChangedMask(LLInventoryObserver::REBUILD, item_id);  		} -		gInventory.notifyObservers(); +		notifyObservers();  	}  } @@ -1464,6 +1494,9 @@ BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const  void LLInventoryModel::idleNotifyObservers()  { +	// *FIX:  Think I want this conditional or moved elsewhere... +	handleResponses(true); +	  	if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0))  	{  		return; @@ -1479,7 +1512,7 @@ void LLInventoryModel::notifyObservers()  		// Within notifyObservers, something called notifyObservers  		// again.  This type of recursion is unsafe because it causes items to be   		// processed twice, and this can easily lead to infinite loops. -		LL_WARNS() << "Call was made to notifyObservers within notifyObservers!" << LL_ENDL; +		LL_WARNS(LOG_INV) << "Call was made to notifyObservers within notifyObservers!" << LL_ENDL;  		return;  	} @@ -1509,18 +1542,18 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent)  		// Something marked an item for change within a call to notifyObservers  		// (which is in the process of processing the list of items marked for change).  		// This means the change may fail to be processed. -		LL_WARNS() << "Adding changed mask within notify observers!  Change will likely be lost." << LL_ENDL; +		LL_WARNS(LOG_INV) << "Adding changed mask within notify observers!  Change will likely be lost." << LL_ENDL;  		LLViewerInventoryItem *item = getItem(referent);  		if (item)  		{ -			LL_WARNS() << "Item " << item->getName() << LL_ENDL; +			LL_WARNS(LOG_INV) << "Item " << item->getName() << LL_ENDL;  		}  		else  		{  			LLViewerInventoryCategory *cat = getCategory(referent);  			if (cat)  			{ -				LL_WARNS() << "Category " << cat->getName() << LL_ENDL; +				LL_WARNS(LOG_INV) << "Category " << cat->getName() << LL_ENDL;  			}  		}  	} @@ -1544,91 +1577,18 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent)  	}  } -// If we get back a normal response, handle it here -void LLInventoryModel::fetchInventoryResponder::httpSuccess() -{ -	const LLSD& content = getContent(); -	if (!content.isMap()) -	{ -		failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); -		return; -	} -	start_new_inventory_observer(); - -	/*LLUUID agent_id; -	agent_id = content["agent_id"].asUUID(); -	if(agent_id != gAgent.getID()) -	{ -		LL_WARNS() << "Got a inventory update for the wrong agent: " << agent_id -				<< LL_ENDL; -		return; -	}*/ -	item_array_t items; -	update_map_t update; -	S32 count = content["items"].size(); -	LLUUID folder_id; -	// Does this loop ever execute more than once? -	for(S32 i = 0; i < count; ++i) -	{ -		LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; -		titem->unpackMessage(content["items"][i]); -		 -		LL_DEBUGS() << "LLInventoryModel::fetchInventoryResponder item id: " -					<< titem->getUUID() << LL_ENDL; -		items.push_back(titem); -		// examine update for changes. -		LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID()); -		if(itemp) -		{ -			if(titem->getParentUUID() == itemp->getParentUUID()) -			{ -				update[titem->getParentUUID()]; -			} -			else -			{ -				++update[titem->getParentUUID()]; -				--update[itemp->getParentUUID()]; -			} -		} -		else -		{ -			++update[titem->getParentUUID()]; -		} -		if (folder_id.isNull()) -		{ -			folder_id = titem->getParentUUID(); -		} -	} - -	U32 changes = 0x0; -	//as above, this loop never seems to loop more than once per call -	for (item_array_t::iterator it = items.begin(); it != items.end(); ++it) -	{ -		changes |= gInventory.updateItem(*it); -	} -	gInventory.notifyObservers(); -	gViewerWindow->getWindow()->decBusyCount(); -} - -//If we get back an error (not found, etc...), handle it here -void LLInventoryModel::fetchInventoryResponder::httpFailure() -{ -	LL_WARNS() << dumpResponse() << LL_ENDL; -	gInventory.notifyObservers(); -} -  bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const  {  	if(folder_id.isNull())   	{ -		LL_WARNS() << "Calling fetch descendents on NULL folder id!" << LL_ENDL; +		LL_WARNS(LOG_INV) << "Calling fetch descendents on NULL folder id!" << LL_ENDL;  		return false;  	}  	LLViewerInventoryCategory* cat = getCategory(folder_id);  	if(!cat)  	{ -		LL_WARNS() << "Asked to fetch descendents of non-existent folder: " -				<< folder_id << LL_ENDL; +		LL_WARNS(LOG_INV) << "Asked to fetch descendents of non-existent folder: " +						  << folder_id << LL_ENDL;  		return false;  	}  	//S32 known_descendents = 0; @@ -1649,8 +1609,8 @@ void LLInventoryModel::cache(  	const LLUUID& parent_folder_id,  	const LLUUID& agent_id)  { -	LL_DEBUGS() << "Caching " << parent_folder_id << " for " << agent_id -			 << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "Caching " << parent_folder_id << " for " << agent_id +					   << LL_ENDL;  	LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id);  	if(!root_cat) return;  	cat_array_t categories; @@ -1675,19 +1635,19 @@ void LLInventoryModel::cache(  	gzip_filename.append(".gz");  	if(gzip_file(inventory_filename, gzip_filename))  	{ -		LL_DEBUGS() << "Successfully compressed " << inventory_filename << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "Successfully compressed " << inventory_filename << LL_ENDL;  		LLFile::remove(inventory_filename);  	}  	else  	{ -		LL_WARNS() << "Unable to compress " << inventory_filename << LL_ENDL; +		LL_WARNS(LOG_INV) << "Unable to compress " << inventory_filename << LL_ENDL;  	}  }  void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)  { -	//LL_INFOS() << "LLInventoryModel::addCategory()" << LL_ENDL; +	//LL_INFOS(LOG_INV) << "LLInventoryModel::addCategory()" << LL_ENDL;  	if(category)  	{  		// We aren't displaying the Meshes folder @@ -1757,7 +1717,9 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item)  		if ((item->getType() == LLAssetType::AT_NONE)  		    || LLAssetType::lookup(item->getType()) == LLAssetType::badLookup())  		{ -			LL_WARNS() << "Got bad asset type for item [ name: " << item->getName() << " type: " << item->getType() << " inv-type: " << item->getInventoryType() << " ], ignoring." << LL_ENDL; +			LL_WARNS(LOG_INV) << "Got bad asset type for item [ name: " << item->getName() +							  << " type: " << item->getType() +							  << " inv-type: " << item->getInventoryType() << " ], ignoring." << LL_ENDL;  			return;  		} @@ -1765,7 +1727,9 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item)  		// The item will show up as a broken link.  		if (item->getIsBrokenLink())  		{ -			LL_INFOS() << "Adding broken link [ name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " )  parent: " << item->getParentUUID() << LL_ENDL; +			LL_INFOS(LOG_INV) << "Adding broken link [ name: " << item->getName() +							  << " itemID: " << item->getUUID() +							  << " assetID: " << item->getAssetUUID() << " )  parent: " << item->getParentUUID() << LL_ENDL;  		}  		if (item->getIsLinkType())  		{ @@ -1781,7 +1745,7 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item)  // Empty the entire contents  void LLInventoryModel::empty()  { -//	LL_INFOS() << "LLInventoryModel::empty()" << LL_ENDL; +//	LL_INFOS(LOG_INV) << "LLInventoryModel::empty()" << LL_ENDL;  	std::for_each(  		mParentChildCategoryTree.begin(),  		mParentChildCategoryTree.end(), @@ -1814,29 +1778,29 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const  				descendents_actual += update.mDescendentDelta;  				cat->setDescendentCount(descendents_actual);  				cat->setVersion(++version); -				LL_DEBUGS("Inventory") << "accounted: '" << cat->getName() << "' " -									   << version << " with " << descendents_actual -									   << " descendents." << LL_ENDL; +				LL_DEBUGS(LOG_INV) << "accounted: '" << cat->getName() << "' " +								   << version << " with " << descendents_actual +								   << " descendents." << LL_ENDL;  			}  			else  			{  				// Error condition, this means that the category did not register that  				// it got new descendents (perhaps because it is still being loaded)  				// which means its descendent count will be wrong. -				LL_WARNS() << "Accounting failed for '" << cat->getName() << "' version:" -						   << version << " due to mismatched descendent count:  server == " -						   << descendents_server << ", viewer == " << descendents_actual << LL_ENDL; +				LL_WARNS(LOG_INV) << "Accounting failed for '" << cat->getName() << "' version:" +								  << version << " due to mismatched descendent count:  server == " +								  << descendents_server << ", viewer == " << descendents_actual << LL_ENDL;  			}  		}  		else  		{ -			LL_WARNS() << "Accounting failed for '" << cat->getName() << "' version: unknown ("  -					   << version << ")" << LL_ENDL; +			LL_WARNS(LOG_INV) << "Accounting failed for '" << cat->getName() << "' version: unknown ("  +							  << version << ")" << LL_ENDL;  		}  	}  	else  	{ -		LL_WARNS() << "No category found for update " << update.mCategoryID << LL_ENDL; +		LL_WARNS(LOG_INV) << "No category found for update " << update.mCategoryID << LL_ENDL;  	}  } @@ -1918,7 +1882,7 @@ bool LLInventoryModel::loadSkeleton(  	const LLSD& options,  	const LLUUID& owner_id)  { -	LL_DEBUGS() << "importing inventory skeleton for " << owner_id << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "importing inventory skeleton for " << owner_id << LL_ENDL;  	typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;  	cat_set_t temp_cats; @@ -1955,7 +1919,7 @@ bool LLInventoryModel::loadSkeleton(  		}  		else  		{ -			LL_WARNS() << "Unable to import near " << name.asString() << LL_ENDL; +			LL_WARNS(LOG_INV) << "Unable to import near " << name.asString() << LL_ENDL;              rv = false;  		}  	} @@ -1992,7 +1956,7 @@ bool LLInventoryModel::loadSkeleton(  			}  			else  			{ -				LL_INFOS() << "Unable to gunzip " << gzip_filename << LL_ENDL; +				LL_INFOS(LOG_INV) << "Unable to gunzip " << gzip_filename << LL_ENDL;  			}  		}  		bool is_cache_obsolete = false; @@ -2073,10 +2037,10 @@ bool LLInventoryModel::loadSkeleton(  						if (item->getIsBrokenLink())  						{  							//bad_link_count++; -							LL_DEBUGS() << "Attempted to add cached link item without baseobj present ( name: " -									 << item->getName() << " itemID: " << item->getUUID() -									 << " assetID: " << item->getAssetUUID() -									 << " ).  Ignoring and invalidating " << cat->getName() << " . " << LL_ENDL; +							LL_DEBUGS(LOG_INV) << "Attempted to add cached link item without baseobj present ( name: " +											   << item->getName() << " itemID: " << item->getUUID() +											   << " assetID: " << item->getAssetUUID() +											   << " ).  Ignoring and invalidating " << cat->getName() << " . " << LL_ENDL;  							possible_broken_links.push_back(item);  							continue;  						} @@ -2103,7 +2067,7 @@ bool LLInventoryModel::loadSkeleton(  					{  						bad_link_count++;  						invalid_categories.insert(cit->second); -						//LL_INFOS() << "link still broken: " << item->getName() << " in folder " << cat->getName() << LL_ENDL; +						//LL_INFOS(LOG_INV) << "link still broken: " << item->getName() << " in folder " << cat->getName() << LL_ENDL;  					}  					else  					{ @@ -2115,11 +2079,11 @@ bool LLInventoryModel::loadSkeleton(  					}  				} - 				LL_INFOS() << "Attempted to add " << bad_link_count - 						<< " cached link items without baseobj present. " -					    << good_link_count << " link items were successfully added. " -					    << recovered_link_count << " links added in recovery. " - 						<< "The corresponding categories were invalidated." << LL_ENDL; + 				LL_INFOS(LOG_INV) << "Attempted to add " << bad_link_count +								  << " cached link items without baseobj present. " +								  << good_link_count << " link items were successfully added. " +								  << recovered_link_count << " links added in recovery. " +								  << "The corresponding categories were invalidated." << LL_ENDL;  			}  		} @@ -2143,9 +2107,9 @@ bool LLInventoryModel::loadSkeleton(  		{  			LLViewerInventoryCategory* cat = (*invalid_cat_it).get();  			cat->setVersion(NO_VERSION); -			LL_DEBUGS("Inventory") << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << LL_ENDL; +			LL_DEBUGS(LOG_INV) << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << LL_ENDL;  		} -		LL_INFOS("Inventory") << "Invalidated " << invalid_categories.size() << " categories due to invalid descendents cache" << LL_ENDL; +		LL_INFOS(LOG_INV) << "Invalidated " << invalid_categories.size() << " categories due to invalid descendents cache" << LL_ENDL;  		// At this point, we need to set the known descendents for each  		// category which successfully cached so that we do not @@ -2177,15 +2141,15 @@ bool LLInventoryModel::loadSkeleton(  		if(is_cache_obsolete)  		{  			// If out of date, remove the gzipped file too. -			LL_WARNS() << "Inv cache out of date, removing" << LL_ENDL; +			LL_WARNS(LOG_INV) << "Inv cache out of date, removing" << LL_ENDL;  			LLFile::remove(gzip_filename);  		}  		categories.clear(); // will unref and delete entries  	} -	LL_INFOS() << "Successfully loaded " << cached_category_count -			<< " categories and " << cached_item_count << " items from cache." -			<< LL_ENDL; +	LL_INFOS(LOG_INV) << "Successfully loaded " << cached_category_count +					  << " categories and " << cached_item_count << " items from cache." +					  << LL_ENDL;  	return rv;  } @@ -2195,7 +2159,7 @@ bool LLInventoryModel::loadSkeleton(  // should be sufficient for our needs.   void LLInventoryModel::buildParentChildMap()  { -	LL_INFOS() << "LLInventoryModel::buildParentChildMap()" << LL_ENDL; +	LL_INFOS(LOG_INV) << "LLInventoryModel::buildParentChildMap()" << LL_ENDL;  	// *NOTE: I am skipping the logic around folder version  	// synchronization here because it seems if a folder is lost, we @@ -2264,15 +2228,15 @@ void LLInventoryModel::buildParentChildMap()  			// implement it, we would need a set or map of uuid pairs  			// which would be (folder_id, new_parent_id) to be sent up  			// to the server. -			LL_INFOS() << "Lost category: " << cat->getUUID() << " - " -					   << cat->getName() << LL_ENDL; +			LL_INFOS(LOG_INV) << "Lost category: " << cat->getUUID() << " - " +							  << cat->getName() << LL_ENDL;  			++lost;  			lost_cats.push_back(cat);  		}  	}  	if(lost)  	{ -		LL_WARNS() << "Found  " << lost << " lost categories." << LL_ENDL; +		LL_WARNS(LOG_INV) << "Found  " << lost << " lost categories." << LL_ENDL;  	}  	// Do moves in a separate pass to make sure we've properly filed @@ -2307,7 +2271,7 @@ void LLInventoryModel::buildParentChildMap()  		}  		else  		{		 -			LL_WARNS() << "Lost and found Not there!!" << LL_ENDL; +			LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL;  		}  	} @@ -2342,8 +2306,8 @@ void LLInventoryModel::buildParentChildMap()  		}  		else  		{ -			LL_INFOS() << "Lost item: " << item->getUUID() << " - " -					   << item->getName() << LL_ENDL; +			LL_INFOS(LOG_INV) << "Lost item: " << item->getUUID() << " - " +							  << item->getName() << LL_ENDL;  			++lost;  			// plop it into the lost & found.  			// @@ -2359,13 +2323,13 @@ void LLInventoryModel::buildParentChildMap()  			}  			else  			{ -				LL_WARNS() << "Lost and found Not there!!" << LL_ENDL; +				LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL;  			}  		}  	}  	if(lost)  	{ -		LL_WARNS() << "Found " << lost << " lost items." << LL_ENDL; +		LL_WARNS(LOG_INV) << "Found " << lost << " lost items." << LL_ENDL;  		LLMessageSystem* msg = gMessageSystem;  		BOOL start_new_message = TRUE;  		const LLUUID lnf = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); @@ -2443,10 +2407,82 @@ void LLInventoryModel::buildParentChildMap()  	if (!gInventory.validate())  	{ -	 	LL_WARNS() << "model failed validity check!" << LL_ENDL; +	 	LL_WARNS(LOG_INV) << "model failed validity check!" << LL_ENDL;  	}  } +// Would normally do this at construction but that's too early +// in the process for gInventory.  Have the first requestPost() +// call set things up. +void LLInventoryModel::initHttpRequest() +{ +	if (! mHttpRequestFG) +	{ +		// Haven't initialized, get to it +		LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); + +		mHttpRequestFG = new LLCore::HttpRequest; +		mHttpRequestBG = new LLCore::HttpRequest; +		mHttpOptions = new LLCore::HttpOptions; +		mHttpOptions->setTransferTimeout(300); +		mHttpOptions->setUseRetryAfter(true); +		// mHttpOptions->setTrace(2);		// Do tracing of requests +		mHttpHeaders = new LLCore::HttpHeaders; +		mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); +		mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML); +		mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_INVENTORY); +	} +} + +void LLInventoryModel::handleResponses(bool foreground) +{ +	if (foreground && mHttpRequestFG) +	{ +		mHttpRequestFG->update(0); +	} +	else if (! foreground && mHttpRequestBG) +	{ +		mHttpRequestBG->update(50000L); +	} +} + +LLCore::HttpHandle LLInventoryModel::requestPost(bool foreground, +												 const std::string & url, +												 const LLSD & body, +												 LLCore::HttpHandler * handler, +												 const char * const message) +{ +	if (! mHttpRequestFG) +	{ +		// We do the initialization late and lazily as this class is +		// statically-constructed and not all the bits are ready at +		// that time. +		initHttpRequest(); +	} +	 +	LLCore::HttpRequest * request(foreground ? mHttpRequestFG : mHttpRequestBG); +	LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); +		 +	handle = LLCoreHttpUtil::requestPostWithLLSD(request, +												 mHttpPolicyClass, +												 (foreground ? mHttpPriorityFG : mHttpPriorityBG), +												 url, +												 body, +												 mHttpOptions, +												 mHttpHeaders, +												 handler); +	if (LLCORE_HTTP_HANDLE_INVALID == handle) +	{ +		LLCore::HttpStatus status(request->getStatus()); +		LL_WARNS(LOG_INV) << "HTTP POST request failed for " << message +						  << ", Status: " << status.toTerseString() +						  << " Reason: '" << status.toString() << "'" +						  << LL_ENDL; +		delete handler; +	} +	return handle; +} +  void LLInventoryModel::createCommonSystemCategories()  {  	gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH,true); @@ -2495,14 +2531,14 @@ bool LLInventoryModel::loadFromFile(const std::string& filename,  {  	if(filename.empty())  	{ -		LL_ERRS() << "Filename is Null!" << LL_ENDL; +		LL_ERRS(LOG_INV) << "Filename is Null!" << LL_ENDL;  		return false;  	} -	LL_INFOS() << "LLInventoryModel::loadFromFile(" << filename << ")" << LL_ENDL; +	LL_INFOS(LOG_INV) << "LLInventoryModel::loadFromFile(" << filename << ")" << LL_ENDL;  	LLFILE* file = LLFile::fopen(filename, "rb");		/*Flawfinder: ignore*/  	if(!file)  	{ -		LL_INFOS() << "unable to load inventory from: " << filename << LL_ENDL; +		LL_INFOS(LOG_INV) << "unable to load inventory from: " << filename << LL_ENDL;  		return false;  	}  	// *NOTE: This buffer size is hard coded into scanf() below. @@ -2541,7 +2577,7 @@ bool LLInventoryModel::loadFromFile(const std::string& filename,  			}  			else  			{ -				LL_WARNS() << "loadInventoryFromFile().  Ignoring invalid inventory category: " << inv_cat->getName() << LL_ENDL; +				LL_WARNS(LOG_INV) << "loadInventoryFromFile().  Ignoring invalid inventory category: " << inv_cat->getName() << LL_ENDL;  				//delete inv_cat; // automatic when inv_cat is reassigned or destroyed  			}  		} @@ -2559,8 +2595,8 @@ bool LLInventoryModel::loadFromFile(const std::string& filename,  				if(inv_item->getUUID().isNull())  				{  					//delete inv_item; // automatic when inv_cat is reassigned or destroyed -					LL_WARNS() << "Ignoring inventory with null item id: " -							<< inv_item->getName() << LL_ENDL; +					LL_WARNS(LOG_INV) << "Ignoring inventory with null item id: " +									  << inv_item->getName() << LL_ENDL;  				}  				else @@ -2570,14 +2606,14 @@ bool LLInventoryModel::loadFromFile(const std::string& filename,  			}  			else  			{ -				LL_WARNS() << "loadInventoryFromFile().  Ignoring invalid inventory item: " << inv_item->getName() << LL_ENDL; +				LL_WARNS(LOG_INV) << "loadInventoryFromFile().  Ignoring invalid inventory item: " << inv_item->getName() << LL_ENDL;  				//delete inv_item; // automatic when inv_cat is reassigned or destroyed  			}  		}  		else  		{ -			LL_WARNS() << "Unknown token in inventory file '" << keyword << "'" -					<< LL_ENDL; +			LL_WARNS(LOG_INV) << "Unknown token in inventory file '" << keyword << "'" +							  << LL_ENDL;  		}  	}  	fclose(file); @@ -2593,14 +2629,14 @@ bool LLInventoryModel::saveToFile(const std::string& filename,  {  	if(filename.empty())  	{ -		LL_ERRS() << "Filename is Null!" << LL_ENDL; +		LL_ERRS(LOG_INV) << "Filename is Null!" << LL_ENDL;  		return false;  	} -	LL_INFOS() << "LLInventoryModel::saveToFile(" << filename << ")" << LL_ENDL; +	LL_INFOS(LOG_INV) << "LLInventoryModel::saveToFile(" << filename << ")" << LL_ENDL;  	LLFILE* file = LLFile::fopen(filename, "wb");		/*Flawfinder: ignore*/  	if(!file)  	{ -		LL_WARNS() << "unable to save inventory to: " << filename << LL_ENDL; +		LL_WARNS(LOG_INV) << "unable to save inventory to: " << filename << LL_ENDL;  		return false;  	} @@ -2705,8 +2741,8 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, U32  	msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);  	if(agent_id != gAgent.getID())  	{ -		LL_WARNS() << "Got a inventory update for the wrong agent: " << agent_id -				<< LL_ENDL; +		LL_WARNS(LOG_INV) << "Got a inventory update for the wrong agent: " << agent_id +						  << LL_ENDL;  		return false;  	}  	item_array_t items; @@ -2718,8 +2754,8 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, U32  	{  		LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;  		titem->unpackMessage(msg, _PREHASH_InventoryData, i); -		LL_DEBUGS() << "LLInventoryModel::messageUpdateCore() item id:" -				 << titem->getUUID() << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "LLInventoryModel::messageUpdateCore() item id: " +						   << titem->getUUID() << LL_ENDL;  		items.push_back(titem);  		// examine update for changes.  		LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID()); @@ -2770,17 +2806,17 @@ void LLInventoryModel::removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg  {  	LLUUID item_id;  	S32 count = msg->getNumberOfBlocksFast(msg_label); -	LL_DEBUGS() << "Message has " << count << " item blocks" << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "Message has " << count << " item blocks" << LL_ENDL;  	uuid_vec_t item_ids;  	update_map_t update;  	for(S32 i = 0; i < count; ++i)  	{  		msg->getUUIDFast(msg_label, _PREHASH_ItemID, item_id, i); -		LL_DEBUGS() << "Checking for item-to-be-removed " << item_id << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "Checking for item-to-be-removed " << item_id << LL_ENDL;  		LLViewerInventoryItem* itemp = gInventory.getItem(item_id);  		if(itemp)  		{ -		LL_DEBUGS() << "Item will be removed " << item_id << LL_ENDL; +			LL_DEBUGS(LOG_INV) << "Item will be removed " << item_id << LL_ENDL;  			// we only bother with the delete and account if we found  			// the item - this is usually a back-up for permissions,  			// so frequently the item will already be gone. @@ -2791,7 +2827,7 @@ void LLInventoryModel::removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg  	gInventory.accountForUpdate(update);  	for(uuid_vec_t::iterator it = item_ids.begin(); it != item_ids.end(); ++it)  	{ -		LL_DEBUGS() << "Calling deleteObject " << *it << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "Calling deleteObject " << *it << LL_ENDL;  		gInventory.deleteObject(*it);  	}  } @@ -2799,13 +2835,13 @@ void LLInventoryModel::removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg  // 	static  void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)  { -	LL_DEBUGS() << "LLInventoryModel::processRemoveInventoryItem()" << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "LLInventoryModel::processRemoveInventoryItem()" << LL_ENDL;  	LLUUID agent_id, item_id;  	msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);  	if(agent_id != gAgent.getID())  	{ -		LL_WARNS() << "Got a RemoveInventoryItem for the wrong agent." -				<< LL_ENDL; +		LL_WARNS(LOG_INV) << "Got a RemoveInventoryItem for the wrong agent." +						  << LL_ENDL;  		return;  	}  	LLInventoryModel::removeInventoryItem(agent_id, msg, _PREHASH_InventoryData); @@ -2816,14 +2852,14 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)  void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg,  													void**)  { -	LL_DEBUGS() << "LLInventoryModel::processUpdateInventoryFolder()" << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "LLInventoryModel::processUpdateInventoryFolder()" << LL_ENDL;  	LLUUID agent_id, folder_id, parent_id;  	//char name[DB_INV_ITEM_NAME_BUF_SIZE];  	msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);  	if(agent_id != gAgent.getID())  	{ -		LL_WARNS() << "Got an UpdateInventoryFolder for the wrong agent." -				<< LL_ENDL; +		LL_WARNS(LOG_INV) << "Got an UpdateInventoryFolder for the wrong agent." +						  << LL_ENDL;  		return;  	}  	LLPointer<LLViewerInventoryCategory> lastfolder; // hack @@ -3881,7 +3917,7 @@ bool LLInventoryModel::validate() const  ///---------------------------------------------------------------------------- -/* +#if 0  BOOL decompress_file(const char* src_filename, const char* dst_filename)  {  	BOOL rv = FALSE; @@ -3920,4 +3956,177 @@ BOOL decompress_file(const char* src_filename, const char* dst_filename)  	if(dst != NULL) fclose(dst);  	return rv;  } -*/ +#endif + + +///---------------------------------------------------------------------------- +/// Class LLInventoryModel::FetchItemHttpHandler +///---------------------------------------------------------------------------- + +LLInventoryModel::FetchItemHttpHandler::FetchItemHttpHandler(const LLSD & request_sd) +	: LLCore::HttpHandler(), +	  mRequestSD(request_sd) +{} + +LLInventoryModel::FetchItemHttpHandler::~FetchItemHttpHandler() +{} + +void LLInventoryModel::FetchItemHttpHandler::onCompleted(LLCore::HttpHandle handle, +														 LLCore::HttpResponse * response) +{ +	do		// Single-pass do-while used for common exit handling +	{ +		LLCore::HttpStatus status(response->getStatus()); +		// status = LLCore::HttpStatus(404);				// Dev tool to force error handling +		if (! status) +		{ +			processFailure(status, response); +			break;			// Goto common exit +		} + +		LLCore::BufferArray * body(response->getBody()); +		// body = NULL;									// Dev tool to force error handling +		if (! body || ! body->size()) +		{ +			LL_WARNS(LOG_INV) << "Missing data in inventory item query." << LL_ENDL; +			processFailure("HTTP response for inventory item query missing body", response); +			break;			// Goto common exit +		} + +		// body->write(0, "Garbage Response", 16);		// Dev tool to force error handling +		LLSD body_llsd; +		if (! LLCoreHttpUtil::responseToLLSD(response, true, body_llsd)) +		{ +			// INFOS-level logging will occur on the parsed failure +			processFailure("HTTP response for inventory item query has malformed LLSD", response); +			break;			// Goto common exit +		} + +		// Expect top-level structure to be a map +		// body_llsd = LLSD::emptyArray();				// Dev tool to force error handling +		if (! body_llsd.isMap()) +		{ +			processFailure("LLSD response for inventory item not a map", response); +			break;			// Goto common exit +		} + +		// Check for 200-with-error failures +		// +		// Original Responder-based serivce model didn't check for these errors. +		// It may be more robust to ignore this condition.  With aggregated requests, +		// an error in one inventory item might take down the entire request. +		// So if this instead broke up the aggregated items into single requests, +		// maybe that would make progress.  Or perhaps there's structured information +		// that can tell us what went wrong.  Need to dig into this and firm up +		// the API. +		// +		// body_llsd["error"] = LLSD::emptyMap();		// Dev tool to force error handling +		// body_llsd["error"]["identifier"] = "Development"; +		// body_llsd["error"]["message"] = "You left development code in the viewer"; +		if (body_llsd.has("error")) +		{ +			processFailure("Inventory application error (200-with-error)", response); +			break;			// Goto common exit +		} + +		// Okay, process data if possible +		processData(body_llsd, response); +	} +	while (false); + +	// Must delete on completion. +	delete this; +} + +void LLInventoryModel::FetchItemHttpHandler::processData(LLSD & content, LLCore::HttpResponse * response) +{ +	start_new_inventory_observer(); + +#if 0 +	LLUUID agent_id; +	agent_id = content["agent_id"].asUUID(); +	if (agent_id != gAgent.getID()) +	{ +		LL_WARNS(LOG_INV) << "Got a inventory update for the wrong agent: " << agent_id +						  << LL_ENDL; +		return; +	} +#endif +	 +	LLInventoryModel::item_array_t items; +	LLInventoryModel::update_map_t update; +	LLUUID folder_id; +	LLSD content_items(content["items"]); +	const S32 count(content_items.size()); + +	// Does this loop ever execute more than once? +	for (S32 i(0); i < count; ++i) +	{ +		LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; +		titem->unpackMessage(content_items[i]); +		 +		LL_DEBUGS(LOG_INV) << "ItemHttpHandler::httpSuccess item id: " +						   << titem->getUUID() << LL_ENDL; +		items.push_back(titem); +		 +		// examine update for changes. +		LLViewerInventoryItem * itemp(gInventory.getItem(titem->getUUID())); + +		if (itemp) +		{ +			if (titem->getParentUUID() == itemp->getParentUUID()) +			{ +				update[titem->getParentUUID()]; +			} +			else +			{ +				++update[titem->getParentUUID()]; +				--update[itemp->getParentUUID()]; +			} +		} +		else +		{ +			++update[titem->getParentUUID()]; +		} +		 +		if (folder_id.isNull()) +		{ +			folder_id = titem->getParentUUID(); +		} +	} + +	// as above, this loop never seems to loop more than once per call +	U32 changes(0U); +	for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it) +	{ +		changes |= gInventory.updateItem(*it); +	} +	// *HUH:  Have computed 'changes', nothing uses it. +	 +	gInventory.notifyObservers(); +	gViewerWindow->getWindow()->decBusyCount(); +} + + +void LLInventoryModel::FetchItemHttpHandler::processFailure(LLCore::HttpStatus status, LLCore::HttpResponse * response) +{ +	const std::string & ct(response->getContentType()); +	LL_WARNS(LOG_INV) << "Inventory item fetch failure\n" +					  << "[Status: " << status.toTerseString() << "]\n" +					  << "[Reason: " << status.toString() << "]\n" +					  << "[Content-type: " << ct << "]\n" +					  << "[Content (abridged): " +					  << LLCoreHttpUtil::responseToString(response) << "]" << LL_ENDL; +	gInventory.notifyObservers(); +} + +void LLInventoryModel::FetchItemHttpHandler::processFailure(const char * const reason, LLCore::HttpResponse * response) +{ +	LL_WARNS(LOG_INV) << "Inventory item fetch failure\n" +					  << "[Status: internal error]\n" +					  << "[Reason: " << reason << "]\n" +					  << "[Content (abridged): " +					  << LLCoreHttpUtil::responseToString(response) << "]" << LL_ENDL; +	gInventory.notifyObservers(); +} + diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 2e957809be..ac336e347c 100755 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -27,6 +27,11 @@  #ifndef LL_LLINVENTORYMODEL_H  #define LL_LLINVENTORYMODEL_H +#include <map> +#include <set> +#include <string> +#include <vector> +  #include "llassettype.h"  #include "llfoldertype.h"  #include "llframetimer.h" @@ -36,10 +41,11 @@  #include "llviewerinventory.h"  #include "llstring.h"  #include "llmd5.h" -#include <map> -#include <set> -#include <string> -#include <vector> +#include "httpcommon.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" +#include "httphandler.h"  class LLInventoryObserver;  class LLInventoryObject; @@ -60,9 +66,8 @@ class LLInventoryCollectFunctor;  class LLInventoryModel  {  	LOG_CLASS(LLInventoryModel); -public: -	friend class LLInventoryModelFetchDescendentsResponder; +public:  	enum EHasChildren  	{  		CHILDREN_NO, @@ -74,14 +79,31 @@ public:  	typedef std::vector<LLPointer<LLViewerInventoryItem> > item_array_t;  	typedef std::set<LLUUID> changed_items_t; -	class fetchInventoryResponder : public LLCurl::Responder +	// HTTP handler for individual item requests (inventory or library). +	// Background item requests are derived from this in the background +	// inventory system.  All folder requests are also located there +	// but have their own handler derived from HttpHandler. +	class FetchItemHttpHandler : public LLCore::HttpHandler  	{ -		LOG_CLASS(fetchInventoryResponder);  	public: -		fetchInventoryResponder(const LLSD& request_sd) : mRequestSD(request_sd) {}; +		LOG_CLASS(FetchItemHttpHandler); + +		FetchItemHttpHandler(const LLSD & request_sd); +		virtual ~FetchItemHttpHandler(); +  	protected: -		virtual void httpSuccess(); -		virtual void httpFailure(); +		FetchItemHttpHandler(const FetchItemHttpHandler &);				// Not defined +		void operator=(const FetchItemHttpHandler &);					// Not defined + +	public: +		virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + +	private: +		void processData(LLSD & body, LLCore::HttpResponse * response); +		void processFailure(LLCore::HttpStatus status, LLCore::HttpResponse * response); +		void processFailure(const char * const reason, LLCore::HttpResponse * response); + +	private:  		LLSD mRequestSD;  	}; @@ -109,6 +131,9 @@ public:  private:  	bool mIsAgentInvUsable; // used to handle an invalid inventory state +	// One-time initialization of HTTP system. +	void initHttpRequest(); +	  	//--------------------------------------------------------------------  	// Root Folders  	//-------------------------------------------------------------------- @@ -520,6 +545,41 @@ private:  /********************************************************************************   **                                                                            ** + **                    HTTP Transport + **/ +public: +	// Invoke handler completion method (onCompleted) for all +	// requests that are ready. +	void handleResponses(bool foreground); + +	// Request an inventory HTTP operation to either the +	// foreground or background processor.  These are actually +	// the same service queue but the background requests are +	// seviced more slowly effectively de-prioritizing new +	// requests. +	LLCore::HttpHandle requestPost(bool foreground, +								   const std::string & url, +								   const LLSD & body, +								   LLCore::HttpHandler * handler, +								   const char * const message); +	 +private: +	// Usual plumbing for LLCore:: HTTP operations. +	LLCore::HttpRequest *				mHttpRequestFG; +	LLCore::HttpRequest *				mHttpRequestBG; +	LLCore::HttpOptions *				mHttpOptions; +	LLCore::HttpHeaders *				mHttpHeaders; +	LLCore::HttpRequest::policy_t		mHttpPolicyClass; +	LLCore::HttpRequest::priority_t		mHttpPriorityFG; +	LLCore::HttpRequest::priority_t		mHttpPriorityBG; +	 +/**                    HTTP Transport + **                                                                            ** + *******************************************************************************/ + + +/******************************************************************************** + **                                                                            **   **                    MISCELLANEOUS   **/ diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 2de37b0790..f18832fe95 100755 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2002&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2014, Linden Research, Inc.   *    * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -37,31 +37,180 @@  #include "llviewermessage.h"  #include "llviewerregion.h"  #include "llviewerwindow.h" +#include "llhttpconstants.h" +#include "bufferarray.h" +#include "bufferstream.h" +#include "llcorehttputil.h" + +// History (may be apocryphal) +// +// Around V2, an HTTP inventory download mechanism was added +// along with inventory LINK items referencing other inventory +// items.  As part of this, at login, the entire inventory +// structure is downloaded 'in the background' using the +// backgroundFetch()/bulkFetch() methods.  The UDP path can +// still be used and is found in the 'DEPRECATED OLD CODE' +// section. +// +// The old UDP path implemented a throttle that adapted +// itself during running.  The mechanism survived info HTTP +// somewhat but was pinned to poll the HTTP plumbing at +// 0.5S intervals.  The reasons for this particular value +// have been lost.  It's possible to switch between UDP +// and HTTP while this is happening but there may be +// surprises in what happens in that case. +// +// Conversion to llcorehttp reduced the number of connections +// used but batches more data and queues more requests (but +// doesn't due pipelining due to libcurl restrictions).  The +// poll interval above was re-examined and reduced to get +// inventory into the viewer more quickly. +// +// Possible future work: +// +// * Don't download the entire heirarchy in one go (which +//   might have been how V1 worked).  Implications for +//   links (which may not have a valid target) and search +//   which would then be missing data. +// +// * Review the download rate throttling.  Slow then fast? +//   Detect bandwidth usage and speed up when it drops? +// +// * A lot of calls to notifyObservers().  It looks like +//   these could be collapsed by maintaining a 'dirty' +//   bit and there appears to be an attempt to do this. +//   But it isn't used or is used in a limited fashion. +//   Are there semanic issues requiring a call after certain +//   updateItem() calls? +// +// * An error on a fetch could be due to one item in the batch. +//   If the batch were broken up, perhaps more of the inventory +//   would download.  (Handwave here, not certain this is an +//   issue in practice.) +// +// * Conversion to AISv3. +// + + +namespace +{ + +///---------------------------------------------------------------------------- +/// Class <anonymous>::BGItemHttpHandler +///---------------------------------------------------------------------------- + +// +// Http request handler class for single inventory item requests. +// +// We'll use a handler-per-request pattern here rather than +// a shared handler.  Mainly convenient as this was converted +// from a Responder class model. +// +// Derives from and is identical to the normal FetchItemHttpHandler +// except that:  1) it uses the background request object which is +// updated more slowly than the foreground and 2) keeps a count of +// active requests on the LLInventoryModelBackgroundFetch object +// to indicate outstanding operations are in-flight. +// +class BGItemHttpHandler : public LLInventoryModel::FetchItemHttpHandler +{ +	LOG_CLASS(BGItemHttpHandler); +	 +public: +	BGItemHttpHandler(const LLSD & request_sd) +		: LLInventoryModel::FetchItemHttpHandler(request_sd) +		{ +			LLInventoryModelBackgroundFetch::instance().incrFetchCount(1); +		} + +	virtual ~BGItemHttpHandler() +		{ +			LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); +		} + +protected: +	BGItemHttpHandler(const BGItemHttpHandler &);				// Not defined +	void operator=(const BGItemHttpHandler &);					// Not defined +}; + + +///---------------------------------------------------------------------------- +/// Class <anonymous>::BGFolderHttpHandler +///---------------------------------------------------------------------------- + +// Http request handler class for folders. +// +// Handler for FetchInventoryDescendents2 and FetchLibDescendents2 +// caps requests for folders. +// +class BGFolderHttpHandler : public LLCore::HttpHandler +{ +	LOG_CLASS(BGFolderHttpHandler); +	 +public: +	BGFolderHttpHandler(const LLSD & request_sd, const uuid_vec_t & recursive_cats) +		: LLCore::HttpHandler(), +		  mRequestSD(request_sd), +		  mRecursiveCatUUIDs(recursive_cats) +		{ +			LLInventoryModelBackgroundFetch::instance().incrFetchCount(1); +		} + +	virtual ~BGFolderHttpHandler() +		{ +			LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); +		} +	 +protected: +	BGFolderHttpHandler(const BGFolderHttpHandler &);			// Not defined +	void operator=(const BGFolderHttpHandler &);				// Not defined + +public: +	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + +	bool getIsRecursive(const LLUUID & cat_id) const; + +private: +	void processData(LLSD & body, LLCore::HttpResponse * response); +	void processFailure(LLCore::HttpStatus status, LLCore::HttpResponse * response); +	void processFailure(const char * const reason, LLCore::HttpResponse * response); + +private: +	LLSD mRequestSD; +	const uuid_vec_t mRecursiveCatUUIDs; // hack for storing away which cat fetches are recursive +}; + -const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f;  const S32 MAX_FETCH_RETRIES = 10; -LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch() : +const char * const LOG_INV("Inventory"); + +} // end of namespace anonymous + + +///---------------------------------------------------------------------------- +/// Class LLInventoryModelBackgroundFetch +///---------------------------------------------------------------------------- + +LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch():  	mBackgroundFetchActive(FALSE),  	mFolderFetchActive(false), +	mFetchCount(0),  	mAllFoldersFetched(FALSE),  	mRecursiveInventoryFetchStarted(FALSE),  	mRecursiveLibraryFetchStarted(FALSE),  	mNumFetchRetries(0),  	mMinTimeBetweenFetches(0.3f),  	mMaxTimeBetweenFetches(10.f), -	mTimelyFetchPending(FALSE), -	mFetchCount(0) -{ -} +	mTimelyFetchPending(FALSE) +{}  LLInventoryModelBackgroundFetch::~LLInventoryModelBackgroundFetch() -{ -} +{}  bool LLInventoryModelBackgroundFetch::isBulkFetchProcessingComplete() const  { -	return mFetchQueue.empty() && mFetchCount<=0; +	return mFetchQueue.empty() && mFetchCount <= 0;  }  bool LLInventoryModelBackgroundFetch::libraryFetchStarted() const @@ -91,7 +240,7 @@ bool LLInventoryModelBackgroundFetch::inventoryFetchCompleted() const  bool LLInventoryModelBackgroundFetch::inventoryFetchInProgress() const  { -	return inventoryFetchStarted() && !inventoryFetchCompleted(); +	return inventoryFetchStarted() && ! inventoryFetchCompleted();  }  bool LLInventoryModelBackgroundFetch::isEverythingFetched() const @@ -104,24 +253,36 @@ BOOL LLInventoryModelBackgroundFetch::folderFetchActive() const  	return mFolderFetchActive;  } +void LLInventoryModelBackgroundFetch::addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category) +{ +	mFetchQueue.push_front(FetchQueueInfo(id, recursive, is_category)); +} + +void LLInventoryModelBackgroundFetch::addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category) +{ +	mFetchQueue.push_back(FetchQueueInfo(id, recursive, is_category)); +} +  void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive)  { -	LLViewerInventoryCategory* cat = gInventory.getCategory(id); -	if (cat || (id.isNull() && !isEverythingFetched())) -	{	// it's a folder, do a bulk fetch -		LL_DEBUGS("InventoryFetch") << "Start fetching category: " << id << ", recursive: " << recursive << LL_ENDL; +	LLViewerInventoryCategory * cat(gInventory.getCategory(id)); + +	if (cat || (id.isNull() && ! isEverythingFetched())) +	{ +		// it's a folder, do a bulk fetch +		LL_DEBUGS(LOG_INV) << "Start fetching category: " << id << ", recursive: " << recursive << LL_ENDL;  		mBackgroundFetchActive = TRUE;  		mFolderFetchActive = true;  		if (id.isNull())  		{ -			if (!mRecursiveInventoryFetchStarted) +			if (! mRecursiveInventoryFetchStarted)  			{  				mRecursiveInventoryFetchStarted |= recursive;  				mFetchQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursive));  				gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);  			} -			if (!mRecursiveLibraryFetchStarted) +			if (! mRecursiveLibraryFetchStarted)  			{  				mRecursiveLibraryFetchStarted |= recursive;  				mFetchQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursive)); @@ -146,9 +307,9 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive)  			}  		}  	} -	else if (LLViewerInventoryItem* itemp = gInventory.getItem(id)) +	else if (LLViewerInventoryItem * itemp = gInventory.getItem(id))  	{ -		if (!itemp->mIsComplete && (mFetchQueue.empty() || mFetchQueue.front().mUUID != id)) +		if (! itemp->mIsComplete && (mFetchQueue.empty() || mFetchQueue.front().mUUID != id))  		{  			mBackgroundFetchActive = TRUE; @@ -172,11 +333,12 @@ void LLInventoryModelBackgroundFetch::setAllFoldersFetched()  		mRecursiveLibraryFetchStarted)  	{  		mAllFoldersFetched = TRUE; -		//LL_INFOS() << "All folders fetched, validating" << LL_ENDL; +		//LL_INFOS(LOG_INV) << "All folders fetched, validating" << LL_ENDL;  		//gInventory.validate();  	}  	mFolderFetchActive = false;  	mBackgroundFetchActive = false; +	LL_INFOS(LOG_INV) << "Inventory background fetch completed" << LL_ENDL;  }  void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *) @@ -203,12 +365,7 @@ void LLInventoryModelBackgroundFetch::backgroundFetch()  		// No more categories to fetch, stop fetch process.  		if (mFetchQueue.empty())  		{ -			LL_INFOS() << "Inventory fetch completed" << LL_ENDL; -  			setAllFoldersFetched(); -			mBackgroundFetchActive = false; -			mFolderFetchActive = false; -  			return;  		} @@ -219,7 +376,7 @@ void LLInventoryModelBackgroundFetch::backgroundFetch()  			// Double timeouts on failure.  			mMinTimeBetweenFetches = llmin(mMinTimeBetweenFetches * 2.f, 10.f);  			mMaxTimeBetweenFetches = llmin(mMaxTimeBetweenFetches * 2.f, 120.f); -			LL_DEBUGS() << "Inventory fetch times grown to (" << mMinTimeBetweenFetches << ", " << mMaxTimeBetweenFetches << ")" << LL_ENDL; +			LL_DEBUGS(LOG_INV) << "Inventory fetch times grown to (" << mMinTimeBetweenFetches << ", " << mMaxTimeBetweenFetches << ")" << LL_ENDL;  			// fetch is no longer considered "timely" although we will wait for full time-out.  			mTimelyFetchPending = FALSE;  		} @@ -231,7 +388,7 @@ void LLInventoryModelBackgroundFetch::backgroundFetch()  				break;  			} -			if(gDisconnected) +			if (gDisconnected)  			{  				// Just bail if we are disconnected.  				break; @@ -292,7 +449,7 @@ void LLInventoryModelBackgroundFetch::backgroundFetch()  						// Shrink timeouts based on success.  						mMinTimeBetweenFetches = llmax(mMinTimeBetweenFetches * 0.8f, 0.3f);  						mMaxTimeBetweenFetches = llmax(mMaxTimeBetweenFetches * 0.8f, 10.f); -						LL_DEBUGS() << "Inventory fetch times shrunk to (" << mMinTimeBetweenFetches << ", " << mMaxTimeBetweenFetches << ")" << LL_ENDL; +						LL_DEBUGS(LOG_INV) << "Inventory fetch times shrunk to (" << mMinTimeBetweenFetches << ", " << mMaxTimeBetweenFetches << ")" << LL_ENDL;  					}  					mTimelyFetchPending = FALSE; @@ -355,258 +512,61 @@ void LLInventoryModelBackgroundFetch::backgroundFetch()  	}  } -void LLInventoryModelBackgroundFetch::incrFetchCount(S16 fetching)  +void LLInventoryModelBackgroundFetch::incrFetchCount(S32 fetching)   {    	mFetchCount += fetching;   	if (mFetchCount < 0)  	{ +		LL_WARNS_ONCE(LOG_INV) << "Inventory fetch count fell below zero (0)." << LL_ENDL;  		mFetchCount = 0;   	}  } -class LLInventoryModelFetchItemResponder : public LLInventoryModel::fetchInventoryResponder -{ -	LOG_CLASS(LLInventoryModelFetchItemResponder); -public: -	LLInventoryModelFetchItemResponder(const LLSD& request_sd) : -		LLInventoryModel::fetchInventoryResponder(request_sd) -	{ -		LLInventoryModelBackgroundFetch::instance().incrFetchCount(1); -	} -private: -	/* virtual */ void httpCompleted() -	{ -		LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); -		LLInventoryModel::fetchInventoryResponder::httpCompleted(); -	} -}; - -class LLInventoryModelFetchDescendentsResponder: public LLHTTPClient::Responder -{ -	LOG_CLASS(LLInventoryModelFetchDescendentsResponder); -public: -	LLInventoryModelFetchDescendentsResponder(const LLSD& request_sd, uuid_vec_t recursive_cats) :  -		mRequestSD(request_sd), -		mRecursiveCatUUIDs(recursive_cats) -	{ -		LLInventoryModelBackgroundFetch::instance().incrFetchCount(1); -	} -	//LLInventoryModelFetchDescendentsResponder() {}; -private: -	/* virtual */ void httpCompleted() -	{ -		LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); -		LLHTTPClient::Responder::httpCompleted(); -	} -	/* virtual */ void httpSuccess(); -	/* virtual */ void httpFailure(); -protected: -	BOOL getIsRecursive(const LLUUID& cat_id) const; -private: -	LLSD mRequestSD; -	uuid_vec_t mRecursiveCatUUIDs; // hack for storing away which cat fetches are recursive -}; - -// If we get back a normal response, handle it here. -void LLInventoryModelFetchDescendentsResponder::httpSuccess() +// Bundle up a bunch of requests to send all at once. +void LLInventoryModelBackgroundFetch::bulkFetch()  { -	const LLSD& content = getContent(); -	if (!content.isMap()) +	//Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped. +	//If there are items in mFetchQueue, 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.   +	LLViewerRegion * region(gAgent.getRegion()); +	if (! region || gDisconnected)  	{ -		failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);  		return;  	} -	LLInventoryModelBackgroundFetch *fetcher = LLInventoryModelBackgroundFetch::getInstance(); -	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. -			//{ -			//	LL_WARNS() << "Got a UpdateInventoryItem for the wrong agent." -			//			<< LL_ENDL; -			//	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); -            if (parent_id.isNull()) -            { -			    LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; -			    for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray(); -				    item_it != folder_sd["items"].endArray(); -				    ++item_it) -			    {	 -                    const LLUUID lost_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); -                    if (lost_uuid.notNull()) -                    { -				        LLSD item = *item_it; -				        titem->unpackMessage(item); -				 -                        LLInventoryModel::update_list_t update; -                        LLInventoryModel::LLCategoryUpdate new_folder(lost_uuid, 1); -                        update.push_back(new_folder); -                        gInventory.accountForUpdate(update); - -                        titem->setParent(lost_uuid); -                        titem->updateParentOnServer(FALSE); -                        gInventory.updateItem(titem); -                        gInventory.notifyObservers(); -                         -                    } -                } -            } - -	        LLViewerInventoryCategory* pcat = gInventory.getCategory(parent_id); -			if (!pcat) -			{ -				continue; -			} - -			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);  -				 -				const BOOL recursive = getIsRecursive(tcategory->getUUID()); -				 -				if (recursive) -				{ -					fetcher->mFetchQueue.push_back(LLInventoryModelBackgroundFetch::FetchQueueInfo(tcategory->getUUID(), recursive)); -				} -				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); -				cat->determineFolderType(); -			} - -		} -	} -		 -	if (content.has("bad_folders")) -	{ -		for(LLSD::array_const_iterator folder_it = content["bad_folders"].beginArray(); -			folder_it != content["bad_folders"].endArray(); -			++folder_it) -		{ -			// *TODO: Stop copying data -			LLSD folder_sd = *folder_it; -			 -			// These folders failed on the dataserver.  We probably don't want to retry them. -			LL_WARNS() << "Folder " << folder_sd["folder_id"].asString()  -					   << "Error: " << folder_sd["error"].asString() << LL_ENDL; -		} -	} +	// *TODO:  These values could be tweaked at runtime to effect +	// a fast/slow fetch throttle.  Once login is complete and the scene +	// is mostly loaded, we could turn up the throttle and fill missing +	// inventory more quickly. +	static const U32 max_batch_size(10); +	static const S32 max_concurrent_fetches(12);		// Outstanding requests, not connections +	static const F32 new_min_time(0.05f);		// *HACK:  Clean this up when old code goes away entirely. -	if (fetcher->isBulkFetchProcessingComplete()) +	mMinTimeBetweenFetches = new_min_time; +	if (mMinTimeBetweenFetches < new_min_time)   	{ -		LL_INFOS() << "Inventory fetch completed" << LL_ENDL; -		fetcher->setAllFoldersFetched(); +		mMinTimeBetweenFetches = new_min_time;  // *HACK:  See above.  	} -	 -	gInventory.notifyObservers(); -} - -// If we get back an error (not found, etc...), handle it here. -void LLInventoryModelFetchDescendentsResponder::httpFailure() -{ -	LL_WARNS() << dumpResponse() << LL_ENDL; -	LLInventoryModelBackgroundFetch *fetcher = LLInventoryModelBackgroundFetch::getInstance(); -	LL_INFOS() << dumpResponse() << LL_ENDL; -						 -	fetcher->incrFetchCount(-1); - -	if (getStatus()==HTTP_INTERNAL_ERROR) // timed out or curl failure -	{ -		for(LLSD::array_const_iterator folder_it = mRequestSD["folders"].beginArray(); -			folder_it != mRequestSD["folders"].endArray(); -			++folder_it) -		{ -			LLSD folder_sd = *folder_it; -			LLUUID folder_id = folder_sd["folder_id"]; -			const BOOL recursive = getIsRecursive(folder_id); -			fetcher->mFetchQueue.push_front(LLInventoryModelBackgroundFetch::FetchQueueInfo(folder_id, recursive)); -		} -	} -	else +	if (mFetchCount)  	{ -		if (fetcher->isBulkFetchProcessingComplete()) -		{ -			fetcher->setAllFoldersFetched(); -		} -	} -	gInventory.notifyObservers(); -} - -BOOL LLInventoryModelFetchDescendentsResponder::getIsRecursive(const LLUUID& cat_id) const -{ -	return (std::find(mRecursiveCatUUIDs.begin(),mRecursiveCatUUIDs.end(), cat_id) != mRecursiveCatUUIDs.end()); -} -// Bundle up a bunch of requests to send all at once. -// static    -void LLInventoryModelBackgroundFetch::bulkFetch() -{ -	//Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped. -	//If there are items in mFetchQueue, 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.   -	LLViewerRegion* region = gAgent.getRegion(); -	if (!region) return; - -	S16 max_concurrent_fetches=8; -	F32 new_min_time = 0.5f;			//HACK!  Clean this up when old code goes away entirely. -	if (mMinTimeBetweenFetches < new_min_time)  -	{ -		mMinTimeBetweenFetches=new_min_time;  //HACK!  See above. +		// Process completed background HTTP requests +		gInventory.handleResponses(false);  	} -	if (gDisconnected || -		(mFetchCount > max_concurrent_fetches) || +	if ((mFetchCount > max_concurrent_fetches) ||  		(mFetchTimer.getElapsedTimeF32() < mMinTimeBetweenFetches))  	{ -		return; // just bail if we are disconnected +		return;  	} -	U32 item_count=0; -	U32 folder_count=0; -	U32 max_batch_size=5; +	U32 item_count(0); +	U32 folder_count(0); -	U32 sort_order = gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER) & 0x1; +	const U32 sort_order(gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER) & 0x1); +	// *TODO:  Think I'd like to get a shared pointer to this and share it +	// among all the folder requests.  	uuid_vec_t recursive_cats;  	LLSD folder_request_body; @@ -614,27 +574,27 @@ void LLInventoryModelBackgroundFetch::bulkFetch()  	LLSD item_request_body;  	LLSD item_request_body_lib; -	while (!mFetchQueue.empty()  +	while (! mFetchQueue.empty()   			&& (item_count + folder_count) < max_batch_size)  	{ -		const FetchQueueInfo& fetch_info = mFetchQueue.front(); +		const FetchQueueInfo & fetch_info(mFetchQueue.front());  		if (fetch_info.mIsCategory)  		{ -			const LLUUID &cat_id = fetch_info.mUUID; +			const LLUUID & cat_id(fetch_info.mUUID);  			if (cat_id.isNull()) //DEV-17797  			{  				LLSD folder_sd;  				folder_sd["folder_id"]		= LLUUID::null.asString();  				folder_sd["owner_id"]		= gAgent.getID(); -				folder_sd["sort_order"]		= (LLSD::Integer)sort_order; -				folder_sd["fetch_folders"]	= (LLSD::Boolean)FALSE; -				folder_sd["fetch_items"]	= (LLSD::Boolean)TRUE; +				folder_sd["sort_order"]		= LLSD::Integer(sort_order); +				folder_sd["fetch_folders"]	= LLSD::Boolean(FALSE); +				folder_sd["fetch_items"]	= LLSD::Boolean(TRUE);  				folder_request_body["folders"].append(folder_sd);  				folder_count++;  			}  			else  			{ -				const LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); +				const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id));  				if (cat)  				{ @@ -643,21 +603,26 @@ void LLInventoryModelBackgroundFetch::bulkFetch()  						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"]	= TRUE; //(LLSD::Boolean)sFullFetchStarted; -						folder_sd["fetch_items"]	= (LLSD::Boolean)TRUE; +						folder_sd["sort_order"]		= LLSD::Integer(sort_order); +						folder_sd["fetch_folders"]	= LLSD::Boolean(TRUE); //(LLSD::Boolean)sFullFetchStarted; +						folder_sd["fetch_items"]	= LLSD::Boolean(TRUE);  						if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) +						{  							folder_request_body_lib["folders"].append(folder_sd); +						}  						else +						{  							folder_request_body["folders"].append(folder_sd); +						}  						folder_count++;  					} +  					// May already have this folder, but append child folders to list.  					if (fetch_info.mRecursive)  					{	 -						LLInventoryModel::cat_array_t* categories; -						LLInventoryModel::item_array_t* items; +						LLInventoryModel::cat_array_t * categories(NULL); +						LLInventoryModel::item_array_t * items(NULL);  						gInventory.getDirectDescendentsOf(cat->getUUID(), categories, items);  						for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();  							 it != categories->end(); @@ -669,11 +634,14 @@ void LLInventoryModelBackgroundFetch::bulkFetch()  				}  			}  			if (fetch_info.mRecursive) +			{  				recursive_cats.push_back(cat_id); +			}  		}  		else  		{ -			LLViewerInventoryItem* itemp = gInventory.getItem(fetch_info.mUUID); +			LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID)); +  			if (itemp)  			{  				LLSD item_sd; @@ -694,72 +662,80 @@ void LLInventoryModelBackgroundFetch::bulkFetch()  		mFetchQueue.pop_front();  	} -		 + +	// Issue HTTP POST requests to fetch folders and items +	  	if (item_count + folder_count > 0)  	{  		if (folder_count)  		{ -			std::string url = region->getCapability("FetchInventoryDescendents2");   			 -			if ( !url.empty() ) +			if (folder_request_body["folders"].size())  			{ -				if (folder_request_body["folders"].size()) +				const std::string url(region->getCapability("FetchInventoryDescendents2")); + +				if (! url.empty())  				{ -					LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body, recursive_cats); -					LLHTTPClient::post(url, folder_request_body, fetcher, 300.0); +					BGFolderHttpHandler * handler(new BGFolderHttpHandler(folder_request_body, recursive_cats)); +					gInventory.requestPost(false, url, folder_request_body, handler, "Inventory Folder");  				} -				if (folder_request_body_lib["folders"].size()) -				{ -					std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents2"); +			} +			 +			if (folder_request_body_lib["folders"].size()) +			{ +				const std::string url(region->getCapability("FetchLibDescendents2")); -					LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body_lib, recursive_cats); -					LLHTTPClient::post(url_lib, folder_request_body_lib, fetcher, 300.0); +				if (! url.empty()) +				{ +					BGFolderHttpHandler * handler(new BGFolderHttpHandler(folder_request_body_lib, recursive_cats)); +					gInventory.requestPost(false, url, folder_request_body_lib, handler, "Library Folder");  				}  			} -		} +		} // if (folder_count) +  		if (item_count)  		{ -			std::string url; -  			if (item_request_body.size())  			{ -				url = region->getCapability("FetchInventory2"); -				if (!url.empty()) +				const std::string url(region->getCapability("FetchInventory2")); + +				if (! url.empty())  				{  					LLSD body;  					body["items"] = item_request_body; - -					LLHTTPClient::post(url, body, new LLInventoryModelFetchItemResponder(body)); +					BGItemHttpHandler * handler(new BGItemHttpHandler(body)); +					gInventory.requestPost(false, url, body, handler, "Inventory Item");  				}  			}  			if (item_request_body_lib.size())  			{ +				const std::string url(region->getCapability("FetchLib2")); -				url = region->getCapability("FetchLib2"); -				if (!url.empty()) +				if (! url.empty())  				{  					LLSD body;  					body["items"] = item_request_body_lib; - -					LLHTTPClient::post(url, body, new LLInventoryModelFetchItemResponder(body)); +					BGItemHttpHandler * handler(new BGItemHttpHandler(body)); +					gInventory.requestPost(false, url, body, handler, "Library Item");  				}  			} -		} +		} // if (item_count) +		  		mFetchTimer.reset();  	} -  	else if (isBulkFetchProcessingComplete())  	{  		setAllFoldersFetched();  	}  } -bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const +bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LLUUID & cat_id) const  {  	for (fetch_queue_t::const_iterator it = mFetchQueue.begin(); -		 it != mFetchQueue.end(); ++it) +		 it != mFetchQueue.end(); +		 ++it)  	{ -		const LLUUID& fetch_id = (*it).mUUID; +		const LLUUID & fetch_id = (*it).mUUID;  		if (gInventory.isObjectDescendentOf(fetch_id, cat_id))  			return false;  	} @@ -767,3 +743,304 @@ bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LL  } +namespace +{ + +///---------------------------------------------------------------------------- +/// Class <anonymous>::BGFolderHttpHandler +///---------------------------------------------------------------------------- + +void BGFolderHttpHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ +	do  	// Single-pass do-while used for common exit handling +	{ +		LLCore::HttpStatus status(response->getStatus()); +		// status = LLCore::HttpStatus(404);				// Dev tool to force error handling +		if (! status) +		{ +			processFailure(status, response); +			break;			// Goto common exit +		} + +		// Response body should be present. +		LLCore::BufferArray * body(response->getBody()); +		// body = NULL;									// Dev tool to force error handling +		if (! body || ! body->size()) +		{ +			LL_WARNS(LOG_INV) << "Missing data in inventory folder query." << LL_ENDL; +			processFailure("HTTP response missing expected body", response); +			break;			// Goto common exit +		} + +		// Could test 'Content-Type' header but probably unreliable. + +		// Convert response to LLSD +		// body->write(0, "Garbage Response", 16);		// Dev tool to force error handling +		LLSD body_llsd; +		if (! LLCoreHttpUtil::responseToLLSD(response, true, body_llsd)) +		{ +			// INFOS-level logging will occur on the parsed failure +			processFailure("HTTP response contained malformed LLSD", response); +			break;			// goto common exit +		} + +		// Expect top-level structure to be a map +		// body_llsd = LLSD::emptyArray();				// Dev tool to force error handling +		if (! body_llsd.isMap()) +		{ +			processFailure("LLSD response not a map", response); +			break;			// goto common exit +		} + +		// Check for 200-with-error failures +		// +		// See comments in llinventorymodel.cpp about this mode of error. +		// +		// body_llsd["error"] = LLSD::emptyMap();		// Dev tool to force error handling +		// body_llsd["error"]["identifier"] = "Development"; +		// body_llsd["error"]["message"] = "You left development code in the viewer"; +		if (body_llsd.has("error")) +		{ +			processFailure("Inventory application error (200-with-error)", response); +			break;			// goto common exit +		} + +		// Okay, process data if possible +		processData(body_llsd, response); +	} +	while (false); + +	// Must delete on completion. +	delete this; +} + + +void BGFolderHttpHandler::processData(LLSD & content, LLCore::HttpResponse * response) +{ +	LLInventoryModelBackgroundFetch * fetcher(LLInventoryModelBackgroundFetch::getInstance()); + +	// API V2 and earlier should probably be testing for "error" map +	// in response as an application-level error. + +	// Instead, we assume success and attempt to extract information. +	if (content.has("folders"))	 +	{ +		LLSD folders(content["folders"]); +		 +		for (LLSD::array_const_iterator folder_it = folders.beginArray(); +			folder_it != 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. +			//{ +			//	LL_WARNS(LOG_INV) << "Got a UpdateInventoryItem for the wrong agent." +			//			<< LL_ENDL; +			//	break; +			//} + +			LLUUID parent_id(folder_sd["folder_id"].asUUID()); +			LLUUID owner_id(folder_sd["owner_id"].asUUID()); +			S32    version(folder_sd["version"].asInteger()); +			S32    descendents(folder_sd["descendents"].asInteger()); +			LLPointer<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id); + +            if (parent_id.isNull()) +            { +				LLSD items(folder_sd["items"]); +			    LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; +				 +			    for (LLSD::array_const_iterator item_it = items.beginArray(); +				    item_it != items.endArray(); +				    ++item_it) +			    {	 +                    const LLUUID lost_uuid(gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); + +                    if (lost_uuid.notNull()) +                    { +				        LLSD item(*item_it); + +				        titem->unpackMessage(item); +				 +                        LLInventoryModel::update_list_t update; +                        LLInventoryModel::LLCategoryUpdate new_folder(lost_uuid, 1); +                        update.push_back(new_folder); +                        gInventory.accountForUpdate(update); + +                        titem->setParent(lost_uuid); +                        titem->updateParentOnServer(FALSE); +                        gInventory.updateItem(titem); +                        gInventory.notifyObservers(); +                    } +                } +            } + +	        LLViewerInventoryCategory * pcat(gInventory.getCategory(parent_id)); +			if (! pcat) +			{ +				continue; +			} + +			LLSD categories(folder_sd["categories"]); +			for (LLSD::array_const_iterator category_it = categories.beginArray(); +				category_it != categories.endArray(); +				++category_it) +			{	 +				LLSD category(*category_it); +				tcategory->fromLLSD(category);  +				 +				const bool recursive(getIsRecursive(tcategory->getUUID())); +				if (recursive) +				{ +					fetcher->addRequestAtBack(tcategory->getUUID(), recursive, true); +				} +				else if (! gInventory.isCategoryComplete(tcategory->getUUID())) +				{ +					gInventory.updateCategory(tcategory); +				} +			} + +			LLSD items(folder_sd["items"]); +			LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; +			for (LLSD::array_const_iterator item_it = items.beginArray(); +				 item_it != 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); +				cat->determineFolderType(); +			} +		} +	} +		 +	if (content.has("bad_folders")) +	{ +		LLSD bad_folders(content["bad_folders"]); +		for (LLSD::array_const_iterator folder_it = bad_folders.beginArray(); +			 folder_it != bad_folders.endArray(); +			 ++folder_it) +		{ +			// *TODO: Stop copying data [ed:  this isn't copying data] +			LLSD folder_sd(*folder_it); +			 +			// These folders failed on the dataserver.  We probably don't want to retry them. +			LL_WARNS(LOG_INV) << "Folder " << folder_sd["folder_id"].asString()  +							  << "Error: " << folder_sd["error"].asString() << LL_ENDL; +		} +	} +	 +	if (fetcher->isBulkFetchProcessingComplete()) +	{ +		fetcher->setAllFoldersFetched(); +	} +	 +	gInventory.notifyObservers(); +} + + +void BGFolderHttpHandler::processFailure(LLCore::HttpStatus status, LLCore::HttpResponse * response) +{ +	const std::string & ct(response->getContentType()); +	LL_WARNS(LOG_INV) << "Inventory folder fetch failure\n" +					  << "[Status: " << status.toTerseString() << "]\n" +					  << "[Reason: " << status.toString() << "]\n" +					  << "[Content-type: " << ct << "]\n" +					  << "[Content (abridged): " +					  << LLCoreHttpUtil::responseToString(response) << "]" << LL_ENDL; + +	// Could use a 404 test here to try to detect revoked caps... +	 +	// This was originally the request retry logic for the inventory +	// request which tested on HTTP_INTERNAL_ERROR status.  This +	// retry logic was unbounded and lacked discrimination as to the +	// cause of the retry.  The new http library should be doing +	// adquately on retries but I want to keep the structure of a +	// retry for reference. +	LLInventoryModelBackgroundFetch *fetcher = LLInventoryModelBackgroundFetch::getInstance(); +	if (false) +	{ +		// timed out or curl failure +		for (LLSD::array_const_iterator folder_it = mRequestSD["folders"].beginArray(); +			 folder_it != mRequestSD["folders"].endArray(); +			 ++folder_it) +		{ +			LLSD folder_sd(*folder_it); +			LLUUID folder_id(folder_sd["folder_id"].asUUID()); +			const BOOL recursive = getIsRecursive(folder_id); +			fetcher->addRequestAtFront(folder_id, recursive, true); +		} +	} +	else +	{ +		if (fetcher->isBulkFetchProcessingComplete()) +		{ +			fetcher->setAllFoldersFetched(); +		} +	} +	gInventory.notifyObservers(); +} + + +void BGFolderHttpHandler::processFailure(const char * const reason, LLCore::HttpResponse * response) +{ +	LL_WARNS(LOG_INV) << "Inventory folder fetch failure\n" +					  << "[Status: internal error]\n" +					  << "[Reason: " << reason << "]\n" +					  << "[Content (abridged): " +					  << LLCoreHttpUtil::responseToString(response) << "]" << LL_ENDL; + +	// Reverse of previous processFailure() method, this is invoked +	// when response structure is found to be invalid.  Original +	// always re-issued the request (without limit).  This does +	// the same but be aware that this may be a source of problems. +	// Philosophy is that inventory folders are so essential to +	// operation that this is a reasonable action. +	LLInventoryModelBackgroundFetch *fetcher = LLInventoryModelBackgroundFetch::getInstance(); +	if (true) +	{ +		for (LLSD::array_const_iterator folder_it = mRequestSD["folders"].beginArray(); +			 folder_it != mRequestSD["folders"].endArray(); +			 ++folder_it) +		{ +			LLSD folder_sd(*folder_it); +			LLUUID folder_id(folder_sd["folder_id"].asUUID()); +			const BOOL recursive = getIsRecursive(folder_id); +			fetcher->addRequestAtFront(folder_id, recursive, true); +		} +	} +	else +	{ +		if (fetcher->isBulkFetchProcessingComplete()) +		{ +			fetcher->setAllFoldersFetched(); +		} +	} +	gInventory.notifyObservers(); +} + + +bool BGFolderHttpHandler::getIsRecursive(const LLUUID & cat_id) const +{ +	return std::find(mRecursiveCatUUIDs.begin(), mRecursiveCatUUIDs.end(), cat_id) != mRecursiveCatUUIDs.end(); +} + +///---------------------------------------------------------------------------- +/// Class <anonymous>::BGItemHttpHandler +///---------------------------------------------------------------------------- + +// Nothing to implement here.  All ctor/dtor changes. + +} // end namespace anonymous diff --git a/indra/newview/llinventorymodelbackgroundfetch.h b/indra/newview/llinventorymodelbackgroundfetch.h index 9dfedddd6d..2139f85519 100755 --- a/indra/newview/llinventorymodelbackgroundfetch.h +++ b/indra/newview/llinventorymodelbackgroundfetch.h @@ -29,6 +29,11 @@  #include "llsingleton.h"  #include "lluuid.h" +#include "httpcommon.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" +#include "httphandler.h"  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  // Class LLInventoryModelBackgroundFetch @@ -38,8 +43,6 @@  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  class LLInventoryModelBackgroundFetch : public LLSingleton<LLInventoryModelBackgroundFetch>  { -	friend class LLInventoryModelFetchDescendentsResponder; -  public:  	LLInventoryModelBackgroundFetch();  	~LLInventoryModelBackgroundFetch(); @@ -60,16 +63,22 @@ public:  	bool inventoryFetchInProgress() const;      void findLostItems();	 -	void incrFetchCount(S16 fetching); -protected: +	void incrFetchCount(S32 fetching); +  	bool isBulkFetchProcessingComplete() const; +	void setAllFoldersFetched(); + +	void addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category); +	void addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category); + +protected:  	void bulkFetch();  	void backgroundFetch();  	static void backgroundFetchCB(void*); // background fetch idle function -	void setAllFoldersFetched();  	bool fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const; +  private:   	BOOL mRecursiveInventoryFetchStarted;  	BOOL mRecursiveLibraryFetchStarted; @@ -77,7 +86,7 @@ private:  	BOOL mBackgroundFetchActive;  	bool mFolderFetchActive; -	S16 mFetchCount; +	S32 mFetchCount;  	BOOL mTimelyFetchPending;  	S32 mNumFetchRetries; @@ -87,9 +96,12 @@ private:  	struct FetchQueueInfo  	{ -		FetchQueueInfo(const LLUUID& id, BOOL recursive, bool is_category = true) : -			mUUID(id), mRecursive(recursive), mIsCategory(is_category) +		FetchQueueInfo(const LLUUID& id, BOOL recursive, bool is_category = true) +			: mUUID(id), +			  mIsCategory(is_category), +			  mRecursive(recursive)  		{} +		  		LLUUID mUUID;  		bool mIsCategory;  		BOOL mRecursive; diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index 2dd8dce42f..d81401b59b 100755 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -237,7 +237,8 @@ void fetch_items_from_llsd(const LLSD& items_llsd)  		if (!url.empty())  		{  			body[i]["agent_id"]	= gAgent.getID(); -			LLHTTPClient::post(url, body[i], new LLInventoryModel::fetchInventoryResponder(body[i])); +			LLInventoryModel::FetchItemHttpHandler * handler(new LLInventoryModel::FetchItemHttpHandler(body[i])); +			gInventory.requestPost(true, url, body[i], handler, (i ? "Library Item" : "Inventory Item"));  			continue;  		} diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 8f50555a73..648056484e 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1,4 +1,3 @@ -  /**    * @file llmeshrepository.cpp   * @brief Mesh repository implementation. @@ -72,6 +71,7 @@  #include "bufferarray.h"  #include "bufferstream.h"  #include "llfasttimer.h" +#include "llcorehttputil.h"  #include "boost/lexical_cast.hpp" @@ -338,14 +338,17 @@ static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");  LLMeshRepository gMeshRepo;  const S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space +  const S32 REQUEST_HIGH_WATER_MIN = 32;					// Limits for GetMesh regions  const S32 REQUEST_HIGH_WATER_MAX = 150;					// Should remain under 2X throttle  const S32 REQUEST_LOW_WATER_MIN = 16;  const S32 REQUEST_LOW_WATER_MAX = 75; +  const S32 REQUEST2_HIGH_WATER_MIN = 32;					// Limits for GetMesh2 regions -const S32 REQUEST2_HIGH_WATER_MAX = 80; +const S32 REQUEST2_HIGH_WATER_MAX = 100;  const S32 REQUEST2_LOW_WATER_MIN = 16; -const S32 REQUEST2_LOW_WATER_MAX = 40; +const S32 REQUEST2_LOW_WATER_MAX = 50; +  const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;		// Size at which requests goes to narrow/slow queue  const long SMALL_MESH_XFER_TIMEOUT = 120L;				// Seconds to complete xfer, small mesh downloads  const long LARGE_MESH_XFER_TIMEOUT = 600L;				// Seconds to complete xfer, large downloads @@ -518,11 +521,13 @@ class LLMeshHandlerBase : public LLCore::HttpHandler  {  public:  	LOG_CLASS(LLMeshHandlerBase); -	LLMeshHandlerBase() +	LLMeshHandlerBase(U32 offset, U32 requested_bytes)  		: LLCore::HttpHandler(),  		  mMeshParams(),  		  mProcessed(false), -		  mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) +		  mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), +		  mOffset(offset), +		  mRequestedBytes(requested_bytes)  		{}  	virtual ~LLMeshHandlerBase() @@ -534,13 +539,15 @@ protected:  public:  	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); -	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size) = 0; +	virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size) = 0;  	virtual void processFailure(LLCore::HttpStatus status) = 0;  public:  	LLVolumeParams mMeshParams;  	bool mProcessed; -	LLCore::HttpHandle	mHttpHandle; +	LLCore::HttpHandle mHttpHandle; +	U32 mOffset; +	U32 mRequestedBytes;  }; @@ -551,8 +558,8 @@ class LLMeshHeaderHandler : public LLMeshHandlerBase  {  public:  	LOG_CLASS(LLMeshHeaderHandler); -	LLMeshHeaderHandler(const LLVolumeParams & mesh_params) -		: LLMeshHandlerBase() +	LLMeshHeaderHandler(const LLVolumeParams & mesh_params, U32 offset, U32 requested_bytes) +		: LLMeshHandlerBase(offset, requested_bytes)  	{  		mMeshParams = mesh_params;  		LLMeshRepoThread::incActiveHeaderRequests(); @@ -564,7 +571,7 @@ protected:  	void operator=(const LLMeshHeaderHandler &);				// Not defined  public: -	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); +	virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);  	virtual void processFailure(LLCore::HttpStatus status);  }; @@ -573,17 +580,16 @@ public:  //  // Thread:  repo  class LLMeshLODHandler : public LLMeshHandlerBase -	{ +{  public: +	LOG_CLASS(LLMeshLODHandler);  	LLMeshLODHandler(const LLVolumeParams & mesh_params, S32 lod, U32 offset, U32 requested_bytes) -		: LLMeshHandlerBase(), -		  mLOD(lod), -		  mRequestedBytes(requested_bytes), -		  mOffset(offset) +		: LLMeshHandlerBase(offset, requested_bytes), +		  mLOD(lod)  		{ -		mMeshParams = mesh_params; -		LLMeshRepoThread::incActiveLODRequests(); -			} +			mMeshParams = mesh_params; +			LLMeshRepoThread::incActiveLODRequests(); +		}  	virtual ~LLMeshLODHandler();  protected: @@ -591,13 +597,11 @@ protected:  	void operator=(const LLMeshLODHandler &);					// Not defined  public: -	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); +	virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);  	virtual void processFailure(LLCore::HttpStatus status);  public:  	S32 mLOD; -	U32 mRequestedBytes; -	U32 mOffset;  }; @@ -605,14 +609,12 @@ public:  //  // Thread:  repo  class LLMeshSkinInfoHandler : public LLMeshHandlerBase -	{ +{  public:  	LOG_CLASS(LLMeshSkinInfoHandler); -	LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 size) -		: LLMeshHandlerBase(), -		  mMeshID(id), -		  mRequestedBytes(size), -		  mOffset(offset) +	LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 requested_bytes) +		: LLMeshHandlerBase(offset, requested_bytes), +		  mMeshID(id)  	{}  	virtual ~LLMeshSkinInfoHandler(); @@ -621,13 +623,11 @@ protected:  	void operator=(const LLMeshSkinInfoHandler &);				// Not defined  public: -	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); +	virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);  	virtual void processFailure(LLCore::HttpStatus status);  public:  	LLUUID mMeshID; -	U32 mRequestedBytes; -	U32 mOffset;  }; @@ -635,14 +635,12 @@ public:  //  // Thread:  repo  class LLMeshDecompositionHandler : public LLMeshHandlerBase -	{ +{  public:  	LOG_CLASS(LLMeshDecompositionHandler); -	LLMeshDecompositionHandler(const LLUUID& id, U32 offset, U32 size) -		: LLMeshHandlerBase(), -		  mMeshID(id), -		  mRequestedBytes(size), -		  mOffset(offset) +	LLMeshDecompositionHandler(const LLUUID& id, U32 offset, U32 requested_bytes) +		: LLMeshHandlerBase(offset, requested_bytes), +		  mMeshID(id)  	{}  	virtual ~LLMeshDecompositionHandler(); @@ -651,13 +649,11 @@ protected:  	void operator=(const LLMeshDecompositionHandler &);					// Not defined  public: -	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); +	virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);  	virtual void processFailure(LLCore::HttpStatus status);  public:  	LLUUID mMeshID; -	U32 mRequestedBytes; -	U32 mOffset;  }; @@ -665,14 +661,12 @@ public:  //  // Thread:  repo  class LLMeshPhysicsShapeHandler : public LLMeshHandlerBase -	{ +{  public:  	LOG_CLASS(LLMeshPhysicsShapeHandler); -	LLMeshPhysicsShapeHandler(const LLUUID& id, U32 offset, U32 size) -		: LLMeshHandlerBase(), -		  mMeshID(id), -		  mRequestedBytes(size), -		  mOffset(offset) +	LLMeshPhysicsShapeHandler(const LLUUID& id, U32 offset, U32 requested_bytes) +		: LLMeshHandlerBase(offset, requested_bytes), +		  mMeshID(id)  	{}  	virtual ~LLMeshPhysicsShapeHandler(); @@ -681,13 +675,11 @@ protected:  	void operator=(const LLMeshPhysicsShapeHandler &);				// Not defined  public: -	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); +	virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size);  	virtual void processFailure(LLCore::HttpStatus status);  public:  	LLUUID mMeshID; -	U32 mRequestedBytes; -	U32 mOffset;  }; @@ -713,8 +705,8 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,  		LL_WARNS(LOG_MESH) << "error: " << err << LL_ENDL;  		LL_WARNS(LOG_MESH) << "  mesh upload failed, stage '" << stage  						   << "', error '" << err["error"].asString() -				<< "', message '" << err["message"].asString() -				<< "', id '" << err["identifier"].asString() +						   << "', message '" << err["message"].asString() +						   << "', id '" << err["identifier"].asString()  						   << "'" << LL_ENDL;  		if (err.has("errors"))  		{ @@ -754,7 +746,9 @@ LLMeshRepoThread::LLMeshRepoThread()    mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),    mHttpPriority(0),    mGetMeshVersion(2) -	{ +{ +	LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); +  	mMutex = new LLMutex(NULL);  	mHeaderMutex = new LLMutex(NULL);  	mSignal = new LLCondition(NULL); @@ -766,15 +760,15 @@ LLMeshRepoThread::LLMeshRepoThread()  	mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT);  	mHttpLargeOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter"));  	mHttpHeaders = new LLCore::HttpHeaders; -	mHttpHeaders->append("Accept", "application/vnd.ll.mesh"); -	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH2); -	mHttpLegacyPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH1); -	mHttpLargePolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_LARGE_MESH); -	} +	mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_VND_LL_MESH); +	mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH2); +	mHttpLegacyPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH1); +	mHttpLargePolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_LARGE_MESH); +}  LLMeshRepoThread::~LLMeshRepoThread() -		{ +{  	LL_INFOS(LOG_MESH) << "Small GETs issued:  " << LLMeshRepository::sHTTPRequestCount  					   << ", Large GETs issued:  " << LLMeshRepository::sHTTPLargeRequestCount  					   << ", Max Lock Holdoffs:  " << LLMeshRepository::sMaxLockHoldoffs @@ -785,23 +779,23 @@ LLMeshRepoThread::~LLMeshRepoThread()  		 ++iter)  	{  		delete *iter; -		} +	}  	mHttpRequestSet.clear();  	if (mHttpHeaders) -		{ +	{  		mHttpHeaders->release();  		mHttpHeaders = NULL; -		} +	}  	if (mHttpOptions) -		{ +	{  		mHttpOptions->release();  		mHttpOptions = NULL; -			} +	}  	if (mHttpLargeOptions) -{  +	{  		mHttpLargeOptions->release();  		mHttpLargeOptions = NULL; -} +	}  	delete mHttpRequest;  	mHttpRequest = NULL;  	delete mMutex; @@ -846,48 +840,49 @@ void LLMeshRepoThread::run()  		{  			// Dispatch all HttpHandler notifications  			mHttpRequest->update(0L); -			} +		}  		sRequestWaterLevel = mHttpRequestSet.size();			// Stats data update  		// NOTE: order of queue processing intentionally favors LOD requests over header requests  		while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) -			{ +		{  			if (! mMutex) -				{ +			{  				break;  			} -					mMutex->lock(); -					LODRequest req = mLODReqQ.front(); -					mLODReqQ.pop(); -					LLMeshRepository::sLODProcessing--; -					mMutex->unlock(); +			mMutex->lock(); +			LODRequest req = mLODReqQ.front(); +			mLODReqQ.pop(); +			LLMeshRepository::sLODProcessing--; +			mMutex->unlock(); +  			if (!fetchMeshLOD(req.mMeshParams, req.mLOD))		// failed, resubmit -					{ -						mMutex->lock(); -						mLODReqQ.push(req);  +			{ +				mMutex->lock(); +				mLODReqQ.push(req);   				++LLMeshRepository::sLODProcessing; -						mMutex->unlock(); -					} -				} +				mMutex->unlock(); +			} +		}  		while (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) -			{ +		{  			if (! mMutex) -				{ +			{  				break;  			} -					mMutex->lock(); -					HeaderRequest req = mHeaderReqQ.front(); -					mHeaderReqQ.pop(); -					mMutex->unlock(); +			mMutex->lock(); +			HeaderRequest req = mHeaderReqQ.front(); +			mHeaderReqQ.pop(); +			mMutex->unlock();  			if (!fetchMeshHeader(req.mMeshParams))//failed, resubmit -					{ -						mMutex->lock(); -						mHeaderReqQ.push(req) ; -						mMutex->unlock(); -					} -				} +			{ +				mMutex->lock(); +				mHeaderReqQ.push(req) ; +				mMutex->unlock(); +			} +		}  		// For the final three request lists, similar goal to above but  		// slightly different queue structures.  Stay off the mutex when @@ -983,7 +978,7 @@ void LLMeshRepoThread::run()  				}  			}  			mMutex->unlock(); -			} +		}  		// For dev purposes only.  A dynamic change could make this false  		// and that shouldn't assert. @@ -1131,6 +1126,9 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c  												  size_t offset, size_t len,  												  LLCore::HttpHandler * handler)  { +	// Also used in lltexturefetch.cpp +	static LLCachedControl<bool> disable_range_req(gSavedSettings, "HttpRangeRequestsDisable", false); +	  	LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);  	if (len < LARGE_MESH_FETCH_THRESHOLD) @@ -1140,8 +1138,8 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c  													: mHttpLegacyPolicyClass),  												   mHttpPriority,  												   url, -												   offset, -												   len, +												   (disable_range_req ? size_t(0) : offset), +												   (disable_range_req ? size_t(0) : len),  												   mHttpOptions,  												   mHttpHeaders,  												   handler); @@ -1155,8 +1153,8 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c  		handle = mHttpRequest->requestGetByteRange(mHttpLargePolicyClass,  												   mHttpPriority,  												   url, -												   offset, -												   len, +												   (disable_range_req ? size_t(0) : offset), +												   (disable_range_req ? size_t(0) : len),  												   mHttpLargeOptions,  												   mHttpHeaders,  												   handler); @@ -1250,7 +1248,6 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)  									   << LL_ENDL;  					delete handler;  					ret = false; -  				}  				else  				{ @@ -1527,7 +1524,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)  		//within the first 4KB  		//NOTE -- this will break of headers ever exceed 4KB		 -		LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params); +		LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params, 0, MESH_HEADER_SIZE);  		LLCore::HttpHandle handle = getByteRange(http_url, cap_version, 0, MESH_HEADER_SIZE, handler);  		if (LLCORE_HTTP_HANDLE_INVALID == handle)  		{ @@ -1860,7 +1857,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,  									   bool upload_skin, bool upload_joints, const std::string & upload_url, bool do_upload,  									   LLHandle<LLWholeModelFeeObserver> fee_observer,  									   LLHandle<LLWholeModelUploadObserver> upload_observer) -: LLThread("mesh upload"), +  : LLThread("mesh upload"),  	LLCore::HttpHandler(),  	mDiscarded(false),  	mDoUpload(do_upload), @@ -1890,7 +1887,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,  	mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter"));  	mHttpOptions->setRetries(UPLOAD_RETRY_LIMIT);  	mHttpHeaders = new LLCore::HttpHeaders; -	mHttpHeaders->append("Content-Type", "application/llsd+xml"); +	mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML);  	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS);  	mHttpPriority = 0;  } @@ -2240,21 +2237,17 @@ void LLMeshUploadThread::doWholeModelUpload()  		mModelData = LLSD::emptyMap();  		wholeModelToLLSD(mModelData, true);  		LLSD body = mModelData["asset_resources"]; -		dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num)); - -		LLCore::BufferArray * ba = new LLCore::BufferArray; -		LLCore::BufferArrayStream bas(ba); -		LLSDSerialize::toXML(body, bas); -		// LLSDSerialize::toXML(mModelData, bas);		// <- Enabling this will generate a convenient upload error -		LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass, -															  mHttpPriority, -															  mWholeModelUploadURL, -															  ba, -															  mHttpOptions, -															  mHttpHeaders, -															  this); -		ba->release(); -		 + +		dump_llsd_to_file(body, make_dump_name("whole_model_body_", dump_num)); + +		LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, +																		mHttpPolicyClass, +																		mHttpPriority, +																		mWholeModelUploadURL, +																		body, +																		mHttpOptions, +																		mHttpHeaders, +																		this);  		if (LLCORE_HTTP_HANDLE_INVALID == handle)  		{  			mHttpStatus = mHttpRequest->getStatus(); @@ -2271,7 +2264,7 @@ void LLMeshUploadThread::doWholeModelUpload()  			mHttpRequest->update(0);  			while (! LLApp::isQuitting() && ! finished() && ! isDiscarded()) -		{ +			{  				ms_sleep(sleep_time);  				sleep_time = llmin(250U, sleep_time + sleep_time);  				mHttpRequest->update(0); @@ -2287,7 +2280,7 @@ void LLMeshUploadThread::doWholeModelUpload()  			}  		}  	} -			} +}  void LLMeshUploadThread::requestWholeModelFee()  { @@ -2298,19 +2291,14 @@ void LLMeshUploadThread::requestWholeModelFee()  	mModelData = LLSD::emptyMap();  	wholeModelToLLSD(mModelData, false);  	dump_llsd_to_file(mModelData, make_dump_name("whole_model_fee_request_", dump_num)); - -	LLCore::BufferArray * ba = new LLCore::BufferArray; -	LLCore::BufferArrayStream bas(ba); -	LLSDSerialize::toXML(mModelData, bas); -		 -	LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass, -														  mHttpPriority, -														  mWholeModelFeeCapability, -														  ba, -														  mHttpOptions, -														  mHttpHeaders, -														  this); -	ba->release(); +	LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, +																	mHttpPolicyClass, +																	mHttpPriority, +																	mWholeModelFeeCapability, +																	mModelData, +																	mHttpOptions, +																	mHttpHeaders, +																	this);  	if (LLCORE_HTTP_HANDLE_INVALID == handle)  	{  		mHttpStatus = mHttpRequest->getStatus(); @@ -2318,7 +2306,7 @@ void LLMeshUploadThread::requestWholeModelFee()  		LL_WARNS(LOG_MESH) << "Couldn't issue request for model fee.  Reason:  " << mHttpStatus.toString()  						   << " (" << mHttpStatus.toTerseString() << ")"  						   << LL_ENDL; -		} +	}  	else  	{  		U32 sleep_time(10); @@ -2335,7 +2323,7 @@ void LLMeshUploadThread::requestWholeModelFee()  			LL_DEBUGS(LOG_MESH) << "Mesh fee query operation discarded." << LL_ENDL;  		}  	} -	} +}  // Does completion duty for both fee queries and actual uploads. @@ -2383,17 +2371,13 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp  			}  			else  			{ -				LLCore::BufferArray * ba(response->getBody()); -				if (ba && ba->size()) -				{ -					LLCore::BufferArrayStream bas(ba); -					LLSDSerialize::fromXML(body, bas); -} +				// *TODO:  handle error in conversion process +				LLCoreHttpUtil::responseToLLSD(response, true, body);  			}  			dump_llsd_to_file(body, make_dump_name("whole_model_upload_response_", dump_num));  			if (body["state"].asString() == "complete") -{ +			{  				// requested "mesh" asset type isn't actually the type  				// of the resultant object, fix it up here.  				mModelData["asset_type"] = "object"; @@ -2446,18 +2430,14 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp  				body = llsd_from_file("fake_upload_error.xml");  			}  			else -	{ -				LLCore::BufferArray * ba(response->getBody()); -				if (ba && ba->size()) -		{ -					LLCore::BufferArrayStream bas(ba); -					LLSDSerialize::fromXML(body, bas); -		} -	} +			{ +				// *TODO:  handle error in conversion process +				LLCoreHttpUtil::responseToLLSD(response, true, body); +			}  			dump_llsd_to_file(body, make_dump_name("whole_model_fee_response_", dump_num));  			if (body["state"].asString() == "upload") -	{ +			{  				mWholeModelUploadURL = body["uploader"].asString();  				if (observer) @@ -2543,18 +2523,18 @@ void LLMeshRepoThread::notifyLoadedMeshes()  				skin_info_q.swap(mSkinInfoQ);  			}  			if (! mDecompositionQ.empty()) -	{ +			{  				decomp_q.swap(mDecompositionQ); -	} +			}  			mMutex->unlock();  			// Process the elements free of the lock  			while (! skin_info_q.empty()) -	{ +			{  				gMeshRepo.notifySkinInfoReceived(skin_info_q.front());  				skin_info_q.pop_front(); -	} +			}  			while (! decomp_q.empty())  			{ @@ -2648,6 +2628,17 @@ void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header)  } +// Handle failed or successful requests for mesh assets. +// +// Support for 200 responses was added for several reasons.  One, +// a service or cache can ignore range headers and give us a +// 200 with full asset should it elect to.  We also support +// a debug flag which disables range requests for those very +// few users that have some sort of problem with their networking +// services.  But the 200 response handling is suboptimal:  rather +// than cache the whole asset, we just extract the part that would +// have been sent in a 206 and process that.  Inefficient but these +// are cases far off the norm.  void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)  {  	mProcessed = true; @@ -2676,35 +2667,78 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo  		// rather than partial) and 416 (request completely unsatisfyable).  		// Always been exposed to these but are less likely here where  		// speculative loads aren't done. -		static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); +		LLCore::BufferArray * body(response->getBody()); +		S32 body_offset(0); +		U8 * data(NULL); +		S32 data_size(body ? body->size() : 0); -		if (par_status != status) +		if (data_size > 0)  		{ -			LL_WARNS_ONCE(LOG_MESH) << "Non-206 successful status received for fetch:  " -									<< status.toTerseString() << LL_ENDL; -		} +			static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); +			 +			unsigned int offset(0), length(0), full_length(0); +				 +			if (par_status == status) +			{ +				// 206 case +				response->getRange(&offset, &length, &full_length); +				if (! offset && ! length) +				{ +					// This is the case where we receive a 206 status but +					// there wasn't a useful Content-Range header in the response. +					// This could be because it was badly formatted but is more +					// likely due to capabilities services which scrub headers +					// from responses.  Assume we got what we asked for...` +					// length = data_size; +					offset = mOffset; +				} +			} +			else +			{ +				// 200 case, typically +				offset = 0; +			} -		LLCore::BufferArray * body(response->getBody()); -		S32 data_size(body ? body->size() : 0); -		U8 * data(NULL); +			// *DEBUG:  To test validation below +			// offset += 1; -	if (data_size > 0) -	{ +			// Validate that what we think we received is consistent with +			// what we've asked for.  I.e. first byte we wanted lies somewhere +			// in the response. +			if (offset > mOffset +				|| (offset + data_size) <= mOffset +				|| (mOffset - offset) >= data_size) +			{ +				// No overlap with requested range.  Fail request with +				// suitable error.  Shouldn't happen unless server/cache/ISP +				// is doing something awful. +				LL_WARNS(LOG_MESH) << "Mesh response (bytes [" +								   << offset << ".." << (offset + length - 1) +								   << "]) didn't overlap with request's origin (bytes [" +								   << mOffset << ".." << (mOffset + mRequestedBytes - 1) +								   << "])." << LL_ENDL; +				processFailure(LLCore::HttpStatus(LLCore::HttpStatus::LLCORE, LLCore::HE_INV_CONTENT_RANGE_HDR)); +				++LLMeshRepository::sHTTPErrorCount; +				goto common_exit; +			} +			  			// *TODO: Try to get rid of data copying and add interfaces  			// that support BufferArray directly.  Introduce a two-phase  			// handler, optional first that takes a body, fallback second  			// that requires a temporary allocation and data copy. -		data = new U8[data_size]; -			body->read(0, (char *) data, data_size); +			body_offset = mOffset - offset; +			data = new U8[data_size - body_offset]; +			body->read(body_offset, (char *) data, data_size - body_offset);  			LLMeshRepository::sBytesReceived += data_size;  		} -		processData(body, data, data_size); +		processData(body, body_offset, data, data_size - body_offset);  		delete [] data;  	}  	// Release handler +common_exit:  	gMeshRepo.mThread->mHttpRequestSet.erase(this);  	delete this;		// Must be last statement  } @@ -2739,9 +2773,10 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)  	{  		gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i));  	} -	} +} -void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, +									  U8 * data, S32 data_size)  {  	LLUUID mesh_id = mMeshParams.getSculptID();  	bool success = (! MESH_HEADER_PROCESS_FAILED) && gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); @@ -2756,12 +2791,12 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32  		// Can't get the header so none of the LODs will be available  		LLMutexLock lock(gMeshRepo.mThread->mMutex);  		for (int i(0); i < 4; ++i) -	{ +		{  			gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i)); -	}  		} +	}  	else if (data && data_size > 0) -		{ +	{  		// header was successfully retrieved from sim, cache in vfs  		LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; @@ -2774,11 +2809,11 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32  			S32 lod_bytes = 0;  			for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) -	{ +			{  				// figure out how many bytes we'll need to reserve in the file  				const std::string & lod_name = header_lod[i];  				lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger()); -	} +			}  			// just in case skin info or decomposition is at the end of the file (which it shouldn't be)  			lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger()); @@ -2794,7 +2829,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32  			LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE);  			if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) -		{ +			{  				LLMeshRepository::sCacheBytesWritten += data_size;  				++LLMeshRepository::sCacheWrites; @@ -2805,19 +2840,19 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32  				memset(block, 0, sizeof(block));  				while (bytes-file.tell() > sizeof(block)) -	{ +				{  					file.write(block, sizeof(block)); -	} +				}  				S32 remaining = bytes-file.tell();  				if (remaining > 0) -	{ +				{  					file.write(block, remaining);  				}  			}  		}  	} -	} +}  LLMeshLODHandler::~LLMeshLODHandler()  { @@ -2843,8 +2878,9 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)  	gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));  } -void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) -	{ +void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, +								   U8 * data, S32 data_size) +{  	if ((! MESH_LOD_PROCESS_FAILED) && gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))  	{  		//good fetch from sim, write to VFS for caching @@ -2860,7 +2896,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da  			LLMeshRepository::sCacheBytesWritten += size;  			++LLMeshRepository::sCacheWrites;  		} -		} +	}  	else  	{  		LL_WARNS(LOG_MESH) << "Error during mesh LOD processing.  ID:  " << mMeshParams.getSculptID() @@ -2872,12 +2908,12 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da  }  LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() -	{ -		llassert(mProcessed); -	} +{ +	llassert(mProcessed); +}  void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) -	{ +{  	LL_WARNS(LOG_MESH) << "Error during mesh skin info handling.  ID:  " << mMeshID  					   << ", Reason:  " << status.toString()  					   << " (" << status.toTerseString() << ").  Not retrying." @@ -2885,10 +2921,11 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)  	// *TODO:  Mark mesh unavailable on error.  For now, simply leave  	// request unfulfilled rather than retry forever. -		} +} -void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) -	{ +void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, +										U8 * data, S32 data_size) +{  	if ((! MESH_SKIN_INFO_PROCESS_FAILED) && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))  	{  		//good fetch from sim, write to VFS for caching @@ -2916,20 +2953,21 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S  LLMeshDecompositionHandler::~LLMeshDecompositionHandler()  { -		llassert(mProcessed); +	llassert(mProcessed);  }  void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status) -	{ +{  	LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling.  ID:  " << mMeshID  					   << ", Reason:  " << status.toString()  					   << " (" << status.toTerseString() << ").  Not retrying."  					   << LL_ENDL;  	// *TODO:  Mark mesh unavailable on error.  For now, simply leave  	// request unfulfilled rather than retry forever. -	} +} -void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, +											 U8 * data, S32 data_size)  {  	if ((! MESH_DECOMP_PROCESS_FAILED) && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))  	{ @@ -2946,34 +2984,35 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * da  			file.seek(offset);  			file.write(data, size);  		} -		} -		else -		{ +	} +	else +	{  		LL_WARNS(LOG_MESH) << "Error during mesh decomposition processing.  ID:  " << mMeshID  						   << ", Unknown reason.  Not retrying."  						   << LL_ENDL;  		// *TODO:  Mark mesh unavailable on error -		}  	} +}  LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler() -	{ -		llassert(mProcessed); -	} +{ +	llassert(mProcessed); +}  void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status) -	{ +{  	LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling.  ID:  " << mMeshID  					   << ", Reason:  " << status.toString()  					   << " (" << status.toTerseString() << ").  Not retrying."  					   << LL_ENDL;  	// *TODO:  Mark mesh unavailable on error -	} +} -void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) -			{ +void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, +											U8 * data, S32 data_size) +{  	if ((! MESH_PHYS_SHAPE_PROCESS_FAILED) && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size)) -				{ +	{  		// good fetch from sim, write to VFS for caching  		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); @@ -2981,13 +3020,13 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat  		S32 size = mRequestedBytes;  		if (file.getSize() >= offset+size) -				{ +		{  			LLMeshRepository::sCacheBytesWritten += size;  			++LLMeshRepository::sCacheWrites;  			file.seek(offset);  			file.write(data, size); -			}  		} +	}  	else  	{  		LL_WARNS(LOG_MESH) << "Error during mesh physics shape processing.  ID:  " << mMeshID @@ -3187,7 +3226,7 @@ void LLMeshRepository::notifyLoadedMeshes()  	if (1 == mGetMeshVersion)  	{  		// Legacy GetMesh operation with high connection concurrency -	LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); +		LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests");  		LLMeshRepoThread::sRequestHighWater = llclamp(2 * S32(LLMeshRepoThread::sMaxConcurrentRequests),  													  REQUEST_HIGH_WATER_MIN,  													  REQUEST_HIGH_WATER_MAX); @@ -3198,9 +3237,15 @@ void LLMeshRepository::notifyLoadedMeshes()  	else  	{  		// GetMesh2 operation with keepalives, etc.  With pipelining, -		// we'll increase this. +		// we'll increase this.  See llappcorehttp and llcorehttp for +		// discussion on connection strategies. +		LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); +		S32 scale(app_core_http.isPipelined(LLAppCoreHttp::AP_MESH2) +				  ? (2 * LLAppCoreHttp::PIPELINING_DEPTH) +				  : 5); +  		LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests"); -		LLMeshRepoThread::sRequestHighWater = llclamp(5 * S32(LLMeshRepoThread::sMaxConcurrentRequests), +		LLMeshRepoThread::sRequestHighWater = llclamp(scale * S32(LLMeshRepoThread::sMaxConcurrentRequests),  													  REQUEST2_HIGH_WATER_MIN,  													  REQUEST2_HIGH_WATER_MAX);  		LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, @@ -3300,18 +3345,18 @@ void LLMeshRepository::notifyLoadedMeshes()  			// If we can't get the locks, skip and pick this up later.  			++hold_offs;  			sMaxLockHoldoffs = llmax(sMaxLockHoldoffs, hold_offs); -		return; -	} +			return; +		}  		hold_offs = 0;  		if (gAgent.getRegion())  		{  			// Update capability urls -	static std::string region_name("never name a region this"); +			static std::string region_name("never name a region this"); -		if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) -		{ -			region_name = gAgent.getRegion()->getName(); +			if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) +			{ +				region_name = gAgent.getRegion()->getName();  				const bool use_v1(gSavedSettings.getBOOL("MeshUseGetMesh1"));  				const std::string mesh1(gAgent.getRegion()->getCapability("GetMesh"));  				const std::string mesh2(gAgent.getRegion()->getCapability("GetMesh2")); @@ -3322,8 +3367,8 @@ void LLMeshRepository::notifyLoadedMeshes()  									<< ", GetMesh:  " << mesh1  									<< ", using version:  " << mGetMeshVersion  									<< LL_ENDL; +			}  		} -	}  		//popup queued error messages from background threads  		while (!mUploadErrorQ.empty()) @@ -3338,46 +3383,46 @@ void LLMeshRepository::notifyLoadedMeshes()  			S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count;  			if (mPendingRequests.size() > push_count) -		{ +			{  				// More requests than the high-water limit allows so  				// sort and forward the most important. -			//calculate "score" for pending requests +				//calculate "score" for pending requests -			//create score map -			std::map<LLUUID, F32> score_map; +				//create score map +				std::map<LLUUID, F32> score_map; -			for (U32 i = 0; i < 4; ++i) -			{ -				for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter) +				for (U32 i = 0; i < 4; ++i)  				{ -					F32 max_score = 0.f; -					for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) +					for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter)  					{ -						LLViewerObject* object = gObjectList.findObject(*obj_iter); - -						if (object) +						F32 max_score = 0.f; +						for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)  						{ -							LLDrawable* drawable = object->mDrawable; -							if (drawable) +							LLViewerObject* object = gObjectList.findObject(*obj_iter); +							 +							if (object)  							{ -								F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); -								max_score = llmax(max_score, cur_score); +								LLDrawable* drawable = object->mDrawable; +								if (drawable) +								{ +									F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); +									max_score = llmax(max_score, cur_score); +								}  							}  						} -					} -					score_map[iter->first.getSculptID()] = max_score; +						score_map[iter->first.getSculptID()] = max_score; +					}  				} -			} -			//set "score" for pending requests -			for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) -			{ -				iter->mScore = score_map[iter->mMeshParams.getSculptID()]; -			} +				//set "score" for pending requests +				for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) +				{ +					iter->mScore = score_map[iter->mMeshParams.getSculptID()]; +				} -			//sort by "score" +				//sort by "score"  				std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count,  								  mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater());  			} @@ -3588,7 +3633,6 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)  			}  		}  	} -  }  LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id) diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 1aa7041175..600ebf5914 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -5,7 +5,7 @@  *  * $LicenseInfo:firstyear=2013&license=viewerlgpl$  * Second Life Viewer Source Code -* Copyright (C) 2013, Linden Research, Inc. +* Copyright (C) 2014, Linden Research, Inc.  *  * This library is free software; you can redistribute it and/or  * modify it under the terms of the GNU Lesser General Public diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index d9a874be49..acd4cf2d8d 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -56,13 +56,13 @@  #include "llsdparam.h"  #include "llsdutil.h"  #include "llstartup.h" -#include "llsdserialize.h"  #include "httprequest.h"  #include "httphandler.h"  #include "httpresponse.h"  #include "bufferarray.h"  #include "bufferstream.h" +#include "llcorehttputil.h"  #include "llhttpretrypolicy.h" @@ -241,8 +241,10 @@ LLTrace::EventStatHandle<F64Milliseconds > LLTextureFetch::sCacheReadLatency("te  // Tuning/Parameterization Constants -static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40;		// Maximum requests to have active in HTTP -static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20;			// Active level at which to refill +static const S32 HTTP_PIPE_REQUESTS_HIGH_WATER = 100;		// Maximum requests to have active in HTTP (pipelined) +static const S32 HTTP_PIPE_REQUESTS_LOW_WATER = 50;			// Active level at which to refill +static const S32 HTTP_NONPIPE_REQUESTS_HIGH_WATER = 40; +static const S32 HTTP_NONPIPE_REQUESTS_LOW_WATER = 20;  // BUG-3323/SH-4375  // *NOTE:  This is a heuristic value.  Texture fetches have a habit of using a @@ -481,12 +483,12 @@ private:  	bool acquireHttpSemaphore()  		{  			llassert(! mHttpHasResource); -			if (mFetcher->mHttpSemaphore <= 0) +			if (mFetcher->mHttpSemaphore >= mFetcher->mHttpHighWater)  			{  				return false;  			}  			mHttpHasResource = true; -			mFetcher->mHttpSemaphore--; +			mFetcher->mHttpSemaphore++;  			return true;  		} @@ -496,7 +498,8 @@ private:  		{  			llassert(mHttpHasResource);  			mHttpHasResource = false; -			mFetcher->mHttpSemaphore++; +			mFetcher->mHttpSemaphore--; +			llassert_always(mFetcher->mHttpSemaphore >= 0);  		}  private: @@ -608,16 +611,16 @@ private:  	LLCore::HttpHandle		mHttpHandle;				// Handle of any active request  	LLCore::BufferArray	*	mHttpBufferArray;			// Refcounted pointer to response data  -	S32							mHttpPolicyClass; +	S32						mHttpPolicyClass;  	bool					mHttpActive;				// Active request to http library -	U32							mHttpReplySize,				// Actual received data size -								mHttpReplyOffset;			// Actual received data offset +	U32						mHttpReplySize,				// Actual received data size +							mHttpReplyOffset;			// Actual received data offset  	bool					mHttpHasResource;			// Counts against Fetcher's mHttpSemaphore  	// State history -	U32							mCacheReadCount, -								mCacheWriteCount, -								mResourceWaitCount;			// Requests entering WAIT_HTTP_RESOURCE2 +	U32						mCacheReadCount, +							mCacheWriteCount, +							mResourceWaitCount;			// Requests entering WAIT_HTTP_RESOURCE2  };  ////////////////////////////////////////////////////////////////////////////// @@ -1325,7 +1328,7 @@ bool LLTextureFetchWorker::doWork(S32 param)  			}  		} -		static LLCachedControl<bool> use_http(gSavedSettings,"ImagePipelineUseHTTP", true); +		static LLCachedControl<bool> use_http(gSavedSettings, "ImagePipelineUseHTTP", true);  // 		if (mHost != LLHost::invalid) get_url = false;  		if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. @@ -1346,20 +1349,20 @@ bool LLTextureFetchWorker::doWork(S32 param)  						LL_WARNS(LOG_TXT) << "trying to seek a non-default texture on the sim. Bad!" << LL_ENDL;  					}  					setUrl(http_url + "/?texture_id=" + mID.asString().c_str()); -					LL_DEBUGS("Texture") << "Texture URL " << mUrl << LL_ENDL; +					LL_DEBUGS(LOG_TXT) << "Texture URL: " << mUrl << LL_ENDL;  					mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id.  				}  				else  				{  					mCanUseHTTP = false ; -					LL_DEBUGS("Texture") << "Texture not available via HTTP: no URL " << mUrl << LL_ENDL; +					LL_DEBUGS(LOG_TXT) << "Texture not available via HTTP: empty URL." << LL_ENDL;  				}  			}  			else  			{  				// This will happen if not logged in or if a region deoes not have HTTP Texture enabled  				//LL_WARNS(LOG_TXT) << "Region not found for host: " << mHost << LL_ENDL; -				LL_DEBUGS("Texture") << "Texture not available via HTTP: no region " << mUrl << LL_ENDL; +				LL_DEBUGS(LOG_TXT) << "Texture not available via HTTP: no region " << mUrl << LL_ENDL;  				mCanUseHTTP = false;  			}  		} @@ -1473,6 +1476,9 @@ bool LLTextureFetchWorker::doWork(S32 param)  	if (mState == SEND_HTTP_REQ)  	{ +		// Also used in llmeshrepository +		static LLCachedControl<bool> disable_range_req(gSavedSettings, "HttpRangeRequestsDisable", false); +		  		if (! mCanUseHTTP)  		{  			releaseHttpSemaphore(); @@ -1528,22 +1534,47 @@ bool LLTextureFetchWorker::doWork(S32 param)  			mRequestedOffset -= 1;  			mRequestedSize += 1;  		} -		  		mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; -		if (!mUrl.empty()) -		{ -			mRequestedTimer.reset(); -			mLoaded = FALSE; -			mGetStatus = LLCore::HttpStatus(); -			mGetReason.clear(); -			LL_DEBUGS(LOG_TXT) << "HTTP GET: " << mID << " Offset: " << mRequestedOffset -							   << " Bytes: " << mRequestedSize -							   << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth -							   << LL_ENDL; -			// Will call callbackHttpGet when curl request completes -			// Only server bake images use the returned headers currently, for getting retry-after field. -			LLCore::HttpOptions *options = (mFTType == FTT_SERVER_BAKE) ? mFetcher->mHttpOptionsWithHeaders: mFetcher->mHttpOptions; +		if (mUrl.empty()) +		{ +			// *FIXME:  This should not be reachable except it has become +			// so after some recent 'work'.  Need to track this down +			// and illuminate the unenlightened. +			LL_WARNS(LOG_TXT) << "HTTP GET request failed for " << mID +							  << " on empty URL." << LL_ENDL; +			resetFormattedData(); +			releaseHttpSemaphore(); +			return true; // failed +		} +		 +		mRequestedTimer.reset(); +		mLoaded = FALSE; +		mGetStatus = LLCore::HttpStatus(); +		mGetReason.clear(); +		LL_DEBUGS(LOG_TXT) << "HTTP GET: " << mID << " Offset: " << mRequestedOffset +						   << " Bytes: " << mRequestedSize +						   << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth +						   << LL_ENDL; + +		// Will call callbackHttpGet when curl request completes +		// Only server bake images use the returned headers currently, for getting retry-after field. +		LLCore::HttpOptions *options = (mFTType == FTT_SERVER_BAKE) ? mFetcher->mHttpOptionsWithHeaders: mFetcher->mHttpOptions; +		if (disable_range_req) +		{ +			// 'Range:' requests may be disabled in which case all HTTP +			// texture fetches result in full fetches.  This can be used +			// by people with questionable ISPs or networking gear that +			// doesn't handle these well. +			mHttpHandle = mFetcher->mHttpRequest->requestGet(mHttpPolicyClass, +															 mWorkPriority, +															 mUrl, +															 options, +															 mFetcher->mHttpHeaders, +															 this); +		} +		else +		{  			mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass,  																	  mWorkPriority,  																	  mUrl, @@ -1557,7 +1588,11 @@ bool LLTextureFetchWorker::doWork(S32 param)  		}  		if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle)  		{ -			LL_WARNS(LOG_TXT) << "HTTP GET request failed for " << mID << LL_ENDL; +			LLCore::HttpStatus status(mFetcher->mHttpRequest->getStatus()); +			LL_WARNS(LOG_TXT) << "HTTP GET request failed for " << mID +							  << ", Status: " << status.toTerseString() +							  << " Reason: '" << status.toString() << "'" +							  << LL_ENDL;  			resetFormattedData();  			releaseHttpSemaphore();  			return true; // failed @@ -1613,10 +1648,6 @@ bool LLTextureFetchWorker::doWork(S32 param)  				else if (http_service_unavail == mGetStatus)  				{  					LL_INFOS_ONCE(LOG_TXT) << "Texture server busy (503): " << mUrl << LL_ENDL; -					LL_INFOS(LOG_TXT) << "503: HTTP GET failed for: " << mUrl -									  << " Status: " << mGetStatus.toHex() -									  << " Reason: '" << mGetReason << "'" -									  << LL_ENDL;  				}  				else if (http_not_sat == mGetStatus)  				{ @@ -1774,7 +1805,7 @@ bool LLTextureFetchWorker::doWork(S32 param)  	if (mState == DECODE_IMAGE)  	{ -		static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled", false); +		static LLCachedControl<bool> textures_decode_disabled(gSavedSettings, "TextureDecodeDisabled", false);  		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it  		if (textures_decode_disabled) @@ -2483,9 +2514,9 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image  	  mHttpOptions(NULL),  	  mHttpOptionsWithHeaders(NULL),  	  mHttpHeaders(NULL), -	  mHttpMetricsHeaders(NULL),  	  mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), -	  mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), +	  mHttpMetricsHeaders(NULL), +	  mHttpMetricsPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),  	  mTotalCacheReadCount(0U),  	  mTotalCacheWriteCount(0U),  	  mTotalResourceWaitCount(0U), @@ -2497,6 +2528,23 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image  	mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");  	mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), U32Bytes(gSavedSettings.getU32("TextureLoggingThreshold"))); +	LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); +	mHttpRequest = new LLCore::HttpRequest; +	mHttpOptions = new LLCore::HttpOptions; +	mHttpOptionsWithHeaders = new LLCore::HttpOptions; +	mHttpOptionsWithHeaders->setWantHeaders(true); +	mHttpHeaders = new LLCore::HttpHeaders; +	mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C); +	mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_TEXTURE); +	mHttpMetricsHeaders = new LLCore::HttpHeaders; +	mHttpMetricsHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); +	mHttpMetricsPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_REPORTING); +	mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; +	mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; +	mHttpSemaphore = 0; + +	// Conditionally construct debugger object after 'this' is +	// fully initialized.  	LLTextureFetchDebugger::sDebuggerEnabled = gSavedSettings.getBOOL("TextureFetchDebuggerEnabled");  	if(LLTextureFetchDebugger::isEnabled())  	{ @@ -2509,16 +2557,6 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image  		}  		mOriginFetchSource = mFetchSource;  	} -	 -	mHttpRequest = new LLCore::HttpRequest; -	mHttpOptions = new LLCore::HttpOptions; -	mHttpOptionsWithHeaders = new LLCore::HttpOptions; -	mHttpOptionsWithHeaders->setWantHeaders(true); -	mHttpHeaders = new LLCore::HttpHeaders; -	mHttpHeaders->append("Accept", "image/x-j2c"); -	mHttpMetricsHeaders = new LLCore::HttpHeaders; -	mHttpMetricsHeaders->append("Content-Type", "application/llsd+xml"); -	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_TEXTURE);  }  LLTextureFetch::~LLTextureFetch() @@ -2990,6 +3028,20 @@ bool LLTextureFetch::runCondition()  // Threads:  Ttf  void LLTextureFetch::commonUpdate()  { +	// Update low/high water levels based on pipelining.  We pick +	// up setting eventually, so the semaphore/request level can +	// fall outside the [0..HIGH_WATER] range.  Expect that. +	if (LLAppViewer::instance()->getAppCoreHttp().isPipelined(LLAppCoreHttp::AP_TEXTURE)) +	{ +		mHttpHighWater = HTTP_PIPE_REQUESTS_HIGH_WATER; +		mHttpLowWater = HTTP_PIPE_REQUESTS_LOW_WATER; +	} +	else +	{ +		mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; +		mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; +	} +  	// Release waiters  	releaseHttpWaiters(); @@ -3651,8 +3703,16 @@ void LLTextureFetch::releaseHttpWaiters()  {  	// Use mHttpSemaphore rather than mHTTPTextureQueue.size()  	// to avoid a lock.   -	if (mHttpSemaphore < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - HTTP_REQUESTS_IN_QUEUE_LOW_WATER)) +	if (mHttpSemaphore >= mHttpLowWater)  		return; +	S32 needed(mHttpHighWater - mHttpSemaphore); +	if (needed <= 0) +	{ +		// Would only happen if High/LowWater were changed behind +		// our back.  In that case, defer fill until usage falls within +		// limits. +		return; +	}  	// Quickly make a copy of all the LLUIDs.  Get off the  	// mutex as early as possible. @@ -3701,10 +3761,10 @@ void LLTextureFetch::releaseHttpWaiters()  	tids.clear();  	// Sort into priority order, if necessary and only as much as needed -	if (tids2.size() > mHttpSemaphore) +	if (tids2.size() > needed)  	{  		LLTextureFetchWorker::Compare compare; -		std::partial_sort(tids2.begin(), tids2.begin() + mHttpSemaphore, tids2.end(), compare); +		std::partial_sort(tids2.begin(), tids2.begin() + needed, tids2.end(), compare);  	}  	// Release workers up to the high water mark.  Since we aren't @@ -3968,7 +4028,9 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)  	// Update sequence number  	if (S32_MAX == ++report_sequence) +	{  		report_sequence = 0; +	}  	reporting_started = true;  	// Limit the size of the stats report if necessary. @@ -3977,18 +4039,15 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)  	if (! mCapsURL.empty())  	{ -		LLCore::BufferArray * ba = new LLCore::BufferArray; -		LLCore::BufferArrayStream bas(ba); -		LLSDSerialize::toXML(sd, bas); -		 -		fetcher->getHttpRequest().requestPost(fetcher->getPolicyClass(), -											  report_priority, -											  mCapsURL, -											  ba, -											  NULL, -											  fetcher->getMetricsHeaders(), -											  handler); -		ba->release(); +		// Don't care about handle, this is a fire-and-forget operation.   +		LLCoreHttpUtil::requestPostWithLLSD(&fetcher->getHttpRequest(), +											fetcher->getMetricsPolicyClass(), +											report_priority, +											mCapsURL, +											sd, +											NULL, +											fetcher->getMetricsHeaders(), +											handler);  		LLTextureFetch::svMetricsDataBreak = false;  	}  	else @@ -3999,7 +4058,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)  	// In QA mode, Metrics submode, log the result for ease of testing  	if (fetcher->isQAMode())  	{ -		LL_INFOS("Textures") << ll_pretty_print_sd(sd) << LL_ENDL; +		LL_INFOS(LOG_TXT) << ll_pretty_print_sd(sd) << LL_ENDL;  	}  	return true; @@ -4169,7 +4228,7 @@ void LLTextureFetchDebugger::init()  	if (! mHttpHeaders)  	{  		mHttpHeaders = new LLCore::HttpHeaders; -		mHttpHeaders->append("Accept", "image/x-j2c"); +		mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C);  	}  } @@ -4544,7 +4603,7 @@ S32 LLTextureFetchDebugger::fillCurlQueue()  		mNbCurlCompleted = mFetchingHistory.size();  		return 0;  	} -	if (mNbCurlRequests > HTTP_REQUESTS_IN_QUEUE_LOW_WATER) +	if (mNbCurlRequests > HTTP_NONPIPE_REQUESTS_LOW_WATER)  	{  		return mNbCurlRequests;  	} @@ -4577,7 +4636,7 @@ S32 LLTextureFetchDebugger::fillCurlQueue()  			mFetchingHistory[i].mHttpHandle = handle;  			mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS;  			mNbCurlRequests++; -			if (mNbCurlRequests >= HTTP_REQUESTS_IN_QUEUE_HIGH_WATER)	// emulate normal pipeline +			if (mNbCurlRequests >= HTTP_NONPIPE_REQUESTS_HIGH_WATER)	// emulate normal pipeline  			{  				break;  			} diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index c4da2e8685..27779a31e0 100755 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -179,6 +179,9 @@ public:  	// Threads:  T*  	LLCore::HttpHeaders * getMetricsHeaders() const	{ return mHttpMetricsHeaders; } +	// Threads:  T* +	LLCore::HttpRequest::policy_t getMetricsPolicyClass() const { return mHttpMetricsPolicyClass; } +  	bool isQAMode() const				{ return mQAMode; }  	// ---------------------------------- @@ -354,9 +357,12 @@ private:  	LLCore::HttpOptions *				mHttpOptions;					// Ttf  	LLCore::HttpOptions *				mHttpOptionsWithHeaders;		// Ttf  	LLCore::HttpHeaders *				mHttpHeaders;					// Ttf -	LLCore::HttpHeaders *				mHttpMetricsHeaders;			// Ttf  	LLCore::HttpRequest::policy_t		mHttpPolicyClass;				// T* - +	LLCore::HttpHeaders *				mHttpMetricsHeaders;			// Ttf +	LLCore::HttpRequest::policy_t		mHttpMetricsPolicyClass;		// T* +	S32									mHttpHighWater;					// Ttf +	S32									mHttpLowWater;					// Ttf +	  	// We use a resource semaphore to keep HTTP requests in  	// WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the  	// transport.  This keeps them near where they can be cheaply @@ -364,7 +370,11 @@ private:  	// where it's more expensive to get at them.  Requests in either  	// SEND_HTTP_REQ or WAIT_HTTP_REQ charge against the semaphore  	// and tracking state transitions is critical to liveness. -	LLAtomicS32							mHttpSemaphore;					// Ttf + Tmain +	// +	// Originally implemented as a traditional semaphore (heading towards +	// zero), it now is an outstanding request count that is allowed to +	// exceed the high water level (but not go below zero). +	LLAtomicS32							mHttpSemaphore;					// Ttf  	typedef std::set<LLUUID> wait_http_res_queue_t;  	wait_http_res_queue_t				mHttpWaitResource;				// Mfnq diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 4e4c3471be..d364fce45a 100755 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2002&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2014, Linden Research, Inc.   *    * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -75,6 +75,10 @@ void no_op_inventory_func(const LLUUID&) {}  void no_op_llsd_func(const LLSD&) {}  void no_op() {} +static const char * const LOG_INV("Inventory"); +static const char * const LOG_LOCAL("InventoryLocalize"); +static const char * const LOG_NOTECARD("copy_inventory_from_notecard"); +  ///----------------------------------------------------------------------------  /// Helper class to store special inventory item names and their localized values.  ///---------------------------------------------------------------------------- @@ -189,7 +193,7 @@ public:  	 */  	bool localizeInventoryObjectName(std::string& object_name)  	{ -		LL_DEBUGS("InventoryLocalize") << "Searching for localization: " << object_name << LL_ENDL; +		LL_DEBUGS(LOG_LOCAL) << "Searching for localization: " << object_name << LL_ENDL;  		std::map<std::string, std::string>::const_iterator dictionary_iter = mInventoryItemsDict.find(object_name); @@ -197,7 +201,7 @@ public:  		if(found)  		{  			object_name = dictionary_iter->second; -			LL_DEBUGS("InventoryLocalize") << "Found, new name is: " << object_name << LL_ENDL; +			LL_DEBUGS(LOG_LOCAL) << "Found, new name is: " << object_name << LL_ENDL;  		}  		return found;  	} @@ -307,8 +311,8 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLViewerInventoryItem* other)  	copyViewerItem(other);  	if (!mIsComplete)  	{ -		LL_WARNS() << "LLViewerInventoryItem copy constructor for incomplete item" -			<< mUUID << LL_ENDL; +		LL_WARNS(LOG_INV) << "LLViewerInventoryItem copy constructor for incomplete item" +						  << mUUID << LL_ENDL;  	}  } @@ -355,16 +359,16 @@ void LLViewerInventoryItem::updateServer(BOOL is_new) const  	{  		// *FIX: deal with this better.  		// If we're crashing here then the UI is incorrectly enabled. -		LL_ERRS() << "LLViewerInventoryItem::updateServer() - for incomplete item" -			   << LL_ENDL; +		LL_ERRS(LOG_INV) << "LLViewerInventoryItem::updateServer() - for incomplete item" +						 << LL_ENDL;  		return;  	}  	if(gAgent.getID() != mPermissions.getOwner())  	{  		// *FIX: deal with this better. -		LL_WARNS() << "LLViewerInventoryItem::updateServer() - for unowned item " -				   << ll_pretty_print_sd(this->asLLSD()) -				   << LL_ENDL; +		LL_WARNS(LOG_INV) << "LLViewerInventoryItem::updateServer() - for unowned item " +						  << ll_pretty_print_sd(this->asLLSD()) +						  << LL_ENDL;  		return;  	}  	LLInventoryModel::LLCategoryUpdate up(mParentUUID, is_new ? 1 : 0); @@ -392,18 +396,18 @@ void LLViewerInventoryItem::fetchFromServer(void) const  		// we have to check region. It can be null after region was destroyed. See EXT-245  		if (region)  		{ -		  if(gAgent.getID() != mPermissions.getOwner()) -		    { +		  if (gAgent.getID() != mPermissions.getOwner()) +		  {  		      url = region->getCapability("FetchLib2"); -		    } +		  }  		  else -		    {	 +		  {	  		      url = region->getCapability("FetchInventory2"); -		    } +		  }  		}  		else  		{ -			LL_WARNS() << "Agent Region is absent" << LL_ENDL; +			LL_WARNS(LOG_INV) << "Agent Region is absent" << LL_ENDL;  		}  		if (!url.empty()) @@ -413,7 +417,8 @@ void LLViewerInventoryItem::fetchFromServer(void) const  			body["items"][0]["owner_id"]	= mPermissions.getOwner();  			body["items"][0]["item_id"]		= mUUID; -			LLHTTPClient::post(url, body, new LLInventoryModel::fetchInventoryResponder(body)); +			LLInventoryModel::FetchItemHttpHandler * handler(new LLInventoryModel::FetchItemHttpHandler(body)); +			gInventory.requestPost(true, url, body, handler, "Inventory Item");  		}  		else  		{ @@ -649,7 +654,7 @@ bool LLViewerInventoryCategory::fetch()  	if((VERSION_UNKNOWN == getVersion())  	   && mDescendentsRequested.hasExpired())	//Expired check prevents multiple downloads.  	{ -		LL_DEBUGS("InventoryFetch") << "Fetching category children: " << mName << ", UUID: " << mUUID << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "Fetching category children: " << mName << ", UUID: " << mUUID << LL_ENDL;  		const F32 FETCH_TIMER_EXPIRY = 10.0f;  		mDescendentsRequested.reset();  		mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY); @@ -674,7 +679,7 @@ bool LLViewerInventoryCategory::fetch()  		}  		else  		{ -			LL_WARNS() << "agent region is null" << LL_ENDL; +			LL_WARNS(LOG_INV) << "agent region is null" << LL_ENDL;  		}  		if (!url.empty()) //Capability found.  Build up LLSD and use it.  		{ @@ -682,7 +687,8 @@ bool LLViewerInventoryCategory::fetch()  		}  		else  		{	//Deprecated, but if we don't have a capability, use the old system. -			LL_INFOS() << "FetchInventoryDescendents2 capability not found.  Using deprecated UDP message." << LL_ENDL; +			LL_INFOS(LOG_INV) << "FetchInventoryDescendents2 capability not found.  Using deprecated UDP message." << LL_ENDL; +			  			LLMessageSystem* msg = gMessageSystem;  			msg->newMessage("FetchInventoryDescendents");  			msg->nextBlock("AgentData"); @@ -777,8 +783,8 @@ bool LLViewerInventoryCategory::importFileLocal(LLFILE* fp)  		}  		else  		{ -			LL_WARNS() << "unknown keyword '" << keyword -					<< "' in inventory import category "  << mUUID << LL_ENDL; +			LL_WARNS(LOG_INV) << "unknown keyword '" << keyword +							  << "' in inventory import category "  << mUUID << LL_ENDL;  		}  	}  	return true; @@ -906,7 +912,7 @@ LLInventoryCallbackManager::LLInventoryCallbackManager() :  {  	if( sInstance != NULL )  	{ -		LL_WARNS() << "LLInventoryCallbackManager::LLInventoryCallbackManager: unexpected multiple instances" << LL_ENDL; +		LL_WARNS(LOG_INV) << "LLInventoryCallbackManager::LLInventoryCallbackManager: unexpected multiple instances" << LL_ENDL;  		return;  	}  	sInstance = this; @@ -916,7 +922,7 @@ LLInventoryCallbackManager::~LLInventoryCallbackManager()  {  	if( sInstance != this )  	{ -		LL_WARNS() << "LLInventoryCallbackManager::~LLInventoryCallbackManager: unexpected multiple instances" << LL_ENDL; +		LL_WARNS(LOG_INV) << "LLInventoryCallbackManager::~LLInventoryCallbackManager: unexpected multiple instances" << LL_ENDL;  		return;  	}  	sInstance = NULL; @@ -1144,7 +1150,7 @@ void link_inventory_object(const LLUUID& category,  {  	if (!baseobj)  	{ -		LL_WARNS() << "Attempt to link to non-existent object" << LL_ENDL; +		LL_WARNS(LOG_INV) << "Attempt to link to non-existent object" << LL_ENDL;  		return;  	} @@ -1178,7 +1184,7 @@ void link_inventory_array(const LLUUID& category,  		const LLInventoryObject* baseobj = *it;  		if (!baseobj)  		{ -			LL_WARNS() << "attempt to link to unknown object" << LL_ENDL; +			LL_WARNS(LOG_INV) << "attempt to link to unknown object" << LL_ENDL;  			continue;  		} @@ -1187,7 +1193,7 @@ void link_inventory_array(const LLUUID& category,  			// Fail if item can be found but is of a type that can't be linked.  			// Arguably should fail if the item can't be found too, but that could  			// be a larger behavioral change. -			LL_WARNS() << "attempt to link an unlinkable object, type = " << baseobj->getActualType() << LL_ENDL; +			LL_WARNS(LOG_INV) << "attempt to link an unlinkable object, type = " << baseobj->getActualType() << LL_ENDL;  			continue;  		} @@ -1223,7 +1229,7 @@ void link_inventory_array(const LLUUID& category,  			}  			else  			{ -				LL_WARNS() << "could not convert object into an item or category: " << baseobj->getUUID() << LL_ENDL; +				LL_WARNS(LOG_INV) << "could not convert object into an item or category: " << baseobj->getUUID() << LL_ENDL;  				continue;  			}  		} @@ -1237,10 +1243,10 @@ void link_inventory_array(const LLUUID& category,  		links.append(link);  #ifndef LL_RELEASE_FOR_DOWNLOAD -		LL_DEBUGS("Inventory") << "Linking Object [ name:" << baseobj->getName()  -							   << " UUID:" << baseobj->getUUID()  -							   << " ] into Category [ name:" << cat_name  -							   << " UUID:" << category << " ] " << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "Linking Object [ name:" << baseobj->getName()  +						   << " UUID:" << baseobj->getUUID()  +						   << " ] into Category [ name:" << cat_name  +						   << " UUID:" << category << " ] " << LL_ENDL;  #endif  	} @@ -1331,7 +1337,7 @@ void update_inventory_item(  	if (!ais_ran)  	{  		LLPointer<LLViewerInventoryItem> obj = gInventory.getItem(item_id); -		LL_DEBUGS("Inventory") << "item_id: [" << item_id << "] name " << (update_item ? update_item->getName() : "(NOT FOUND)") << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (update_item ? update_item->getName() : "(NOT FOUND)") << LL_ENDL;  		if(obj)  		{  			LLMessageSystem* msg = gMessageSystem; @@ -1373,7 +1379,7 @@ void update_inventory_item(  	if (!ais_ran)  	{  		LLPointer<LLViewerInventoryItem> obj = gInventory.getItem(item_id); -		LL_DEBUGS("Inventory") << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL;  		if(obj)  		{  			LLPointer<LLViewerInventoryItem> new_item(new LLViewerInventoryItem); @@ -1408,7 +1414,7 @@ void update_inventory_category(  	LLPointer<LLInventoryCallback> cb)  {  	LLPointer<LLViewerInventoryCategory> obj = gInventory.getCategory(cat_id); -	LL_DEBUGS("Inventory") << "cat_id: [" << cat_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL;  	if(obj)  	{  		if (LLFolderType::lookupIsProtectedType(obj->getPreferredType())) @@ -1471,7 +1477,7 @@ void remove_inventory_item(  	}  	else  	{ -		LL_DEBUGS("Inventory") << "item_id: [" << item_id << "] name " << "(NOT FOUND)" << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << "(NOT FOUND)" << LL_ENDL;  	}  } @@ -1482,7 +1488,7 @@ void remove_inventory_item(  	if(obj)  	{  		const LLUUID item_id(obj->getUUID()); -		LL_DEBUGS("Inventory") << "item_id: [" << item_id << "] name " << obj->getName() << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << obj->getName() << LL_ENDL;  		if (AISCommand::isAPIAvailable())  		{  			LLPointer<AISCommand> cmd_ptr = new RemoveItemCommand(item_id, cb); @@ -1511,7 +1517,7 @@ void remove_inventory_item(  	else  	{  		// *TODO: Clean up callback? -		LL_WARNS() << "remove_inventory_item called for invalid or nonexistent item." << LL_ENDL; +		LL_WARNS(LOG_INV) << "remove_inventory_item called for invalid or nonexistent item." << LL_ENDL;  	}  } @@ -1529,7 +1535,7 @@ public:  		LLInventoryModel::EHasChildren children = gInventory.categoryHasChildren(mID);  		if(children != LLInventoryModel::CHILDREN_NO)  		{ -			LL_WARNS() << "remove descendents failed, cannot remove category " << LL_ENDL; +			LL_WARNS(LOG_INV) << "remove descendents failed, cannot remove category " << LL_ENDL;  		}  		else  		{ @@ -1545,7 +1551,7 @@ void remove_inventory_category(  	const LLUUID& cat_id,  	LLPointer<LLInventoryCallback> cb)  { -	LL_DEBUGS("Inventory") << "cat_id: [" << cat_id << "] " << LL_ENDL; +	LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] " << LL_ENDL;  	LLPointer<LLViewerInventoryCategory> obj = gInventory.getCategory(cat_id);  	if(obj)  	{ @@ -1566,7 +1572,7 @@ void remove_inventory_category(  			LLInventoryModel::EHasChildren children = gInventory.categoryHasChildren(cat_id);  			if(children != LLInventoryModel::CHILDREN_NO)  			{ -				LL_DEBUGS("Inventory") << "Will purge descendents first before deleting category " << cat_id << LL_ENDL; +				LL_DEBUGS(LOG_INV) << "Will purge descendents first before deleting category " << cat_id << LL_ENDL;  				LLPointer<LLInventoryCallback> wrap_cb = new LLRemoveCategoryOnDestroy(cat_id, cb);   				purge_descendents_of(cat_id, wrap_cb);  				return; @@ -1592,7 +1598,7 @@ void remove_inventory_category(  	}  	else  	{ -		LL_WARNS() << "remove_inventory_category called for invalid or nonexistent item " << cat_id << LL_ENDL; +		LL_WARNS(LOG_INV) << "remove_inventory_category called for invalid or nonexistent item " << cat_id << LL_ENDL;  	}  } @@ -1620,7 +1626,7 @@ void purge_descendents_of(const LLUUID& id, LLPointer<LLInventoryCallback> cb)  	LLInventoryModel::EHasChildren children = gInventory.categoryHasChildren(id);  	if(children == LLInventoryModel::CHILDREN_NO)  	{ -		LL_DEBUGS("Inventory") << "No descendents to purge for " << id << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "No descendents to purge for " << id << LL_ENDL;  		return;  	}  	LLPointer<LLViewerInventoryCategory> cat = gInventory.getCategory(id); @@ -1629,8 +1635,8 @@ void purge_descendents_of(const LLUUID& id, LLPointer<LLInventoryCallback> cb)  		if (LLClipboard::instance().hasContents() && LLClipboard::instance().isCutMode())  		{  			// Something on the clipboard is in "cut mode" and needs to be preserved -			LL_DEBUGS("Inventory") << "purge_descendents_of clipboard case " << cat->getName() -								   << " iterate and purge non hidden items" << LL_ENDL; +			LL_DEBUGS(LOG_INV) << "purge_descendents_of clipboard case " << cat->getName() +							   << " iterate and purge non hidden items" << LL_ENDL;  			LLInventoryModel::cat_array_t* categories;  			LLInventoryModel::item_array_t* items;  			// Get the list of direct descendants in tha categoy passed as argument @@ -1665,7 +1671,7 @@ void purge_descendents_of(const LLUUID& id, LLPointer<LLInventoryCallback> cb)  			else // no cap  			{  				// Fast purge -				LL_DEBUGS("Inventory") << "purge_descendents_of fast case " << cat->getName() << LL_ENDL; +				LL_DEBUGS(LOG_INV) << "purge_descendents_of fast case " << cat->getName() << LL_ENDL;  				// send it upstream  				LLMessageSystem* msg = gMessageSystem; @@ -1708,9 +1714,9 @@ void copy_inventory_from_notecard(const LLUUID& destination_id,  {  	if (NULL == src)  	{ -		LL_WARNS("copy_inventory_from_notecard") << "Null pointer to item was passed for object_id " -												 << object_id << " and notecard_inv_id " -												 << notecard_inv_id << LL_ENDL; +		LL_WARNS(LOG_NOTECARD) << "Null pointer to item was passed for object_id " +							   << object_id << " and notecard_inv_id " +							   << notecard_inv_id << LL_ENDL;  		return;  	} @@ -1730,9 +1736,9 @@ void copy_inventory_from_notecard(const LLUUID& destination_id,  	if (! viewer_region)  	{ -        LL_WARNS("copy_inventory_from_notecard") << "Can't find region from object_id " -                                                 << object_id << " or gAgent" -                                                 << LL_ENDL; +        LL_WARNS(LOG_NOTECARD) << "Can't find region from object_id " +							   << object_id << " or gAgent" +							   << LL_ENDL;          return;      } @@ -1740,9 +1746,9 @@ void copy_inventory_from_notecard(const LLUUID& destination_id,  	std::string url = viewer_region->getCapability("CopyInventoryFromNotecard");  	if (url.empty())  	{ -        LL_WARNS("copy_inventory_from_notecard") << "There is no 'CopyInventoryFromNotecard' capability" -												 << " for region: " << viewer_region->getName() -                                                 << LL_ENDL; +        LL_WARNS(LOG_NOTECARD) << "There is no 'CopyInventoryFromNotecard' capability" +							   << " for region: " << viewer_region->getName() +							   << LL_ENDL;  		return;  	} @@ -1816,15 +1822,15 @@ void slam_inventory_folder(const LLUUID& folder_id,  {  	if (AISCommand::isAPIAvailable())  	{ -		LL_DEBUGS("Avatar") << "using AISv3 to slam folder, id " << folder_id -							<< " new contents: " << ll_pretty_print_sd(contents) << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "using AISv3 to slam folder, id " << folder_id +						   << " new contents: " << ll_pretty_print_sd(contents) << LL_ENDL;  		LLPointer<AISCommand> cmd_ptr = new SlamFolderCommand(folder_id, contents, cb);  		cmd_ptr->run_command();  	}  	else // no cap  	{ -		LL_DEBUGS("Avatar") << "using item-by-item calls to slam folder, id " << folder_id -							<< " new contents: " << ll_pretty_print_sd(contents) << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "using item-by-item calls to slam folder, id " << folder_id +						   << " new contents: " << ll_pretty_print_sd(contents) << LL_ENDL;  		for (LLSD::array_const_iterator it = contents.beginArray();  			 it != contents.endArray();  			 ++it) @@ -1926,7 +1932,7 @@ void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge,  		}  		else  		{ -			LL_WARNS() << "Can't create unrecognized type " << type_name << LL_ENDL; +			LL_WARNS(LOG_INV) << "Can't create unrecognized type " << type_name << LL_ENDL;  		}  	}  	panel->getRootFolder()->setNeedsAutoRename(TRUE);	 @@ -2161,7 +2167,7 @@ LLViewerInventoryItem *LLViewerInventoryItem::getLinkedItem() const  		LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID);  		if (linked_item && linked_item->getIsLinkType())  		{ -			LL_WARNS() << "Warning: Accessing link to link" << LL_ENDL; +			LL_WARNS(LOG_INV) << "Warning: Accessing link to link" << LL_ENDL;  			return NULL;  		}  		return linked_item; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 4cd60336b3..1a916d5942 100755 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2002&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2014, Linden Research, Inc.   *    * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -8287,7 +8287,7 @@ class LLWorldEnableEnvSettings : public view_listener_t  			}  			else  			{ -				llwarns << "Unknown item" << llendl; +				LL_WARNS() << "Unknown time-of-day item:  " << tod << LL_ENDL;  			}  		}  		return result; diff --git a/indra/newview/tests/llslurl_test.cpp b/indra/newview/tests/llslurl_test.cpp index 86229ad636..2bc0d5a086 100755 --- a/indra/newview/tests/llslurl_test.cpp +++ b/indra/newview/tests/llslurl_test.cpp @@ -6,7 +6,7 @@   *   * $LicenseInfo:firstyear=2009&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -31,6 +31,15 @@  #include "../llslurl.h"  #include "../../llxml/llcontrol.h"  #include "llsdserialize.h" + +namespace +{ + +// Should not collide with other test programs creating temp files. +static const char * const TEST_FILENAME("llslurl_test.xml"); + +} +	  //----------------------------------------------------------------------------  // Mock objects for the dependencies of the code we're testing @@ -143,11 +152,11 @@ namespace tut  	template<> template<>  	void slurlTestObject::test<1>()  	{ -		llofstream gridfile("grid_test.xml"); +		llofstream gridfile(TEST_FILENAME);  		gridfile << gSampleGridFile;  		gridfile.close(); -		LLGridManager::getInstance()->initialize("grid_test.xml"); +		LLGridManager::getInstance()->initialize(TEST_FILENAME);  		LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com"); @@ -260,11 +269,11 @@ namespace tut  	template<> template<>  	void slurlTestObject::test<2>()  	{ -		llofstream gridfile("grid_test.xml"); +		llofstream gridfile(TEST_FILENAME);  		gridfile << gSampleGridFile;  		gridfile.close(); -		LLGridManager::getInstance()->initialize("grid_test.xml"); +		LLGridManager::getInstance()->initialize(TEST_FILENAME);  		LLSLURL slurl = LLSLURL("my.grid.com", "my region");  		ensure_equals("grid/region - type", slurl.getType(), LLSLURL::LOCATION); @@ -293,11 +302,11 @@ namespace tut  	template<> template<>  	void slurlTestObject::test<3>()  	{ -		llofstream gridfile("grid_test.xml"); +		llofstream gridfile(TEST_FILENAME);  		gridfile << gSampleGridFile;  		gridfile.close(); -		LLGridManager::getInstance()->initialize("grid_test.xml"); +		LLGridManager::getInstance()->initialize(TEST_FILENAME);  		LLGridManager::getInstance()->setGridChoice("my.grid.com");  		LLSLURL slurl = LLSLURL("https://my.grid.com/region/my%20region/1/2/3"); diff --git a/indra/newview/tests/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp index 7ad7947ca4..0eb0ab6500 100755 --- a/indra/newview/tests/llviewernetwork_test.cpp +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -6,7 +6,7 @@   *   * $LicenseInfo:firstyear=2009&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2014, Linden Research, Inc.   *   * This library is free software; you can redistribute it and/or   * modify it under the terms of the GNU Lesser General Public @@ -31,6 +31,13 @@  #include "../../llxml/llcontrol.h"  #include "llfile.h" +namespace +{ + +// Should not collide with other test programs creating temp files. +static const char * const TEST_FILENAME("llviewernetwork_test.xml"); + +}  //----------------------------------------------------------------------------  // Mock objects for the dependencies of the code we're testing @@ -143,7 +150,7 @@ namespace tut  	{  		viewerNetworkTest()  		{ -			LLFile::remove("grid_test.xml"); +			LLFile::remove(TEST_FILENAME);  			gCmdLineLoginURI.clear();  			gCmdLineGridChoice.clear();  			gCmdLineHelperURI.clear(); @@ -152,7 +159,7 @@ namespace tut  		}  		~viewerNetworkTest()  		{ -			LLFile::remove("grid_test.xml"); +			LLFile::remove(TEST_FILENAME);  		}  	}; @@ -170,7 +177,7 @@ namespace tut  	{  		LLGridManager *manager = LLGridManager::getInstance();  		// grid file doesn't exist -		manager->initialize("grid_test.xml"); +		manager->initialize(TEST_FILENAME);  		// validate that some of the defaults are available.  		std::map<std::string, std::string> known_grids = manager->getKnownGrids();  		ensure_equals("Known grids is a string-string map of size 2", known_grids.size(), 2); @@ -238,11 +245,11 @@ namespace tut  	template<> template<>  	void viewerNetworkTestObject::test<2>()  	{ -		llofstream gridfile("grid_test.xml"); +		llofstream gridfile(TEST_FILENAME);  		gridfile << gSampleGridFile;  		gridfile.close(); -		LLGridManager::getInstance()->initialize("grid_test.xml"); +		LLGridManager::getInstance()->initialize(TEST_FILENAME);  		std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids();  		ensure_equals("adding a grid via a grid file increases known grid size",4,   					  known_grids.size()); @@ -369,11 +376,11 @@ namespace tut  	void viewerNetworkTestObject::test<7>()  	{  		// adding a grid with simply a name will populate the values. -		llofstream gridfile("grid_test.xml"); +		llofstream gridfile(TEST_FILENAME);  		gridfile << gSampleGridFile;  		gridfile.close(); -		LLGridManager::getInstance()->initialize("grid_test.xml"); +		LLGridManager::getInstance()->initialize(TEST_FILENAME);  		LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com");  		ensure_equals("getGridLabel", | 
