summaryrefslogtreecommitdiff
path: root/indra/llcorehttp
diff options
context:
space:
mode:
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