diff options
Diffstat (limited to 'indra')
44 files changed, 6544 insertions, 0 deletions
| diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 4b1bf49d07..031ffb26db 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -43,6 +43,7 @@ add_subdirectory(cmake)  add_subdirectory(${LIBS_OPEN_PREFIX}llaudio)  add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter)  add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) +add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp)  add_subdirectory(${LIBS_OPEN_PREFIX}llimage)  add_subdirectory(${LIBS_OPEN_PREFIX}llkdu)  add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt new file mode 100644 index 0000000000..8d21007ca0 --- /dev/null +++ b/indra/llcorehttp/CMakeLists.txt @@ -0,0 +1,112 @@ +# -*- cmake -*- + +project(llcorehttp) + +include(00-Common) +include(LLCoreHttp) +include(GoogleMock) +include(LLAddBuildTest) +include(LLCommon) +include(Tut) + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +include_directories( +    ${LLCOMMON_INCLUDE_DIRS} +    ${LLCOREHTTP_INCLUDE_DIRS} +    ) + +set(llcorehttp_SOURCE_FILES +    bufferarray.cpp +    httpcommon.cpp +    httprequest.cpp +    httpresponse.cpp +    httpoptions.cpp +    httpheaders.cpp +    _httprequestqueue.cpp +    _httpoperation.cpp +    _httpoprequest.cpp +    _httpreplyqueue.cpp +    _httppolicy.cpp +    _httplibcurl.cpp +    _httpservice.cpp +    _refcounted.cpp +    ) + +set(llcorehttp_HEADER_FILES +    CMakeLists.txt + +    bufferarray.h +    httpcommon.h +    httphandler.h +    httpheaders.h +    httpoptions.h +    httprequest.h +    httpresponse.h +    _httpoperation.h +    _httpoprequest.h +    _httprequestqueue.h +    _httpreplyqueue.h +    _httpservice.h +    _httppolicy.h +    _httplibcurl.h +    _assert.h +    _refcounted.h +    _mutex.h +    _thread.h +    ) + +set_source_files_properties(${llcorehttp_HEADER_FILES} +                            PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES}) + +add_library (llcorehttp ${llcorehttp_SOURCE_FILES}) +target_link_libraries( +  llcorehttp +  ${CURL_LIBRARIES} +  ${CARES_LIBRARIES} +  ${OPENSSL_LIBRARIES} +  ${CRYPTO_LIBRARIES} +  ) + +# tests +#if (LL_TESTS) +if (LL_TESTS AND 0) +  SET(llcorehttp_TEST_SOURCE_FILES +      test_allocator.cpp +      ) + +  set(llcorehttp_TEST_HEADER_FILS +      test_httpstatus.hpp +      test_refcounted.hpp +      test_httpoperation.hpp +      test_httprequest.hpp +      test_httprequestqueue.hpp +      test_httpheaders.hpp +      test_bufferarray.hpp +      ) + +  set_source_files_properties(${llcorehttp_TEST_HEADER_FILES} +                              PROPERTIES HEADER_FILE_ONLY TRUE) + +  list(APPEND llcorehttp_TEST_SOURCE_FILES ${llcorehttp_TEST_HEADER_FILES}) + +  # LL_ADD_PROJECT_UNIT_TESTS(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}") + +  #    set(TEST_DEBUG on) +  set(test_libs +      ${LLCOREHTTP_LIBRARIES} +      ${WINDOWS_LIBRARIES} +      ${LLCOMMON_LIBRARIES} +      ${GOOGLEMOCK_LIBRARIES} +      ) + +  LL_ADD_INTEGRATION_TEST(all +                          "${llcorehttp_TEST_SOURCE_FILES}" +                          "${test_libs}" +                          ) + +#endif (LL_TESTS) +endif (LL_TESTS AND 0) + diff --git a/indra/llcorehttp/_assert.h b/indra/llcorehttp/_assert.h new file mode 100644 index 0000000000..054f23ef32 --- /dev/null +++ b/indra/llcorehttp/_assert.h @@ -0,0 +1,39 @@ +/**  + * @file _assert + * @brief assert abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT__ASSERT_H_ +#define LLCOREINT__ASSERT_H_ + +#ifdef DEBUG_ASSERT +#include <cassert> +#define LLINT_ASSERT(x)  assert(x) +#else +#define LLINT_ASSERT(x) +#endif + +#endif	// LLCOREINT__ASSERT_H_ + + diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp new file mode 100644 index 0000000000..15be977adf --- /dev/null +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -0,0 +1,214 @@ +/** + * @file _httplibcurl.cpp + * @brief Internal definitions of the Http libcurl thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "_httplibcurl.h" + +#include "httpheaders.h" + +#include "_httpoprequest.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +HttpLibcurl::HttpLibcurl(HttpService * service) +	: mService(service) +{ +	mMultiHandles[0] = curl_multi_init(); +} + + +HttpLibcurl::~HttpLibcurl() +{ +	// *FIXME:  need to cancel requests in this class, not in op class. +	while (! mActiveOps.empty()) +	{ +		active_set_t::iterator item(mActiveOps.begin()); + +		(*item)->cancel(); +		(*item)->release(); +	} + +	if (mMultiHandles[0]) +	{ +		// *FIXME:  Do some multi cleanup here first + +		 +		curl_multi_cleanup(mMultiHandles[0]); +		mMultiHandles[0] = NULL; +	} + +	mService = NULL; +} +	 + +void HttpLibcurl::init() +{} + + +void HttpLibcurl::term() +{} + + +void HttpLibcurl::processTransport() +{ +	if (mMultiHandles[0]) +	{ +		// Give libcurl some cycles to do I/O & callbacks +		int running(0); +		CURLMcode status(CURLM_CALL_MULTI_PERFORM); +		do +		{ +			running = 0; +			status = curl_multi_perform(mMultiHandles[0], &running); +		} +		while (0 != running && CURLM_CALL_MULTI_PERFORM == status); + +		// Run completion on anything done +		CURLMsg * msg(NULL); +		int msgs_in_queue(0); +		while ((msg = curl_multi_info_read(mMultiHandles[0], &msgs_in_queue))) +		{ +			if (CURLMSG_DONE == msg->msg) +			{ +				CURL * handle(msg->easy_handle); +				CURLcode result(msg->data.result); + +				completeRequest(mMultiHandles[0], handle, result); +				handle = NULL;			// No longer valid on return +			} +			else if (CURLMSG_NONE == msg->msg) +			{ +				// Ignore this... it shouldn't mean anything. +				; +			} +			else +			{ +				// *FIXME:  Issue a logging event for this. +				; +			} +			msgs_in_queue = 0; +		} +	} +} + + +void HttpLibcurl::addOp(HttpOpRequest * op) +{ +	// Create standard handle +	if (! op->prepareForGet(mService)) +	{ +		// Couldn't issue request, fail with notification +		// *FIXME:  Need failure path +		return; +	} + +	// Make the request live +	curl_multi_add_handle(mMultiHandles[0], op->mCurlHandle); +	op->mCurlActive = true; +	 +	// On success, make operation active +	mActiveOps.insert(op); +} + + +void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) +{ +	HttpOpRequest * op(NULL); +	curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); +	// *FIXME:  check the pointer + +	if (handle != op->mCurlHandle || ! op->mCurlActive) +	{ +		// *FIXME:  This is a sanity check that needs validation/termination. +		; +	} + +	active_set_t::iterator it(mActiveOps.find(op)); +	if (mActiveOps.end() == it) +	{ +		// *FIXME:  Fatal condition.  This must be here. +		; +	} +	else +	{ +		mActiveOps.erase(it); +	} + +	// Deactivate request +	op->mCurlActive = false; + +	// Set final status of request +	if (op->mStatus) +	{ +		// Only set if it hasn't failed by other mechanisms yet +		op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status); +	} +	if (op->mStatus) +	{ +		int http_status(200); + +		curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); +		op->mReplyStatus = http_status; +	} + +	// Detach from multi and recycle handle +	curl_multi_remove_handle(multi_handle, handle); +	curl_easy_cleanup(handle); +	op->mCurlHandle = NULL; +	 +	// Deliver to reply queue and release +	op->stageFromActive(mService); +	op->release(); +} + + +int HttpLibcurl::activeCount() const +{ +	return mActiveOps.size(); +} + + +// --------------------------------------- +// Free functions +// --------------------------------------- + + +struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist) +{ +	for (HttpHeaders::container_t::const_iterator it(headers->mHeaders.begin()); +		 headers->mHeaders.end() != it; +		 ++it) +	{ +		slist = curl_slist_append(slist, (*it).c_str()); +	} +	return slist; +} + + +}  // end namespace LLCore diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h new file mode 100644 index 0000000000..5ba244cee4 --- /dev/null +++ b/indra/llcorehttp/_httplibcurl.h @@ -0,0 +1,89 @@ +/** + * @file _httplibcurl.h + * @brief Declarations for internal class providing libcurl transport. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_LIBCURL_H_ +#define	_LLCORE_HTTP_LIBCURL_H_ + + +#include <curl/curl.h> +#include <curl/multi.h> + +#include <set> + + +namespace LLCore +{ + + +class HttpService; +class HttpPolicy; +class HttpOpRequest; +class HttpHeaders; + + +class HttpLibcurl +{ +public: +	HttpLibcurl(HttpService * service); +	virtual ~HttpLibcurl(); + +private: +	HttpLibcurl(const HttpLibcurl &);			// Not defined +	void operator=(const HttpLibcurl &);		// Not defined + +public: +	static void init(); +	static void term(); + +	void processTransport(); +	void addOp(HttpOpRequest * op); + +	int activeCount() const; + +protected: +	void completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); +	 +protected: +	typedef std::set<HttpOpRequest *> active_set_t; +	 +protected: +	HttpService *		mService; +	active_set_t		mActiveOps; +	CURLM *				mMultiHandles[1]; +};  // end class HttpLibcurl + + +// --------------------------------------- +// Free functions +// --------------------------------------- + + +curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); + + +}  // end namespace LLCore + +#endif // _LLCORE_HTTP_LIBCURL_H_ diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp new file mode 100644 index 0000000000..17c65b0379 --- /dev/null +++ b/indra/llcorehttp/_httpoperation.cpp @@ -0,0 +1,203 @@ +/** + * @file _httpoperation.cpp + * @brief Definitions for internal classes based on HttpOperation + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "_httpoperation.h" + +#include "httphandler.h" +#include "httpresponse.h" +#include "httprequest.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOperation +// ================================== + + +HttpOperation::HttpOperation() +	: LLCoreInt::RefCounted(true), +	  mReplyQueue(NULL), +	  mLibraryHandler(NULL), +	  mUserHandler(NULL), +	  mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), +	  mReqPriority(0.0f) +{ +} + + +HttpOperation::~HttpOperation() +{ +	setHandlers(NULL, NULL, NULL); +} + + +void HttpOperation::setHandlers(HttpReplyQueue * reply_queue, +								HttpHandler * lib_handler, +								HttpHandler * user_handler) +{ +	if (reply_queue != mReplyQueue) +	{ +		if (mReplyQueue) +		{ +			mReplyQueue->release(); +		} + +		if (reply_queue) +		{ +			reply_queue->addRef(); +		} + +		mReplyQueue = reply_queue; +	} + +	// Not refcounted +	mLibraryHandler = lib_handler; + +	// Not refcounted +	mUserHandler = user_handler; +} + + + +void HttpOperation::stageFromRequest(HttpService *) +{ +	// *FIXME:  Message this a better way later. +	// Default implementation should never be called.  This +	// indicates an operation making a transition that isn't +	// defined. +	LLINT_ASSERT(false); +} + + +void HttpOperation::stageFromReady(HttpService *) +{ +	// *FIXME:  Message this a better way later. +	// Default implementation should never be called.  This +	// indicates an operation making a transition that isn't +	// defined. +	LLINT_ASSERT(false); +} + + +void HttpOperation::stageFromActive(HttpService *) +{ +	// *FIXME:  Message this a better way later. +	// Default implementation should never be called.  This +	// indicates an operation making a transition that isn't +	// defined. +	LLINT_ASSERT(false); +} + +	 +void HttpOperation::visitNotifier(HttpRequest *) +{ +	if (mLibraryHandler) +	{ +		HttpResponse * response = new HttpResponse(); + +		mLibraryHandler->onCompleted(static_cast<HttpHandle>(this), response); + +		response->release(); +	} +} + + +HttpStatus HttpOperation::cancel() +{ +	HttpStatus status; + +	return status; +} + + +void HttpOperation::addAsReply() +{ +	if (mReplyQueue && mLibraryHandler) +	{ +		addRef(); +		mReplyQueue->addOp(this); +	} +} + + +// ================================== +// HttpOpStop +// ================================== + + +HttpOpStop::HttpOpStop() +	: HttpOperation() +{} + + +HttpOpStop::~HttpOpStop() +{} + + +void HttpOpStop::stageFromRequest(HttpService * service) +{ +	// Do operations +	service->stopRequested(); +	 +	// Prepare response if needed +	addAsReply(); +} + + +// ================================== +// HttpOpNull +// ================================== + + +HttpOpNull::HttpOpNull() +	: HttpOperation() +{} + + +HttpOpNull::~HttpOpNull() +{} + + +void HttpOpNull::stageFromRequest(HttpService * service) +{ +	// Perform op +	// Nothing to perform.  This doesn't fall into the libcurl +	// ready/active queues, it just bounces over to the reply +	// queue directly. +	 +	// Prepare response if needed +	addAsReply(); +} + + +}   // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h new file mode 100644 index 0000000000..d04961c47b --- /dev/null +++ b/indra/llcorehttp/_httpoperation.h @@ -0,0 +1,164 @@ +/** + * @file _httpoperation.h + * @brief Internal declarations for HttpOperation and sub-classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_OPERATION_H_ +#define	_LLCORE_HTTP_OPERATION_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + +class HttpReplyQueue; +class HttpHandler; +class HttpService; +class HttpRequest; + +/// HttpOperation is the base class for all request/reply +/// pairs. +/// +class HttpOperation : public LLCoreInt::RefCounted +{ +public: +	HttpOperation(); +	virtual ~HttpOperation(); + +private: +	HttpOperation(const HttpOperation &);				// Not defined +	void operator=(const HttpOperation &);				// Not defined + +public: +	void setHandlers(HttpReplyQueue * reply_queue, +					 HttpHandler * lib_handler, +					 HttpHandler * user_handler); + +	HttpHandler * getUserHandler() const +		{ +			return mUserHandler; +		} +	 +	virtual void stageFromRequest(HttpService *); +	virtual void stageFromReady(HttpService *); +	virtual void stageFromActive(HttpService *); + +	virtual void visitNotifier(HttpRequest *); +	 +	virtual HttpStatus cancel(); +	 +protected: +	void addAsReply(); +	 +protected: +	HttpReplyQueue *	mReplyQueue;					// Have refcount +	HttpHandler *		mLibraryHandler;				// Have refcount +	HttpHandler *		mUserHandler;					// Have refcount + +public: +	unsigned int		mReqPolicy; +	float				mReqPriority; +	 +};  // end class HttpOperation + + +/// HttpOpCancel requests that a previously issued request +/// be canceled, if possible.  Requests that have been made +/// active and are available for sending on the wire cannot +/// be canceled. + +class HttpOpCancel : public HttpOperation +{ +public: +	HttpOpCancel(); +	virtual ~HttpOpCancel(); + +private: +	HttpOpCancel(const HttpOpCancel &);					// Not defined +	void operator=(const HttpOpCancel &);				// Not defined + +public: +	virtual void stageFromRequest(HttpService *); +	virtual void stageFromReady(HttpService *); +	virtual void stageFromActive(HttpService *); + +public: +	HttpHandle			mHandle; +};  // end class HttpOpCancel + + +/// HttpOpStop requests the servicing thread to shutdown +/// operations, cease pulling requests from the request +/// queue and release shared resources (particularly +/// those shared via reference count).  The servicing +/// thread will then exit.  The underlying thread object +/// remains so that another thread can join on the +/// servicing thread prior to final cleanup.  The +/// request *does* generate a reply on the response +/// queue, if requested. + +class HttpOpStop : public HttpOperation +{ +public: +	HttpOpStop(); +	virtual ~HttpOpStop(); + +private: +	HttpOpStop(const HttpOpStop &);					// Not defined +	void operator=(const HttpOpStop &);				// Not defined + +public: +	virtual void stageFromRequest(HttpService *); + +};  // end class HttpOpStop + + +/// HttpOpNull is a do-nothing operation used for testing via +/// a basic loopback pattern.  It's executed immediately by +/// the servicing thread which bounces a reply back to the +/// caller without any further delay. + +class HttpOpNull : public HttpOperation +{ +public: +	HttpOpNull(); +	virtual ~HttpOpNull(); + +private: +	HttpOpNull(const HttpOpNull &);					// Not defined +	void operator=(const HttpOpNull &);				// Not defined + +public: +	virtual void stageFromRequest(HttpService *); + +};  // end class HttpOpNull + +}   // end namespace LLCore + +#endif	// _LLCORE_HTTP_OPERATION_H_ + diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp new file mode 100644 index 0000000000..d71ace5d57 --- /dev/null +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -0,0 +1,473 @@ +/** + * @file _httpoprequest.cpp + * @brief Definitions for internal class HttpOpRequest + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "_httpoprequest.h" + +#include <cstdio> +#include <algorithm> + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" +#include "httpheaders.h" +#include "httpoptions.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" + + +namespace +{ + +// Attempts to parse a 'Content-Range:' header.  Caller must already +// have verified that the header tag is present.  The 'buffer' argument +// will be processed by strtok_r calls which will modify the buffer. +// +// @return		-1 if invalid and response should be dropped, 0 if valid an +//				correct, 1 if couldn't be parsed.  If 0, the first, last, +//				and length arguments are also written.  'length' may be +//				0 if the length wasn't available to the server. +// +int parse_content_range_header(char * buffer, +							   unsigned int * first, +							   unsigned int * last, +							   unsigned int * length); + +#if defined(WIN32) + +// Not available on windows where the legacy strtok interface +// is thread-safe. +char *strtok_r(char *str, const char *delim, char **saveptr); + +#endif + +} + + +namespace LLCore +{ + + +// ================================== +// HttpOpRequest +// ================================== + + +HttpOpRequest::HttpOpRequest() +	: HttpOperation(), +	  mProcFlags(0U), +	  mReqMethod(HOR_GET), +	  mReqBody(NULL), +	  mReqOffset(0), +	  mReqLen(0), +	  mReqHeaders(NULL), +	  mReqOptions(NULL), +	  mCurlActive(false), +	  mCurlHandle(NULL), +	  mCurlHeaders(NULL), +	  mCurlService(NULL), +	  mReplyStatus(200), +	  mReplyBody(NULL), +	  mReplyOffset(0), +	  mReplyLen(0), +	  mReplyHeaders(NULL) +{} + + +HttpOpRequest::~HttpOpRequest() +{ +	if (mCurlHandle) +	{ +		curl_easy_cleanup(mCurlHandle); +		mCurlHandle = NULL; +	} + +	if (mCurlHeaders) +	{ +		curl_slist_free_all(mCurlHeaders); +		mCurlHeaders = NULL; +	} + +	mCurlService = NULL; +	 +	if (mReqBody) +	{ +		mReqBody->release(); +		mReqBody = NULL; +	} +	 +	if (mReqHeaders) +	{ +		curl_slist_free_all(mReqHeaders); +		mReqHeaders = NULL; +	} + +	if (mReqOptions) +	{ +		mReqOptions->release(); +		mReqOptions = NULL; +	} + +	if (mReplyBody) +	{ +		mReplyBody->release(); +		mReplyBody = NULL; +	} + +	if (mReplyHeaders) +	{ +		mReplyHeaders->release(); +		mReplyHeaders = NULL; +	} +} + + +void HttpOpRequest::stageFromRequest(HttpService * service) +{ +	addRef(); +	service->getPolicy()->addOp(this);			// transfers refcount +} + + +void HttpOpRequest::stageFromReady(HttpService * service) +{ +	addRef(); +	service->getTransport()->addOp(this);		// transfers refcount +} + + +void HttpOpRequest::stageFromActive(HttpService * service) +{ +	if (mReplyLen) +	{ +		// 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 (mReplyLen != mReplyBody->size()) +		{ +			// Not as expected, fail the request +			mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); +		} +	} +	 +	if (mReqHeaders) +	{ +		// We take these headers out of the request now as they were +		// allocated originally in this thread and the notifier doesn't +		// need them.  This eliminates one source of heap moving across +		// threads. + +		curl_slist_free_all(mReqHeaders); +		mReqHeaders = NULL; +	} +	addAsReply(); +} + + +void HttpOpRequest::visitNotifier(HttpRequest * request) +{ +	if (mLibraryHandler) +	{ +		HttpResponse * response = new HttpResponse(); + +		// *FIXME:  add http status, offset, length +		response->setStatus(mStatus); +		response->setReplyStatus(mReplyStatus); +		response->setBody(mReplyBody); +		response->setHeaders(mReplyHeaders); +		mLibraryHandler->onCompleted(static_cast<HttpHandle>(this), response); + +		response->release(); +	} +} + + +HttpStatus HttpOpRequest::cancel() +{ +	mStatus = HttpStatus(HttpStatus::LLCORE, HE_OP_CANCELED); + +	addAsReply(); + +	return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupGetByteRange(unsigned int policy_id, +											float priority, +											const std::string & url, +											size_t offset, +											size_t len, +											HttpOptions * options, +											HttpHeaders * headers) +{ +	HttpStatus status; + +	mProcFlags = 0; +	mReqPolicy = policy_id; +	mReqPriority = priority; +	mReqMethod = HOR_GET; +	mReqURL = url; +	mReqOffset = offset; +	mReqLen = len; +	if (offset || len) +	{ +		mProcFlags |= PF_SCAN_RANGE_HEADER; +	} +	if (headers && ! mReqHeaders) +	{ +		mReqHeaders = append_headers_to_slist(headers, mReqHeaders); +	} +	if (options && ! mReqOptions) +	{ +		mReqOptions = new HttpOptions(*options); +	} +	 +	return status; +} + + +HttpStatus HttpOpRequest::prepareForGet(HttpService * service) +{ +	// *FIXME:  better error handling later +	HttpStatus status; +	 +	mCurlHandle = curl_easy_init(); +	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, ""); +	// curl_easy_setopt(handle, CURLOPT_PROXY, ""); + +	mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); +	curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); +	curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); +	curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); +	curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); +	curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); + +	if (mReqOffset || mReqLen) +	{ +		static const char * fmt1("Range: bytes=%d-%d"); +		static const char * fmt2("Range: bytes=%d-"); + +		char range_line[64]; + +#if defined(WIN32) +		_snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, +					(mReqLen ? fmt1 : fmt2), +					mReqOffset, mReqOffset + mReqLen - 1); +#else +		snprintf(range_line, sizeof(range_line), +				 (mReqLen ? fmt1 : fmt2), +				 mReqOffset, mReqOffset + mReqLen - 1); +#endif // defined(WIN32) +		range_line[sizeof(range_line) - 1] = '\0'; +		mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); +	} +	 +	if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) +	{ +		curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); +		curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, mCurlHandle); +	} +	 +	if (status) +	{ +		mCurlService = service; +	} +	return status; +} + + +size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ +	CURL * handle(static_cast<CURL *>(userdata)); +	HttpOpRequest * op(NULL); +	curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); +	// *FIXME:  check the pointer + +	if (! op->mReplyBody) +	{ +		op->mReplyBody = new BufferArray(); +	} +	const size_t req_size(size * nmemb); +	char * lump(op->mReplyBody->appendBufferAlloc(req_size)); +	memcpy(lump, data, req_size); + +	return req_size; +} + +		 +size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ +	static const char status_line[] = "HTTP/"; +	static const size_t status_line_len = sizeof(status_line) - 1; + +	static const char con_ran_line[] = "content-range:"; +	static const size_t con_ran_line_len = sizeof(con_ran_line) - 1; +	 +	CURL * handle(static_cast<CURL *>(userdata)); +	HttpOpRequest * op(NULL); +	curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); +	// *FIXME:  check the pointer + +	const size_t hdr_size(size * nmemb); +	const char * hdr_data(static_cast<const char *>(data));		// Not null terminated +	 +	if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len)) +	{ +		// One of possibly several status lines.  Reset what we know and start over +		op->mReplyOffset = 0; +		op->mReplyLen = 0; +		op->mStatus = HttpStatus(); +	} +	else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) +	{ +		char hdr_buffer[128]; +		size_t frag_size((std::min)(hdr_size, sizeof(hdr_buffer) - 1)); +		 +		memcpy(hdr_buffer, hdr_data, frag_size); +		hdr_buffer[frag_size] = '\0'; +#if defined(WIN32) +		if (! _strnicmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) +#else +		if (! strncasecmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) +#endif +		{ +			unsigned int first(0), last(0), length(0); +			int status; + +			if (! (status = parse_content_range_header(hdr_buffer, &first, &last, &length))) +			{ +				// Success, record the fragment position +				op->mReplyOffset = first; +				op->mReplyLen = last - first + 1; +			} +			else if (-1 == status) +			{ +				// Response is badly formed and shouldn't be accepted +				op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); +			} +			else +			{ +				// Ignore the unparsable. +				// *FIXME:  Maybe issue a warning into the log here +				; +			} +		} +	} + +	if (op->mProcFlags & PF_SAVE_HEADERS) +	{ +		// Save headers in response +		; +		 +	} + +	return hdr_size; +} + +}   // end namespace LLCore + + +// ======================================= +// Anonymous Namespace +// ======================================= + +namespace +{ + +int parse_content_range_header(char * buffer, +							   unsigned int * first, +							   unsigned int * last, +							   unsigned int * length) +{ +	char * tok_state(NULL), * tok; +	bool match(true); +			 +	if (! strtok_r(buffer, ": \t", &tok_state)) +		match = false; +	if (match && (tok = strtok_r(NULL, " \t", &tok_state))) +#if defined(WIN32) +		match = 0 == _stricmp("bytes", tok); +#else +		match = 0 == strcasecmp("bytes", tok); +#endif +	if (match && ! (tok = strtok_r(NULL, " \t", &tok_state))) +		match = false; +	if (match) +	{ +		unsigned int lcl_first(0), lcl_last(0), lcl_len(0); + +#if defined(WIN32) +		if (3 == sscanf_s(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) +#else +		if (3 == sscanf(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) +#endif +		{ +			if (lcl_first > lcl_last || lcl_last >= lcl_len) +				return -1; +			*first = lcl_first; +			*last = lcl_last; +			*length = lcl_len; +			return 0; +		} +#if defined(WIN32) +		if (2 == sscanf_s(tok, "%u-%u/*", &lcl_first, &lcl_last)) +#else +		if (2 == sscanf(tok, "%u-%u/*", &lcl_first, &lcl_last)) +#endif +		{ +			if (lcl_first > lcl_last) +				return -1; +			*first = lcl_first; +			*last = lcl_last; +			*length = 0; +			return 0; +		} +	} + +	// Header is there but badly/unexpectedly formed, try to ignore it. +	return 1; +} + +#if defined(WIN32) + +char *strtok_r(char *str, const char *delim, char ** savestate) +{ +	return strtok_s(str, delim, savestate); +} + +#endif + +}  // end anonymous namespace + +		 diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h new file mode 100644 index 0000000000..e912851aed --- /dev/null +++ b/indra/llcorehttp/_httpoprequest.h @@ -0,0 +1,127 @@ +/** + * @file _httpoprequest.h + * @brief Internal declarations for the HttpOpRequest subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_OPREQUEST_H_ +#define	_LLCORE_HTTP_OPREQUEST_H_ + + +#include "httpcommon.h" + +#include <curl/curl.h> + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +class BufferArray; +class HttpHeaders; +class HttpOptions; + + +/// HttpOpRequest requests a supported HTTP method invocation with +/// option and header overrides. + +class HttpOpRequest : public HttpOperation +{ +public: +	HttpOpRequest(); +	virtual ~HttpOpRequest(); + +private: +	HttpOpRequest(const HttpOpRequest &);				// Not defined +	void operator=(const HttpOpRequest &);				// Not defined + +public: +	enum EMethod +	{ +		HOR_GET, +		HOR_POST, +		HOR_PUT +	}; +	 +	virtual void stageFromRequest(HttpService *); +	virtual void stageFromReady(HttpService *); +	virtual void stageFromActive(HttpService *); + +	virtual void visitNotifier(HttpRequest * request); +			 +public: +	// Setup Methods +	HttpStatus setupGetByteRange(unsigned int policy_id, +								 float priority, +								 const std::string & url, +								 size_t offset, +								 size_t len, +								 HttpOptions * options, +								 HttpHeaders * headers); +	 +	HttpStatus prepareForGet(HttpService * service); +	 +	virtual HttpStatus cancel(); + +protected: +	static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); +	static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); + +protected: +	unsigned int		mProcFlags; +	static const unsigned int	PF_SCAN_RANGE_HEADER = 0x00000001U; +	static const unsigned int	PF_SAVE_HEADERS = 0x00000002U; +	 +public: +	// Request data +	EMethod				mReqMethod; +	std::string			mReqURL; +	BufferArray *		mReqBody; +	off_t				mReqOffset; +	size_t				mReqLen; +	curl_slist *		mReqHeaders; +	HttpOptions *		mReqOptions; + +	// Transport data +	bool				mCurlActive; +	CURL *				mCurlHandle; +	curl_slist *		mCurlHeaders; +	HttpService *		mCurlService; +	 +	// Result data +	HttpStatus			mStatus; +	int					mReplyStatus; +	BufferArray *		mReplyBody; +	off_t				mReplyOffset; +	size_t				mReplyLen; +	HttpHeaders *		mReplyHeaders; +};  // end class HttpOpRequest + + +}   // end namespace LLCore + +#endif	// _LLCORE_HTTP_OPREQUEST_H_ + diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp new file mode 100644 index 0000000000..d965a6cf3a --- /dev/null +++ b/indra/llcorehttp/_httppolicy.cpp @@ -0,0 +1,76 @@ +/** + * @file _httppolicy.cpp + * @brief Internal definitions of the Http policy thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "_httppolicy.h" + +#include "_httpoprequest.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +HttpPolicy::HttpPolicy(HttpService * service) +	: mService(service) +{} + + +HttpPolicy::~HttpPolicy() +{ +	for (ready_queue_t::reverse_iterator i(mReadyQueue.rbegin()); +		 mReadyQueue.rend() != i;) +	{ +		ready_queue_t::reverse_iterator cur(i++); + +		(*cur)->cancel(); +		(*cur)->release(); +	} + +	mService = NULL; +} + + +void HttpPolicy::addOp(HttpOpRequest * op) +{ +	mReadyQueue.push_back(op); +} + + +void HttpPolicy::processReadyQueue() +{ +	while (! mReadyQueue.empty()) +	{ +		HttpOpRequest * op(mReadyQueue.front()); +		mReadyQueue.erase(mReadyQueue.begin()); + +		op->stageFromReady(mService); +		op->release(); +	} +} + + +}  // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h new file mode 100644 index 0000000000..28aea27f38 --- /dev/null +++ b/indra/llcorehttp/_httppolicy.h @@ -0,0 +1,68 @@ +/** + * @file _httppolicy.h + * @brief Declarations for internal class enforcing policy decisions. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_POLICY_H_ +#define	_LLCORE_HTTP_POLICY_H_ + + +#include <vector> + + +namespace LLCore +{ + + +class HttpService; +class HttpOpRequest; + + +class HttpPolicy +{ +public: +	HttpPolicy(HttpService *); +	virtual ~HttpPolicy(); + +private: +	HttpPolicy(const HttpPolicy &);				// Not defined +	void operator=(const HttpPolicy &);			// Not defined + +public: +	void processReadyQueue(); + +	void addOp(HttpOpRequest *); +	 +protected: +	typedef std::vector<HttpOpRequest *> ready_queue_t; +	 +protected: +	HttpService *		mService;				// Naked pointer, not refcounted +	ready_queue_t		mReadyQueue; +	 +};  // end class HttpPolicy + +}  // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_H_ diff --git a/indra/llcorehttp/_httpreplyqueue.cpp b/indra/llcorehttp/_httpreplyqueue.cpp new file mode 100644 index 0000000000..a354ed7e10 --- /dev/null +++ b/indra/llcorehttp/_httpreplyqueue.cpp @@ -0,0 +1,87 @@ +/** + * @file _httpreplyqueue.cpp + * @brief Internal definitions for the operation reply queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "_httpreplyqueue.h" + + +#include "_mutex.h" +#include "_thread.h" +#include "_httpoperation.h" + +using namespace LLCoreInt; + + +namespace LLCore +{ + + +HttpReplyQueue::HttpReplyQueue() +	: RefCounted(true) +{ +} + + +HttpReplyQueue::~HttpReplyQueue() +{ +	while (! mQueue.empty()) +	{ +		HttpOperation * op = mQueue.back(); +		mQueue.pop_back(); +		op->release(); +	} +} + + +void HttpReplyQueue::addOp(HttpOperation * op) +{ +	{ +		HttpScopedLock lock(mQueueMutex); + +		mQueue.push_back(op); +	} +	mQueueCV.notify_all(); +} + + +HttpOperation * HttpReplyQueue::fetchOp() +{ +	HttpOperation * result(NULL); + +	{ +		HttpScopedLock lock(mQueueMutex); + +		if (mQueue.empty()) +			return NULL; + +		result = mQueue.front(); +		mQueue.erase(mQueue.begin()); +	} + +	// Caller also acquires the reference count +	return result; +} + +}  // end namespace LLCore diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h new file mode 100644 index 0000000000..56dadec87c --- /dev/null +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -0,0 +1,106 @@ +/** + * @file _httpreplyqueue.h + * @brief Internal declarations for the operation reply queue. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_REPLY_QUEUE_H_ +#define	_LLCORE_HTTP_REPLY_QUEUE_H_ + + +#include "_refcounted.h" +#include "_mutex.h" + + +namespace LLCore +{ + + +class HttpOperation; + + +/// Almost identical to the HttpRequestQueue class but +/// whereas that class is a singleton and is known to the +/// HttpService object, this queue is 1:1 with HttpRequest +/// instances and isn't explicitly referenced by the +/// service object.  Instead, HttpOperation objects that +/// want to generate replies back to their creators also +/// keep references to the corresponding HttpReplyQueue. +/// The HttpService plumbing then simply delivers replies +/// to the requested reply queue. +/// +/// One result of that is that the fetch operations do +/// not have a wait forever option.  The service object +/// doesn't keep handles on everything it would need to +/// notify so it can't wake up sleepers should it need to +/// shutdown.  So only non-blocking or timed-blocking modes +/// are anticipated.  These are how most application consumers +/// will be coded anyway so it shouldn't be too much of a +/// burden. + +class HttpReplyQueue: public LLCoreInt::RefCounted +{ +public: +	/// Caller acquires a Refcount on construction +	HttpReplyQueue(); +	virtual ~HttpReplyQueue(); + +private: +	HttpReplyQueue(const HttpReplyQueue &);				// Not defined +	void operator=(const HttpReplyQueue &);				// Not defined + +public: +	typedef std::vector<HttpOperation *> OpContainer; + +	/// Insert an object at the back of the reply queue. +	/// +	/// Library also takes possession of one reference count to pass +	/// through the queue. +	/// +	/// Threading:  callable by any thread. +	void addOp(HttpOperation * op); + +	/// Fetch an operation from the head of the queue.  Returns +	/// NULL if none exists. +	/// +	/// Caller acquires reference count on returned operation. +	/// +	/// Threading:  callable by any thread. +	HttpOperation * fetchOp(); + +	/// Caller acquires reference count on each returned operation +	/// +	/// Threading:  callable by any thread. +	void fetchAll(OpContainer & ops); +	 +protected: +	OpContainer							mQueue; +	LLCoreInt::HttpMutex				mQueueMutex; +	LLCoreInt::HttpConditionVariable	mQueueCV; +	 +}; // end class HttpReplyQueue + +}  // end namespace LLCore + + +#endif	// _LLCORE_HTTP_REPLY_QUEUE_H_ diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp new file mode 100644 index 0000000000..c36814aee3 --- /dev/null +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -0,0 +1,132 @@ +/** + * @file _httprequestqueue.cpp + * @brief  + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "_httprequestqueue.h" + +#include "_httpoperation.h" +#include "_mutex.h" + + +using namespace LLCoreInt; + +namespace LLCore +{ + +HttpRequestQueue * HttpRequestQueue::sInstance(NULL); + + +HttpRequestQueue::HttpRequestQueue() +	: RefCounted(true) +{ +} + + +HttpRequestQueue::~HttpRequestQueue() +{ +	while (! mQueue.empty()) +	{ +		HttpOperation * op = mQueue.back(); +		mQueue.pop_back(); +		op->release(); +	} +} + + +void HttpRequestQueue::init() +{ +	LLINT_ASSERT(! sInstance); +	sInstance = new HttpRequestQueue(); +} + + +void HttpRequestQueue::term() +{ +	if (sInstance) +	{ +		sInstance->release(); +		sInstance = NULL; +	} +} + + +void HttpRequestQueue::addOp(HttpOperation * op) +{ +	{ +		HttpScopedLock lock(mQueueMutex); + +		mQueue.push_back(op); +	} +	mQueueCV.notify_all(); +} + + +HttpOperation * HttpRequestQueue::fetchOp(bool wait) +{ +	HttpOperation * result(NULL); + +	{ +		HttpScopedLock lock(mQueueMutex); + +		while (mQueue.empty()) +		{ +			if (! wait) +				return NULL; +			mQueueCV.wait(lock); +		} + +		result = mQueue.front(); +		mQueue.erase(mQueue.begin()); +	} + +	// Caller also acquires the reference count +	return result; +} + + +void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) +{ +	// Note:  Should probably test whether we're empty or not here. +	// A target passed in with entries is likely also carrying +	// reference counts and we're going to leak something. +	ops.clear(); +	{ +		HttpScopedLock lock(mQueueMutex); + +		while (mQueue.empty()) +		{ +			if (! wait) +				return; +			mQueueCV.wait(lock); +		} + +		mQueue.swap(ops); +	} + +	// Caller also acquires the reference counts on each op. +	return; +} + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h new file mode 100644 index 0000000000..3a9ce0c3c6 --- /dev/null +++ b/indra/llcorehttp/_httprequestqueue.h @@ -0,0 +1,104 @@ +/** + * @file _httprequestqueue.h + * @brief Internal declaration for the operation request queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_REQUEST_QUEUE_H_ +#define	_LLCORE_HTTP_REQUEST_QUEUE_H_ + + +#include <vector> + +#include "_refcounted.h" +#include "_mutex.h" + + +namespace LLCore +{ + + +class HttpOperation; + + +/// Thread-safe queue of HttpOperation objects.  Just +/// a simple queue that handles the transfer of operation +/// requests from all HttpRequest instances into the +/// singleton HttpService instance. + +class HttpRequestQueue: public LLCoreInt::RefCounted +{ +protected: +	/// Caller acquires a Refcount on construction +	HttpRequestQueue(); +	virtual ~HttpRequestQueue(); + +private: +	HttpRequestQueue(const HttpRequestQueue &);			// Not defined +	void operator=(const HttpRequestQueue &);			// Not defined + +public: +	static void init(); +	static void term(); +	 +	/// Threading:  callable by any thread once inited. +	inline static HttpRequestQueue * instanceOf() +		{ +			return sInstance; +		} +	 +public: +	typedef std::vector<HttpOperation *> OpContainer; + +	/// Insert an object at the back of the reply queue. +	/// +	/// Caller my provide one refcount to the Library which takes +	/// possession of the count. +	/// +	/// Threading:  callable by any thread. +	void addOp(HttpOperation * op); + +	/// Caller acquires reference count on returned operation +	/// +	/// Threading:  callable by any thread. +	HttpOperation * fetchOp(bool wait); + +	/// Caller acquires reference count on each returned operation +	/// +	/// Threading:  callable by any thread. +	void fetchAll(bool wait, OpContainer & ops); +	 +protected: +	static HttpRequestQueue *			sInstance; +	 +protected: +	OpContainer							mQueue; +	LLCoreInt::HttpMutex				mQueueMutex; +	LLCoreInt::HttpConditionVariable	mQueueCV; +	 +}; // end class HttpRequestQueue + +}  // end namespace LLCore + + +#endif	// _LLCORE_HTTP_REQUEST_QUEUE_H_ diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp new file mode 100644 index 0000000000..6ebc0ec6cb --- /dev/null +++ b/indra/llcorehttp/_httpservice.cpp @@ -0,0 +1,209 @@ +/** + * @file _httpservice.cpp + * @brief Internal definitions of the Http service thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "_httpservice.h" + +#include <boost/bind.hpp> +#include <boost/function.hpp> + +#include "_httpoperation.h" +#include "_httprequestqueue.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" +#include "_thread.h" + + +namespace LLCore +{ + +HttpService * HttpService::sInstance(NULL); +volatile HttpService::EState HttpService::sState(NOT_INITIALIZED); + +HttpService::HttpService() +	: mRequestQueue(NULL), +	  mExitRequested(false), +	  mThread(NULL), +	  mPolicy(NULL), +	  mTransport(NULL) +{ +} + + +HttpService::~HttpService() +{ +	if (mRequestQueue) +	{ +		mRequestQueue->release(); +		mRequestQueue = NULL; +	} + +	if (mPolicy) +	{ +		// *TODO:  need a finalization here +		; +	} +	 +	if (mTransport) +	{ +		// *TODO:  need a finalization here +		delete mTransport; +		mTransport = NULL; +	} +	 +	if (mPolicy) +	{ +		delete mPolicy; +		mPolicy = NULL; +	} + +	if (mThread) +	{ +		mThread->release(); +		mThread = NULL; +	} +} +	 + +void HttpService::init(HttpRequestQueue * queue) +{ +	LLINT_ASSERT(! sInstance); +	LLINT_ASSERT(NOT_INITIALIZED == sState); +	sInstance = new HttpService(); + +	queue->addRef(); +	sInstance->mRequestQueue = queue; +	sInstance->mPolicy = new HttpPolicy(sInstance); +	sInstance->mTransport = new HttpLibcurl(sInstance); +	sState = INITIALIZED; +} + + +void HttpService::term() +{ +	LLINT_ASSERT(RUNNING != sState); +	if (sInstance) +	{ +		delete sInstance; +		sInstance = NULL; +	} +	sState = NOT_INITIALIZED; +} + + +bool HttpService::isStopped() +{ +	// What is really wanted here is something like: +	// +	//     HttpService * service = instanceOf(); +	//     return STOPPED == sState && (! service || ! service->mThread || ! service->mThread->joinable()); +	// +	// But boost::thread is not giving me a consistent story on joinability +	// of a thread after it returns.  Debug and non-debug builds are showing +	// different behavior on Linux/Etch so we do a weaker test that may +	// not be globally correct (i.e. thread *is* stopping, may not have +	// stopped but will very soon): +	 +	return STOPPED == sState; +} + + +void HttpService::startThread() +{ +	LLINT_ASSERT(! mThread || STOPPED == sState); +	LLINT_ASSERT(INITIALIZED == sState || STOPPED == sState); + +	if (mThread) +	{ +		mThread->release(); +	} +	mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); +	mThread->addRef();		// Need an explicit reference, implicit one is used internally +	sState = RUNNING; +} + + +void HttpService::stopRequested() +{ +	mExitRequested = true; +} + + +void HttpService::shutdown() +{ +	// *FIXME:  Run down everything.... +} + + +void HttpService::threadRun(LLCoreInt::HttpThread * thread) +{ +	boost::this_thread::disable_interruption di; + +	while (! mExitRequested) +	{ +		processRequestQueue(); + +		// Process ready queue issuing new requests as needed +		mPolicy->processReadyQueue(); +		 +		// Give libcurl some cycles +		mTransport->processTransport(); +		 +		// Determine whether to spin, sleep briefly or sleep for next request +		// *FIXME:  For now, do this +#if defined(WIN32) +		Sleep(50); +#else +		usleep(5000); +#endif +	} +	shutdown(); +	sState = STOPPED; +} + + +void HttpService::processRequestQueue() +{ +	HttpRequestQueue::OpContainer ops; + +	mRequestQueue->fetchAll(false, ops); +	while (! ops.empty()) +	{ +		HttpOperation * op(ops.front()); +		ops.erase(ops.begin()); + +		// Process operation +		if (! mExitRequested) +		{ +			op->stageFromRequest(this); +		} +				 +		// Done with operation +		op->release(); +	} +} + + +}  // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h new file mode 100644 index 0000000000..c052e35452 --- /dev/null +++ b/indra/llcorehttp/_httpservice.h @@ -0,0 +1,162 @@ +/** + * @file _httpservice.h + * @brief Declarations for internal class providing HTTP service. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_SERVICE_H_ +#define	_LLCORE_HTTP_SERVICE_H_ + + +namespace LLCoreInt +{ + +class HttpThread; + +} + + +namespace LLCore +{ + + +class HttpRequestQueue; +class HttpPolicy; +class HttpLibcurl; + + +/// The HttpService class does the work behind the request  queue.  It +/// oversees the HTTP workflow carrying out a number of tasks: +/// - Pulling requests from the global request queue +/// - Executing 'immediate' requests directly +/// - Prioritizing and re-queuing on internal queues the slower requests +/// - Providing cpu cycles to the libcurl plumbing +/// - Overseeing retry operations +/// +/// Note that the service object doesn't have a pointer to any +/// reply queue.  These are kept by HttpRequest and HttpOperation +/// only. +/// +/// Service, Policy and Transport +/// +/// HttpService could have been a monolithic class combining a request +/// queue servicer, request policy manager and network transport. +/// Instead, to prevent monolithic growth and allow for easier +/// replacement, it was developed as three separate classes:  HttpService, +/// HttpPolicy and HttpLibcurl (transport).  These always exist in a +/// 1:1:1 relationship with HttpService managing instances of the other +/// two.  So, these classes do not use reference counting to refer +/// to one-another, their lifecycles are always managed together. + +class HttpService +{ +protected: +	HttpService(); +	virtual ~HttpService(); + +private: +	HttpService(const HttpService &);			// Not defined +	void operator=(const HttpService &);		// Not defined + +public: +	enum EState +	{ +		NOT_INITIALIZED = -1, +		INITIALIZED,			///< init() has been called +		RUNNING,				///< thread created and running +		STOPPED					///< thread has committed to exiting +	}; +	 +	static void init(HttpRequestQueue *); +	static void term(); + +	/// Threading:  callable by any thread once inited. +	inline static HttpService * instanceOf() +		{ +			return sInstance; +		} + +	/// Return the state of the worker thread.  Note that the +	/// transition from RUNNING to STOPPED is performed by the +	/// worker thread itself.  This has two weaknesses: +	/// - race where the thread hasn't really stopped but will +	/// - data ordering between threads where a non-worker thread +	///   may see a stale RUNNING status. +	/// +	/// This transition is generally of interest only to unit tests +	/// and these weaknesses shouldn't be any real burden. +	/// +	/// Threading:  callable by any thread with above exceptions. +	static EState getState() +		{ +			return sState; +		} + +	/// Threading:  callable by any thread but uses @see getState() and +	/// acquires its weaknesses. +	static bool isStopped(); + +	/// Threading:  callable by application thread *once*. +	void startThread(); + +	/// Threading:  callable by worker thread. +	void stopRequested(); + +	/// Threading:  callable by worker thread. +	void shutdown(); +	 +	HttpPolicy * getPolicy() +		{ +			return mPolicy; +		} + +	HttpLibcurl * getTransport() +		{ +			return mTransport; +		} +	 +protected: +	void threadRun(LLCoreInt::HttpThread * thread); +	 +	void processRequestQueue(); +	 +protected: +	static HttpService *		sInstance; +	 +	// === shared data === +	static volatile EState		sState; +	HttpRequestQueue *			mRequestQueue; +	volatile bool				mExitRequested; +	 +	// === calling-thread-only data === +	LLCoreInt::HttpThread *		mThread; + +	// === working-thread-only data === +	HttpPolicy *				mPolicy; +	HttpLibcurl *				mTransport; +	 +};  // end class HttpService + +}  // end namespace LLCore + +#endif // _LLCORE_HTTP_SERVICE_H_ diff --git a/indra/llcorehttp/_mutex.h b/indra/llcorehttp/_mutex.h new file mode 100644 index 0000000000..4be4d016d4 --- /dev/null +++ b/indra/llcorehttp/_mutex.h @@ -0,0 +1,55 @@ +/**  + * @file _mutex.hpp + * @brief mutex type abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT_MUTEX_H_ +#define LLCOREINT_MUTEX_H_ + + +#include <boost/thread.hpp> + + +namespace LLCoreInt +{ + +// MUTEX TYPES + +// unique mutex type +typedef boost::mutex HttpMutex; + +// CONDITION VARIABLES + +// standard condition variable +typedef boost::condition_variable HttpConditionVariable; + +// LOCKS AND FENCES + +// scoped unique lock +typedef boost::unique_lock<HttpMutex> HttpScopedLock; + +} + +#endif	// LLCOREINT_MUTEX_H + diff --git a/indra/llcorehttp/_refcounted.cpp b/indra/llcorehttp/_refcounted.cpp new file mode 100644 index 0000000000..2c132e1b36 --- /dev/null +++ b/indra/llcorehttp/_refcounted.cpp @@ -0,0 +1,39 @@ +/**  + * @file _refcounted.cpp + * @brief Atomic, thread-safe ref counting and destruction mixin class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "_refcounted.h" + + +namespace LLCoreInt +{ + +RefCounted::~RefCounted() +{} + + +} // end namespace LLCoreInt + + diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h new file mode 100644 index 0000000000..4a6ce8420a --- /dev/null +++ b/indra/llcorehttp/_refcounted.h @@ -0,0 +1,141 @@ +/**  + * @file _refcounted.h + * @brief Atomic, thread-safe ref counting and destruction mixin class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT__REFCOUNTED_H_ +#define LLCOREINT__REFCOUNTED_H_ + + +#include <boost/thread.hpp> + +#include "_assert.h" + + +namespace LLCoreInt +{ + + +class RefCounted +{ +private: +	RefCounted();								// Not defined - may not be default constructed +	void operator=(const RefCounted &);			// Not defined +	 +public: +	explicit RefCounted(bool const implicit)  +		: mRefCount(implicit) +		{} + +	// ref-count interface +	void addRef() const; +	void release() const; +	bool isLastRef() const; +	int getRefCount() const; +	void noRef() const; + +	static const int			NOT_REF_COUNTED = -1; +	 +protected: +	virtual ~RefCounted(); +	virtual void destroySelf(); + +private: +	mutable int					mRefCount; +	mutable boost::mutex		mRefLock; + +}; // end class RefCounted + + +inline void RefCounted::addRef() const +{ +	boost::mutex::scoped_lock lock(mRefLock); +	LLINT_ASSERT(mRefCount >= 0); +	++mRefCount; +} + + +inline void RefCounted::release() const +{ +	int count(0); +	{ +		// CRITICAL SECTION +		boost::mutex::scoped_lock lock(mRefLock); +		LLINT_ASSERT(mRefCount != NOT_REF_COUNTED); +		LLINT_ASSERT(mRefCount > 0); +		count = --mRefCount; +		// CRITICAL SECTION +	} + + +	// clean ourselves up if that was the last reference +	if (0 == count) +	{ +		const_cast<RefCounted *>(this)->destroySelf(); +	} +} + + +inline bool RefCounted::isLastRef() const +{ +	int count(0); +	{ +		// CRITICAL SECTION +		boost::mutex::scoped_lock lock(mRefLock); + +		LLINT_ASSERT(mRefCount != NOT_REF_COUNTED); +		LLINT_ASSERT(mRefCount >= 1); +		count = mRefCount; +		// CRITICAL SECTION +	} + +	return (1 == count); +} + + +inline int RefCounted::getRefCount() const +{ +	boost::mutex::scoped_lock lock(mRefLock); +	const int result(mRefCount); +	return result; +} + + +inline void RefCounted::noRef() const +{ +	boost::mutex::scoped_lock lock(mRefLock); +	LLINT_ASSERT(mRefCount <= 1); +	mRefCount = NOT_REF_COUNTED; +} + + +inline void RefCounted::destroySelf() +{ +	delete this; +} + +} // end namespace LLCoreInt + +#endif	// LLCOREINT__REFCOUNTED_H_ + diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h new file mode 100644 index 0000000000..5960f0dcdb --- /dev/null +++ b/indra/llcorehttp/_thread.h @@ -0,0 +1,106 @@ +/**  + * @file _thread.h + * @brief thread type abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT_THREAD_H_ +#define LLCOREINT_THREAD_H_ + + +#include "_refcounted.h" + + +namespace LLCoreInt +{ + +class HttpThread : public RefCounted +{ +private: +	HttpThread();							// Not defined +	void operator=(const HttpThread &);		// Not defined + +	void at_exit() +		{ +			// the thread function has exited so we need to release our reference +			// to ourself so that we will be automagically cleaned up. +			release(); +		} + +	void run() +		{ // THREAD CONTEXT  + +			// The implicit reference to this object is taken for the at_exit +			// function so that the HttpThread instance doesn't disappear out +			// from underneath it.  Other holders of the object may want to +			// take a reference as well. +			boost::this_thread::at_thread_exit(boost::bind(&HttpThread::at_exit, this)); + +			// run the thread function +			mThreadFunc(this); + +		} // THREAD CONTEXT + +public: +	/// Constructs a thread object for concurrent execution but does +	/// not start running.  Unlike other classes that mixin RefCounted, +	/// this does take out a reference but it is used internally for +	/// final cleanup during at_exit processing.  Callers needing to +	/// keep a reference must increment it themselves. +	/// +	explicit HttpThread(boost::function<void (HttpThread *)> threadFunc) +		: RefCounted(true), // implicit reference +		  mThreadFunc(threadFunc) +		{ +			// this creates a boost thread that will call HttpThread::run on this instance +			// and pass it the threadfunc callable... +			boost::function<void()> f = boost::bind(&HttpThread::run, this); + +			mThread = new boost::thread(f); +		} + +	virtual ~HttpThread() +		{ +			delete mThread; +		} + +	inline void join() +		{ +			mThread->join(); +		} + +	inline bool joinable() const +		{ +			mThread->joinable(); +		} + +private: +	boost::function<void(HttpThread *)> mThreadFunc; +	boost::thread * mThread; +}; // end class HttpThread + +} // end namespace LLCoreInt + +#endif // LLCOREINT_THREAD_H_ + + diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp new file mode 100644 index 0000000000..4c20350b13 --- /dev/null +++ b/indra/llcorehttp/bufferarray.cpp @@ -0,0 +1,281 @@ +/** + * @file bufferarray.cpp + * @brief Implements the BufferArray scatter/gather buffer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "bufferarray.h" + + +namespace LLCore +{ + + +// ================================== +// BufferArray::Block Declaration +// ================================== + +class BufferArray::Block +{ +public: +	~Block(); + +	void operator delete(void *); +	void operator delete(void *, size_t len); + +protected: +	Block(size_t len); + +	Block(const Block &);						// Not defined +	void operator=(const Block &);				// Not defined + +	// Allocate the block with the additional space for the +	// buffered data at the end of the object. +	void * operator new(size_t len, size_t addl_len); +	 +public: +	// Only public entry to get a block. +	static Block * alloc(size_t len); + +public: +	size_t mLen; + +	// *NOTE:  Must be last member of the object.  We'll +	// overallocate as requested via operator new and index +	// into the array at will. +	char mData[1];		 +}; + + +// ================================== +// BufferArray Definitions +// ================================== + + +BufferArray::BufferArray() +	: LLCoreInt::RefCounted(true), +	  mPos(0), +	  mLen(0) +{} + + +BufferArray::~BufferArray() +{ +	for (container_t::iterator it(mBlocks.begin()); +		 it != mBlocks.end(); +		 ++it) +	{ +		delete *it; +		*it = NULL; +	} +	mBlocks.clear(); +} + + +size_t BufferArray::append(const char * src, size_t len) +{ +	if (len) +	{ +		if (mBlocks.size() >= mBlocks.capacity()) +		{ +			mBlocks.reserve(mBlocks.size() + 5); +		} +		Block * block = Block::alloc(len); +		memcpy(block->mData, src, len); +		mBlocks.push_back(block); +		mLen += len; +		mPos = mLen; +	} +	return len; +} + + +char * BufferArray::appendBufferAlloc(size_t len) +{ +	// If someone asks for zero-length, we give them a valid pointer. +	if (mBlocks.size() >= mBlocks.capacity()) +	{ +		mBlocks.reserve(mBlocks.size() + 5); +	} +	Block * block = Block::alloc(len); +	mBlocks.push_back(block); +	mLen += len; +	mPos = mLen; +	return block->mData; +} + + +size_t BufferArray::seek(size_t pos) +{ +	if (pos > mLen) +		pos = mLen; +	mPos = pos; +	return mPos; +} + +	 +size_t BufferArray::read(char * dst, size_t len) +{ +	size_t result(0), offset(0); +	size_t len_limit(mLen - mPos); +	len = std::min(len, len_limit); + +	if (mPos >= mLen || 0 == len) +		return 0; +	 +	const int block_limit(mBlocks.size()); +	int block_start(findBlock(mPos, &offset)); +	if (block_start < 0) +		return 0; + +	do +	{ +		Block & block(*mBlocks[block_start]); +		size_t block_limit(block.mLen - offset); +		size_t block_len(std::min(block_limit, len)); +		 +		memcpy(dst, &block.mData[offset], block_len); +		result += block_len; +		len -= block_len; +		dst += block_len; +		offset = 0; +		++block_start; +	} +	while (len && block_start < block_limit); + +	mPos += result; +	return result; +} + + +size_t BufferArray::write(const char * src, size_t len) +{ +	size_t result(0), offset(0); +	if (mPos > mLen || 0 == len) +		return 0; +	 +	const int block_limit(mBlocks.size()); +	int block_start(findBlock(mPos, &offset)); + +	if (block_start >= 0) +	{ +		// Some or all of the write will be on top of +		// existing data. +		do +		{ +			Block & block(*mBlocks[block_start]); +			size_t block_limit(block.mLen - offset); +			size_t block_len(std::min(block_limit, len)); +		 +			memcpy(&block.mData[offset], src, block_len); +			result += block_len; +			len -= block_len; +			src += block_len; +			offset = 0; +			++block_start; +		} +		while (len && block_start < block_limit); +	} +	mPos += result; + +	if (len) +	{ +		// Some or all of the remaining write data will +		// be an append. +		result += append(src, len); +	} + +	return result; +} +		 + +int BufferArray::findBlock(size_t pos, size_t * ret_offset) +{ +	*ret_offset = 0; +	if (pos >= mLen) +		return -1;		// Doesn't exist + +	const int block_limit(mBlocks.size()); +	for (int i(0); i < block_limit; ++i) +	{ +		if (pos < mBlocks[i]->mLen) +		{ +			*ret_offset = pos; +			return i; +		} +		pos -= mBlocks[i]->mLen; +	} + +	// Shouldn't get here but... +	return -1; +} + + +// ================================== +// BufferArray::Block Definitions +// ================================== + + +BufferArray::Block::Block(size_t len) +	: mLen(len) +{ +	memset(mData, 0, len); +} +			 + +BufferArray::Block::~Block() +{ +	mLen = 0; +} + + +void * BufferArray::Block::operator new(size_t len, size_t addl_len) +{ +	void * mem = new char[len + addl_len + sizeof(void *)]; +	return mem; +} + + +void BufferArray::Block::operator delete(void * mem) +{ +	char * cmem = static_cast<char *>(mem); +	delete [] cmem; +} + + +void BufferArray::Block::operator delete(void * mem, size_t) +{ +	operator delete(mem); +} + + +BufferArray::Block * BufferArray::Block::alloc(size_t len) +{ +	Block * block = new (len) Block(len); +	return block; +} +	 + +}  // end namespace LLCore + + diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h new file mode 100644 index 0000000000..b26ad1b297 --- /dev/null +++ b/indra/llcorehttp/bufferarray.h @@ -0,0 +1,168 @@ +/** + * @file bufferarray.h + * @brief Public-facing declaration for the BufferArray scatter/gather class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_BUFFER_ARRAY_H_ +#define	_LLCORE_BUFFER_ARRAY_H_ + + +#include <cstdlib> +#include <vector> + +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// A very simple scatter/gather type map for bulk data.  The motivation +/// for this class is the writedata callback used by libcurl.  Response +/// bodies are delivered to the caller in a sequence of sequential write +/// operations and this class captures them without having to reallocate +/// and move data. +/// +/// The interface looks a little like a unix file descriptor but only +/// just.  There is a notion of a current position, starting from 0, +/// which is used as the position in the data when performing read and +/// write operations.  The position also moves after various operations: +/// - seek(...) +/// - read(...) +/// - write(...) +/// - append(...) +/// - appendBufferAlloc(...) +/// The object also keeps a total length value which is updated after +/// write and append operations and beyond which the current position +/// cannot be set. +/// +/// Threading:  not thread-safe +/// +/// Allocation:  Refcounted, heap only.  Caller of the constructor +/// is given a single refcount. +/// +class BufferArray : public LLCoreInt::RefCounted +{ +public: +	BufferArray(); +	virtual ~BufferArray(); + +private: +	BufferArray(const BufferArray &);			// Not defined +	void operator=(const BufferArray &);		// Not defined + +public: +	/// Appends the indicated data to the BufferArray +	/// modifying current position and total size.  New +	/// position is one beyond the final byte of the buffer. +	/// +	/// @return			Count of bytes copied to BufferArray +	size_t append(const char * src, size_t len); + +	/// Similar to @see append(), this call guarantees a +	/// contiguous block of memory of requested size placed +	/// at the current end of the BufferArray.  On return, +	/// the data in the memory is considered valid whether +	/// the caller writes to it or not. +	/// +	/// @return			Pointer to contiguous region at end +	///					of BufferArray of 'len' size. +	char * appendBufferAlloc(size_t len); + +	/// Current count of bytes in BufferArray instance. +	size_t size() const +		{ +			return mLen; +		} + +	/// Set the current position for subsequent read and +	/// write operations.  'pos' values before the beginning +	/// or greater than the size of the buffer are coerced +	/// to a value within the buffer. +	/// +	/// @return			Actual current position after seek. +	size_t seek(size_t pos); + +	/// Copies data from the current position in the instance +	/// to the caller's buffer.  Will return a short count of +	/// bytes copied if the 'len' extends beyond the data. +	size_t read(char * dst, size_t len); + +	/// Copies data from the caller's buffer to the instance +	/// at the current position.  May overwrite existing data, +	/// append data when current position is equal to the +	/// size of the instance or do a mix of both. +	size_t write(const char * src, size_t len); +	 +protected: +	int findBlock(size_t pos, size_t * ret_offset); +	 +protected: +	class Block; +	typedef std::vector<Block *> container_t; + +	container_t			mBlocks; +	size_t				mPos; +	size_t				mLen; +};  // end class BufferArray + + +#if 0 + +// Conceptual for now.  Another possibility is going with +// something like Boost::asio's buffers interface.  They're +// trying to achieve the same thing above and below.... + +class BufferStream : public std::streambuf +{ +public: +	BufferStream(BufferArray * buffer); +	virtual ~BufferStream(); + +private: +	BufferStream(const BufferStream &);			// Not defined +	void operator=(const BufferStream &);		// Not defined + +public: +	// Types +	typedef std::streambuf::pos_type pos_type; +	typedef std::streambuf::off_type off_type; + +	virtual int underflow(); + +	virtual int overflow(int c); + +	virtual int sync(); + +	virtual pos_type seekoff(off_type off, std::ios::seekdir way, std::ios::openmode which); + +protected: +	BufferArray *		mBufferArray; +};  // end class BufferStream + +#endif // 0 + +}  // end namespace LLCore + +#endif	// _LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp new file mode 100644 index 0000000000..c37d081150 --- /dev/null +++ b/indra/llcorehttp/httpcommon.cpp @@ -0,0 +1,76 @@ +/** + * @file httpcommon.cpp + * @brief  + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "httpcommon.h" + +#include <curl/curl.h> +#include <string> + + +namespace LLCore +{ + +HttpStatus::type_enum_t EXT_CURL_EASY; +HttpStatus::type_enum_t EXT_CURL_MULTI; +HttpStatus::type_enum_t LLCORE; + +std::string HttpStatus::toString() const +{ +	static const char * llcore_errors[] = +		{ +			"", +			"Services shutting down", +			"Operation canceled", +			"Invalid Content-Range header encountered" +		}; +	static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); +	 +	if (*this) +	{ +		return std::string(""); +	} + +	switch (mType) +	{ +	case EXT_CURL_EASY: +		return std::string(curl_easy_strerror(CURLcode(mStatus))); + +	case EXT_CURL_MULTI: +		return std::string(curl_multi_strerror(CURLMcode(mStatus))); + +	case LLCORE: +		if (mStatus >= 0 && mStatus < llcore_errors_count) +		{ +			return std::string(llcore_errors[mStatus]); +		} +		break; +		 +	} +	return std::string("Unknown error"); +} +		 +} // end namespace LLCore + diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h new file mode 100644 index 0000000000..617286fb38 --- /dev/null +++ b/indra/llcorehttp/httpcommon.h @@ -0,0 +1,204 @@ +/** + * @file httpcommon.h + * @brief Public-facing declarations and definitions of common types + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_COMMON_H_ +#define	_LLCORE_HTTP_COMMON_H_ + +/// @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 +/// 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. +/// +/// Features of the library include: +/// - Single, private working thread where all transport and processing occurs. +/// - Support for multiple consumers running in multiple threads. +/// - Scatter/gather (a.k.a. buffer array) model for bulk data movement. +/// - Reference counting used for many object instance lifetimes. +/// - Minimal data sharing across threads for correctness and low latency. +///  +/// The public interface is declared in a few key header files: +/// - <core-http/bufferarray.h> +/// - <core-http/httpcommon.h> +/// - <core-http/httphandler.h> +/// - <core-http/httpheaders.h> +/// - <core-http/httpoptions.h> +/// - <core-http/httprequest.h> +/// - <core-http/httpresponse.h> +/// +/// The library is still under early 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. +/// +/// Using the library is fairly easy.  Global setup needs a few +/// steps: +/// +/// - libcurl initialization with thread-safely callbacks for c-ares +///   DNS lookups. +/// - HttpRequest::createService() called to instantiate singletons +///   and support objects. +/// +/// An HTTP consumer in an application, and an application may have many +/// consumers, does a few things: +/// +/// - Instantiate and retain an object based on HttpRequest.  This +///   object becomes the portal into runtime services for the consumer. +/// - Derive or mixin the HttpHandler class if you want notification +///   when requests succeed or fail.  This object's onCompleted() +///   method is invoked and an instance can be shared across +///   requests. +/// +/// Issuing a request is straightforward: +/// - Construct a suitable URL. +/// - Configure HTTP options for the request.  (optional) +/// - Build a list of additional headers.  (optional) +/// - Invoke one of the requestXXXX() methods (requestGetByteRange, +///   requestPost, etc.) on the HttpRequest instance supplying the +///   above along with a policy class, a priority and an optional +///   pointer to an HttpHandler instance.  Work is then queued to +///   the worker thread and occurs asynchronously. +/// - Periodically invoke the update() method on the HttpRequest +///   instance which performs completion notification to HttpHandler +///   objects. +/// - Do completion processing in your onCompletion() method. +/// +/// Code fragments: +/// <TBD> +/// + +#include <string> + + +namespace LLCore +{ + + +/// All queued requests are represented by an HttpHandle value. +/// The invalid value is returned when a request failed to queue. +/// The actual status for these failures is then fetched with +/// HttpRequest::getStatus(). +/// +/// The handle is valid only for the life of a request.  On +/// return from any HttpHandler notification, the handle immediately +/// becomes invalid and may be recycled for other queued requests. + +typedef void * HttpHandle; +#define LLCORE_HTTP_HANDLE_INVALID		(NULL) + + +/// Error codes defined by the library itself as distinct from +/// libcurl (or any other transport provider). +enum HttpError +{ +	// Successful value compatible with the libcurl codes. +	HE_SUCCESS = 0, + +	// Service is shutting down and requested operation will +	// not be queued or performed. +	HE_SHUTTING_DOWN = 1, +	 +	// Operation was canceled by request. +	HE_OP_CANCELED = 2, +	 +	// Invalid content range header received. +	HE_INV_CONTENT_RANGE_HDR = 3 +	 +}; // end enum HttpError + + +/// HttpStatus encapsulates errors from libcurl (easy, multi) as well as +/// internal errors.  The encapsulation isn't expected to completely +/// isolate the caller from libcurl but basic operational tests (success +/// or failure) are provided. +struct HttpStatus +{ +	typedef unsigned short type_enum_t; +	 +	HttpStatus() +		: mType(LLCORE), +		  mStatus(HE_SUCCESS) +		{} + +	HttpStatus(type_enum_t type, short status) +		: mType(type), +		  mStatus(status) +		{} +	 +	HttpStatus(const HttpStatus & rhs) +		: mType(rhs.mType), +		  mStatus(rhs.mStatus) +		{} + +	HttpStatus & operator=(const HttpStatus & rhs) +		{ +			// Don't care if lhs & rhs are the same object + +			mType = rhs.mType; +			mStatus = rhs.mStatus; +			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; +	 +	type_enum_t			mType; +	short				mStatus; + +	/// Test for successful status in the code regardless +	/// of error source (internal, libcurl). +	/// +	/// @return			'true' when status is successful. +	/// +	operator bool() const +	{ +		return 0 == mStatus; +	} + +	/// Inverse of previous operator. +	/// +	/// @return			'true' on any error condition +	bool operator !() const +	{ +		return 0 != mStatus; +	} + +	/// Convert status to a string representation.  For +	/// success, returns an empty string.  For failure +	/// statuses, a string as appropriate for the source of +	/// the error code (libcurl easy, libcurl multi, or +	/// LLCore itself). +	std::string toString() const; +	 +}; // end struct HttpStatus + +}  // end namespace LLCore + +#endif	// _LLCORE_HTTP_COMMON_H_ diff --git a/indra/llcorehttp/httphandler.h b/indra/llcorehttp/httphandler.h new file mode 100644 index 0000000000..9171e4e7b9 --- /dev/null +++ b/indra/llcorehttp/httphandler.h @@ -0,0 +1,88 @@ +/** + * @file httphandler.h + * @brief Public-facing declarations for the HttpHandler class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_HANDLER_H_ +#define	_LLCORE_HTTP_HANDLER_H_ + + +#include "httpcommon.h" + + +namespace LLCore +{ + +class HttpResponse; + + +/// HttpHandler defines an interface used by the library to +/// notify library callers of significant events, currently +/// request completion.  Callers must derive or mixin this class +/// then provide an implementation of the @see onCompleted +/// method to receive such notifications.  An instance may +/// be shared by any number of requests and across instances +/// of HttpRequest running in the same thread. +/// +/// Threading:  HttpHandler itself is pure interface and is +/// tread-compatible.  Most derivations, however, will have +/// different constraints. +/// +/// Allocation:  Not refcounted, may be stack allocated though +/// that is rarely a good idea.  Queued requests and replies keep +/// a naked pointer to the handler and this can result in a +/// dangling pointer if lifetimes aren't managed correctly. + +class HttpHandler +{ +public: +	virtual ~HttpHandler() +		{} + +	/// Method invoked during calls to @see update().  Each invocation +	/// represents the completion of some requested operation.  Caller +	/// can identify the request from the handle and interrogate the +	/// response argument for success/failure, data and other information. +	/// +	/// @param	handle			Identifier of the request generating +	///							the notification. +	/// @param	response		Supplies detailed information about +	///							the request including status codes +	///							(both programming and HTTP), HTTP body +	///							data and encodings, headers, etc. +	///							The response object is refcounted and +	///							the called code may retain the object +	///							by invoking @see addRef() on it.  The +	///							library itself drops all references to +	///							to object on return and never touches +	///							it again. +	/// +	virtual void onCompleted(HttpHandle handle, HttpResponse * response) = 0; + +};  // end class HttpHandler + + +}   // end namespace LLCore + +#endif	// _LLCORE_HTTP_HANDLER_H_ diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp new file mode 100644 index 0000000000..2832696271 --- /dev/null +++ b/indra/llcorehttp/httpheaders.cpp @@ -0,0 +1,44 @@ +/** + * @file httpheaders.cpp + * @brief Implementation of the HTTPHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "httpheaders.h" + + +namespace LLCore +{ + + +HttpHeaders::HttpHeaders() +	: RefCounted(true) +{} + + +HttpHeaders::~HttpHeaders() +{} + + +}   // end namespace LLCore + diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h new file mode 100644 index 0000000000..0b6d82561b --- /dev/null +++ b/indra/llcorehttp/httpheaders.h @@ -0,0 +1,86 @@ +/** + * @file httpheaders.h + * @brief Public-facing declarations for the HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_HEADERS_H_ +#define	_LLCORE_HTTP_HEADERS_H_ + + +#include <string> + +#include "_refcounted.h" + + +namespace LLCore +{ + +/// +/// Maintains an ordered list of name/value pairs representing +/// HTTP header lines.  This is used both to provide additional +/// headers when making HTTP requests and in responses when the +/// caller has asked that headers be returned (not the default +/// option). +/// +/// @note +/// This is a minimally-functional placeholder at the moment +/// to fill out the class hierarchy.  The final class will be +/// something else, probably more pair-oriented.  It's also +/// an area where shared values are desirable so refcounting is +/// already specced and a copy-on-write scheme imagined. +/// Expect changes here. +/// +/// Threading:  Not intrinsically thread-safe.  It *is* expected +/// that callers will build these objects and then share them +/// via reference counting with the worker thread.  The implication +/// is that once an HttpHeader instance is handed to a request, +/// the object must be treated as read-only. +/// +/// Allocation:  Refcounted, heap only.  Caller of the +/// constructor is given a refcount. +/// + +class HttpHeaders : public LLCoreInt::RefCounted +{ +public: +	/// @post In addition to the instance, caller has a refcount +	/// to the instance.  A call to @see release() will destroy +	/// the instance. +	HttpHeaders(); +	~HttpHeaders(); + +protected: +	HttpHeaders(const HttpHeaders &);			// Not defined +	void operator=(const HttpHeaders &);		// Not defined + +public: +	typedef std::vector<std::string> container_t; +	container_t			mHeaders; +	 +}; // end class HttpHeaders + +}  // end namespace LLCore + + +#endif	// _LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp new file mode 100644 index 0000000000..15b505f5bf --- /dev/null +++ b/indra/llcorehttp/httpoptions.cpp @@ -0,0 +1,50 @@ +/** + * @file httpoptions.cpp + * @brief Implementation of the HTTPOptions class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "httpoptions.h" + + +namespace LLCore +{ + + +HttpOptions::HttpOptions() +	: RefCounted(true), +	  mWantHeaders(false) +{} + + +HttpOptions::HttpOptions(const HttpOptions & rhs) +	: RefCounted(true), +	  mWantHeaders(rhs.mWantHeaders) +{} + + +HttpOptions::~HttpOptions() +{} + + +}   // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h new file mode 100644 index 0000000000..267a982dd5 --- /dev/null +++ b/indra/llcorehttp/httpoptions.h @@ -0,0 +1,80 @@ +/** + * @file httpoptions.h + * @brief Public-facing declarations for the HTTPOptions class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_OPTIONS_H_ +#define	_LLCORE_HTTP_OPTIONS_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// Really a struct in spirit, it provides options that +/// modify HTTP requests. +/// +/// Sharing instances across requests.  It's intended that +/// these be shared across requests:  caller can create one +/// of these, set it up as needed and then reference it +/// repeatedly in HTTP operations.  But see the Threading +/// note about references. +/// +/// Threading:  While this class does nothing to ensure thread +/// safety, it *is* intended to be shared between the application +/// thread and the worker thread.  This means that once an instance +/// is delivered to the library in request operations, the +/// option data must not be written until all such requests +/// complete and relinquish their references. +/// +/// Allocation:  Refcounted, heap only.  Caller of the constructor +/// is given a refcount. +/// +class HttpOptions : public LLCoreInt::RefCounted +{ +public: +	HttpOptions(); +	HttpOptions(const HttpOptions &); +	virtual ~HttpOptions(); + +protected: +	void operator=(const HttpOptions &);		// Not defined + +public: +	 +protected: +	// *TODO:  add some options +	bool		mWantHeaders; +	 +}; // end class HttpOptions + + +}  // end namespace HttpOptions + +#endif	// _LLCORE_HTTP_OPTIONS_H_ diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp new file mode 100644 index 0000000000..2a87f5231a --- /dev/null +++ b/indra/llcorehttp/httprequest.cpp @@ -0,0 +1,295 @@ +/** + * @file httprequest.cpp + * @brief Implementation of the HTTPRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "httprequest.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httpoperation.h" +#include "_httpoprequest.h" + + +namespace +{ + +bool has_inited(false); + +} + +namespace LLCore +{ + +// ==================================== +// InternalHandler Implementation +// ==================================== + + +class HttpRequest::InternalHandler : public HttpHandler +{ +public: +	InternalHandler(HttpRequest & request) +		: mRequest(request) +		{} + +protected: +	InternalHandler(const InternalHandler &);			// Not defined +	void operator=(const InternalHandler &);			// Not defined + +public: +	void onCompleted(HttpHandle handle, HttpResponse * response) +		{ +			HttpOperation * op(static_cast<HttpOperation *>(handle)); +			HttpHandler * user_handler(op->getUserHandler()); +			if (user_handler) +			{ +				user_handler->onCompleted(handle, response); +			} +		} + +protected: +	HttpRequest &		mRequest; +	 +};  // end class HttpRequest::InternalHandler + + +// ==================================== +// HttpRequest Implementation +// ==================================== + + +unsigned int HttpRequest::sNextPolicyID(1); + + +HttpRequest::HttpRequest() +	: //HttpHandler(), +	  mReplyQueue(NULL), +	  mRequestQueue(NULL), +	  mSelfHandler(NULL) +{ +	mRequestQueue = HttpRequestQueue::instanceOf(); +	mRequestQueue->addRef(); + +	mReplyQueue = new HttpReplyQueue(); + +	mSelfHandler = new InternalHandler(*this); +} + + +HttpRequest::~HttpRequest() +{ +	if (mRequestQueue) +	{ +		mRequestQueue->release(); +		mRequestQueue = NULL; +	} + +	if (mReplyQueue) +	{ +		mReplyQueue->release(); +		mReplyQueue = NULL; +	} + +	delete mSelfHandler; +	mSelfHandler = NULL; +} + + +// ==================================== +// Policy Methods +// ==================================== + + +HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) +{ +	HttpStatus status; + +	return status; +} + + +unsigned int HttpRequest::createPolicyClass() +{ +	unsigned int policy_id = 1; +	 +	return policy_id; +} + + +HttpStatus HttpRequest::setPolicyClassOption(unsigned int policy_id, +											 EClassPolicy opt, +											 long value) +{ +	HttpStatus status; + +	return status; +} + + +// ==================================== +// Request Methods +// ==================================== + + +HttpStatus HttpRequest::getStatus() const +{ +	return mLastReqStatus; +} + + +HttpHandle HttpRequest::requestGetByteRange(unsigned int policy_id, +											float priority, +											const std::string & url, +											size_t offset, +											size_t len, +											HttpOptions * options, +											HttpHeaders * headers, +											HttpHandler * user_handler) +{ +	HttpStatus status; +	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + +	HttpOpRequest * op = new HttpOpRequest(); +	if (! (status = op->setupGetByteRange(policy_id, priority, url, offset, len, options, headers))) +	{ +		op->release(); +		mLastReqStatus = status; +		return handle; +	} +	op->setHandlers(mReplyQueue, mSelfHandler, user_handler); +	mRequestQueue->addOp(op);			// transfers refcount +	 +	mLastReqStatus = status; +	handle = static_cast<HttpHandle>(op); +	 +	return handle; +} + + +HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) +{ +	HttpStatus status; +	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + +	HttpOpNull * op = new HttpOpNull(); +	op->setHandlers(mReplyQueue, mSelfHandler, user_handler); +	mRequestQueue->addOp(op);			// transfer refcount as well + +	mLastReqStatus = status; +	handle = static_cast<HttpHandle>(op); +	 +	return handle; +} + + +HttpStatus HttpRequest::update(long millis) +{ +	HttpStatus status; + +	// *FIXME:  need timer stuff +	// long now(getNow()); +	// long limit(now + millis); +	 +	HttpOperation * op(NULL); +	while ((op = mReplyQueue->fetchOp())) +	{ +		// Process operation +		op->visitNotifier(this); +		 +		// We're done with the operation +		op->release(); +	} +	 +	return status; +} + + + + +// ==================================== +// Request Management Methods +// ==================================== + + +// ==================================== +// Utility Methods +// ==================================== + +HttpStatus HttpRequest::createService() +{ +	HttpStatus status; + +	LLINT_ASSERT(! has_inited); +	HttpRequestQueue::init(); +	HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); +	HttpService::init(rq); +	has_inited = true; +	 +	return status; +} + + +HttpStatus HttpRequest::destroyService() +{ +	HttpStatus status; + +	LLINT_ASSERT(has_inited); +	HttpService::term(); +	HttpRequestQueue::term(); +	has_inited = false; +	 +	return status; +} + + +HttpStatus HttpRequest::startThread() +{ +	HttpStatus status; + +	HttpService::instanceOf()->startThread(); +	 +	return status; +} + + +HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) +{ +	HttpStatus status; +	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + +	HttpOpStop * op = new HttpOpStop(); +	op->setHandlers(mReplyQueue, mSelfHandler, user_handler); +	mRequestQueue->addOp(op);			// transfer refcount as well + +	mLastReqStatus = status; +	handle = static_cast<HttpHandle>(op); + +	return handle; +} + + +}   // end namespace LLCore + diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h new file mode 100644 index 0000000000..4bbd13a13a --- /dev/null +++ b/indra/llcorehttp/httprequest.h @@ -0,0 +1,316 @@ +/** + * @file httprequest.h + * @brief Public-facing declarations for HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_REQUEST_H_ +#define	_LLCORE_HTTP_REQUEST_H_ + + +#include "httpcommon.h" +#include "httphandler.h" + + +namespace LLCore +{ + +class HttpRequestQueue; +class HttpReplyQueue; +class HttpService; +class HttpOptions; +class HttpHeaders; +class HttpOperation; + +/// HttpRequest supplies the entry into the HTTP transport +/// services in the LLCore libraries.  Services provided include: +/// +/// - Some, but not all, global initialization of libcurl. +/// - Starting asynchronous, threaded HTTP requests. +/// - Definition of policy classes affect request handling. +/// - Utilities to control request options and headers +/// +/// Requests +/// +/// The class supports the current HTTP request operations: +/// +/// - requestGetByteRange:  GET with Range header for a single range of bytes +/// +/// Policy Classes +/// +/// <TBD> +/// +/// Usage +/// +/// <TBD> +/// +/// Threading:  An instance may only be used by one application/ +/// consumer thread.  But a thread may have as many instances of +/// this as it likes. +/// +/// Allocation:  Not refcounted, may be stack allocated though that +/// hasn't been tested.  Queued requests can still run and any +/// queued replies will keep refcounts to the reply queue leading +/// to memory leaks. +/// +/// @pre Before using this class (static or instances), some global +/// initialization is required.  See @see httpcommon.h for more information. +/// +/// @nosubgrouping +/// + +class HttpRequest +{ +public: +	HttpRequest(); +	virtual ~HttpRequest(); + +private: +	HttpRequest(const HttpRequest &);			// Disallowed +	void operator=(const HttpRequest &);		// Disallowed		 + +public: +	/// @name PolicyMethods +	/// @{ + +	/// Represents a default, catch-all policy class that guarantees +	/// eventual service for any HTTP request. +	static const int DEFAULT_POLICY_ID = 0; +	 +	enum EGlobalPolicy +	{ +		/// Maximum number of connections the library will use to +		/// perform operations.  This is somewhat soft as the underlying +		/// transport will cache some connections (up to 5). +		GLOBAL_CONNECTION_LIMIT +	}; + +	/// 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. +	HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); + +	/// Create a new policy class into which requests can be made. +	/// +	/// @return			If positive, the policy_id used to reference +	///					the class in other methods.  If -1, an error +	///					occurred and @see getStatus() may provide more +	///					detail on the reason. +	unsigned int createPolicyClass(); + +	enum EClassPolicy +	{ +		/// Limits the number of connections used for the class. +		CLASS_CONNECTION_LIMIT, + +		/// Limits the number of connections used for a single +		/// literal address/port pair within the class. +		PER_HOST_CONNECTION_LIMIT, + +		/// Suitable requests are allowed to pipeline on their +		/// connections when they ask for it. +		ENABLE_PIPELINING +	}; +	 +	/// 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. +	HttpStatus setPolicyClassOption(unsigned int policy_id, +									EClassPolicy opt, +									long value); + +	/// @} + +	/// @name RequestMethods +	/// +	/// @{ + +	/// Some calls expect to succeed as the normal part of operation and so +	/// return a useful value rather than a status.  When they do fail, the +	/// status is saved and can be fetched with this method. +	/// +	/// @return			Status of the failing method invocation.  If the +	///					preceding call succeeded or other HttpStatus +	///					returning calls immediately preceded this method, +	///					the returned value may not be reliable. +	/// +	HttpStatus getStatus() const; + +	/// Queue a full HTTP GET request to be issued with a 'Range' header. +	/// The request is queued and serviced by the working thread and +	/// notification of completion delivered to the optional HttpHandler +	/// argument during @see update() calls. +	/// +	/// With a valid handle returned, it can be used to reference the +	/// request in other requests (like cancellation) and will be an +	/// argument when any HttpHandler object is invoked. +	/// +	/// @param	policy_id		Default or user-defined policy class under +	///							which this request is to be serviced. +	/// @param	priority		Standard priority scheme inherited from +	///							Indra code base. +	/// @param	url +	/// @param	offset +	/// @param	len +	/// @param	options			(optional) +	/// @param	headers			(optional) +	/// @param	handler			(optional) +	/// @return					The handle of the request if successfully +	///							queued or LLCORE_HTTP_HANDLE_INVALID if the +	///							request could not be queued.  In the latter +	///							case, @see getStatus() will return more info. +	/// +	HttpHandle requestGetByteRange(unsigned int policy_id, +								   float priority, +								   const std::string & url, +								   size_t offset, +								   size_t len, +								   HttpOptions * options, +								   HttpHeaders * headers, +								   HttpHandler * handler); + + +	/// Queue a NoOp request. +	/// The request is queued and serviced by the working thread which +	/// immediately processes it and returns the request to the reply +	/// queue. +	/// +	/// @param	handler			(optional) +	/// @return					The handle of the request if successfully +	///							queued or LLCORE_HTTP_HANDLE_INVALID if the +	///							request could not be queued.  In the latter +	///							case, @see getStatus() will return more info. +	/// +	HttpHandle requestNoOp(HttpHandler * handler); + +	/// While all the heavy work is done by the worker thread, notifications +	/// must be performed in the context of the application thread.  These +	/// are done synchronously during calls to this method which gives the +	/// library control so notification can be performed.  Application handlers +	/// are expected to return 'quickly' and do any significant processing +	/// outside of the notification callback to onCompleted(). +	/// +	/// @param	millis			Maximum number of wallclock milliseconds to +	///							spend in the call.  As hinted at above, this +	///							is partly a function of application code so it's +	///							a soft limit.  (And not currently implemented.) +	/// +	/// @return					Standard status code. +	HttpStatus update(long millis); + +	/// @} +	 +	/// @name RequestMgmtMethods +	/// +	/// @{ +	 +	HttpHandle requestCancel(HttpHandle request, HttpHandler *); + +	/// @} + +	/// @name UtilityMethods +	/// +	/// @{ + +	/// Initialization method that needs to be called before queueing any +	/// requests.  Doesn't start the worker thread and may be called befoer +	/// or after policy setup. +	static HttpStatus createService(); + +	/// Mostly clean shutdown of services prior to exit.  Caller is expected +	/// to have stopped a running worker thread before calling this. +	static HttpStatus destroyService(); + +	/// Called once after @see createService() to start the worker thread. +	/// Stopping the thread is achieved by requesting it via @see requestStopThread(). +	/// May be called before or after requests are issued. +	static HttpStatus startThread(); + +	/// Queues a request to the worker thread to have it stop processing +	/// and exit (without exiting the program).  When the operation is +	/// picked up by the worker thread, it immediately processes it and +	/// begins detaching from refcounted resources like request and +	/// reply queues and then returns to the host OS.  It *does* queue a +	/// reply to give the calling application thread a notification that +	/// the operation has been performed. +	/// +	/// @param	handler			(optional) +	/// @return					The handle of the request if successfully +	///							queued or LLCORE_HTTP_HANDLE_INVALID if the +	///							request could not be queued.  In the latter +	///							case, @see getStatus() will return more info. +	///							As the request cannot be cancelled, the handle +	///							is generally not useful. +	/// +	HttpHandle requestStopThread(HttpHandler * handler); + +	/// @} + +protected: +	void generateNotification(HttpOperation * op); + +	class InternalHandler; +	friend class InternalHandler; + +private: +	/// @name InstanceData +	/// +	/// @{ +	HttpStatus			mLastReqStatus; +	HttpReplyQueue *	mReplyQueue; +	HttpRequestQueue *	mRequestQueue; +	InternalHandler *	mSelfHandler; +	 +	/// @} +	 +	// ==================================== +	/// @name GlobalState +	/// +	/// @{ +	/// +	/// Must be established before any threading is allowed to +	/// start. +	/// +	static unsigned int		sNextPolicyID; +	 +	/// @} +	// End Global State +	// ==================================== +	 +};  // end class HttpRequest + + +}  // end namespace LLCore + + + +#endif	// _LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp new file mode 100644 index 0000000000..9ac8276f05 --- /dev/null +++ b/indra/llcorehttp/httpresponse.cpp @@ -0,0 +1,89 @@ +/** + * @file httpresponse.cpp + * @brief  + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "httpresponse.h" +#include "bufferarray.h" +#include "httpheaders.h" + + +namespace LLCore +{ + + +HttpResponse::HttpResponse() +	: LLCoreInt::RefCounted(true), +	  mReplyStatus(0U), +	  mBufferArray(NULL), +	  mHeaders(NULL) +{} + + +HttpResponse::~HttpResponse() +{ +	setBody(NULL); +	setHeaders(NULL); +} + + +void HttpResponse::setBody(BufferArray * ba) +{ +	if (mBufferArray == ba) +		return; +	 +	if (mBufferArray) +	{ +		mBufferArray->release(); +	} + +	if (ba) +	{ +		ba->addRef(); +	} +	 +	mBufferArray = ba; +} + + +void HttpResponse::setHeaders(HttpHeaders * headers) +{ +	if (mHeaders == headers) +		return; +	 +	if (mHeaders) +	{ +		mHeaders->release(); +	} + +	if (headers) +	{ +		headers->addRef(); +	} +	 +	mHeaders = headers; +} + + +}   // end namespace LLCore diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h new file mode 100644 index 0000000000..a25e22aef6 --- /dev/null +++ b/indra/llcorehttp/httpresponse.h @@ -0,0 +1,137 @@ +/** + * @file httpresponse.h + * @brief Public-facing declarations for the HttpResponse class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef	_LLCORE_HTTP_RESPONSE_H_ +#define	_LLCORE_HTTP_RESPONSE_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + +class BufferArray; +class HttpHeaders; + +/// HttpResponse is instantiated by the library and handed to +/// the caller during callbacks to the handler.  It supplies +/// all the status, header and HTTP body data the caller is +/// interested in.  Methods provide simple getters to return +/// 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. +/// +/// Threading:  Not intrinsically thread-safe. +/// +/// Allocation:  Refcounted, heap only.  Caller of the constructor +/// is given a refcount. +/// +class HttpResponse : public LLCoreInt::RefCounted +{ +public: +	HttpResponse(); +	virtual ~HttpResponse(); + +protected: +	HttpResponse(const HttpResponse &);					// Not defined +	void operator=(const HttpResponse &);				// Not defined +	 +public: +	/// Returns the final status of the requested operation. +	/// +	// *FIXME:  Haven't incorporated HTTP status into this yet. +	// Will do soon. +	HttpStatus getStatus() const +		{ +			return mStatus; +		} + +	void setStatus(const HttpStatus & status) +		{ +			mStatus = status; +		} + +	/// Fetch the HTTP reply status.  This is only guaranteed to be +	/// valid if the HttpStatus tests successful and was the result +	/// of a completed HTTP request. +	unsigned int getReplyStatus() const +		{ +			return mReplyStatus; +		} + +	void setReplyStatus(unsigned int status) +		{ +			mReplyStatus = status; +		} +			 +	/// Simple getter for the response body returned as a scatter/gather +	/// buffer.  If the operation doesn't produce data (such as the Null +	/// or StopThread operations), this may be NULL. +	/// +	/// Caller can hold onto the response by incrementing the reference +	/// count of the returned object. +	BufferArray * getBody() const +		{ +			return mBufferArray; +		} + +	/// Set the response data in the instance.  Will drop the reference +	/// count to any existing data and increment the count of that passed +	/// in.  It is legal to set the data to NULL. +	void setBody(BufferArray * ba); +	 +	/// And a getter for the headers.  And as with @see getResponse(), +	/// if headers aren't available because the operation doesn't produce +	/// any or delivery of headers wasn't requested in the options, this +	/// will be NULL. +	/// +	/// Caller can hold onto the headers by incrementing the reference +	/// count of the returned object. +	HttpHeaders * getHeaders() const +		{ +			return mHeaders; +		} + +	/// Behaves like @see setResponse() but for header data. +	void setHeaders(HttpHeaders * headers); +			 +protected: +	// Response data here +	HttpStatus			mStatus; +	unsigned int		mReplyStatus; +	BufferArray *		mBufferArray; +	HttpHeaders *		mHeaders; +}; + + +}   // end namespace LLCore + +#endif	// _LLCORE_HTTP_RESPONSE_H_ diff --git a/indra/llcorehttp/tests/all_test.cpp b/indra/llcorehttp/tests/all_test.cpp new file mode 100644 index 0000000000..636f6f8c05 --- /dev/null +++ b/indra/llcorehttp/tests/all_test.cpp @@ -0,0 +1,64 @@ +/**  + * @file test_all + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + + +#include <iostream> + +#include <tut/tut.hpp> +#include <tut/tut_reporter.hpp> + +#include <curl/curl.h> + +// Pull in each of the test sets +#include "test_httpstatus.hpp" +#include "test_refcounted.hpp" +#include "test_httpoperation.hpp" +#include "test_httprequest.hpp" +#include "test_httpheaders.hpp" +#include "test_bufferarray.hpp" +#include "test_httprequestqueue.hpp" + + +namespace tut +{ +	test_runner_singleton runner; +} + +int main() +{ +	curl_global_init(CURL_GLOBAL_ALL); + +	// *FIXME:  Need threaded/SSL curl setup here. +	 +	tut::reporter reporter; + +	tut::runner.get().set_callback(&reporter); +	tut::runner.get().run_tests(); +	return !reporter.all_ok(); + +	curl_global_cleanup(); +} + diff --git a/indra/llcorehttp/tests/test_allocator.cpp b/indra/llcorehttp/tests/test_allocator.cpp new file mode 100644 index 0000000000..926de0a208 --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.cpp @@ -0,0 +1,178 @@ +/**  + * @file test_allocator.cpp + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "test_allocator.h" + +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 +#include <libkern/OSAtomic.h> +#elif defined(_MSC_VER) +#include <Windows.h> +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 +// atomic extensions are built into GCC on posix platforms +#endif + +#include <cassert> +#include <cstdlib> +#include <cstring> +#include <vector> +#include <iostream> +#include <new> + +#include <boost/thread.hpp> + + +#if	defined(WIN32) +#define	THROW_BAD_ALLOC()	_THROW1(std::bad_alloc) +#define	THROW_NOTHING()		_THROW0() +#else +#define	THROW_BAD_ALLOC()	throw(std::bad_alloc) +#define	THROW_NOTHING()		throw() +#endif + + +struct BlockHeader +{ +	struct Block * next; +	std::size_t size; +	bool in_use; +}; + +struct Block +{ +	BlockHeader hdr; +	unsigned char data[1]; +}; + +#define TRACE_MSG(val) std::cout << __FUNCTION__ << "(" << val << ") [" << __FILE__ << ":" << __LINE__ << "]" << std::endl; + +static unsigned char MemBuf[ 4096 * 1024 ]; +Block * pNext = static_cast<Block *>(static_cast<void *>(MemBuf)); +volatile std::size_t MemTotal = 0; + +// cross-platform compare and swap operation +static bool CAS(void * volatile * ptr, void * expected, void * new_value) +{ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 +	return OSAtomicCompareAndSwapPtr( expected, new_value, ptr ); +#elif defined(_MSC_VER) +	return expected == InterlockedCompareExchangePointer( ptr, new_value, expected ); +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 +	return __sync_bool_compare_and_swap( ptr, expected, new_value ); +#endif +} + +static void * GetMem(std::size_t size) +{ +	// TRACE_MSG(size); +	volatile Block * pBlock = NULL; +	volatile Block * pNewNext = NULL; + +	// do a lock-free update of the global next pointer +	do +	{ +		pBlock = pNext; +		pNewNext = (volatile Block *)(pBlock->data + size); + +	} while(! CAS((void * volatile *) &pNext, (void *) pBlock, (void *) pNewNext)); + +	// if we get here, we safely carved out a block of memory in the +	// memory pool... + +	// initialize our block +	pBlock->hdr.next = (Block *)(pBlock->data + size); +	pBlock->hdr.size = size; +	pBlock->hdr.in_use = true; +	memset((void *) pBlock->data, 0, pBlock->hdr.size); + +	// do a lock-free update of the global memory total +	volatile size_t total = 0; +	volatile size_t new_total = 0; +	do +	{ +		total = MemTotal; +		new_total = total + size; + +	} while (! CAS((void * volatile *) &MemTotal, (void *) total, (void *) new_total)); + +	return (void *) pBlock->data; +} + + +static void FreeMem(void * p) +{ +	// get the pointer to the block record +	Block * pBlock = (Block *)((unsigned char *) p - sizeof(BlockHeader)); + +	// TRACE_MSG(pBlock->hdr.size); +	bool * cur_in_use = &(pBlock->hdr.in_use); +	volatile bool in_use = false; +	bool new_in_use = false; +	do +	{ +		in_use = pBlock->hdr.in_use; +	} while (! CAS((void * volatile *) cur_in_use, (void *) in_use, (void *) new_in_use)); + +	// do a lock-free update of the global memory total +	volatile size_t total = 0; +	volatile size_t new_total = 0; +	do +	{ +		total = MemTotal; +		new_total = total - pBlock->hdr.size; +	} while (! CAS((void * volatile *)&MemTotal, (void *) total, (void *) new_total)); +} + + +std::size_t GetMemTotal() +{ +	return MemTotal; +} + + +void * operator new(std::size_t size) THROW_BAD_ALLOC() +{ +	return GetMem( size ); +} + + +void * operator new[](std::size_t size) THROW_BAD_ALLOC() +{ +	return GetMem( size ); +} + + +void operator delete(void * p) THROW_NOTHING() +{ +	FreeMem( p ); +} + + +void operator delete[](void * p) THROW_NOTHING() +{ +	FreeMem( p ); +} + + diff --git a/indra/llcorehttp/tests/test_allocator.h b/indra/llcorehttp/tests/test_allocator.h new file mode 100644 index 0000000000..3572bbc5c5 --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.h @@ -0,0 +1,47 @@ +/**  + * @file test_allocator.h + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef TEST_ALLOCATOR_H +#define TEST_ALLOCATOR_H + +#include <cstdlib> +#include <new> + +size_t GetMemTotal(); +#if	defined(WIN32) +void * operator new(std::size_t size) _THROW1(std::bad_alloc); +void * operator new[](std::size_t size) _THROW1(std::bad_alloc); +void operator delete(void * p) _THROW0(); +void operator delete[](void * p) _THROW0(); +#else +void * operator new(std::size_t size) throw (std::bad_alloc); +void * operator new[](std::size_t size) throw (std::bad_alloc); +void operator delete(void * p) throw (); +void operator delete[](void * p) throw (); +#endif + +#endif // TEST_ALLOCATOR_H + diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp new file mode 100644 index 0000000000..4f5d0284a1 --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -0,0 +1,456 @@ +/**  + * @file test_bufferarray.hpp + * @brief unit tests for the LLCore::BufferArray class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_BUFFER_ARRAY_H_ +#define TEST_LLCORE_BUFFER_ARRAY_H_ + +#include <core-http/bufferarray.h> + +#include <iostream> + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct BufferArrayTestData +{ +	// the test objects inherit from this so the member functions and variables +	// can be referenced directly inside of the test functions. +	size_t mMemTotal; +}; + +typedef test_group<BufferArrayTestData> BufferArrayTestGroupType; +typedef BufferArrayTestGroupType::object BufferArrayTestObjectType; +BufferArrayTestGroupType BufferArrayTestGroup("BufferArray Tests"); + +template <> template <> +void BufferArrayTestObjectType::test<1>() +{ +	set_test_name("BufferArray construction"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); +	ensure("One ref on construction of BufferArray", ba->getRefCount() == 1); +	ensure("Memory being used", mMemTotal < GetMemTotal()); +	ensure("Nothing in BA", 0 == ba->size()); + +	// Try to read +	char buffer[20]; +	size_t read_len(ba->read(buffer, sizeof(buffer))); +	ensure("Read returns empty", 0 == read_len); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<2>() +{ +	set_test_name("BufferArray single write"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; + 	char buffer[256]; +	 +	size_t len = ba->write(str1, strlen(str1)); +	ensure("Wrote length correct", strlen(str1) == len); +	ensure("Recorded size correct", strlen(str1) == ba->size()); + +	// read some data back +	len = ba->seek(2); +	ensure("Seek worked", 2 == len); + +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(buffer, 2); +	ensure("Read length correct", 2 == len); +	ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); +	ensure("Read didn't overwrite", 'X' == buffer[2]); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferArrayTestObjectType::test<3>() +{ +	set_test_name("BufferArray multiple writes"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); + 	char buffer[256]; +	 +	size_t len = ba->write(str1, str1_len); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", str1_len == ba->size()); + +	// again... +	len = ba->write(str1, strlen(str1)); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", (2 * str1_len) == ba->size()); + +	// read some data back +	len = ba->seek(8); +	ensure("Seek worked", 8 == len); + +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(buffer, 4); +	ensure("Read length correct", 4 == len); +	ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); +	ensure("Read content correct", 'a' == buffer[2] && 'b' == buffer[3]); +	ensure("Read didn't overwrite", 'X' == buffer[4]); + +	// Read whole thing +	len = ba->seek(0); +	ensure("Seek worked (2)", 0 == len); + +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(buffer, sizeof(buffer)); +	ensure("Read length correct", (2 * str1_len) == len); +	ensure("Read content correct (3)", 0 == strncmp(buffer, str1, str1_len)); +	ensure("Read content correct (4)", 0 == strncmp(&buffer[str1_len], str1, str1_len)); +	ensure("Read didn't overwrite (5)", 'X' == buffer[2 * str1_len]); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<4>() +{ +	set_test_name("BufferArray overwriting"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); +	char str2[] = "ABCDEFGHIJ"; +	size_t str2_len(strlen(str2)); + 	char buffer[256]; +	 +	size_t len = ba->write(str1, str1_len); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", str1_len == ba->size()); + +	// again... +	len = ba->write(str1, strlen(str1)); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", (2 * str1_len) == ba->size()); + +	// reposition and overwrite +	len = ba->seek(8); +	ensure("Seek worked", 8 == len); +	len = ba->write(str2, 4); +	ensure("Overwrite length correct", 4 == len); + +	// Leave position and read verifying content +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(buffer, 4); +	ensure("Read length correct", 4 == len); +	ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); +	ensure("Read content correct.2", 'e' == buffer[2] && 'f' == buffer[3]); +	ensure("Read didn't overwrite", 'X' == buffer[4]); + +	// reposition and check +	len = ba->seek(6); +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(buffer, 8); +	ensure("Read length correct.2", 8 == len); +	ensure("Read content correct.3", 'g' == buffer[0] && 'h' == buffer[1]); +	ensure("Read content correct.4", 'A' == buffer[2] && 'B' == buffer[3]); +	ensure("Read content correct.5", 'C' == buffer[4] && 'D' == buffer[5]); +	ensure("Read content correct.6", 'c' == buffer[6] && 'd' == buffer[7]); +	ensure("Read didn't overwrite.7", 'X' == buffer[8]); + +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<5>() +{ +	set_test_name("BufferArray multiple writes - sequential reads"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); + 	char buffer[256]; +	 +	size_t len = ba->write(str1, str1_len); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", str1_len == ba->size()); + +	// again... +	len = ba->write(str1, strlen(str1)); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", (2 * str1_len) == ba->size()); + +	// read some data back +	len = ba->seek(8); +	ensure("Seek worked", 8 == len); + +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(buffer, 4); +	ensure("Read length correct", 4 == len); +	ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); +	ensure("Read content correct.2", 'a' == buffer[2] && 'b' == buffer[3]); +	ensure("Read didn't overwrite", 'X' == buffer[4]); + +	// Read some more without repositioning +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(buffer, sizeof(buffer)); +	ensure("Read length correct", (str1_len - 2) == len); +	ensure("Read content correct.3", 0 == strncmp(buffer, str1+2, str1_len-2)); +	ensure("Read didn't overwrite.2", 'X' == buffer[str1_len-1]); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<6>() +{ +	set_test_name("BufferArray overwrite spanning blocks and appending"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); +	char str2[] = "ABCDEFGHIJKLMNOPQRST"; +	size_t str2_len(strlen(str2)); + 	char buffer[256]; +	 +	size_t len = ba->write(str1, str1_len); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", str1_len == ba->size()); + +	// again... +	len = ba->write(str1, strlen(str1)); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", (2 * str1_len) == ba->size()); + +	// reposition and overwrite +	len = ba->seek(8); +	ensure("Seek worked", 8 == len); +	len = ba->write(str2, str2_len); +	ensure("Overwrite length correct", str2_len == len); + +	// Leave position and read verifying content +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(buffer, 0); +	ensure("Read length correct", 0 == len); +	ensure("Read didn't overwrite", 'X' == buffer[0]); + +	// reposition and check +	len = ba->seek(0); +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(buffer, sizeof(buffer)); +	ensure("Read length correct.2", (str1_len + str2_len - 2) == len); +	ensure("Read content correct", 0 == strncmp(buffer, str1, str1_len-2)); +	ensure("Read content correct.2", 0 == strncmp(buffer+str1_len-2, str2, str2_len)); +	ensure("Read didn't overwrite.2", 'X' == buffer[str1_len + str2_len - 2]); + +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<7>() +{ +	set_test_name("BufferArray overwrite spanning blocks and sequential writes"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); +	char str2[] = "ABCDEFGHIJKLMNOPQRST"; +	size_t str2_len(strlen(str2)); + 	char buffer[256]; + +	// 2x str1 +	size_t len = ba->write(str1, str1_len); +	len = ba->write(str1, strlen(str1)); + +	// reposition and overwrite +	len = ba->seek(6); +	len = ba->write(str2, 2); +	ensure("Overwrite length correct", 2 == len); + +	len = ba->write(str2, 2); +	ensure("Overwrite length correct.2", 2 == len); + +	len = ba->write(str2, 2); +	ensure("Overwrite length correct.3", 2 == len); + +	// append some data +	len = ba->append(str2, str2_len); +	ensure("Append length correct", str2_len == len); + +	// append some more +	char * out_buf(ba->appendBufferAlloc(str1_len)); +	memcpy(out_buf, str1, str1_len); + +	// And some final writes +	len = ba->write(str2, 2); +	ensure("Write length correct.2", 2 == len); +	 +	// Check contents +	memset(buffer, 'X', sizeof(buffer)); +	ba->seek(0); +	len = ba->read(buffer, sizeof(buffer)); +	ensure("Final buffer length correct", (3 * str1_len + str2_len + 2) == len); +	ensure("Read content correct", 0 == strncmp(buffer, str1, 6)); +	ensure("Read content correct.2", 0 == strncmp(buffer + 6, str2, 2)); +	ensure("Read content correct.3", 0 == strncmp(buffer + 8, str2, 2)); +	ensure("Read content correct.4", 0 == strncmp(buffer + 10, str2, 2)); +	ensure("Read content correct.5", 0 == strncmp(buffer + str1_len + 2, str1 + 2, str1_len - 2)); +	ensure("Read content correct.6", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); +	ensure("Read content correct.7", 0 == strncmp(buffer + str1_len + str1_len + str2_len, str1, str1_len)); +	ensure("Read content correct.8", 0 == strncmp(buffer + str1_len + str1_len + str2_len + str1_len, str2, 2)); +	ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len + str1_len + 2]); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<8>() +{ +	set_test_name("BufferArray zero-length appendBufferAlloc"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); +	char str2[] = "ABCDEFGHIJKLMNOPQRST"; +	size_t str2_len(strlen(str2)); + 	char buffer[256]; + +	// 2x str1 +	size_t len = ba->write(str1, str1_len); +	len = ba->write(str1, strlen(str1)); + +	// zero-length allocate (we allow this with a valid pointer returned) +	char * out_buf(ba->appendBufferAlloc(0)); +	ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf); + +	// Do it again +	char * out_buf2(ba->appendBufferAlloc(0)); +	ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2); +	ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); + +	// And some final writes +	len = ba->write(str2, str2_len); +	 +	// Check contents +	memset(buffer, 'X', sizeof(buffer)); +	ba->seek(0); +	len = ba->read(buffer, sizeof(buffer)); +	ensure("Final buffer length correct", (2 * str1_len + str2_len) == len); +	ensure("Read content correct.1", 0 == strncmp(buffer, str1, str1_len)); +	ensure("Read content correct.2", 0 == strncmp(buffer + str1_len, str1, str1_len)); +	ensure("Read content correct.3", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); +	ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len]); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure("All memory released", mMemTotal == GetMemTotal()); +} + +}  // end namespace tut + + +#endif  // TEST_LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp new file mode 100644 index 0000000000..d22b516691 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -0,0 +1,108 @@ +/**  + * @file test_httpheaders.hpp + * @brief unit tests for the LLCore::HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_HEADERS_H_ +#define TEST_LLCORE_HTTP_HEADERS_H_ + +#include <core-http/httpheaders.h> + +#include <iostream> + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpHeadersTestData +{ +	// the test objects inherit from this so the member functions and variables +	// can be referenced directly inside of the test functions. +	size_t mMemTotal; +}; + +typedef test_group<HttpHeadersTestData> HttpHeadersTestGroupType; +typedef HttpHeadersTestGroupType::object HttpHeadersTestObjectType; +HttpHeadersTestGroupType HttpHeadersTestGroup("HttpHeaders Tests"); + +template <> template <> +void HttpHeadersTestObjectType::test<1>() +{ +	set_test_name("HttpHeaders construction"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpHeaders * headers = new HttpHeaders(); +	ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1); +	ensure("Memory being used", mMemTotal < GetMemTotal()); +	ensure("Nothing in headers", 0 == headers->mHeaders.size()); + +	// release the implicit reference, causing the object to be released +	headers->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpHeadersTestObjectType::test<2>() +{ +	set_test_name("HttpHeaders construction"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpHeaders * headers = new HttpHeaders(); +	 +	{ +		// Append a few strings +		std::string str1("Pragma:"); +		headers->mHeaders.push_back(str1); +		std::string str2("Accept: application/json"); +		headers->mHeaders.push_back(str2); +	 +		ensure("Headers retained", 2 == headers->mHeaders.size()); +		ensure("First is first", headers->mHeaders[0] == str1); +		ensure("Second is second", headers->mHeaders[1] == str2); +	} +	 +	// release the implicit reference, causing the object to be released +	headers->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +}  // end namespace tut + + +#endif  // TEST_LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp new file mode 100644 index 0000000000..c9feaddb2a --- /dev/null +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -0,0 +1,128 @@ +/**  + * @file test_httpoperation.hpp + * @brief unit tests for the LLCore::HttpOperation-derived classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_OPERATION_H_ +#define TEST_LLCORE_HTTP_OPERATION_H_ + +#include "_httpoperation.h" +#include <core-http/httphandler.h> + +#include <iostream> + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + +namespace +{ + +class TestHandler : public LLCore::HttpHandler +{ +public: +	virtual void onCompleted(HttpHandle, HttpResponse *) +		{ +			std::cout << "TestHandler::onCompleted() invoked" << std::endl; +		} +	 +}; + + +}  // end namespace anonymous + + +namespace tut +{ +	struct HttpOperationTestData +	{ +		// the test objects inherit from this so the member functions and variables +		// can be referenced directly inside of the test functions. +		size_t mMemTotal; +	}; + +	typedef test_group<HttpOperationTestData> HttpOperationTestGroupType; +	typedef HttpOperationTestGroupType::object HttpOperationTestObjectType; +	HttpOperationTestGroupType HttpOperationTestGroup("HttpOperation Tests"); + +	template <> template <> +	void HttpOperationTestObjectType::test<1>() +	{ +		set_test_name("HttpOpNull construction"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		// create a new ref counted object with an implicit reference +		HttpOpNull * op = new HttpOpNull(); +		ensure(op->getRefCount() == 1); +		ensure(mMemTotal < GetMemTotal()); +		 +		// release the implicit reference, causing the object to be released +		op->release(); + +		// make sure we didn't leak any memory +		ensure(mMemTotal == GetMemTotal()); +	} + +	template <> template <> +	void HttpOperationTestObjectType::test<2>() +	{ +		set_test_name("HttpOpNull construction with handlers"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		// Get some handlers +		TestHandler * h1 = new TestHandler(); +		TestHandler * h2 = new TestHandler(); +		 +		// create a new ref counted object with an implicit reference +		HttpOpNull * op = new HttpOpNull(); + +		// Add the handlers +		op->setHandlers(NULL, h1, h2); + +		// Check ref count +		ensure(op->getRefCount() == 1); + +		// release the reference, releasing the operation but +		// not the handlers. +		op->release(); +		op = NULL; +		ensure(mMemTotal != GetMemTotal()); +		 +		// release the handlers +		delete h1; +		h1 = NULL; +		delete h2; +		h2 = NULL; + +		ensure(mMemTotal == GetMemTotal()); +	} + +} + +#endif  // TEST_LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp new file mode 100644 index 0000000000..df5640859f --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -0,0 +1,437 @@ +/**  + * @file test_httprequest.hpp + * @brief unit tests for the LLCore::HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_REQUEST_H_ +#define TEST_LLCORE_HTTP_REQUEST_H_ + +#include <core-http/httprequest.h> +#include <core-http/httphandler.h> +#include <core-http/httpresponse.h> +#include <core-http/_httpservice.h> +#include <core-http/_httprequestqueue.h> + +#include <curl/curl.h> + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec); + +#endif + +} + +namespace tut +{ + +struct HttpRequestTestData +{ +	// the test objects inherit from this so the member functions and variables +	// can be referenced directly inside of the test functions. +	size_t			mMemTotal; +	int				mHandlerCalls; +	HttpStatus		mStatus; +}; + +class TestHandler2 : public LLCore::HttpHandler +{ +public: +	TestHandler2(HttpRequestTestData * state, +				 const std::string & name) +		: mState(state), +		  mName(name), +		  mExpectHandle(LLCORE_HTTP_HANDLE_INVALID) +		{} +	 +	virtual void onCompleted(HttpHandle handle, HttpResponse * response) +		{ +			if (LLCORE_HTTP_HANDLE_INVALID != mExpectHandle) +			{ +				ensure("Expected handle received in handler", mExpectHandle == handle); +			} +			ensure("Handler got a response", NULL != response); +			if (response && mState) +			{ +				const HttpStatus actual_status(response->getStatus()); +				 +				ensure("Expected HttpStatus received in response", actual_status == mState->mStatus); +			} +			if (mState) +			{ +				mState->mHandlerCalls++; +			} +			// std::cout << "TestHandler2::onCompleted() invoked" << std::endl; +		} + +	HttpRequestTestData * mState; +	std::string mName; +	HttpHandle mExpectHandle; +}; + +typedef test_group<HttpRequestTestData> HttpRequestTestGroupType; +typedef HttpRequestTestGroupType::object HttpRequestTestObjectType; +HttpRequestTestGroupType HttpRequestTestGroup("HttpRequest Tests"); + +template <> template <> +void HttpRequestTestObjectType::test<1>() +{ +	set_test_name("HttpRequest construction"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// Get singletons created +	HttpRequest::createService(); +		 +	// create a new ref counted object with an implicit reference +	HttpRequest * req = new HttpRequest(); +	ensure(mMemTotal < GetMemTotal()); +		 +	// release the request object +	delete req; +	req = NULL; + +	HttpRequest::destroyService(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestTestObjectType::test<2>() +{ +	set_test_name("HttpRequest and Null Op queued"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// Get singletons created +	HttpRequest::createService(); +		 +	// create a new ref counted object with an implicit reference +	HttpRequest * req = new HttpRequest(); +	ensure(mMemTotal < GetMemTotal()); + +	// Issue a NoOp +	HttpHandle handle = req->requestNoOp(NULL); +	ensure(handle != LLCORE_HTTP_HANDLE_INVALID); +		 +	// release the request object +	delete req; +	req = NULL; + +	// We're still holding onto the operation which is +	// sitting, unserviced, on the request queue so... +	ensure(mMemTotal < GetMemTotal()); + +	// Request queue should have two references:  global singleton & service object +	ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); + +	// Okay, tear it down +	HttpRequest::destroyService(); +	// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +	ensure(mMemTotal == GetMemTotal()); +} + + +template <> template <> +void HttpRequestTestObjectType::test<3>() +{ +	set_test_name("HttpRequest NoOp + Stop execution"); + +	// 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; +		 +	// 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 +	HttpRequest * req = new HttpRequest(); +	ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +	// Issue a NoOp +	HttpHandle handle = req->requestNoOp(&handler); +	ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + +	// Run the notification pump. +	int count(0); +	int limit(20); +	while (count++ < limit && mHandlerCalls < 1) +	{ +		req->update(1000); +		usleep(100000); +	} +	ensure("Request executed in reasonable time", count < limit); +	ensure("One handler invocation for request", mHandlerCalls == 1); + +	// Okay, request a shutdown of the servicing thread +	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 < 2) +	{ +		req->update(1000); +		usleep(100000); +	} +	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; +	while (count++ < limit && ! HttpService::isStopped()) +	{ +		usleep(100000); +	} +	ensure("Thread actually stopped running", HttpService::isStopped()); +	 +	// release the request object +	delete req; +	req = NULL; + +	// Shut down service +	HttpRequest::destroyService(); +	 +	// We have left over state so can't really say something +	// definitive about memory usage at the end of this. +	ensure("Two handler calls on the way out", 2 == mHandlerCalls); +	// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +	ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestTestObjectType::test<4>() +{ +	set_test_name("2 HttpRequest instances, one thread"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	TestHandler2 handler1(this, "handler1"); +	TestHandler2 handler2(this, "handler2"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; +		 +	// 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 +	HttpRequest * req1 = new HttpRequest(); +	HttpRequest * req2 = new HttpRequest(); +	ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +	// Issue some NoOps +	HttpHandle handle = req1->requestNoOp(&handler1); +	ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); +	handler1.mExpectHandle = handle; + +	handle = req2->requestNoOp(&handler2); +	ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); +	handler2.mExpectHandle = handle; + +	// Run the notification pump. +	int count(0); +	int limit(20); +	while (count++ < limit && mHandlerCalls < 2) +	{ +		req1->update(1000); +		req2->update(1000); +		usleep(100000); +	} +	ensure("Request executed in reasonable time", count < limit); +	ensure("One handler invocation for request", mHandlerCalls == 2); + +	// Okay, request a shutdown of the servicing thread +	handle = req2->requestStopThread(&handler2); +	ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	handler2.mExpectHandle = handle; +	 +	// Run the notification pump again +	count = 0; +	limit = 100; +	while (count++ < limit && mHandlerCalls < 3) +	{ +		req1->update(1000); +		req2->update(1000); +		usleep(100000); +	} +	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; +	while (count++ < limit && ! HttpService::isStopped()) +	{ +		usleep(100000); +	} +	ensure("Thread actually stopped running", HttpService::isStopped()); +	 +	// release the request object +	delete req1; +	req1 = NULL; +	delete req2; +	req2 = NULL; + +	// Shut down service +	HttpRequest::destroyService(); +	 +	// We have left over state so can't really say something +	// definitive about memory usage at the end of this. +	ensure("Two handler calls on the way out", 3 == mHandlerCalls); +	// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +	ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestTestObjectType::test<5>() +{ +	set_test_name("HttpRequest GET + Stop execution"); + +	// 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; + +	// 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 +	HttpRequest * req = new HttpRequest(); +	ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +	// Issue a GET that can't connect +	mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); +	HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +												 0.0f, +												 "http://localhost:2/nothing/here", +												 0, +												 0, +												 NULL, +												 NULL, +												 &handler); +	ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +	// Run the notification pump. +	int count(0); +	int limit(20); +	while (count++ < limit && mHandlerCalls < 1) +	{ +		req->update(1000); +		usleep(100000); +	} +	ensure("Request executed in reasonable time", count < limit); +	ensure("One handler invocation for request", mHandlerCalls == 1); + +	// Okay, request a shutdown of the servicing thread +	mStatus = HttpStatus(); +	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 < 2) +	{ +		req->update(1000); +		usleep(100000); +	} +	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; +	while (count++ < limit && ! HttpService::isStopped()) +	{ +		usleep(100000); +	} +	ensure("Thread actually stopped running", HttpService::isStopped()); +	 +	// release the request object +	delete req; +	req = NULL; + +	// Shut down service +	HttpRequest::destroyService(); +	 +	// We have left over state so can't really say something +	// definitive about memory usage at the end of this. +	ensure("Two handler calls on the way out", 2 == mHandlerCalls); +	// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +	ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +} + +}  // end namespace tut + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec) +{ +	Sleep((DWORD) (usec / 1000UL)); +} + +#endif + +} + +#endif  // TEST_LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/tests/test_httprequestqueue.hpp b/indra/llcorehttp/tests/test_httprequestqueue.hpp new file mode 100644 index 0000000000..1de2d8f9ab --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp @@ -0,0 +1,186 @@ +/**  + * @file test_httprequestqueue.hpp + * @brief unit tests for the LLCore::HttpRequestQueue class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_REQUESTQUEUE_H_ +#define TEST_LLCORE_HTTP_REQUESTQUEUE_H_ + +#include "_httprequestqueue.h" + +#include <iostream> + +#include "test_allocator.h" +#include "_httpoperation.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpRequestqueueTestData +{ +	// the test objects inherit from this so the member functions and variables +	// can be referenced directly inside of the test functions. +	size_t mMemTotal; +}; + +typedef test_group<HttpRequestqueueTestData> HttpRequestqueueTestGroupType; +typedef HttpRequestqueueTestGroupType::object HttpRequestqueueTestObjectType; +HttpRequestqueueTestGroupType HttpRequestqueueTestGroup("HttpRequestqueue Tests"); + +template <> template <> +void HttpRequestqueueTestObjectType::test<1>() +{ +	set_test_name("HttpRequestQueue construction"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpRequestQueue::init(); +	 +	ensure("One ref on construction of HttpRequestQueue", HttpRequestQueue::instanceOf()->getRefCount() == 1); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// release the implicit reference, causing the object to be released +	HttpRequestQueue::term(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<2>() +{ +	set_test_name("HttpRequestQueue refcount works"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpRequestQueue::init(); + +	HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); +	rq->addRef(); +	 +	// release the singleton, hold on to the object +	HttpRequestQueue::term(); +	 +	ensure("One ref after term() called", rq->getRefCount() == 1); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// Drop ref +	rq->release(); +	 +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<3>() +{ +	set_test_name("HttpRequestQueue addOp/fetchOp work"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpRequestQueue::init(); + +	HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + +	HttpOperation * op = new HttpOpNull(); + +	rq->addOp(op);		// transfer my refcount + +	op = rq->fetchOp(true);		// Potentially hangs the test on failure +	ensure("One goes in, one comes out", NULL != op); +	op->release(); + +	op = rq->fetchOp(false); +	ensure("Better not be two of them", NULL == op); +	 +	// release the singleton, hold on to the object +	HttpRequestQueue::term(); +	 +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<4>() +{ +	set_test_name("HttpRequestQueue addOp/fetchAll work"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpRequestQueue::init(); + +	HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + +	HttpOperation * op = new HttpOpNull(); +	rq->addOp(op);		// transfer my refcount + +	op = new HttpOpNull(); +	rq->addOp(op);		// transfer my refcount + +	op = new HttpOpNull(); +	rq->addOp(op);		// transfer my refcount +	 +	{ +		HttpRequestQueue::OpContainer ops; +		rq->fetchAll(true, ops);		// Potentially hangs the test on failure +		ensure("Three go in, three come out", 3 == ops.size()); + +		op = rq->fetchOp(false); +		ensure("Better not be any more of them", NULL == op); +	 +		// release the singleton, hold on to the object +		HttpRequestQueue::term(); +	 +		// We're still holding onto the ops. +		ensure(mMemTotal < GetMemTotal()); + +		// Release them +		while (! ops.empty()) +		{ +			HttpOperation * op = ops.front(); +			ops.erase(ops.begin()); +			op->release(); +		} +	} + +	// Should be clean +	ensure("All memory returned", mMemTotal == GetMemTotal()); +} + +}  // end namespace tut + + +#endif  // TEST_LLCORE_HTTP_REQUESTQUEUE_H_ diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp new file mode 100644 index 0000000000..fa5803a17c --- /dev/null +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -0,0 +1,163 @@ +/**  + * @file test_llrefcounted + * @brief unit tests for HttpStatus struct + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef TEST_HTTP_STATUS_H_ +#define TEST_HTTP_STATUS_H_ + +#include <core-http/httpcommon.h> + +#include <curl/curl.h> +#include <curl/multi.h> + +using namespace LLCore; + +namespace tut +{ + +struct HttpStatusTestData +{ +	HttpStatusTestData() +		{} +}; + +typedef test_group<HttpStatusTestData> HttpStatusTestGroupType; +typedef HttpStatusTestGroupType::object HttpStatusTestObjectType; + +HttpStatusTestGroupType HttpStatusTestGroup("HttpStatus Tests"); + +template <> template <> +void HttpStatusTestObjectType::test<1>() +{ +	set_test_name("HttpStatus construction"); +	 +	// auto allocation fine for this +	HttpStatus status; +	status.mType = HttpStatus::EXT_CURL_EASY; +	status.mStatus = 0; +	 +	ensure(bool(status)); +	ensure(false == !(status)); + +	status.mType = HttpStatus::EXT_CURL_MULTI; +	status.mStatus = 0; + +	ensure(bool(status)); +	ensure(false == !(status)); +	 +	status.mType = HttpStatus::LLCORE; +	status.mStatus = HE_SUCCESS; +	 +	ensure(bool(status)); +	ensure(false == !(status)); + +	status.mType = HttpStatus::EXT_CURL_MULTI; +	status.mStatus = -1; + +	ensure(false == bool(status)); +	ensure(!(status)); + +	status.mType = HttpStatus::EXT_CURL_EASY; +	status.mStatus = CURLE_BAD_DOWNLOAD_RESUME; + +	ensure(false == bool(status)); +	ensure(!(status)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<2>() +{ +	set_test_name("HttpStatus memory structure"); + +	// Require that an HttpStatus object can be trivially +	// returned as a function return value in registers. +	// One should fit in an int on all platforms. + +	ensure(sizeof(HttpStatus) <= sizeof(int)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<3>() +{ +	set_test_name("HttpStatus valid error string conversion"); +	 +	HttpStatus status; +	status.mType = HttpStatus::EXT_CURL_EASY; +	status.mStatus = 0; +	std::string msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(msg.empty()); +	 +	status.mType = HttpStatus::EXT_CURL_EASY; +	status.mStatus = CURLE_BAD_FUNCTION_ARGUMENT; +	msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); + +	status.mType = HttpStatus::EXT_CURL_MULTI; +	status.mStatus = CURLM_OUT_OF_MEMORY; +	msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); + +	status.mType = HttpStatus::LLCORE; +	status.mStatus = HE_SHUTTING_DOWN; +	msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); +} + + +template <> template <> +void HttpStatusTestObjectType::test<4>() +{ +	set_test_name("HttpStatus invalid error string conversion"); +	 +	HttpStatus status; +	status.mType = HttpStatus::EXT_CURL_EASY; +	status.mStatus = 32726; +	std::string msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); +	 +	status.mType = HttpStatus::EXT_CURL_MULTI; +	status.mStatus = -470; +	msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); + +	status.mType = HttpStatus::LLCORE; +	status.mStatus = 923; +	msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); +} + +} // end namespace tut + +#endif	// TEST_HTTP_STATUS_H + diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp new file mode 100644 index 0000000000..6a17947288 --- /dev/null +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -0,0 +1,156 @@ +/**  + * @file test_refcounted.hpp + * @brief unit tests for the LLCoreInt::RefCounted class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCOREINT_REF_COUNTED_H_ +#define TEST_LLCOREINT_REF_COUNTED_H_ + +#include <_refcounted.h> + +#include "test_allocator.h" + +using namespace LLCoreInt; + +namespace tut +{ +	struct RefCountedTestData +	{ +		// the test objects inherit from this so the member functions and variables +		// can be referenced directly inside of the test functions. +		size_t mMemTotal; +	}; + +	typedef test_group<RefCountedTestData> RefCountedTestGroupType; +	typedef RefCountedTestGroupType::object RefCountedTestObjectType; +	RefCountedTestGroupType RefCountedTestGroup("RefCounted Tests"); + +	template <> template <> +	void RefCountedTestObjectType::test<1>() +	{ +		set_test_name("RefCounted construction with implicit count"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		// create a new ref counted object with an implicit reference +		RefCounted * rc = new RefCounted(true); +		ensure(rc->getRefCount() == 1); + +		// release the implicit reference, causing the object to be released +		rc->release(); + +		// make sure we didn't leak any memory +		ensure(mMemTotal == GetMemTotal()); +	} + +	template <> template <> +	void RefCountedTestObjectType::test<2>() +	{ +		set_test_name("RefCounted construction without implicit count"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		// create a new ref counted object with an implicit reference +		RefCounted * rc = new RefCounted(false); +		ensure(rc->getRefCount() == 0); + +		// add a reference +		rc->addRef(); +		ensure(rc->getRefCount() == 1); + +		// release the implicit reference, causing the object to be released +		rc->release(); + +		ensure(mMemTotal == GetMemTotal()); +	} + +	template <> template <> +	void RefCountedTestObjectType::test<3>() +	{ +		set_test_name("RefCounted addRef and release"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		RefCounted * rc = new RefCounted(false); + +		for (int i = 0; i < 1024; ++i) +		{ +			rc->addRef(); +		} + +		ensure(rc->getRefCount() == 1024); + +		for (int i = 0; i < 1024; ++i) +		{ +			rc->release(); +		} + +		// make sure we didn't leak any memory +		ensure(mMemTotal == GetMemTotal()); +	} + +	template <> template <> +	void RefCountedTestObjectType::test<4>() +	{ +		set_test_name("RefCounted isLastRef check"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		RefCounted * rc = new RefCounted(true); + +		// with only one reference, isLastRef should be true +		ensure(rc->isLastRef()); + +		// release it to clean up memory +		rc->release(); + +		// make sure we didn't leak any memory +		ensure(mMemTotal == GetMemTotal()); +	} + +	template <> template <> +	void RefCountedTestObjectType::test<5>() +	{ +		set_test_name("RefCounted noRef check"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		RefCounted * rc = new RefCounted(false); + +		// set the noRef +		rc->noRef(); + +		// with only one reference, isLastRef should be true +		ensure(rc->getRefCount() == RefCounted::NOT_REF_COUNTED); + +		// allow this memory leak, but check that we're leaking a known amount +		ensure(mMemTotal == (GetMemTotal() - sizeof(RefCounted))); +	} +} + +#endif	// TEST_LLCOREINT_REF_COUNTED_H_ | 
