diff options
Diffstat (limited to 'indra/llcorehttp')
| -rwxr-xr-x | indra/llcorehttp/_httpinternal.h | 3 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httpoprequest.cpp | 59 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httpoprequest.h | 1 | ||||
| -rwxr-xr-x | indra/llcorehttp/_httppolicy.cpp | 28 | ||||
| -rwxr-xr-x | indra/llcorehttp/httpcommon.cpp | 34 | ||||
| -rwxr-xr-x | indra/llcorehttp/httpcommon.h | 8 | ||||
| -rwxr-xr-x | indra/llcorehttp/httpresponse.h | 9 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_httprequest.hpp | 423 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_httpstatus.hpp | 26 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_llcorehttp_peer.py | 130 | 
10 files changed, 492 insertions, 229 deletions
| diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index 14f744a9f1..008e4fd95c 100755 --- a/indra/llcorehttp/_httpinternal.h +++ b/indra/llcorehttp/_httpinternal.h @@ -146,9 +146,6 @@ const int HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS = 2;  // Block allocation size (a tuning parameter) is found  // in bufferarray.h. -// Compatibility controls -const bool HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX = true; -  }  // end namespace LLCore  #endif	// _LLCORE_HTTP_INTERNAL_H_ diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 95e0f72c0b..d8057364c4 100755 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -186,9 +186,11 @@ void HttpOpRequest::stageFromActive(HttpService * service)  	if (mReplyLength)  	{  		// If non-zero, we received and processed a Content-Range -		// header with the response.  Verify that what it says -		// is consistent with the received data. -		if (mReplyLength != mReplyBody->size()) +		// header with the response.  If there is received data +		// (and there may not be due to protocol violations, +		// HEAD requests, etc., see BUG-2295) Verify that what it +		// says is consistent with the received data. +		if (mReplyBody && mReplyBody->size() && mReplyLength != mReplyBody->size())  		{  			// Not as expected, fail the request  			mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); @@ -339,7 +341,6 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,  	}  } -  // Sets all libcurl options and data for a request.  //  // Used both for initial requests and to 'reload' for @@ -381,41 +382,15 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)  	// Get policy options  	HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions()); -	mCurlHandle = curl_easy_init(); -	curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); -	curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); -	curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); -	curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); -	curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); -	curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); +	mCurlHandle = LLCurl::createStandardCurlHandle(); -	if (HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX) -	{ -		// The Linksys WRT54G V5 router has an issue with frequent -		// DNS lookups from LAN machines.  If they happen too often, -		// like for every HTTP request, the router gets annoyed after -		// about 700 or so requests and starts issuing TCP RSTs to -		// new connections.  Reuse the DNS lookups for even a few -		// seconds and no RSTs. -		curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); -	} -	else -	{ -		// *TODO:  Revisit this old DNS timeout setting - may no longer be valid -		// I don't think this is valid anymore, the Multi shared DNS -		// cache is working well.  For the case of naked easy handles, -		// consider using a shared DNS object. -		curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); -	} -	curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); -	curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); -	curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);  	curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); -	curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this); -	curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); +	curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION,  readCallback);	  	curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this); -	curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); -	curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); +	curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this); +	curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); +	curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); +	curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);	  	const std::string * opt_value(NULL);  	long opt_long(0L); @@ -697,7 +672,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe  	std::string safe_line;  	std::string tag;  	bool logit(false); -	len = (std::min)(len, size_t(256));					// Keep things reasonable in all cases +	const size_t log_len((std::min)(len, size_t(256)));		// Keep things reasonable in all cases  	switch (info)  	{ @@ -705,7 +680,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe  		if (op->mTracing >= HTTP_TRACE_CURL_HEADERS)  		{  			tag = "TEXT"; -			escape_libcurl_debug_data(buffer, len, true, safe_line); +			escape_libcurl_debug_data(buffer, log_len, true, safe_line);  			logit = true;  		}  		break; @@ -714,7 +689,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe  		if (op->mTracing >= HTTP_TRACE_CURL_HEADERS)  		{  			tag = "HEADERIN"; -			escape_libcurl_debug_data(buffer, len, true, safe_line); +			escape_libcurl_debug_data(buffer, log_len, true, safe_line);  			logit = true;  		}  		break; @@ -723,7 +698,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe  		if (op->mTracing >= HTTP_TRACE_CURL_HEADERS)  		{  			tag = "HEADEROUT"; -			escape_libcurl_debug_data(buffer, 2 * len, true, safe_line);		// Goes out as one line +			escape_libcurl_debug_data(buffer, log_len, true, safe_line);	// Goes out as one line unlike header_in  			logit = true;  		}  		break; @@ -735,7 +710,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe  			logit = true;  			if (op->mTracing >= HTTP_TRACE_CURL_BODIES)  			{ -				escape_libcurl_debug_data(buffer, len, false, safe_line); +				escape_libcurl_debug_data(buffer, log_len, false, safe_line);  			}  			else  			{ @@ -753,7 +728,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe  			logit = true;  			if (op->mTracing >= HTTP_TRACE_CURL_BODIES)  			{ -				escape_libcurl_debug_data(buffer, len, false, safe_line); +				escape_libcurl_debug_data(buffer, log_len, false, safe_line);  			}  			else  			{ diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 7b65d17783..74a349b0bf 100755 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -60,7 +60,6 @@ class HttpOptions;  /// the information needed to make a working request which can  /// then be enqueued to a request queue.  /// -  class HttpOpRequest : public HttpOperation  {  public: diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 76c1e22431..014bd37e2e 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 @@ -319,33 +319,13 @@ bool HttpPolicy::cancel(HttpHandle handle)  bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)  { -	static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); -	static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); -	static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); -	static const HttpStatus send_error(HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR); -	static const HttpStatus recv_error(HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR); -	static const HttpStatus upload_failed(HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED); -	static const HttpStatus op_timedout(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); -	static const HttpStatus post_error(HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR); -  	// Retry or finalize  	if (! op->mStatus)  	{ -		// If this failed, we might want to retry.  Have to inspect -		// the status a little more deeply for those reasons worth retrying... -		if (op->mPolicyRetries < op->mPolicyRetryLimit && -			((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || -			 cant_connect == op->mStatus || -			 cant_res_proxy == op->mStatus || -			 cant_res_host == op->mStatus || -			 send_error == op->mStatus || -			 recv_error == op->mStatus || -			 upload_failed == op->mStatus || -			 op_timedout == op->mStatus || -			 post_error == op->mStatus)) +		// If this failed, we might want to retry. +		if (op->mPolicyRetries < op->mPolicyRetryLimit && op->mStatus.isRetryable())  		{ -			// Okay, worth a retry.  We include 499 in this test as -			// it's the old 'who knows?' error from many grid services... +			// Okay, worth a retry.  			retryOp(op);  			return true;				// still active/ready  		} diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index f2fcbf77a3..0738760763 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, 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 @@ -116,6 +116,7 @@ std::string HttpStatus::toString() const  			{ 415, "Unsupported Media Type" },  			{ 416, "Requested range not satisfiable" },  			{ 417, "Expectation Failed" }, +			{ 499, "Linden Catch-All" },  			{ 500, "Internal Server Error" },  			{ 501, "Not Implemented" },  			{ 502, "Bad Gateway" }, @@ -174,6 +175,37 @@ std::string HttpStatus::toString() const  	}  	return std::string("Unknown error");  } + + +// 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. +bool HttpStatus::isRetryable() const +{ +	static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); +	static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); +	static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); +	static const HttpStatus send_error(HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR); +	static const HttpStatus recv_error(HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR); +	static const HttpStatus upload_failed(HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED); +	static const HttpStatus op_timedout(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); +	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); + +	return ((isHttpStatus() && mType >= 499 && mType <= 599) ||	// Include special 499 in retryables +			*this == cant_connect ||	// Connection reset/endpoint problems +			*this == cant_res_proxy ||	// DNS problems +			*this == cant_res_host ||	// DNS problems +			*this == send_error ||		// General socket problems  +			*this == recv_error ||		// General socket problems  +			*this == upload_failed ||	// Transport problem +			*this == op_timedout ||		// Timer expired +			*this == post_error ||		// Transport problem +			*this == partial_file ||	// Data inconsistency in response +			*this == inv_cont_range);	// Short data read disagrees with content-range +} +  } // end namespace LLCore diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index c0d4ec5aad..41fb5164cf 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 @@ -303,6 +303,12 @@ struct HttpStatus  	{  		return 	mType >= type_enum_t(100) && mType <= type_enum_t(999);  	} + +	/// Returns true if the status is one that will be retried +	/// internally.  Provided for external consumption for cases +	/// where that logic needs to be replicated.  Only applies +	/// to failed statuses, successful statuses will return false. +	bool isRetryable() const;  }; // end struct HttpStatus diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index 4a481db6ac..f19b521fbf 100755 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -48,8 +48,9 @@ class HttpHeaders;  /// individual pieces of the response.  ///  /// Typical usage will have the caller interrogate the object -/// and return from the handler callback.  Instances are refcounted -/// and callers can bump the count and retain the object as needed. +/// during the handler callback and then simply returning. +/// But instances are refcounted and and callers can add a +/// reference and hold onto the object after the callback.  ///  /// Threading:  Not intrinsically thread-safe.  /// @@ -119,6 +120,10 @@ public:  	/// caller is going to have to make assumptions on receipt of  	/// a 206 status.  The @full value may also be zero in cases of  	/// parsing problems or a wild-carded length response. +	/// +	/// These values will not necessarily agree with the data in +	/// the body itself (if present).  The BufferArray object +	/// is authoritative for actual data length.  	void getRange(unsigned int * offset, unsigned int * length, unsigned int * full) const  		{  			*offset = mReplyOffset; diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 27d65f171e..0f0876b467 100755 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -45,6 +45,15 @@  using namespace LLCoreInt; +// spin/sleep waiting times for client/server exchange tests +// +// These are now fairly generous to try to get around timeout +// ('reasonable time') failures during execution on a heavily- +// loaded system where the unit test is in competition with +// other programs. +static const int LOOP_SLEEP_INTERVAL(10000); +static const int LOOP_COUNT_SHORT(500);			// 5-second dwell time +static const int LOOP_COUNT_LONG(3000);			// 30-second dwell time  namespace  { @@ -305,11 +314,11 @@ void HttpRequestTestObjectType::test<3>()  		// Run the notification pump.  		int count(0); -		int limit(20); +		int limit(LOOP_COUNT_SHORT);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -320,21 +329,21 @@ void HttpRequestTestObjectType::test<3>()  		// Run the notification pump again  		count = 0; -		limit = 100; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -403,12 +412,12 @@ void HttpRequestTestObjectType::test<4>()  		// Run the notification pump.  		int count(0); -		int limit(20); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 2)  		{  			req1->update(1000000);  			req2->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 2); @@ -420,22 +429,22 @@ void HttpRequestTestObjectType::test<4>()  		// Run the notification pump again  		count = 0; -		limit = 100; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 3)  		{  			req1->update(1000000);  			req2->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 3);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -504,11 +513,11 @@ void HttpRequestTestObjectType::test<5>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_SHORT);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("NoOp notification received", mHandlerCalls == 1); @@ -580,11 +589,11 @@ void HttpRequestTestObjectType::test<6>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_SHORT);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("No notifications received", mHandlerCalls == 0); @@ -661,11 +670,11 @@ void HttpRequestTestObjectType::test<7>()  		// Run the notification pump.  		int count(0); -		int limit(50);				// With one retry, should fail quickish +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -677,21 +686,21 @@ void HttpRequestTestObjectType::test<7>()  		// Run the notification pump again  		count = 0; -		limit = 100; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -777,11 +786,11 @@ void HttpRequestTestObjectType::test<8>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -793,21 +802,21 @@ void HttpRequestTestObjectType::test<8>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -887,11 +896,11 @@ void HttpRequestTestObjectType::test<9>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -903,21 +912,21 @@ void HttpRequestTestObjectType::test<9>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -999,11 +1008,11 @@ void HttpRequestTestObjectType::test<10>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -1015,21 +1024,21 @@ void HttpRequestTestObjectType::test<10>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -1117,11 +1126,11 @@ void HttpRequestTestObjectType::test<11>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -1133,21 +1142,21 @@ void HttpRequestTestObjectType::test<11>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -1237,11 +1246,11 @@ void HttpRequestTestObjectType::test<12>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -1253,21 +1262,21 @@ void HttpRequestTestObjectType::test<12>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -1352,8 +1361,8 @@ void HttpRequestTestObjectType::test<13>()  		HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,  													 0U,  													 url_base, -													 0, -													 0, +													 0,	 +												 0,  													 opts,  													 NULL,  													 &handler); @@ -1365,11 +1374,11 @@ void HttpRequestTestObjectType::test<13>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -1382,21 +1391,21 @@ void HttpRequestTestObjectType::test<13>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -1484,11 +1493,11 @@ void HttpRequestTestObjectType::test<14>()  		// Run the notification pump.  		int count(0); -		int limit(50);				// With one retry, should fail quickish +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -1500,21 +1509,21 @@ void HttpRequestTestObjectType::test<14>()  		// Run the notification pump again  		count = 0; -		limit = 100; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -1607,11 +1616,11 @@ void HttpRequestTestObjectType::test<15>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -1624,21 +1633,21 @@ void HttpRequestTestObjectType::test<15>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -1773,11 +1782,11 @@ void HttpRequestTestObjectType::test<16>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -1850,11 +1859,11 @@ void HttpRequestTestObjectType::test<16>()  		// Run the notification pump.  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 2); @@ -1869,21 +1878,21 @@ void HttpRequestTestObjectType::test<16>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 3)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 3);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -2048,11 +2057,11 @@ void HttpRequestTestObjectType::test<17>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -2067,21 +2076,21 @@ void HttpRequestTestObjectType::test<17>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -2252,11 +2261,11 @@ void HttpRequestTestObjectType::test<18>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -2271,21 +2280,21 @@ void HttpRequestTestObjectType::test<18>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -2456,11 +2465,11 @@ void HttpRequestTestObjectType::test<19>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -2474,21 +2483,21 @@ void HttpRequestTestObjectType::test<19>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -2673,11 +2682,11 @@ void HttpRequestTestObjectType::test<20>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -2692,21 +2701,21 @@ void HttpRequestTestObjectType::test<20>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -2890,11 +2899,11 @@ void HttpRequestTestObjectType::test<21>()  		// Run the notification pump.  		int count(0); -		int limit(10); +		int limit(LOOP_COUNT_LONG);  		while (count++ < limit && mHandlerCalls < 1)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Request executed in reasonable time", count < limit);  		ensure("One handler invocation for request", mHandlerCalls == 1); @@ -2909,21 +2918,21 @@ void HttpRequestTestObjectType::test<21>()  		// Run the notification pump again  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_LONG;  		while (count++ < limit && mHandlerCalls < 2)  		{  			req->update(1000000); -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Second request executed in reasonable time", count < limit);  		ensure("Second handler invocation", mHandlerCalls == 2);  		// See that we actually shutdown the thread  		count = 0; -		limit = 10; +		limit = LOOP_COUNT_SHORT;  		while (count++ < limit && ! HttpService::isStopped())  		{ -			usleep(100000); +			usleep(LOOP_SLEEP_INTERVAL);  		}  		ensure("Thread actually stopped running", HttpService::isStopped()); @@ -2971,6 +2980,200 @@ void HttpRequestTestObjectType::test<21>()  	}  } +// BUG-2295 Tests - Content-Range header received but no body +template <> template <> +void HttpRequestTestObjectType::test<22>() +{ +	ScopedCurlInit ready; + +	std::string url_base(get_base_url()); +	// std::cerr << "Base:  "  << url_base << std::endl; +	 +	set_test_name("BUG-2295"); + +	// 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"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpOptions * options = NULL; +	HttpRequest * req = NULL; + +	try +	{ +		// options set +		options = new HttpOptions(); +		options->setRetries(1);			// Partial_File is retryable and can timeout in here + +		// 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()); + +		// ====================================== +		// Issue bug2295 GETs that will get a 206 +		// ====================================== +		mStatus = HttpStatus(206); +		static const int test_count(3); +		for (int i(0); i < test_count; ++i) +		{ +			char buffer[128]; +			sprintf(buffer, "/bug2295/%d/", i); +			HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +														 0U, +														 url_base + buffer, +														 0, +														 25, +														 options, +														 NULL, +														 &handler); +			ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); +		} +		 +		// Run the notification pump. +		int count(0); +		int limit(LOOP_COUNT_LONG); +		while (count++ < limit && mHandlerCalls < test_count) +		{ +			req->update(1000000); +			usleep(LOOP_SLEEP_INTERVAL); +		} +		ensure("Request executed in reasonable time - ms1", count < limit); +		ensure("One handler invocation for each request - ms1", mHandlerCalls == test_count); + +		// ====================================== +		// Issue bug2295 GETs that will get a libcurl 18 (PARTIAL_FILE) +		// ====================================== +		mHandlerCalls = 0; +		mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_PARTIAL_FILE); +		static const int test2_count(1); +		for (int i(0); i < test2_count; ++i) +		{ +			char buffer[128]; +			sprintf(buffer, "/bug2295/00000012/%d/", i); +			HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +														 0U, +														 url_base + buffer, +														 0, +														 25, +														 options, +														 NULL, +														 &handler); +			ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); +		} +		 +		// Run the notification pump. +		count = 0; +		limit = LOOP_COUNT_LONG; +		while (count++ < limit && mHandlerCalls < test2_count) +		{ +			req->update(1000000); +			usleep(LOOP_SLEEP_INTERVAL); +		} +		ensure("Request executed in reasonable time - ms2", count < limit); +		ensure("One handler invocation for each request - ms2", mHandlerCalls == test2_count); + +		// ====================================== +		// Issue bug2295 GETs that will get an llcorehttp HE_INV_CONTENT_RANGE_HDR status +		// ====================================== +		mHandlerCalls = 0; +		mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); +		static const int test3_count(1); +		for (int i(0); i < test3_count; ++i) +		{ +			char buffer[128]; +			sprintf(buffer, "/bug2295/inv_cont_range/%d/", i); +			HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +														 0U, +														 url_base + buffer, +														 0, +														 25, +														 options, +														 NULL, +														 &handler); +			ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); +		} +		 +		// Run the notification pump. +		count = 0; +		limit = LOOP_COUNT_LONG; +		while (count++ < limit && mHandlerCalls < test3_count) +		{ +			req->update(1000000); +			usleep(LOOP_SLEEP_INTERVAL); +		} +		ensure("Request executed in reasonable time - ms3", count < limit); +		ensure("One handler invocation for each request - ms3", mHandlerCalls == test3_count); + +		// ====================================== +		// 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 = LOOP_COUNT_LONG; +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(LOOP_SLEEP_INTERVAL); +		} +		ensure("Shutdown request executed in reasonable time", count < limit); +		ensure("Shutdown handler invocation", mHandlerCalls == 1); + +		// See that we actually shutdown the thread +		count = 0; +		limit = LOOP_COUNT_SHORT; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(LOOP_SLEEP_INTERVAL); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); + +		// release options +		if (options) +		{ +			options->release(); +			options = 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); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +}  }  // end namespace tut diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp index 887315befc..b5538528c5 100755 --- a/indra/llcorehttp/tests/test_httpstatus.hpp +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -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 @@ -91,9 +91,6 @@ template <> template <>  void HttpStatusTestObjectType::test<2>()  {  	set_test_name("HttpStatus memory structure"); -#if LL_WINDOWS -	skip("MAINT-2302: This frequently (though not always) fails on Windows."); -#endif  	// Require that an HttpStatus object can be trivially  	// returned as a function return value in registers. @@ -106,10 +103,7 @@ void HttpStatusTestObjectType::test<2>()  template <> template <>  void HttpStatusTestObjectType::test<3>()  { -	set_test_name("HttpStatus valid error string conversion"); -#if LL_WINDOWS -	skip("MAINT-2302: This frequently (though not always) fails on Windows."); -#endif +	set_test_name("HttpStatus valid status string conversion");  	HttpStatus status;  	status.mType = HttpStatus::EXT_CURL_EASY; @@ -141,10 +135,7 @@ void HttpStatusTestObjectType::test<3>()  template <> template <>  void HttpStatusTestObjectType::test<4>()  { -	set_test_name("HttpStatus invalid error string conversion"); -#if LL_WINDOWS -	skip("MAINT-2302: This frequently (though not always) fails on Windows."); -#endif +	set_test_name("HttpStatus invalid status string conversion");  	HttpStatus status;  	status.mType = HttpStatus::EXT_CURL_EASY; @@ -170,9 +161,6 @@ template <> template <>  void HttpStatusTestObjectType::test<5>()  {  	set_test_name("HttpStatus equality/inequality testing"); -#if LL_WINDOWS -	skip("MAINT-2302: This frequently (though not always) fails on Windows."); -#endif  	// Make certain equality/inequality tests do not pass  	// through the bool conversion.  Distinct successful @@ -193,9 +181,6 @@ template <> template <>  void HttpStatusTestObjectType::test<6>()  {  	set_test_name("HttpStatus basic HTTP status encoding"); -#if LL_WINDOWS -	skip("MAINT-2302: This frequently (though not always) fails on Windows."); -#endif  	HttpStatus status;  	status.mType = 200; @@ -242,10 +227,7 @@ void HttpStatusTestObjectType::test<6>()  template <> template <>  void HttpStatusTestObjectType::test<7>()  { -	set_test_name("HttpStatus HTTP error text strings"); -#if LL_WINDOWS -	skip("MAINT-2302: This frequently (though not always) fails on Windows."); -#endif +	set_test_name("HttpStatus HTTP status text strings");  	HttpStatus status(100, HE_REPLY_ERROR);  	std::string msg(status.toString()); diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 75a3c39ef2..3c3af8dc75 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 @@ -35,6 +35,10 @@ import time  import select  import getopt  from threading import Thread +try: +    from cStringIO import StringIO +except ImportError: +    from StringIO import StringIO  from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler  from SocketServer import ThreadingMixIn @@ -47,7 +51,30 @@ 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. + +    Target URLs are fairly free-form and are assembled by  +    concatinating fragments.  Currently defined fragments +    are: +    - '/reflect/'       Request headers are bounced back to caller +                        after prefixing with 'X-Reflect-' +    - '/fail/'          Body of request can contain LLSD with  +                        'reason' string and 'status' integer +                        which will become response header. +    - '/bug2295/'       206 response, no data in body: +    -- '/bug2295/0/'       "Content-Range: bytes 0-75/2983" +    -- '/bug2295/1/'       "Content-Range: bytes 0-75/*" +    -- '/bug2295/2/'       "Content-Range: bytes 0-75/2983", +                           "Content-Length: 0" +    -- '/bug2295/00000018/0/'  Generates PARTIAL_FILE (18) error in libcurl. +                           "Content-Range: bytes 0-75/2983", +                           "Content-Length: 76" +    -- '/bug2295/inv_cont_range/0/'  Generates HE_INVALID_CONTENT_RANGE error in llcorehttp. + +    Some combinations make no sense, there's no effort to protect +    you from that.      """ +    ignore_exceptions = (Exception,) +      def read(self):          # The following logic is adapted from the library module          # SimpleXMLRPCServer.py. @@ -87,42 +114,36 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):      def do_GET(self, withdata=True):          # Of course, don't attempt to read data. -        self.answer(dict(reply="success", status=200, -                         reason="Your GET operation worked")) +        try: +            self.answer(dict(reply="success", status=200, +                             reason="Your GET operation worked")) +        except self.ignore_exceptions, e: +            print >> sys.stderr, "Exception during GET (ignoring): %s" % str(e)      def do_POST(self):          # Read the provided POST data.          # self.answer(self.read()) -        self.answer(dict(reply="success", status=200, -                         reason=self.read())) +        try: +            self.answer(dict(reply="success", status=200, +                             reason=self.read())) +        except self.ignore_exceptions, e: +            print >> sys.stderr, "Exception during POST (ignoring): %s" % str(e)      def do_PUT(self):          # Read the provided PUT data.          # self.answer(self.read()) -        self.answer(dict(reply="success", status=200, -                         reason=self.read())) +        try: +            self.answer(dict(reply="success", status=200, +                             reason=self.read())) +        except self.ignore_exceptions, e: +            print >> sys.stderr, "Exception during PUT (ignoring): %s" % str(e)      def answer(self, data, withdata=True):          debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path)          if "/sleep/" in self.path:              time.sleep(30) -        if "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")) -            response = llsd.format_xml(data) -            debug("success: %s", response) -            self.send_response(200) -            if "/reflect/" in self.path: -                self.reflect_headers() -            self.send_header("Content-type", "application/llsd+xml") -            self.send_header("Content-Length", str(len(response))) -            self.send_header("X-LL-Special", "Mememememe"); -            self.end_headers() -            if withdata: -                self.wfile.write(response) -        else:                           # fail requested +        if "fail" in self.path:              status = data.get("status", 500)              # self.responses maps an int status to a (short, long) pair of              # strings. We want the longer string. That's why we pass a string @@ -138,6 +159,57 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):              if "/reflect/" in self.path:                  self.reflect_headers()              self.end_headers() +        elif "/bug2295/" in self.path: +            # Test for https://jira.secondlife.com/browse/BUG-2295 +            # +            # Client can receive a header indicating data should +            # appear in the body without actually getting the body. +            # Library needs to defend against this case. +            # +            body = None +            if "/bug2295/0/" in self.path: +                self.send_response(206) +                self.send_header("Content-Range", "bytes 0-75/2983") +            elif "/bug2295/1/" in self.path: +                self.send_response(206) +                self.send_header("Content-Range", "bytes 0-75/*") +            elif "/bug2295/2/" in self.path: +                self.send_response(206) +                self.send_header("Content-Range", "bytes 0-75/2983") +                self.send_header("Content-Length", "0") +            elif "/bug2295/00000012/0/" in self.path: +                self.send_response(206) +                self.send_header("Content-Range", "bytes 0-75/2983") +                self.send_header("Content-Length", "76") +            elif "/bug2295/inv_cont_range/0/" in self.path: +                self.send_response(206) +                self.send_header("Content-Range", "bytes 0-75/2983") +                body = "Some text, but not enough." +            else: +                # Unknown request +                self.send_response(400) +            if "/reflect/" in self.path: +                self.reflect_headers() +            self.send_header("Content-type", "text/plain") +            self.end_headers() +            if body: +                self.wfile.write(body) +        else: +            # Normal response 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")) +            response = llsd.format_xml(data) +            debug("success: %s", response) +            self.send_response(200) +            if "/reflect/" in self.path: +                self.reflect_headers() +            self.send_header("Content-type", "application/llsd+xml") +            self.send_header("Content-Length", str(len(response))) +            self.send_header("X-LL-Special", "Mememememe"); +            self.end_headers() +            if withdata: +                self.wfile.write(response)      def reflect_headers(self):          for name in self.headers.keys(): @@ -162,6 +234,17 @@ class Server(ThreadingMixIn, HTTPServer):      # operation of freeport() absolutely depends on it being off.      allow_reuse_address = False +    # Override of BaseServer.handle_error().  Not too interested +    # in errors and the default handler emits a scary traceback +    # to stderr which annoys some.  Disable this override to get +    # default behavior which *shouldn't* cause the program to return +    # a failure status. +    def handle_error(self, request, client_address): +        print '-'*40 +        print 'Ignoring exception during processing of request from', +        print client_address +        print '-'*40 +  if __name__ == "__main__":      do_valgrind = False      path_search = False @@ -188,3 +271,4 @@ if __name__ == "__main__":          args = ["valgrind", "--log-file=./valgrind.log"] + args          path_search = True      sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), use_path=path_search, *args)) + | 
