summaryrefslogtreecommitdiff
path: root/indra/llcorehttp
diff options
context:
space:
mode:
authorMonty Brandenberg <monty@lindenlab.com>2012-05-23 19:12:09 -0400
committerMonty Brandenberg <monty@lindenlab.com>2012-05-23 19:12:09 -0400
commit8fc350125c671baeae6b7f8b1814251009f4f50a (patch)
tree8e24b69d950333ac19f94aa8114a971ce71e41a5 /indra/llcorehttp
parent30d72b041f3221b903ac11c0054dc221b0c0329b (diff)
Integrate llcorehttp library into lltexturefetch design.
This is the first functional viewer pass with the HTTP work of the texture fetch code performed by the llcorehttp library. Not exactly a 'drop-in' replacement but a work-alike with some changes (e.g. handler notification in consumer thread versus responder notification in worker thread). This also includes some temporary changes in the priority scheme to prevent the kind of priority inversion found in VWR-28996. Scheme used here does provide liveness if not optimal responsiveness or order-of-operation. The llcorehttp library at this point is far from optimally performing. Its worker thread is making relatively poor use of cycles it gets and it doesn't idle or sleep intelligently yet. This early integration step helps shake out the interfaces, implementation niceties will be covered soon.
Diffstat (limited to 'indra/llcorehttp')
-rw-r--r--indra/llcorehttp/CMakeLists.txt2
-rw-r--r--indra/llcorehttp/_httplibcurl.cpp11
-rw-r--r--indra/llcorehttp/_httplibcurl.h3
-rw-r--r--indra/llcorehttp/_httpopcancel.cpp82
-rw-r--r--indra/llcorehttp/_httpopcancel.h75
-rw-r--r--indra/llcorehttp/_httpoperation.h25
-rw-r--r--indra/llcorehttp/_httpoprequest.cpp105
-rw-r--r--indra/llcorehttp/_httpoprequest.h9
-rw-r--r--indra/llcorehttp/_httppolicy.h3
-rw-r--r--indra/llcorehttp/_httpreplyqueue.h2
-rw-r--r--indra/llcorehttp/_httprequestqueue.h2
-rw-r--r--indra/llcorehttp/_httpservice.h4
-rw-r--r--indra/llcorehttp/httpcommon.cpp90
-rw-r--r--indra/llcorehttp/httpcommon.h59
-rw-r--r--indra/llcorehttp/httprequest.cpp17
-rw-r--r--indra/llcorehttp/httpresponse.cpp3
-rw-r--r--indra/llcorehttp/httpresponse.h32
-rw-r--r--indra/llcorehttp/tests/test_httpstatus.hpp102
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