diff options
Diffstat (limited to 'indra/llcorehttp')
| -rw-r--r-- | indra/llcorehttp/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/llcorehttp/_httplibcurl.cpp | 11 | ||||
| -rw-r--r-- | indra/llcorehttp/_httplibcurl.h | 3 | ||||
| -rw-r--r-- | indra/llcorehttp/_httpopcancel.cpp | 82 | ||||
| -rw-r--r-- | indra/llcorehttp/_httpopcancel.h | 75 | ||||
| -rw-r--r-- | indra/llcorehttp/_httpoperation.h | 25 | ||||
| -rw-r--r-- | indra/llcorehttp/_httpoprequest.cpp | 105 | ||||
| -rw-r--r-- | indra/llcorehttp/_httpoprequest.h | 9 | ||||
| -rw-r--r-- | indra/llcorehttp/_httppolicy.h | 3 | ||||
| -rw-r--r-- | indra/llcorehttp/_httpreplyqueue.h | 2 | ||||
| -rw-r--r-- | indra/llcorehttp/_httprequestqueue.h | 2 | ||||
| -rw-r--r-- | indra/llcorehttp/_httpservice.h | 4 | ||||
| -rw-r--r-- | indra/llcorehttp/httpcommon.cpp | 90 | ||||
| -rw-r--r-- | indra/llcorehttp/httpcommon.h | 59 | ||||
| -rw-r--r-- | indra/llcorehttp/httprequest.cpp | 17 | ||||
| -rw-r--r-- | indra/llcorehttp/httpresponse.cpp | 3 | ||||
| -rw-r--r-- | indra/llcorehttp/httpresponse.h | 32 | ||||
| -rw-r--r-- | indra/llcorehttp/tests/test_httpstatus.hpp | 102 | 
18 files changed, 518 insertions, 108 deletions
diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 81c502b642..ae92fb96fd 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -30,6 +30,7 @@ set(llcorehttp_SOURCE_FILES      _httprequestqueue.cpp      _httpoperation.cpp      _httpoprequest.cpp +    _httpopcancel.cpp      _httpreplyqueue.cpp      _httppolicy.cpp      _httplibcurl.cpp @@ -49,6 +50,7 @@ set(llcorehttp_HEADER_FILES      httpresponse.h      _httpoperation.h      _httpoprequest.h +    _httpopcancel.h      _httprequestqueue.h      _httpreplyqueue.h      _httpservice.h diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 15be977adf..1b951818e4 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -27,7 +27,6 @@  #include "_httplibcurl.h"  #include "httpheaders.h" -  #include "_httpoprequest.h"  #include "_httpservice.h" @@ -173,8 +172,11 @@ void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode  	{  		int http_status(200); -		curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); -		op->mReplyStatus = http_status; +		curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); +		op->mStatus = LLCore::HttpStatus(http_status, +										 (http_status >= 200 && http_status <= 299 +										  ? HE_SUCCESS +										  : HE_REPLY_ERROR));  	}  	// Detach from multi and recycle handle @@ -202,7 +204,8 @@ int HttpLibcurl::activeCount() const  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; + +		headers->mHeaders.end() != it;  		 ++it)  	{  		slist = curl_slist_append(slist, (*it).c_str()); diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 01c68320af..807196628d 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -45,6 +45,7 @@ class HttpOpRequest;  class HttpHeaders; +/// Implements libcurl-based transport for an HttpService instance.  class HttpLibcurl  {  public: @@ -71,7 +72,7 @@ protected:  	typedef std::set<HttpOpRequest *> active_set_t;  protected: -	HttpService *		mService; +	HttpService *		mService;				// Simple reference, not owner  	active_set_t		mActiveOps;  	CURLM *				mMultiHandles[1];  };  // end class HttpLibcurl diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp new file mode 100644 index 0000000000..69dbff4bb4 --- /dev/null +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -0,0 +1,82 @@ +/** + * @file _httpopcancel.cpp + * @brief Definitions for internal class HttpOpCancel + * + * $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 "_httpopcancel.h" + +#include <cstdio> +#include <algorithm> + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOpCancel +// ================================== + + +HttpOpCancel::HttpOpCancel(HttpHandle handle) +	: HttpOperation(), +	  mHandle(handle) +{} + + +HttpOpCancel::~HttpOpCancel() +{} + + +void HttpOpCancel::stageFromRequest(HttpService * service) +{ +	// *FIXME:  Need cancel functionality into services +	addAsReply(); +} + + +void HttpOpCancel::visitNotifier(HttpRequest * request) +{ +	if (mLibraryHandler) +	{ +		HttpResponse * response = new HttpResponse(); +		mLibraryHandler->onCompleted(static_cast<HttpHandle>(this), response); +		response->release(); +	} +} + + +}   // end namespace LLCore + +		 diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h new file mode 100644 index 0000000000..38ccc585ed --- /dev/null +++ b/indra/llcorehttp/_httpopcancel.h @@ -0,0 +1,75 @@ +/** + * @file _httpopcancel.h + * @brief Internal declarations for the HttpOpCancel 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_OPCANCEL_H_ +#define	_LLCORE_HTTP_OPCANCEL_H_ + + +#include "linden_common.h"		// Modifies curl/curl.h interfaces + +#include "httpcommon.h" + +#include <curl/curl.h> + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// 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(HttpHandle handle); +	virtual ~HttpOpCancel(); + +private: +	HttpOpCancel(const HttpOpCancel &);					// Not defined +	void operator=(const HttpOpCancel &);				// Not defined + +public: +	virtual void stageFromRequest(HttpService *); + +	virtual void visitNotifier(HttpRequest * request); +			 +public: +	// Request data +	HttpHandle			mHandle; + +};  // end class HttpOpCancel + + +}   // end namespace LLCore + +#endif	// _LLCORE_HTTP_OPCANCEL_H_ + diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index d04961c47b..5d06a28586 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -87,31 +87,6 @@ public:  };  // 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 diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 3c9eb71b9a..521bd5b879 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -86,53 +86,53 @@ HttpOpRequest::HttpOpRequest()  	  mReqMethod(HOR_GET),  	  mReqBody(NULL),  	  mReqOffset(0), -	  mReqLen(0), +	  mReqLength(0),  	  mReqHeaders(NULL),  	  mReqOptions(NULL),  	  mCurlActive(false),  	  mCurlHandle(NULL), -	  mCurlHeaders(NULL),  	  mCurlService(NULL), -	  mReplyStatus(200), +	  mCurlHeaders(NULL),  	  mReplyBody(NULL),  	  mReplyOffset(0), -	  mReplyLen(0), +	  mReplyLength(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 (mReqOptions) +	{ +		mReqOptions->release(); +		mReqOptions = NULL; +	} +  	if (mReqHeaders)  	{ -		curl_slist_free_all(mReqHeaders); +		mReqHeaders->release();  		mReqHeaders = NULL;  	} -	if (mReqOptions) +	if (mCurlHandle)  	{ -		mReqOptions->release(); -		mReqOptions = NULL; +		curl_easy_cleanup(mCurlHandle); +		mCurlHandle = NULL; +	} + +	mCurlService = NULL; +	 + +	if (mCurlHeaders) +	{ +		curl_slist_free_all(mCurlHeaders); +		mCurlHeaders = NULL;  	}  	if (mReplyBody) @@ -165,28 +165,29 @@ void HttpOpRequest::stageFromReady(HttpService * service)  void HttpOpRequest::stageFromActive(HttpService * service)  { -	if (mReplyLen) +	if (mReplyLength)  	{  		// If non-zero, we received and processed a Content-Range  		// header with the response.  Verify that what it says  		// is consistent with the received data. -		if (mReplyLen != mReplyBody->size()) +		if (mReplyLength != mReplyBody->size())  		{  			// Not as expected, fail the request  			mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);  		}  	} -	if (mReqHeaders) +	if (mCurlHeaders)  	{  		// 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; +		curl_slist_free_all(mCurlHeaders); +		mCurlHeaders = NULL;  	} +  	addAsReply();  } @@ -196,12 +197,24 @@ 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); +		unsigned int offset(0), length(0); +		if (mReplyOffset || mReplyLength) +		{ +			// Got an explicit offset/length in response +			offset = mReplyOffset; +			length = mReplyLength; +		} +		else if (mReplyBody) +		{ +			// Provide implicit offset/length from request/response +			offset = mReqOffset; +			length = mReplyBody->size(); +		} +		response->setRange(offset, length); +  		mLibraryHandler->onCompleted(static_cast<HttpHandle>(this), response);  		response->release(); @@ -235,14 +248,15 @@ HttpStatus HttpOpRequest::setupGetByteRange(unsigned int policy_id,  	mReqMethod = HOR_GET;  	mReqURL = url;  	mReqOffset = offset; -	mReqLen = len; +	mReqLength = len;  	if (offset || len)  	{  		mProcFlags |= PF_SCAN_RANGE_HEADER;  	}  	if (headers && ! mReqHeaders)  	{ -		mReqHeaders = append_headers_to_slist(headers, mReqHeaders); +		headers->addRef(); +		mReqHeaders = headers;  	}  	if (options && ! mReqOptions)  	{ @@ -257,7 +271,7 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service)  {  	// *FIXME:  better error handling later  	HttpStatus status; -	 +  	mCurlHandle = curl_easy_init();  	curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30);  	curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, 30); @@ -268,14 +282,18 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service)  	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) +	if (mReqHeaders) +	{ +		mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); +	} +	mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); +	 +	if (mReqOffset || mReqLength)  	{  		static const char * fmt1("Range: bytes=%d-%d");  		static const char * fmt2("Range: bytes=%d-"); @@ -284,16 +302,17 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service)  #if defined(WIN32)  		_snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, -					(mReqLen ? fmt1 : fmt2), -					mReqOffset, mReqOffset + mReqLen - 1); +					(mReqLength ? fmt1 : fmt2), +					mReqOffset, mReqOffset + mReqLength - 1);  #else  		snprintf(range_line, sizeof(range_line), -				 (mReqLen ? fmt1 : fmt2), -				 mReqOffset, mReqOffset + mReqLen - 1); +				 (mReqLength ? fmt1 : fmt2), +				 mReqOffset, mReqOffset + mReqLength - 1);  #endif // defined(WIN32)  		range_line[sizeof(range_line) - 1] = '\0';  		mCurlHeaders = curl_slist_append(mCurlHeaders, range_line);  	} +	curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders);  	if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS))  	{ @@ -347,8 +366,9 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi  	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 +		// taking results from the last header stanza we receive.  		op->mReplyOffset = 0; -		op->mReplyLen = 0; +		op->mReplyLength = 0;  		op->mStatus = HttpStatus();  	}  	else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) @@ -371,7 +391,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi  			{  				// Success, record the fragment position  				op->mReplyOffset = first; -				op->mReplyLen = last - first + 1; +				op->mReplyLength = last - first + 1;  			}  			else if (-1 == status)  			{ @@ -390,6 +410,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi  	if (op->mProcFlags & PF_SAVE_HEADERS)  	{  		// Save headers in response +		// *FIXME:  Implement this...  		;  	} diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 232ee841d6..601937a943 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -103,22 +103,21 @@ public:  	std::string			mReqURL;  	BufferArray *		mReqBody;  	off_t				mReqOffset; -	size_t				mReqLen; -	curl_slist *		mReqHeaders; +	size_t				mReqLength; +	HttpHeaders *		mReqHeaders;  	HttpOptions *		mReqOptions;  	// Transport data  	bool				mCurlActive;  	CURL *				mCurlHandle; -	curl_slist *		mCurlHeaders;  	HttpService *		mCurlService; +	curl_slist *		mCurlHeaders;  	// Result data  	HttpStatus			mStatus; -	int					mReplyStatus;  	BufferArray *		mReplyBody;  	off_t				mReplyOffset; -	size_t				mReplyLen; +	size_t				mReplyLength;  	HttpHeaders *		mReplyHeaders;  };  // end class HttpOpRequest diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 28aea27f38..192bc73b31 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -39,6 +39,7 @@ class HttpService;  class HttpOpRequest; +/// Implements class-based queuing policies for an HttpService instance.  class HttpPolicy  {  public: @@ -58,7 +59,7 @@ protected:  	typedef std::vector<HttpOpRequest *> ready_queue_t;  protected: -	HttpService *		mService;				// Naked pointer, not refcounted +	HttpService *		mService;				// Naked pointer, not refcounted, not owner  	ready_queue_t		mReadyQueue;  };  // end class HttpPolicy diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h index 56dadec87c..28cb1d68b7 100644 --- a/indra/llcorehttp/_httpreplyqueue.h +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -58,7 +58,7 @@ class HttpOperation;  /// will be coded anyway so it shouldn't be too much of a  /// burden. -class HttpReplyQueue: public LLCoreInt::RefCounted +class HttpReplyQueue : public LLCoreInt::RefCounted  {  public:  	/// Caller acquires a Refcount on construction diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index 3a9ce0c3c6..f96bd7520c 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -46,7 +46,7 @@ class HttpOperation;  /// requests from all HttpRequest instances into the  /// singleton HttpService instance. -class HttpRequestQueue: public LLCoreInt::RefCounted +class HttpRequestQueue : public LLCoreInt::RefCounted  {  protected:  	/// Caller acquires a Refcount on construction diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index c052e35452..ba76e1eeca 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -152,8 +152,8 @@ protected:  	LLCoreInt::HttpThread *		mThread;  	// === working-thread-only data === -	HttpPolicy *				mPolicy; -	HttpLibcurl *				mTransport; +	HttpPolicy *				mPolicy;		// Simple pointer, has ownership +	HttpLibcurl *				mTransport;		// Simple pointer, has ownership  };  // end class HttpService diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index c37d081150..b5872606b8 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -37,22 +37,82 @@ HttpStatus::type_enum_t EXT_CURL_EASY;  HttpStatus::type_enum_t EXT_CURL_MULTI;  HttpStatus::type_enum_t LLCORE; +HttpStatus::operator unsigned long() const +{ +	static const int shift(sizeof(unsigned long) * 4); + +	unsigned long result(((unsigned long) mType) << shift | (unsigned long) (int) mStatus); +	return result; +} + +  std::string HttpStatus::toString() const  {  	static const char * llcore_errors[] =  		{  			"", +			"HTTP error reply status",  			"Services shutting down",  			"Operation canceled",  			"Invalid Content-Range header encountered"  		};  	static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); + +	static const struct +	{ +		type_enum_t		mCode; +		char *			mText; +	} +	http_errors[] = +		{ +			// Keep sorted by mCode, we binary search this list. +			{ 100, "Continue" }, +			{ 101, "Switching Protocols" }, +			{ 200, "OK" }, +			{ 201, "Created" }, +			{ 202, "Accepted" }, +			{ 203, "Non-Authoritative Information" }, +			{ 204, "No Content" }, +			{ 205, "Reset Content" }, +			{ 206, "Partial Content" }, +			{ 300, "Multiple Choices" }, +			{ 301, "Moved Permanently" }, +			{ 302, "Found" }, +			{ 303, "See Other" }, +			{ 304, "Not Modified" }, +			{ 305, "Use Proxy" }, +			{ 307, "Temporary Redirect" }, +			{ 400, "Bad Request" }, +			{ 401, "Unauthorized" }, +			{ 402, "Payment Required" }, +			{ 403, "Forbidden" }, +			{ 404, "Not Found" }, +			{ 405, "Method Not Allowed" }, +			{ 406, "Not Acceptable" }, +			{ 407, "Proxy Authentication Required" }, +			{ 408, "Request Time-out" }, +			{ 409, "Conflict" }, +			{ 410, "Gone" }, +			{ 411, "Length Required" }, +			{ 412, "Precondition Failed" }, +			{ 413, "Request Entity Too Large" }, +			{ 414, "Request-URI Too Large" }, +			{ 415, "Unsupported Media Type" }, +			{ 416, "Requested range not satisfiable" }, +			{ 417, "Expectation Failed" }, +			{ 500, "Internal Server Error" }, +			{ 501, "Not Implemented" }, +			{ 502, "Bad Gateway" }, +			{ 503, "Service Unavailable" }, +			{ 504, "Gateway Time-out" }, +			{ 505, "HTTP Version not supported" } +		}; +	static const int http_errors_count(sizeof(http_errors) / sizeof(http_errors[0]));  	if (*this)  	{  		return std::string("");  	} -  	switch (mType)  	{  	case EXT_CURL_EASY: @@ -67,7 +127,33 @@ std::string HttpStatus::toString() const  			return std::string(llcore_errors[mStatus]);  		}  		break; -		 + +	default: +		if (isHttpStatus()) +		{ +			int bottom(0), top(http_errors_count); +			while (true) +			{ +				int at((bottom + top) / 2); +				if (mType == http_errors[at].mCode) +				{ +					return std::string(http_errors[at].mText); +				} +				if (at == bottom) +				{ +					break; +				} +				else if (mType < http_errors[at].mCode) +				{ +					top = at; +				} +				else +				{ +					bottom = at; +				} +			} +		} +		break;  	}  	return std::string("Unknown error");  } diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 9de5769d57..f81be7103e 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -122,23 +122,38 @@ enum HttpError  	// Successful value compatible with the libcurl codes.  	HE_SUCCESS = 0, +	// Intended for HTTP reply codes 100-999, indicates that +	// the reply should be considered an error by the application. +	HE_REPLY_ERROR = 1, +	  	// Service is shutting down and requested operation will  	// not be queued or performed. -	HE_SHUTTING_DOWN = 1, +	HE_SHUTTING_DOWN = 2,  	// Operation was canceled by request. -	HE_OP_CANCELED = 2, +	HE_OP_CANCELED = 3,  	// Invalid content range header received. -	HE_INV_CONTENT_RANGE_HDR = 3 +	HE_INV_CONTENT_RANGE_HDR = 4  }; // 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. +/// HttpStatus encapsulates errors from libcurl (easy, multi), HTTP +/// reply status codes and internal errors as well.  The encapsulation +/// isn't expected to completely isolate the caller from libcurl but +/// basic operational tests (success or failure) are provided. +/// +/// Non-HTTP status are encoded as (type, status) with type being +/// one of:  EXT_CURL_EASY, EXT_CURL_MULTI or LLCORE and status +/// being the success/error code from that domain.  HTTP status +/// is encoded as (status, error_flag).  Status should be in the +/// range [100, 999] and error_flag is either HE_SUCCESS or +/// HE_REPLY_ERROR to indicate whether this should be treated as +/// a successful status or an error.  The application is responsible +/// for making that determination and a range like [200, 299] isn't +/// automatically assumed to be definitive. +  struct HttpStatus  {  	typedef unsigned short type_enum_t; @@ -192,12 +207,42 @@ struct HttpStatus  		return 0 != mStatus;  	} +	/// Equality and inequality tests to bypass bool conversion +	/// which will do the wrong thing in conditional expressions. +	bool operator==(const HttpStatus & rhs) const +	{ +		return mType == rhs.mType && mStatus == rhs.mStatus; +	} + +	bool operator!=(const HttpStatus & rhs) const +	{ +		return ! operator==(rhs); +	} + +	/// Convert to single numeric representation.  Mainly +	/// for logging or other informal purposes.  Also +	/// creates an ambiguous second path to integer conversion +	/// which tends to find programming errors such as formatting +	/// the status to a stream (operator<<). +	operator unsigned long() const; +	unsigned long toULong() const +	{ +		return operator unsigned long(); +	} +	  	/// 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; + +	/// Returns true if the status value represents an +	/// HTTP response status (100 - 999). +	bool isHttpStatus() const +	{ +		return 	mType >= type_enum_t(100) && mType <= type_enum_t(999); +	}  }; // end struct HttpStatus diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 2a87f5231a..6c62f931ff 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -31,6 +31,7 @@  #include "_httpservice.h"  #include "_httpoperation.h"  #include "_httpoprequest.h" +#include "_httpopcancel.h"  namespace @@ -189,6 +190,22 @@ HttpHandle HttpRequest::requestGetByteRange(unsigned int policy_id,  } +HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +{ +	HttpStatus status; +	HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + +	HttpOpCancel * op = new HttpOpCancel(handle); +	op->setHandlers(mReplyQueue, mSelfHandler, user_handler); +	mRequestQueue->addOp(op);			// transfer refcount as well + +	mLastReqStatus = status; +	ret_handle = static_cast<HttpHandle>(op); +	 +	return ret_handle; +} + +  HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler)  {  	HttpStatus status; diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp index 9ac8276f05..3dcdadb337 100644 --- a/indra/llcorehttp/httpresponse.cpp +++ b/indra/llcorehttp/httpresponse.cpp @@ -35,7 +35,8 @@ namespace LLCore  HttpResponse::HttpResponse()  	: LLCoreInt::RefCounted(true), -	  mReplyStatus(0U), +	  mReplyOffset(0U), +	  mReplyLength(0U),  	  mBufferArray(NULL),  	  mHeaders(NULL)  {} diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index a25e22aef6..5cf3a919f4 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -67,8 +67,6 @@ protected:  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; @@ -79,19 +77,6 @@ public:  			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. @@ -122,11 +107,26 @@ public:  	/// Behaves like @see setResponse() but for header data.  	void setHeaders(HttpHeaders * headers); + +	/// If a 'Range:' header was used, these methods are involved +	/// in setting and returning data about the actual response. +	void getRange(unsigned int * offset, unsigned int * length) const +		{ +			*offset = mReplyOffset; +			*length = mReplyLength; +		} + +	void setRange(unsigned int offset, unsigned int length) +		{ +			mReplyOffset = offset; +			mReplyLength = length; +		}  protected:  	// Response data here  	HttpStatus			mStatus; -	unsigned int		mReplyStatus; +	unsigned int		mReplyOffset; +	unsigned int		mReplyLength;  	BufferArray *		mBufferArray;  	HttpHeaders *		mHeaders;  }; diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp index 38bf494dec..f7b542d3b5 100644 --- a/indra/llcorehttp/tests/test_httpstatus.hpp +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -157,6 +157,108 @@ void HttpStatusTestObjectType::test<4>()  	ensure(! msg.empty());  } +template <> template <> +void HttpStatusTestObjectType::test<5>() +{ +	set_test_name("HttpStatus equality/inequality testing"); + +	// Make certain equality/inequality tests do not pass +	// through the bool conversion.  Distinct successful +	// and error statuses should compare unequal. + +	HttpStatus status1(HttpStatus::LLCORE, HE_SUCCESS); +	HttpStatus status2(HttpStatus::EXT_CURL_EASY, HE_SUCCESS); +	ensure(status1 != status2); + +	status1.mType = HttpStatus::LLCORE; +	status1.mStatus = HE_REPLY_ERROR; +	status2.mType = HttpStatus::LLCORE; +	status2.mStatus= HE_SHUTTING_DOWN; +	ensure(status1 != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<6>() +{ +	set_test_name("HttpStatus basic HTTP status encoding"); +	 +	HttpStatus status; +	status.mType = 200; +	status.mStatus = HE_SUCCESS; +	std::string msg = status.toString(); +	ensure(msg.empty()); +	ensure(bool(status)); + +	// Normally a success but application says error +	status.mStatus = HE_REPLY_ERROR; +	msg = status.toString(); +	ensure(! msg.empty()); +	ensure(! bool(status)); +	ensure(status.toULong() > 1UL);				// Biggish number, not a bool-to-ulong + +	// Same statuses with distinct success/fail are distinct +	status.mType = 200; +	status.mStatus = HE_SUCCESS; +	HttpStatus status2(200, HE_REPLY_ERROR); +	ensure(status != status2); + +	// Normally an error but application says okay +	status.mType = 406; +	status.mStatus = HE_SUCCESS; +	msg = status.toString(); +	ensure(msg.empty()); +	ensure(bool(status)); + +	// Different statuses but both successful are distinct +	status.mType = 200; +	status.mStatus = HE_SUCCESS; +	status2.mType = 201; +	status2.mStatus = HE_SUCCESS; +	ensure(status != status2); + +	// Different statuses but both failed are distinct +	status.mType = 200; +	status.mStatus = HE_REPLY_ERROR; +	status2.mType = 201; +	status2.mStatus = HE_REPLY_ERROR; +	ensure(status != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<7>() +{ +	set_test_name("HttpStatus HTTP error text strings"); + +	HttpStatus status(100, HE_REPLY_ERROR); +	std::string msg(status.toString()); +	ensure(! msg.empty());				// Should be something +	ensure(msg == "Continue"); + +	status.mStatus = HE_SUCCESS; +	msg = status.toString(); +	ensure(msg.empty());				// Success is empty + +	status.mType = 199; +	status.mStatus = HE_REPLY_ERROR; +	msg = status.toString(); +	ensure(msg == "Unknown error"); + +	status.mType = 505;					// Last defined string +	status.mStatus = HE_REPLY_ERROR; +	msg = status.toString(); +	ensure(msg == "HTTP Version not supported"); + +	status.mType = 506;					// One beyond +	status.mStatus = HE_REPLY_ERROR; +	msg = status.toString(); +	ensure(msg == "Unknown error"); + +	status.mType = 999;					// Last HTTP status +	status.mStatus = HE_REPLY_ERROR; +	msg = status.toString(); +	ensure(msg == "Unknown error"); +} +  } // end namespace tut  #endif	// TEST_HTTP_STATUS_H  | 
