diff options
Diffstat (limited to 'indra/llcorehttp')
| -rwxr-xr-x | indra/llcorehttp/_httpinternal.h | 6 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httplibcurl.h | 15 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httpoperation.h | 8 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httpoprequest.cpp | 19 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httpopsetget.cpp | 93 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httpopsetget.h | 27 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httppolicy.cpp | 110 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httppolicy.h | 39 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httppolicyclass.cpp | 37 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httppolicyclass.h | 19 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httppolicyglobal.cpp | 66 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httppolicyglobal.h | 23 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httpservice.cpp | 170 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httpservice.h | 52 | ||||
| -rwxr-xr-x | indra/llcorehttp/examples/http_texture_load.cpp | 7 | ||||
| -rwxr-xr-x | indra/llcorehttp/httpcommon.h | 109 | ||||
| -rwxr-xr-x | indra/llcorehttp/httprequest.cpp | 107 | ||||
| -rwxr-xr-x | indra/llcorehttp/httprequest.h | 128 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_httprequest.hpp | 140 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_llcorehttp_peer.py | 49 | 
20 files changed, 863 insertions, 361 deletions
| diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index f085ca3b91..80f4f34942 100755 --- a/indra/llcorehttp/_httpinternal.h +++ b/indra/llcorehttp/_httpinternal.h @@ -36,7 +36,8 @@  // General library to-do list  //  // - Implement policy classes.  Structure is mostly there just didn't -//   need it for the first consumer. +//   need it for the first consumer.  [Classes are there.  More +//   advanced features, like borrowing, aren't there yet.]  // - Consider Removing 'priority' from the request interface.  Its use  //   in an always active class can lead to starvation of low-priority  //   requests.  Requires coodination of priority values across all @@ -46,6 +47,7 @@  //   may not really need it.  // - Set/get for global policy and policy classes is clumsy.  Rework  //   it heading in a direction that allows for more dynamic behavior. +//   [Mostly fixed]  // - Move HttpOpRequest::prepareRequest() to HttpLibcurl for the  //   pedantic.  // - Update downloader and other long-duration services are going to @@ -73,7 +75,7 @@  //   the main source file.  // - Expand areas of usage eventually leading to the removal of LLCurl.  //   Rough order of expansion: -//   .  Mesh fetch +//   .  Mesh fetch [Underway]  //   .  Avatar names  //   .  Group membership lists  //   .  Caps access in general diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 611f029ef5..0ec90437bb 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, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -71,16 +71,22 @@ public:  	///  	/// @return			Indication of how long this method is  	///					willing to wait for next service call. +	/// +	/// Threading:  called by worker thread.  	HttpService::ELoopSpeed processTransport();  	/// Add request to the active list.  Caller is expected to have  	/// provided us with a reference count on the op to hold the  	/// request.  (No additional references will be added.) +	/// +	/// Threading:  called by worker thread.  	void addOp(HttpOpRequest * op);  	/// One-time call to set the number of policy classes to be  	/// serviced and to create the resources for each.  Value  	/// must agree with HttpPolicy::setPolicies() call. +	/// +	/// Threading:  called by init thread.  	void start(int policy_count);  	/// Synchronously stop libcurl operations.  All active requests @@ -91,9 +97,13 @@ public:  	/// respective reply queues.  	///  	/// Can be restarted with a start() call. +	/// +	/// Threading:  called by worker thread.  	void shutdown();  	/// Return global and per-class counts of active requests. +	/// +	/// Threading:  called by worker thread.  	int getActiveCount() const;  	int getActiveCountInClass(int policy_class) const; @@ -103,6 +113,7 @@ public:  	///  	/// @return			True if handle was found and operation canceled.  	/// +	/// Threading:  called by worker thread.  	bool cancel(HttpHandle handle);  protected: @@ -121,7 +132,7 @@ protected:  	HttpService *		mService;				// Simple reference, not owner  	active_set_t		mActiveOps;  	int					mPolicyCount; -	CURLM **			mMultiHandles; +	CURLM **			mMultiHandles;			// One handle per policy class  }; // end class HttpLibcurl  }  // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 914627fad0..937a61187d 100755 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -72,7 +72,7 @@ class HttpService;  class HttpOperation : public LLCoreInt::RefCounted  {  public: -	/// Threading:  called by a consumer/application thread. +	/// Threading:  called by consumer thread.  	HttpOperation();  protected: @@ -108,7 +108,7 @@ public:  	///							by the worker thread.  This is passible data  	///							until notification is performed.  	/// -	/// Threading:  called by application thread. +	/// Threading:  called by consumer thread.  	///  	void setReplyPath(HttpReplyQueue * reply_queue,  					  HttpHandler * handler); @@ -141,7 +141,7 @@ public:  	/// call to HttpRequest::update().  This method does the necessary  	/// dispatching.  	/// -	/// Threading:  called by application thread. +	/// Threading:  called by consumer thread.  	///  	virtual void visitNotifier(HttpRequest *); diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index ce0fc605ab..d72f8f6119 100755 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -402,7 +402,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)  	// *FIXME:  better error handling later  	HttpStatus status; -	// Get policy options +	// Get global policy options  	HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions());  	mCurlHandle = curl_easy_init(); @@ -441,30 +441,27 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)  	curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);  	curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); -	const std::string * opt_value(NULL); -	long opt_long(0L); -	policy.get(HttpRequest::GP_LLPROXY, &opt_long); -	if (opt_long) +	if (policy.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.get(HttpRequest::GP_HTTP_PROXY, &opt_value)) +	else if (policy.mHttpProxy.size())  	{  		// *TODO:  This is fine for now but get fuller socks5/  		// authentication thing going later.... -		curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); +		curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, policy.mHttpProxy.c_str());  		curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);  	} -	if (policy.get(HttpRequest::GP_CA_PATH, &opt_value)) +	if (policy.mCAPath.size())  	{ -		curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); +		curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, policy.mCAPath.c_str());  	} -	if (policy.get(HttpRequest::GP_CA_FILE, &opt_value)) +	if (policy.mCAFile.size())  	{ -		curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); +		curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, policy.mCAFile.c_str());  	}  	switch (mReqMethod) diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp index 8198528a9b..a5363f9170 100755 --- a/indra/llcorehttp/_httpopsetget.cpp +++ b/indra/llcorehttp/_httpopsetget.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -27,6 +27,7 @@  #include "_httpopsetget.h"  #include "httpcommon.h" +#include "httprequest.h"  #include "_httpservice.h"  #include "_httppolicy.h" @@ -43,10 +44,11 @@ namespace LLCore  HttpOpSetGet::HttpOpSetGet()  	: HttpOperation(), -	  mIsGlobal(false), -	  mDoSet(false), -	  mSetting(-1),				// Nothing requested -	  mLongValue(0L) +	  mReqOption(HttpRequest::PO_CONNECTION_LIMIT), +	  mReqClass(HttpRequest::INVALID_POLICY_ID), +	  mReqDoSet(false), +	  mReqLongValue(0L), +	  mReplyLongValue(0L)  {} @@ -54,37 +56,84 @@ HttpOpSetGet::~HttpOpSetGet()  {} -void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting) +HttpStatus HttpOpSetGet::setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass)  { -	mIsGlobal = true; -	mSetting = setting; +	HttpStatus status; +	 +	mReqOption = opt; +	mReqClass = pclass; +	return status;  } -void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value) +HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value)  { -	mIsGlobal = true; -	mDoSet = true; -	mSetting = setting; -	mStrValue = value; +	HttpStatus status; + +	if (! HttpService::sOptionDesc[opt].mIsLong) +	{ +		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); +	} +	if (! HttpService::sOptionDesc[opt].mIsDynamic) +	{ +		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); +	} +	 +	mReqOption = opt; +	mReqClass = pclass; +	mReqDoSet = true; +	mReqLongValue = value; +	 +	return status;  } -void HttpOpSetGet::stageFromRequest(HttpService * service) +HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value)  { -	HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions()); -	HttpRequest::EGlobalPolicy setting(static_cast<HttpRequest::EGlobalPolicy>(mSetting)); +	HttpStatus status; + +	if (HttpService::sOptionDesc[opt].mIsLong) +	{ +		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); +	} +	if (! HttpService::sOptionDesc[opt].mIsDynamic) +	{ +		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); +	} + +	mReqOption = opt; +	mReqClass = pclass; +	mReqDoSet = true; +	mReqStrValue = value; -	if (mDoSet) +	return status; +} + + +void HttpOpSetGet::stageFromRequest(HttpService * service) +{ +	if (mReqDoSet)  	{ -		mStatus = pol_opt.set(setting, mStrValue); +		if (HttpService::sOptionDesc[mReqOption].mIsLong) +		{ +			mStatus = service->setPolicyOption(mReqOption, mReqClass, +											   mReqLongValue, &mReplyLongValue); +		} +		else +		{ +			mStatus = service->setPolicyOption(mReqOption, mReqClass, +											   mReqStrValue, &mReplyStrValue); +		}  	} -	if (mStatus) +	else  	{ -		const std::string * value(NULL); -		if ((mStatus = pol_opt.get(setting, &value))) +		if (HttpService::sOptionDesc[mReqOption].mIsLong) +		{ +			mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyLongValue); +		} +		else  		{ -			mStrValue = *value; +			mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyStrValue);  		}  	} diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h index 6966b9d94e..a1e76dd429 100755 --- a/indra/llcorehttp/_httpopsetget.h +++ b/indra/llcorehttp/_httpopsetget.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -46,7 +46,10 @@ namespace LLCore  /// configuration settings.  ///  /// *NOTE:  Expect this to change.  Don't really like it yet. - +/// +/// *TODO:  Can't return values to caller yet.  Need to do +/// something better with HttpResponse and visitNotifier(). +///  class HttpOpSetGet : public HttpOperation  {  public: @@ -61,19 +64,23 @@ private:  public:  	/// Threading:  called by application thread -	void setupGet(HttpRequest::EGlobalPolicy setting); -	void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value); +	HttpStatus setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass); +	HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value); +	HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value);  	virtual void stageFromRequest(HttpService *);  public:  	// Request data -	bool				mIsGlobal; -	bool				mDoSet; -	int					mSetting; -	long				mLongValue; -	std::string			mStrValue; - +	HttpRequest::EPolicyOption	mReqOption; +	HttpRequest::policy_t		mReqClass; +	bool						mReqDoSet; +	long						mReqLongValue; +	std::string					mReqStrValue; + +	// Reply Data +	long						mReplyLongValue; +	std::string					mReplyStrValue;  };  // end class HttpOpSetGet diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 5f303dd0fe..2754e8ef07 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, Linden Research, Inc. + * Copyright (C) 2012-2013, 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,57 +41,64 @@ namespace LLCore  // Per-policy-class data for a running system. -// Collection of queues, parameters, history, metrics, etc. +// Collection of queues, options and other data  // for a single policy class.  //  // Threading:  accessed only by worker thread -struct HttpPolicy::State +struct HttpPolicy::ClassState  {  public: -	State() -		: mConnMax(HTTP_CONNECTION_LIMIT_DEFAULT), -		  mConnAt(HTTP_CONNECTION_LIMIT_DEFAULT), -		  mConnMin(1), -		  mNextSample(0), -		  mErrorCount(0), -		  mErrorFactor(0) +	ClassState()  		{}  	HttpReadyQueue		mReadyQueue;  	HttpRetryQueue		mRetryQueue;  	HttpPolicyClass		mOptions; - -	long				mConnMax; -	long				mConnAt; -	long				mConnMin; - -	HttpTime			mNextSample; -	unsigned long		mErrorCount; -	unsigned long		mErrorFactor;  };  HttpPolicy::HttpPolicy(HttpService * service) -	: mActiveClasses(0), -	  mState(NULL), -	  mService(service) -{} +	: mService(service) +{ +	// Create default class +	mClasses.push_back(new ClassState()); +}  HttpPolicy::~HttpPolicy()  {  	shutdown(); + +	for (class_list_t::iterator it(mClasses.begin()); it != mClasses.end(); ++it) +	{ +		delete (*it); +	} +	mClasses.clear();  	mService = NULL;  } +HttpRequest::policy_t HttpPolicy::createPolicyClass() +{ +	const HttpRequest::policy_t policy_class(mClasses.size()); +	if (policy_class >= HTTP_POLICY_CLASS_LIMIT) +	{ +		return HttpRequest::INVALID_POLICY_ID; +	} +	mClasses.push_back(new ClassState()); +	return policy_class; +} + +  void HttpPolicy::shutdown()  { -	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) +	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)  	{ -		HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); +		ClassState & state(*mClasses[policy_class]); +		 +		HttpRetryQueue & retryq(state.mRetryQueue);  		while (! retryq.empty())  		{  			HttpOpRequest * op(retryq.top()); @@ -101,7 +108,7 @@ void HttpPolicy::shutdown()  			op->release();  		} -		HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); +		HttpReadyQueue & readyq(state.mReadyQueue);  		while (! readyq.empty())  		{  			HttpOpRequest * op(readyq.top()); @@ -111,28 +118,11 @@ void HttpPolicy::shutdown()  			op->release();  		}  	} -	delete [] mState; -	mState = NULL; -	mActiveClasses = 0;  } -void HttpPolicy::start(const HttpPolicyGlobal & global, -					   const std::vector<HttpPolicyClass> & classes) -{ -	llassert_always(! mState); - -	mGlobalOptions = global; -	mActiveClasses = classes.size(); -	mState = new State [mActiveClasses]; -	for (int i(0); i < mActiveClasses; ++i) -	{ -		mState[i].mOptions = classes[i]; -		mState[i].mConnMax = classes[i].mConnectionLimit; -		mState[i].mConnAt = mState[i].mConnMax; -		mState[i].mConnMin = 2; -	} -} +void HttpPolicy::start() +{}  void HttpPolicy::addOp(HttpOpRequest * op) @@ -141,7 +131,7 @@ void HttpPolicy::addOp(HttpOpRequest * op)  	op->mPolicyRetries = 0;  	op->mPolicy503Retries = 0; -	mState[policy_class].mReadyQueue.push(op); +	mClasses[policy_class]->mReadyQueue.push(op);  } @@ -183,7 +173,7 @@ void HttpPolicy::retryOp(HttpOpRequest * op)  							 << static_cast<HttpHandle>(op)  							 << LL_ENDL;  	} -	mState[policy_class].mRetryQueue.push(op); +	mClasses[policy_class]->mRetryQueue.push(op);  } @@ -204,11 +194,11 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()  	HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP);  	HttpLibcurl & transport(mService->getTransport()); -	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) +	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)  	{ -		State & state(mState[policy_class]); +		ClassState & state(*mClasses[policy_class]);  		int active(transport.getActiveCountInClass(policy_class)); -		int needed(state.mConnAt - active);		// Expect negatives here +		int needed(state.mOptions.mConnectionLimit - active);		// Expect negatives here  		HttpRetryQueue & retryq(state.mRetryQueue);  		HttpReadyQueue & readyq(state.mReadyQueue); @@ -256,9 +246,9 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()  bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority)  { -	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) +	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)  	{ -		State & state(mState[policy_class]); +		ClassState & state(*mClasses[policy_class]);  		// We don't scan retry queue because a priority change there  		// is meaningless.  The request will be issued based on retry  		// intervals not priority value, which is now moot. @@ -286,9 +276,9 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior  bool HttpPolicy::cancel(HttpHandle handle)  { -	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) +	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)  	{ -		State & state(mState[policy_class]); +		ClassState & state(*mClasses[policy_class]);  		// Scan retry queue  		HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container()); @@ -382,13 +372,21 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)  	return false;						// not active  } +	 +HttpPolicyClass & HttpPolicy::getClassOptions(HttpRequest::policy_t pclass) +{ +	llassert_always(pclass >= 0 && pclass < mClasses.size()); +	 +	return mClasses[pclass]->mOptions; +} +  int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const  { -	if (policy_class < mActiveClasses) +	if (policy_class < mClasses.size())  	{ -		return (mState[policy_class].mReadyQueue.size() -				+ mState[policy_class].mRetryQueue.size()); +		return (mClasses[policy_class]->mReadyQueue.size() +				+ mClasses[policy_class]->mRetryQueue.size());  	}  	return 0;  } diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 03d92c0b8e..bf1aa74267 100755 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -60,6 +60,9 @@ private:  	void operator=(const HttpPolicy &);			// Not defined  public: +	/// Threading:  called by init thread. +	HttpRequest::policy_t createPolicyClass(); +	  	/// Cancel all ready and retry requests sending them to  	/// their notification queues.  Release state resources  	/// making further request handling impossible. @@ -71,9 +74,8 @@ public:  	/// requests.  One-time call invoked before starting  	/// the worker thread.  	/// -	/// Threading:  called by application thread -	void start(const HttpPolicyGlobal & global, -			   const std::vector<HttpPolicyClass> & classes); +	/// Threading:  called by init thread +	void start();  	/// Give the policy layer some cycles to scan the ready  	/// queue promoting higher-priority requests to active @@ -93,7 +95,7 @@ public:  	/// and should not be modified by anyone until retrieved  	/// from queue.  	/// -	/// Threading:  called by any thread +	/// Threading:  called by worker thread  	void addOp(HttpOpRequest *);  	/// Similar to addOp, used when a caller wants to retry a @@ -130,30 +132,39 @@ public:  	/// Threading:  called by worker thread  	bool stageAfterCompletion(HttpOpRequest * op); -	// Get pointer to global policy options.  Caller is expected -	// to do context checks like no setting once running. +	/// Get a reference to global policy options.  Caller is expected +	/// to do context checks like no setting once running.  These +	/// are done, for example, in @see HttpService interfaces.  	///  	/// Threading:  called by any thread *but* the object may  	/// only be modified by the worker thread once running. -	///  	HttpPolicyGlobal & getGlobalOptions()  		{  			return mGlobalOptions;  		} +	/// Get a reference to class policy options.  Caller is expected +	/// to do context checks like no setting once running.  These +	/// are done, for example, in @see HttpService interfaces. +	/// +	/// Threading:  called by any thread *but* the object may +	/// only be modified by the worker thread once running and +	/// read accesses by other threads are exposed to races at +	/// that point. +	HttpPolicyClass & getClassOptions(HttpRequest::policy_t pclass); +	  	/// Get ready counts for a particular policy class  	///  	/// Threading:  called by worker thread  	int getReadyCount(HttpRequest::policy_t policy_class) const;  protected: -	struct State; - -	int									mActiveClasses; -	State *								mState; -	HttpService *						mService;				// Naked pointer, not refcounted, not owner -	HttpPolicyGlobal					mGlobalOptions; +	struct ClassState; +	typedef std::vector<ClassState *>	class_list_t; +	HttpPolicyGlobal					mGlobalOptions; +	class_list_t						mClasses; +	HttpService *						mService;				// Naked pointer, not refcounted, not owner  };  // end class HttpPolicy  }  // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp index 1a55ab1ac6..fe4359081a 100755 --- a/indra/llcorehttp/_httppolicyclass.cpp +++ b/indra/llcorehttp/_httppolicyclass.cpp @@ -34,8 +34,7 @@ namespace LLCore  HttpPolicyClass::HttpPolicyClass() -	: mSetMask(0UL), -	  mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), +	: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),  	  mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),  	  mPipelining(HTTP_PIPELINING_DEFAULT)  {} @@ -49,7 +48,6 @@ HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)  {  	if (this != &other)  	{ -		mSetMask = other.mSetMask;  		mConnectionLimit = other.mConnectionLimit;  		mPerHostConnectionLimit = other.mPerHostConnectionLimit;  		mPipelining = other.mPipelining; @@ -59,26 +57,25 @@ HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)  HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other) -	: mSetMask(other.mSetMask), -	  mConnectionLimit(other.mConnectionLimit), +	: mConnectionLimit(other.mConnectionLimit),  	  mPerHostConnectionLimit(other.mPerHostConnectionLimit),  	  mPipelining(other.mPipelining)  {} -HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value) +HttpStatus HttpPolicyClass::set(HttpRequest::EPolicyOption opt, long value)  {  	switch (opt)  	{ -	case HttpRequest::CP_CONNECTION_LIMIT: +	case HttpRequest::PO_CONNECTION_LIMIT:  		mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));  		break; -	case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: +	case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:  		mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit);  		break; -	case HttpRequest::CP_ENABLE_PIPELINING: +	case HttpRequest::PO_ENABLE_PIPELINING:  		mPipelining = llclamp(value, 0L, 1L);  		break; @@ -86,38 +83,30 @@ HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value)  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);  	} -	mSetMask |= 1UL << int(opt);  	return HttpStatus();  } -HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value) +HttpStatus HttpPolicyClass::get(HttpRequest::EPolicyOption opt, long * value) const  { -	static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); -	long * src(NULL); -	  	switch (opt)  	{ -	case HttpRequest::CP_CONNECTION_LIMIT: -		src = &mConnectionLimit; +	case HttpRequest::PO_CONNECTION_LIMIT: +		*value = mConnectionLimit;  		break; -	case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: -		src = &mPerHostConnectionLimit; +	case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT: +		*value = mPerHostConnectionLimit;  		break; -	case HttpRequest::CP_ENABLE_PIPELINING: -		src = &mPipelining; +	case HttpRequest::PO_ENABLE_PIPELINING: +		*value = mPipelining;  		break;  	default:  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);  	} -	if (! (mSetMask & (1UL << int(opt)))) -		return not_set; - -	*value = *src;  	return HttpStatus();  } diff --git a/indra/llcorehttp/_httppolicyclass.h b/indra/llcorehttp/_httppolicyclass.h index d175413cbd..69fb459d22 100755 --- a/indra/llcorehttp/_httppolicyclass.h +++ b/indra/llcorehttp/_httppolicyclass.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -34,6 +34,18 @@  namespace LLCore  { +/// Options struct for per-class policy options. +/// +/// Combines both raw blob data access with semantics-enforcing +/// set/get interfaces.  For internal operations by the worker +/// thread, just grab the setting directly from instance and test/use +/// as needed.  When attached to external APIs (the public API +/// options interfaces) the set/get methods are available to +/// enforce correct ranges, data types, contexts, etc. and suitable +/// status values are returned. +/// +/// Threading:  Single-threaded.  In practice, init thread before +/// worker starts, worker thread after.  class HttpPolicyClass  {  public: @@ -44,11 +56,10 @@ public:  	HttpPolicyClass(const HttpPolicyClass &);			// Not defined  public: -	HttpStatus set(HttpRequest::EClassPolicy opt, long value); -	HttpStatus get(HttpRequest::EClassPolicy opt, long * value); +	HttpStatus set(HttpRequest::EPolicyOption opt, long value); +	HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;  public: -	unsigned long				mSetMask;  	long						mConnectionLimit;  	long						mPerHostConnectionLimit;  	long						mPipelining; diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index 72f409d3b1..1dc95f3dce 100755 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -34,8 +34,7 @@ namespace LLCore  HttpPolicyGlobal::HttpPolicyGlobal() -	: mSetMask(0UL), -	  mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), +	: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),  	  mTrace(HTTP_TRACE_OFF),  	  mUseLLProxy(0)  {} @@ -49,7 +48,6 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)  {  	if (this != &other)  	{ -		mSetMask = other.mSetMask;  		mConnectionLimit = other.mConnectionLimit;  		mCAPath = other.mCAPath;  		mCAFile = other.mCAFile; @@ -61,19 +59,19 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)  } -HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) +HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, long value)  {  	switch (opt)  	{ -	case HttpRequest::GP_CONNECTION_LIMIT: +	case HttpRequest::PO_CONNECTION_LIMIT:  		mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));  		break; -	case HttpRequest::GP_TRACE: +	case HttpRequest::PO_TRACE:  		mTrace = llclamp(value, long(HTTP_TRACE_MIN), long(HTTP_TRACE_MAX));  		break; -	case HttpRequest::GP_LLPROXY: +	case HttpRequest::PO_LLPROXY:  		mUseLLProxy = llclamp(value, 0L, 1L);  		break; @@ -81,24 +79,23 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);  	} -	mSetMask |= 1UL << int(opt);  	return HttpStatus();  } -HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::string & value) +HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, const std::string & value)  {  	switch (opt)  	{ -	case HttpRequest::GP_CA_PATH: +	case HttpRequest::PO_CA_PATH:  		mCAPath = value;  		break; -	case HttpRequest::GP_CA_FILE: +	case HttpRequest::PO_CA_FILE:  		mCAFile = value;  		break; -	case HttpRequest::GP_HTTP_PROXY: +	case HttpRequest::PO_HTTP_PROXY:  		mCAFile = value;  		break; @@ -106,69 +103,54 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);  	} -	mSetMask |= 1UL << int(opt);  	return HttpStatus();  } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, long * value) const  { -	static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); -	long * src(NULL); -	  	switch (opt)  	{ -	case HttpRequest::GP_CONNECTION_LIMIT: -		src = &mConnectionLimit; +	case HttpRequest::PO_CONNECTION_LIMIT: +		*value = mConnectionLimit;  		break; -	case HttpRequest::GP_TRACE: -		src = &mTrace; +	case HttpRequest::PO_TRACE: +		*value = mTrace;  		break; -	case HttpRequest::GP_LLPROXY: -		src = &mUseLLProxy; +	case HttpRequest::PO_LLPROXY: +		*value = mUseLLProxy;  		break;  	default:  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);  	} -	if (! (mSetMask & (1UL << int(opt)))) -		return not_set; - -	*value = *src;  	return HttpStatus();  } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string ** value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, std::string * value) const  { -	static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); -	const std::string * src(NULL); -  	switch (opt)  	{ -	case HttpRequest::GP_CA_PATH: -		src = &mCAPath; +	case HttpRequest::PO_CA_PATH: +		*value = mCAPath;  		break; -	case HttpRequest::GP_CA_FILE: -		src = &mCAFile; +	case HttpRequest::PO_CA_FILE: +		*value = mCAFile;  		break; -	case HttpRequest::GP_HTTP_PROXY: -		src = &mHttpProxy; +	case HttpRequest::PO_HTTP_PROXY: +		*value = mHttpProxy;  		break;  	default:  		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);  	} -	if (! (mSetMask & (1UL << int(opt)))) -		return not_set; - -	*value = src;  	return HttpStatus();  } diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h index a50d0e4188..67c4ba9481 100755 --- a/indra/llcorehttp/_httppolicyglobal.h +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -34,6 +34,18 @@  namespace LLCore  { +/// Options struct for global policy options. +/// +/// Combines both raw blob data access with semantics-enforcing +/// set/get interfaces.  For internal operations by the worker +/// thread, just grab the setting directly from instance and test/use +/// as needed.  When attached to external APIs (the public API +/// options interfaces) the set/get methods are available to +/// enforce correct ranges, data types, contexts, etc. and suitable +/// status values are returned. +/// +/// Threading:  Single-threaded.  In practice, init thread before +/// worker starts, worker thread after.  class HttpPolicyGlobal  {  public: @@ -46,13 +58,12 @@ private:  	HttpPolicyGlobal(const HttpPolicyGlobal &);			// Not defined  public: -	HttpStatus set(HttpRequest::EGlobalPolicy opt, long value); -	HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value); -	HttpStatus get(HttpRequest::EGlobalPolicy opt, long * value); -	HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string ** value); +	HttpStatus set(HttpRequest::EPolicyOption opt, long value); +	HttpStatus set(HttpRequest::EPolicyOption opt, const std::string & value); +	HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const; +	HttpStatus get(HttpRequest::EPolicyOption opt, std::string * value) const;  public: -	unsigned long		mSetMask;  	long				mConnectionLimit;  	std::string			mCAPath;  	std::string			mCAFile; diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 0821401289..e21d196a3e 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, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -43,6 +43,17 @@  namespace LLCore  { +const HttpService::OptionDescriptor HttpService::sOptionDesc[] = +{ //    isLong     isDynamic  isGlobal    isClass +	{	true,		true,		true,		true	},		// PO_CONNECTION_LIMIT +	{	true,		true,		false,		true	},		// PO_PER_HOST_CONNECTION_LIMIT +	{	false,		false,		true,		false	},		// PO_CA_PATH +	{	false,		false,		true,		false	},		// PO_CA_FILE +	{	false,		true,		true,		false	},		// PO_HTTP_PROXY +	{	true,		true,		true,		false	},		// PO_LLPROXY +	{	true,		true,		true,		false	},		// PO_TRACE +	{	true,		true,		false,		true	}		// PO_ENABLE_PIPELINING +};  HttpService * HttpService::sInstance(NULL);  volatile HttpService::EState HttpService::sState(NOT_INITIALIZED); @@ -51,12 +62,9 @@ HttpService::HttpService()  	  mExitRequested(0U),  	  mThread(NULL),  	  mPolicy(NULL), -	  mTransport(NULL) -{ -	// Create the default policy class -	HttpPolicyClass pol_class; -	mPolicyClasses.push_back(pol_class); -} +	  mTransport(NULL), +	  mLastPolicy(0) +{}  HttpService::~HttpService() @@ -146,13 +154,8 @@ void HttpService::term()  HttpRequest::policy_t HttpService::createPolicyClass()  { -	const HttpRequest::policy_t policy_class(mPolicyClasses.size()); -	if (policy_class >= HTTP_POLICY_CLASS_LIMIT) -	{ -		return 0; -	} -	mPolicyClasses.push_back(HttpPolicyClass()); -	return policy_class; +	mLastPolicy = mPolicy->createPolicyClass(); +	return mLastPolicy;  } @@ -185,8 +188,8 @@ void HttpService::startThread()  	}  	// Push current policy definitions, enable policy & transport components -	mPolicy->start(mPolicyGlobal, mPolicyClasses); -	mTransport->start(mPolicyClasses.size()); +	mPolicy->start(); +	mTransport->start(mLastPolicy + 1);  	mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1));  	sState = RUNNING; @@ -319,7 +322,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)  		{  			// Setup for subsequent tracing  			long tracing(HTTP_TRACE_OFF); -			mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing); +			mPolicy->getGlobalOptions().get(HttpRequest::PO_TRACE, &tracing);  			op->mTracing = (std::max)(op->mTracing, int(tracing));  			if (op->mTracing > HTTP_TRACE_OFF) @@ -342,4 +345,137 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)  } +HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, +										long * ret_value) +{ +	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range +		|| opt >= HttpRequest::PO_LAST													// ditto +		|| (! sOptionDesc[opt].mIsLong)													// datatype is long +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range +		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass))	// class setting permitted +																						// can always get, no dynamic check +	{ +		return HttpStatus(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG); +	} + +	HttpStatus status; +	if (pclass == HttpRequest::GLOBAL_POLICY_ID) +	{ +		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions()); +		 +		status = opts.get(opt, ret_value); +	} +	else +	{ +		HttpPolicyClass & opts(mPolicy->getClassOptions(pclass)); + +		status = opts.get(opt, ret_value); +	} + +	return status; +} + + +HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, +										std::string * ret_value) +{ +	HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG); + +	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range +		|| opt >= HttpRequest::PO_LAST													// ditto +		|| (sOptionDesc[opt].mIsLong)													// datatype is string +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range +		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass))	// class setting permitted +																						// can always get, no dynamic check +	{ +		return status; +	} + +	// Only global has string values +	if (pclass == HttpRequest::GLOBAL_POLICY_ID) +	{ +		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions()); + +		status = opts.get(opt, ret_value); +	} + +	return status; +} + + +HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, +										long value, long * ret_value) +{ +	HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG); +	 +	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range +		|| opt >= HttpRequest::PO_LAST													// ditto +		|| (! sOptionDesc[opt].mIsLong)													// datatype is long +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range +		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)		// class setting permitted +		|| (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic))						// dynamic setting permitted +	{ +		return status; +	} + +	if (pclass == HttpRequest::GLOBAL_POLICY_ID) +	{ +		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions()); +		 +		status = opts.set(opt, value); +		if (status && ret_value) +		{ +			status = opts.get(opt, ret_value); +		} +	} +	else +	{ +		HttpPolicyClass & opts(mPolicy->getClassOptions(pclass)); + +		status = opts.set(opt, value); +		if (status && ret_value) +		{ +			status = opts.get(opt, ret_value); +		} +	} + +	return status; +} + + +HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, +										const std::string & value, std::string * ret_value) +{ +	HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG); +	 +	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range +		|| opt >= HttpRequest::PO_LAST													// ditto +		|| (sOptionDesc[opt].mIsLong)													// datatype is string +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range +		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted +		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)		// class setting permitted +		|| (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic))						// dynamic setting permitted +	{ +		return status; +	} + +	// Only string values are global at this time +	if (pclass == HttpRequest::GLOBAL_POLICY_ID) +	{ +		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions()); +		 +		status = opts.set(opt, value); +		if (status && ret_value) +		{ +			status = opts.get(opt, ret_value); +		} +	} + +	return status; +} +	 +  }  // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index ffe0349d4d..cf23f3ab61 100755 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -53,6 +53,7 @@ namespace LLCore  class HttpRequestQueue;  class HttpPolicy;  class HttpLibcurl; +class HttpOpSetGet;  /// The HttpService class does the work behind the request queue.  It @@ -106,7 +107,7 @@ public:  		NORMAL,					///< continuous polling of request, ready, active queues  		REQUEST_SLEEP			///< can sleep indefinitely waiting for request queue write  	}; -		 +  	static void init(HttpRequestQueue *);  	static void term(); @@ -136,7 +137,7 @@ public:  	/// acquires its weaknesses.  	static bool isStopped(); -	/// Threading:  callable by consumer thread *once*. +	/// Threading:  callable by init thread *once*.  	void startThread();  	/// Threading:  callable by worker thread. @@ -181,27 +182,38 @@ public:  		}  	/// Threading:  callable by consumer thread. -	HttpPolicyGlobal & getGlobalOptions() -		{ -			return mPolicyGlobal; -		} - -	/// Threading:  callable by consumer thread.  	HttpRequest::policy_t createPolicyClass(); -	/// Threading:  callable by consumer thread. -	HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class) -		{ -			llassert(policy_class >= 0 && policy_class < mPolicyClasses.size()); -			return mPolicyClasses[policy_class]; -		} -	  protected:  	void threadRun(LLCoreInt::HttpThread * thread);  	ELoopSpeed processRequestQueue(ELoopSpeed loop); + +protected: +	friend class HttpOpSetGet; +	friend class HttpRequest; +	 +	// Used internally to describe what operations are allowed +	// on each policy option. +	struct OptionDescriptor +	{ +		bool		mIsLong; +		bool		mIsDynamic; +		bool		mIsGlobal; +		bool		mIsClass; +	}; +		 +	HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, +							   long * ret_value); +	HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, +							   std::string * ret_value); +	HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, +							   long value, long * ret_value); +	HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, +							   const std::string & value, std::string * ret_value);  protected: +	static const OptionDescriptor		sOptionDesc[HttpRequest::PO_LAST];  	static HttpService *				sInstance;  	// === shared data === @@ -210,13 +222,13 @@ protected:  	LLAtomicU32							mExitRequested;  	LLCoreInt::HttpThread *				mThread; -	// === consumer-thread-only data === -	HttpPolicyGlobal					mPolicyGlobal; -	std::vector<HttpPolicyClass>		mPolicyClasses; -	  	// === working-thread-only data ===  	HttpPolicy *						mPolicy;		// Simple pointer, has ownership  	HttpLibcurl *						mTransport;		// Simple pointer, has ownership +	 +	// === main-thread-only data === +	HttpRequest::policy_t				mLastPolicy; +	  };  // end class HttpService  }  // end namespace LLCore diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 88c8102b27..73c49687d7 100755 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -236,9 +236,10 @@ int main(int argc, char** argv)  	// Initialization  	init_curl();  	LLCore::HttpRequest::createService(); -	LLCore::HttpRequest::setPolicyClassOption(LLCore::HttpRequest::DEFAULT_POLICY_ID, -											  LLCore::HttpRequest::CP_CONNECTION_LIMIT, -											  concurrency_limit); +	LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT, +											   LLCore::HttpRequest::DEFAULT_POLICY_ID, +											   concurrency_limit, +											   NULL);  	LLCore::HttpRequest::startThread();  	// Get service point diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index c0d4ec5aad..9db884057f 100755 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -29,9 +29,9 @@  /// @package LLCore::HTTP  /// -/// This library implements a high-level, Indra-code-free client interface to -/// HTTP services based on actual patterns found in the viewer and simulator. -/// Interfaces are similar to those supplied by the legacy classes +/// This library implements a high-level, Indra-code-free (somewhat) client +/// interface to HTTP services based on actual patterns found in the viewer +/// and simulator.  Interfaces are similar to those supplied by the legacy classes  /// LLCurlRequest and LLHTTPClient.  To that is added a policy scheme that  /// allows an application to specify connection behaviors:  limits on  /// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc. @@ -52,7 +52,7 @@  /// - "llcorehttp/httprequest.h"  /// - "llcorehttp/httpresponse.h"  /// -/// The library is still under early development and particular users +/// The library is still under development and particular users  /// may need access to internal implementation details that are found  /// in the _*.h header files.  But this is a crutch to be avoided if at  /// all possible and probably indicates some interface work is neeeded. @@ -66,6 +66,8 @@  ///   .  CRYPTO_set_id_callback(...)  /// - HttpRequest::createService() called to instantiate singletons  ///   and support objects. +/// - HttpRequest::startThread() to kick off the worker thread and +///   begin servicing requests.  ///  /// An HTTP consumer in an application, and an application may have many  /// consumers, does a few things: @@ -91,10 +93,12 @@  ///   objects.  /// - Do completion processing in your onCompletion() method.  /// -/// Code fragments: -/// Rather than a poorly-maintained example in comments, look in the -/// example subdirectory which is a minimal yet functional tool to do -/// GET request performance testing.  With four calls: +/// Code fragments. +/// +/// Initialization.  Rather than a poorly-maintained example in +/// comments, look in the example subdirectory which is a minimal +/// yet functional tool to do GET request performance testing. +/// With four calls:  ///  ///   	init_curl();  ///     LLCore::HttpRequest::createService(); @@ -103,7 +107,85 @@  ///  /// the program is basically ready to issue requests.  /// - +/// HttpHandler.  Having started life as a non-indra library, +/// this code broke away from the classic Responder model and +/// introduced a handler class to represent an interface for +/// request responses.  This is a non-reference-counted entity +/// which can be used as a base class or a mixin.  An instance +/// of a handler can be used for each request or can be shared +/// among any number of requests.  Your choice but expect to +/// code something like the following: +/// +///     class AppHandler : public LLCore::HttpHandler +///     { +///     public: +///         virtual void onCompleted(HttpHandle handle, +///                                  HttpResponse * response) +///         { +///             ... +///         } +///         ... +///     }; +///     ... +///     handler = new handler(...); +/// +/// +/// Issuing requests.  Using 'hr' above, +/// +///     hr->requestGet(HttpRequest::DEFAULT_POLICY_ID, +///                    0,				// Priority, not used yet +///                    url, +///                    NULL,			// options +///                    NULL,            // additional headers +///                    handler); +/// +/// If that returns a value other than LLCORE_HTTP_HANDLE_INVALID, +/// the request was successfully issued and there will eventally +/// be a status delivered to the handler.  If invalid is returnedd, +/// the actual status can be retrieved by calling hr->getStatus(). +/// +/// Completing requests and delivering notifications.  Operations +/// are all performed by the worker thread and will be driven to +/// completion regardless of caller actions.  Notification of +/// completion (success or failure) is done by calls to +/// HttpRequest::update() which will invoke handlers for completed +/// requests: +/// +///     hr->update(0); +///       // Callbacks into handler->onCompleted() +/// +/// +/// Threads. +/// +/// Threads are supported and used by this library.  The various +/// classes, methods and members are documented with thread +/// constraints which programmers must follow and which are +/// defined as follows: +/// +/// consumer	Any thread that has instanced HttpRequest and is +///             issuing requests.  A particular instance can only +///             be used by one consumer thread but a consumer may +///             have many instances available to it. +/// init		Special consumer thread, usually the main thread, +///             involved in setting up the library at startup. +/// worker      Thread used internally by the library to perform +///             HTTP operations.  Consumers will not have to deal +///             with this thread directly but some APIs are reserved +///             to it. +/// any         Consumer or worker thread. +/// +/// For the most part, API users will not have to do much in the +/// way of ensuring thread safely.  However, there is a tremendous +/// amount of sharing between threads of read-only data.  So when +/// documentation declares that an option or header instance +/// becomes shared between consumer and worker, the consumer must +/// not modify the shared object. +/// +/// Internally, there is almost no thread synchronization.  During +/// normal operations (non-init, non-term), only the request queue +/// and the multiple reply queues are shared between threads and +/// only here are mutexes used. +///  #include "linden_common.h"		// Modifies curl/curl.h interfaces @@ -239,9 +321,10 @@ struct HttpStatus  			return *this;  		} -	static const type_enum_t EXT_CURL_EASY = 0; -	static const type_enum_t EXT_CURL_MULTI = 1; -	static const type_enum_t LLCORE = 2; +	static const type_enum_t EXT_CURL_EASY = 0;			///< mStatus is an error from a curl_easy_*() call +	static const type_enum_t EXT_CURL_MULTI = 1;		///< mStatus is an error from a curl_multi_*() call +	static const type_enum_t LLCORE = 2;				///< mStatus is an HE_* error code +														///< 100-999 directly represent HTTP status codes  	type_enum_t			mType;  	short				mStatus; diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 9b739a8825..7b1888e3eb 100755 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -54,12 +54,8 @@ namespace LLCore  // ==================================== -HttpRequest::policy_t HttpRequest::sNextPolicyID(1); - -  HttpRequest::HttpRequest() -	: //HttpHandler(), -	  mReplyQueue(NULL), +	: mReplyQueue(NULL),  	  mRequestQueue(NULL)  {  	mRequestQueue = HttpRequestQueue::instanceOf(); @@ -90,45 +86,91 @@ HttpRequest::~HttpRequest()  // ==================================== -HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) +HttpRequest::policy_t HttpRequest::createPolicyClass()  {  	if (HttpService::RUNNING == HttpService::instanceOf()->getState())  	{ -		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); +		return 0;  	} -	return HttpService::instanceOf()->getGlobalOptions().set(opt, value); +	return HttpService::instanceOf()->createPolicyClass();  } -HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value) +HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass, +											  long value, long * ret_value)  {  	if (HttpService::RUNNING == HttpService::instanceOf()->getState())  	{  		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);  	} -	return HttpService::instanceOf()->getGlobalOptions().set(opt, value); +	return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);  } -HttpRequest::policy_t HttpRequest::createPolicyClass() +HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass, +											  const std::string & value, std::string * ret_value)  {  	if (HttpService::RUNNING == HttpService::instanceOf()->getState())  	{ -		return 0; +		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);  	} -	return HttpService::instanceOf()->createPolicyClass(); +	return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);  } -HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id, -											 EClassPolicy opt, -											 long value) +HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass, +										long value, HttpHandler * handler)  { -	if (HttpService::RUNNING == HttpService::instanceOf()->getState()) +	HttpStatus status; +	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + +	HttpOpSetGet * op = new HttpOpSetGet(); +	if (! (status = op->setupSet(opt, pclass, value)))  	{ -		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); +		op->release(); +		mLastReqStatus = status; +		return handle; +	} +	op->setReplyPath(mReplyQueue, handler); +	if (! (status = mRequestQueue->addOp(op)))			// transfers refcount +	{ +		op->release(); +		mLastReqStatus = status; +		return handle; +	} +	 +	mLastReqStatus = status; +	handle = static_cast<HttpHandle>(op); +	 +	return handle; +} + + +HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass, +										const std::string & value, HttpHandler * handler) +{ +	HttpStatus status; +	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + +	HttpOpSetGet * op = new HttpOpSetGet(); +	if (! (status = op->setupSet(opt, pclass, value))) +	{ +		op->release(); +		mLastReqStatus = status; +		return handle; +	} +	op->setReplyPath(mReplyQueue, handler); +	if (! (status = mRequestQueue->addOp(op)))			// transfers refcount +	{ +		op->release(); +		mLastReqStatus = status; +		return handle;  	} -	return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value); +	 +	mLastReqStatus = status; +	handle = static_cast<HttpHandle>(op); +	 +	return handle;  } @@ -474,31 +516,6 @@ HttpHandle HttpRequest::requestSpin(int mode)  	return handle;  } -// ==================================== -// Dynamic Policy Methods -// ==================================== - -HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandler * handler) -{ -	HttpStatus status; -	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - -	HttpOpSetGet * op = new HttpOpSetGet(); -	op->setupSet(GP_HTTP_PROXY, proxy); -	op->setReplyPath(mReplyQueue, handler); -	if (! (status = mRequestQueue->addOp(op)))			// transfers refcount -	{ -		op->release(); -		mLastReqStatus = status; -		return handle; -	} - -	mLastReqStatus = status; -	handle = static_cast<HttpHandle>(op); - -	return handle; -} -  }   // end namespace LLCore diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 5000f47d0d..5c54d35a21 100755 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -56,6 +56,9 @@ class BufferArray;  /// The class supports the current HTTP request operations:  ///  /// - requestGetByteRange:  GET with Range header for a single range of bytes +/// - requestGet: +/// - requestPost: +/// - requestPut:  ///  /// Policy Classes  /// @@ -100,9 +103,26 @@ public:  	/// Represents a default, catch-all policy class that guarantees  	/// eventual service for any HTTP request. -	static const int DEFAULT_POLICY_ID = 0; +	static const policy_t DEFAULT_POLICY_ID = 0; +	static const policy_t INVALID_POLICY_ID = 0xFFFFFFFFU; +	static const policy_t GLOBAL_POLICY_ID = 0xFFFFFFFEU; -	enum EGlobalPolicy +	/// Create a new policy class into which requests can be made. +	/// +	/// All class creation must occur before threads are started and +	/// transport begins.  Policy classes are limited to a small value. +	/// Currently that limit is the default class + 1. +	/// +	/// @return			If positive, the policy_id used to reference +	///					the class in other methods.  If 0, requests +	///					for classes have exceeded internal limits +	///					or caller has tried to create a class after +	///					threads have been started.  Caller must fallback +	///					and recover. +	/// +	static policy_t createPolicyClass(); + +	enum EPolicyOption  	{  		/// Maximum number of connections the library will use to  		/// perform operations.  This is somewhat soft as the underlying @@ -113,24 +133,30 @@ public:  		/// a somewhat soft value.  There may be an additional five  		/// connections per policy class depending upon runtime  		/// behavior. -		GP_CONNECTION_LIMIT, +		/// +		/// Both global and per-class +		PO_CONNECTION_LIMIT, + +		/// Limits the number of connections used for a single +		/// literal address/port pair within the class. +		PO_PER_HOST_CONNECTION_LIMIT,  		/// String containing a system-appropriate directory name  		/// where SSL certs are stored. -		GP_CA_PATH, +		PO_CA_PATH,  		/// String giving a full path to a file containing SSL certs. -		GP_CA_FILE, +		PO_CA_FILE,  		/// String of host/port to use as simple HTTP proxy.  This is  		/// going to change in the future into something more elaborate  		/// that may support richer schemes. -		GP_HTTP_PROXY, +		PO_HTTP_PROXY,  		/// Long value that if non-zero enables the use of the  		/// traditional LLProxy code for http/socks5 support.  If -		/// enabled, has priority over GP_HTTP_PROXY. -		GP_LLPROXY, +		// enabled, has priority over GP_HTTP_PROXY. +		PO_LLPROXY,  		/// Long value setting the logging trace level for the  		/// library.  Possible values are: @@ -143,57 +169,46 @@ public:  		/// These values are also used in the trace modes for  		/// individual requests in HttpOptions.  Also be aware that  		/// tracing tends to impact performance of the viewer. -		GP_TRACE -	}; - -	/// Set a parameter on a global policy option.  Calls -	/// made after the start of the servicing thread are -	/// not honored and return an error status. -	/// -	/// @param opt		Enum of option to be set. -	/// @param value	Desired value of option. -	/// @return			Standard status code. -	static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); -	static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); - -	/// Create a new policy class into which requests can be made. -	/// -	/// All class creation must occur before threads are started and -	/// transport begins.  Policy classes are limited to a small value. -	/// Currently that limit is the default class + 1. -	/// -	/// @return			If positive, the policy_id used to reference -	///					the class in other methods.  If 0, requests -	///					for classes have exceeded internal limits -	///					or caller has tried to create a class after -	///					threads have been started.  Caller must fallback -	///					and recover. -	/// -	static policy_t createPolicyClass(); - -	enum EClassPolicy -	{ -		/// Limits the number of connections used for the class. -		CP_CONNECTION_LIMIT, - -		/// Limits the number of connections used for a single -		/// literal address/port pair within the class. -		CP_PER_HOST_CONNECTION_LIMIT, +		PO_TRACE,  		/// Suitable requests are allowed to pipeline on their  		/// connections when they ask for it. -		CP_ENABLE_PIPELINING +		PO_ENABLE_PIPELINING, + +		PO_LAST  // Always at end  	}; -	 + +	/// Set a policy option for a global or class parameter at +	/// startup time (prior to thread start). +	/// +	/// @param opt			Enum of option to be set. +	/// @param pclass		For class-based options, the policy class ID to +	///					    be changed.  For globals, specify GLOBAL_POLICY_ID. +	/// @param value		Desired value of option. +	/// @param ret_value	Pointer to receive effective set value +	///						if successful.  May be NULL if effective +	///						value not wanted. +	/// @return				Standard status code. +	static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass, +											long value, long * ret_value); +	static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass, +											const std::string & value, std::string * ret_value); +  	/// Set a parameter on a class-based policy option.  Calls  	/// made after the start of the servicing thread are  	/// not honored and return an error status.  	/// -	/// @param policy_id		ID of class as returned by @see createPolicyClass(). -	/// @param opt				Enum of option to be set. -	/// @param value			Desired value of option. -	/// @return					Standard status code. -	static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); +	/// @param opt			Enum of option to be set. +	/// @param pclass		For class-based options, the policy class ID to +	///					    be changed.  Ignored for globals but recommend +	///					    using INVALID_POLICY_ID in this case. +	/// @param value		Desired value of option. +	/// @return				Handle of dynamic request.  Use @see getStatus() if +	///						the returned handle is invalid. +	HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, long value, +							   HttpHandler * handler); +	HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, const std::string & value, +							   HttpHandler * handler);  	/// @} @@ -495,16 +510,6 @@ public:  	/// @} -	/// @name DynamicPolicyMethods -	/// -	/// @{ - -	/// Request that a running transport pick up a new proxy setting. -	/// An empty string will indicate no proxy is to be used. -	HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler); - -    /// @} -  protected:  	void generateNotification(HttpOperation * op); @@ -526,7 +531,6 @@ private:  	/// Must be established before any threading is allowed to  	/// start.  	/// -	static policy_t		sNextPolicyID;  	/// @}  	// End Global State diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 27d65f171e..f1b9c02393 100755 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -1213,7 +1213,7 @@ void HttpRequestTestObjectType::test<12>()  		HttpRequest::createService();  		// Enable tracing -		HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); +		HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);  		// Start threading early so that thread memory is invariant  		// over the test. @@ -1331,7 +1331,7 @@ void HttpRequestTestObjectType::test<13>()  		HttpRequest::createService();  		// Enable tracing -		HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); +		HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);  		// Start threading early so that thread memory is invariant  		// over the test. @@ -2972,6 +2972,142 @@ void HttpRequestTestObjectType::test<21>()  } +template <> template <> +void HttpRequestTestObjectType::test<22>() +{ +	ScopedCurlInit ready; + +	set_test_name("HttpRequest GET 503s with 'Retry-After'"); + +	// This tests mainly that the code doesn't fall over if +	// various well- and mis-formed Retry-After headers are +	// sent along with the response.  Direct inspection of +	// the parsing result isn't supported. +	 +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +	std::string url_base(get_base_url() + "/503/");	// path to 503 generators +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	HttpOptions * opts = NULL; +	 +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		opts = new HttpOptions(); +		opts->setRetries(1);			// Retry once only +		opts->setUseRetryAfter(true);	// Try to parse the retry-after header +		 +		// Issue a GET that 503s with valid retry-after +		mStatus = HttpStatus(503); +		int url_limit(6); +		for (int i(0); i < url_limit; ++i) +		{ +			std::ostringstream url; +			url << url_base << i << "/"; +			HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +														 0U, +														 url.str(), +														 0, +														 0, +														 opts, +														 NULL, +														 &handler); + +			std::ostringstream testtag; +			testtag << "Valid handle returned for 503 request #" << i; +			ensure(testtag.str(), handle != LLCORE_HTTP_HANDLE_INVALID); +		} +		 + +		// Run the notification pump. +		int count(0); +		int limit(300);				// One retry but several seconds needed +		while (count++ < limit && mHandlerCalls < url_limit) +		{ +			req->update(0); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == url_limit); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		mHandlerCalls = 0; +		HttpHandle handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 100; +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 1); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); + +		// release options +		opts->release(); +		opts = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +#if defined(WIN32) +		// Can only do this memory test on Windows.  On other platforms, +		// the LL logging system holds on to memory and produces what looks +		// like memory leaks... +	 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif +	} +	catch (...) +	{ +		stop_thread(req); +		if (opts) +		{ +			opts->release(); +			opts = NULL; +		} +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + +  }  // end namespace tut  namespace diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 75a3c39ef2..f6c4d1a820 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -9,7 +9,7 @@  $LicenseInfo:firstyear=2008&license=viewerlgpl$  Second Life Viewer Source Code -Copyright (C) 2012, Linden Research, Inc. +Copyright (C) 2012-2013, 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,17 @@ from testrunner import freeport, run, debug, VERBOSE  class TestHTTPRequestHandler(BaseHTTPRequestHandler):      """This subclass of BaseHTTPRequestHandler is to receive and echo      LLSD-flavored messages sent by the C++ LLHTTPClient. + +    [Merge with viewer-cat later] +    - '/503/'           Generate 503 responses with various kinds +                        of 'retry-after' headers +    -- '/503/0/'            "Retry-After: 2"    +    -- '/503/1/'            "Retry-After: Thu, 31 Dec 2043 23:59:59 GMT" +    -- '/503/2/'            "Retry-After: Fri, 31 Dec 1999 23:59:59 GMT" +    -- '/503/3/'            "Retry-After: " +    -- '/503/4/'            "Retry-After: (*#*(@*(@(")" +    -- '/503/5/'            "Retry-After: aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo" +    -- '/503/6/'            "Retry-After: 1 2 3 4 5 6 7 8 9 10"      """      def read(self):          # The following logic is adapted from the library module @@ -107,7 +118,41 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):          if "/sleep/" in self.path:              time.sleep(30) -        if "fail" not in self.path: +        if "/503/" in self.path: +            # Tests for various kinds of 'Retry-After' header parsing +            body = None +            if "/503/0/" in self.path: +                self.send_response(503) +                self.send_header("retry-after", "2") +            elif "/503/1/" in self.path: +                self.send_response(503) +                self.send_header("retry-after", "Thu, 31 Dec 2043 23:59:59 GMT") +            elif "/503/2/" in self.path: +                self.send_response(503) +                self.send_header("retry-after", "Fri, 31 Dec 1999 23:59:59 GMT") +            elif "/503/3/" in self.path: +                self.send_response(503) +                self.send_header("retry-after", "") +            elif "/503/4/" in self.path: +                self.send_response(503) +                self.send_header("retry-after", "(*#*(@*(@(") +            elif "/503/5/" in self.path: +                self.send_response(503) +                self.send_header("retry-after", "aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo") +            elif "/503/6/" in self.path: +                self.send_response(503) +                self.send_header("retry-after", "1 2 3 4 5 6 7 8 9 10") +            else: +                # Unknown request +                self.send_response(400) +                body = "Unknown /503/ path in server" +            if "/reflect/" in self.path: +                self.reflect_headers() +            self.send_header("Content-type", "text/plain") +            self.end_headers() +            if body: +                self.wfile.write(body) +        elif "fail" not in self.path:              data = data.copy()          # we're going to modify              # Ensure there's a "reply" key in data, even if there wasn't before              data["reply"] = data.get("reply", llsd.LLSD("success")) | 
