summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--indra/newview/llappviewer.cpp159
-rw-r--r--indra/newview/lltexturefetch.cpp610
-rw-r--r--indra/newview/lltexturefetch.h19
21 files changed, 1057 insertions, 357 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
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 1174d108d2..8e6deb9cce 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -212,6 +212,11 @@
#include "llmachineid.h"
#include "llmainlooprepeater.h"
+// LLCore::HTTP
+#include "httpcommon.h"
+#include "httprequest.h"
+#include "httphandler.h"
+
// *FIX: These extern globals should be cleaned up.
// The globals either represent state/config/resource-storage of either
// this app, or another 'component' of the viewer. App globals should be
@@ -326,6 +331,53 @@ static std::string gLaunchFileOnQuit;
// Used on Win32 for other apps to identify our window (eg, win_setup)
const char* const VIEWER_WINDOW_CLASSNAME = "Second Life";
+namespace
+{
+
+// This class manages the lifecyle of the core http library.
+// Slightly different style than traditional code but reflects
+// the use of handler classes and light-weight interface
+// object instances of the new libraries. To be used
+// as a singleton and static construction is fine.
+class CoreHttp : public LLCore::HttpHandler
+{
+public:
+ CoreHttp();
+ ~CoreHttp();
+
+ // Initialize the LLCore::HTTP library creating service classes
+ // and starting the servicing thread. Caller is expected to do
+ // other initializations (SSL mutex, thread hash function) appropriate
+ // for the application.
+ void init();
+
+ // Request that the servicing thread stop servicing requests,
+ // release resource references and stop.
+ void requestStop();
+
+ // Terminate LLCore::HTTP library services. Caller is expected
+ // to have made a best-effort to shutdown the servicing thread
+ // by issuing a requestThreadStop() and waiting for completion
+ // notification that the stop has completed.
+ void cleanup();
+
+ // Notification when the stop request is complete.
+ virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
+
+private:
+ static const F64 MAX_THREAD_WAIT_TIME;
+
+private:
+ LLCore::HttpRequest * mRequest;
+ LLCore::HttpHandle mStopHandle;
+ F64 mStopRequested;
+ bool mStopped;
+};
+
+CoreHttp coreHttpLib;
+
+} // end anonymous namespace
+
//-- LLDeferredTaskList ------------------------------------------------------
/**
@@ -720,6 +772,9 @@ bool LLAppViewer::init()
LLViewerStatsRecorder::initClass();
#endif
+ // Initialize the non-LLCurl libcurl library
+ coreHttpLib.init();
+
// *NOTE:Mani - LLCurl::initClass is not thread safe.
// Called before threads are created.
LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"),
@@ -1807,6 +1862,7 @@ bool LLAppViewer::cleanup()
// Delete workers first
// shotdown all worker threads before deleting them in case of co-dependencies
+ coreHttpLib.requestStop();
sTextureFetch->shutdown();
sTextureCache->shutdown();
sImageDecodeThread->shutdown();
@@ -1890,6 +1946,9 @@ bool LLAppViewer::cleanup()
// *NOTE:Mani - The following call is not thread safe.
LLCurl::cleanupClass();
+ // Non-LLCurl libcurl library
+ coreHttpLib.cleanup();
+
// If we're exiting to launch an URL, do that here so the screen
// is at the right resolution before we launch IE.
if (!gLaunchFileOnQuit.empty())
@@ -5267,3 +5326,103 @@ void LLAppViewer::metricsSend(bool enable_reporting)
gViewerAssetStatsMain->reset();
}
+namespace
+{
+
+const F64 CoreHttp::MAX_THREAD_WAIT_TIME(10.0);
+
+CoreHttp::CoreHttp()
+ : mRequest(NULL),
+ mStopHandle(LLCORE_HTTP_HANDLE_INVALID),
+ mStopRequested(0.0),
+ mStopped(false)
+{}
+
+
+CoreHttp::~CoreHttp()
+{
+ delete mRequest;
+ mRequest = NULL;
+}
+
+
+void CoreHttp::init()
+{
+ LLCore::HttpStatus status = LLCore::HttpRequest::createService();
+ if (! status)
+ {
+ LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: "
+ << status.toString()
+ << LL_ENDL;
+ }
+
+ status = LLCore::HttpRequest::startThread();
+ if (! status)
+ {
+ LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: "
+ << status.toString()
+ << LL_ENDL;
+ }
+
+ mRequest = new LLCore::HttpRequest;
+}
+
+
+void CoreHttp::requestStop()
+{
+ llassert_always(mRequest);
+
+ mStopHandle = mRequest->requestStopThread(this);
+ if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle)
+ {
+ mStopRequested = LLTimer::getTotalSeconds();
+ }
+}
+
+
+void CoreHttp::cleanup()
+{
+ if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle)
+ {
+ // Should have been started already...
+ requestStop();
+ }
+
+ if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle)
+ {
+ LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services without thread shutdown"
+ << LL_ENDL;
+ }
+ else
+ {
+ while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME))
+ {
+ mRequest->update(200);
+ ms_sleep(50);
+ }
+ if (! mStopped)
+ {
+ LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services with thread shutdown incomplete"
+ << LL_ENDL;
+ }
+ }
+
+ delete mRequest;
+ mRequest = NULL;
+
+ LLCore::HttpStatus status = LLCore::HttpRequest::destroyService();
+ if (! status)
+ {
+ LL_WARNS("Cleanup") << "Failed to shutdown HTTP services, continuing. Reason: "
+ << status.toString()
+ << LL_ENDL;
+ }
+}
+
+
+void CoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *)
+{
+ mStopped = true;
+}
+
+} // end anonymous namespace
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index f18aa8b4e6..17c68f7c22 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * 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
@@ -33,7 +33,6 @@
#include "lltexturefetch.h"
-#include "llcurl.h"
#include "lldir.h"
#include "llhttpclient.h"
#include "llhttpstatuscodes.h"
@@ -53,11 +52,17 @@
#include "llviewerassetstats.h"
#include "llworld.h"
+#include "httprequest.h"
+#include "httphandler.h"
+#include "httpresponse.h"
+#include "bufferarray.h"
+
+
//////////////////////////////////////////////////////////////////////////////
-class LLTextureFetchWorker : public LLWorkerClass
+class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler
+
{
friend class LLTextureFetch;
- friend class HTTPGetResponder;
private:
class CacheReadResponder : public LLTextureCache::ReadResponder
@@ -147,15 +152,14 @@ public:
~LLTextureFetchWorker();
// void relese() { --mActiveCount; }
- S32 callbackHttpGet(const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer,
- bool partial, bool success);
+ S32 callbackHttpGet(LLCore::HttpResponse * response,
+ bool partial, bool success);
void callbackCacheRead(bool success, LLImageFormatted* image,
S32 imagesize, BOOL islocal);
void callbackCacheWrite(bool success);
void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux);
- void setGetStatus(U32 status, const std::string& reason)
+ void setGetStatus(LLCore::HttpStatus status, const std::string& reason)
{
LLMutexLock lock(&mWorkMutex);
@@ -167,6 +171,9 @@ public:
bool getCanUseHTTP() const { return mCanUseHTTP; }
LLTextureFetch & getFetcher() { return *mFetcher; }
+
+ // Inherited from LLCore::HttpHandler
+ virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
protected:
LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host,
@@ -191,9 +198,15 @@ private:
void lockWorkMutex() { mWorkMutex.lock(); }
void unlockWorkMutex() { mWorkMutex.unlock(); }
+ void recordTextureStart(bool is_http);
+ void recordTextureDone(bool is_http);
+
private:
enum e_state // mState
{
+ // *NOTE: Do not change the order/value of state variables, some code
+ // depends upon specific ordering/adjacency.
+
// NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack)
INVALID = 0,
INIT,
@@ -244,9 +257,8 @@ private:
LLFrameTimer mFetchTimer;
LLTextureCache::handle_t mCacheReadHandle;
LLTextureCache::handle_t mCacheWriteHandle;
- U8* mBuffer;
- S32 mBufferSize;
S32 mRequestedSize;
+ S32 mRequestedOffset;
S32 mDesiredSize;
S32 mFileSize;
S32 mCachedSize;
@@ -263,7 +275,7 @@ private:
S32 mHTTPFailCount;
S32 mRetryAttempt;
S32 mActiveCount;
- U32 mGetStatus;
+ LLCore::HttpStatus mGetStatus;
std::string mGetReason;
// Work Data
@@ -283,106 +295,17 @@ private:
U8 mImageCodec;
LLViewerAssetStats::duration_t mMetricsStartTime;
+
+ LLCore::HttpHandle mHttpHandle;
+ LLCore::BufferArray * mHttpBufferArray;
+ int mHttpPolicyClass;
+ bool mHttpActive;
+ unsigned int mHttpReplySize;
+ unsigned int mHttpReplyOffset;
};
//////////////////////////////////////////////////////////////////////////////
-class HTTPGetResponder : public LLCurl::Responder
-{
- LOG_CLASS(HTTPGetResponder);
-public:
- HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir)
- : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir)
- {
- }
- ~HTTPGetResponder()
- {
- }
-
- virtual void completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer)
- {
- static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
- static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
- static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ;
-
- if (log_to_viewer_log || log_to_sim)
- {
- mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime);
- U64 timeNow = LLTimer::getTotalTime();
- mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
- mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize);
- mFetcher->mTextureInfo.setRequestOffset(mID, mOffset);
- mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
- }
-
- lldebugs << "HTTP COMPLETE: " << mID << llendl;
- LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
- if (worker)
- {
- bool success = false;
- bool partial = false;
- if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES)
- {
- success = true;
- if (HTTP_PARTIAL_CONTENT == status) // partial information
- {
- partial = true;
- }
- }
-
- if (!success)
- {
- worker->setGetStatus(status, reason);
-// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl;
- }
-
- S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success);
-
- if(log_texture_traffic && data_size > 0)
- {
- LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ;
- if(tex)
- {
- gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ;
- }
- }
-
- mFetcher->removeFromHTTPQueue(mID, data_size);
-
- if (worker->mMetricsStartTime)
- {
- LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
- true,
- LLImageBase::TYPE_AVATAR_BAKE == worker->mType,
- LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime);
- worker->mMetricsStartTime = 0;
- }
- LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
- true,
- LLImageBase::TYPE_AVATAR_BAKE == worker->mType);
- }
- else
- {
- mFetcher->removeFromHTTPQueue(mID);
- llwarns << "Worker not found: " << mID << llendl;
- }
- }
-
- virtual bool followRedir()
- {
- return mFollowRedir;
- }
-
-private:
- LLTextureFetch* mFetcher;
- LLUUID mID;
- U64 mStartTime;
- S32 mRequestedSize;
- U32 mOffset;
- bool mFollowRedir;
-};
//////////////////////////////////////////////////////////////////////////////
@@ -639,6 +562,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
S32 discard, // Desired discard
S32 size) // Desired size
: LLWorkerClass(fetcher, "TextureFetch"),
+ LLCore::HttpHandler(),
mState(INIT),
mWriteToCacheState(NOT_WRITE),
mFetcher(fetcher),
@@ -655,9 +579,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
mDecodedDiscard(-1),
mCacheReadHandle(LLTextureCache::nullHandle()),
mCacheWriteHandle(LLTextureCache::nullHandle()),
- mBuffer(NULL),
- mBufferSize(0),
mRequestedSize(0),
+ mRequestedOffset(0),
mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE),
mFileSize(0),
mCachedSize(0),
@@ -673,13 +596,18 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
mHTTPFailCount(0),
mRetryAttempt(0),
mActiveCount(0),
- mGetStatus(0),
mWorkMutex(NULL),
mFirstPacket(0),
mLastPacket(-1),
mTotalPackets(0),
mImageCodec(IMG_CODEC_INVALID),
- mMetricsStartTime(0)
+ mMetricsStartTime(0),
+ mHttpHandle(LLCORE_HTTP_HANDLE_INVALID),
+ mHttpBufferArray(NULL),
+ mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
+ mHttpActive(false),
+ mHttpReplySize(0U),
+ mHttpReplyOffset(0U)
{
mCanUseNET = mUrl.empty() ;
@@ -701,6 +629,11 @@ LLTextureFetchWorker::~LLTextureFetchWorker()
// << " Requested=" << mRequestedDiscard
// << " Desired=" << mDesiredDiscard << llendl;
llassert_always(!haveWork());
+ if (mHttpActive)
+ {
+ LL_WARNS("Texture") << "Deleting worker object while HTTP request is active."
+ << LL_ENDL;
+ }
lockWorkMutex();
if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
{
@@ -714,6 +647,11 @@ LLTextureFetchWorker::~LLTextureFetchWorker()
clearPackets();
unlockWorkMutex();
mFetcher->removeFromHTTPQueue(mID);
+ if (mHttpBufferArray)
+ {
+ mHttpBufferArray->release();
+ mHttpBufferArray = NULL;
+ }
}
void LLTextureFetchWorker::clearPackets()
@@ -797,7 +735,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size)
if ((prioritize && mState == INIT) || mState == DONE)
{
mState = INIT;
- U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+ U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_LOW;
setPriority(work_priority);
}
}
@@ -810,16 +748,18 @@ void LLTextureFetchWorker::setImagePriority(F32 priority)
{
mImagePriority = priority;
calcWorkPriority();
- U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS);
+ U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_LOW;
setPriority(work_priority);
}
}
void LLTextureFetchWorker::resetFormattedData()
{
- FREE_MEM(LLImageBase::getPrivatePool(), mBuffer);
- mBuffer = NULL;
- mBufferSize = 0;
+ if (mHttpBufferArray)
+ {
+ mHttpBufferArray->release();
+ mHttpBufferArray = NULL;
+ }
if (mFormattedImage.notNull())
{
mFormattedImage->deleteData();
@@ -875,6 +815,14 @@ bool LLTextureFetchWorker::doWork(S32 param)
mFetchTimer.reset();
}
+ static LLUUID last_id;
+ if (mID != last_id)
+ {
+ // LL_WARNS("Texture") << "DOWORK SWITCH: " << last_id << " to: " << mID
+ // << LL_ENDL;
+ last_id = mID;
+ }
+
if (mState == INIT)
{
mRawImage = NULL ;
@@ -882,15 +830,18 @@ bool LLTextureFetchWorker::doWork(S32 param)
mLoadedDiscard = -1;
mDecodedDiscard = -1;
mRequestedSize = 0;
+ mRequestedOffset = 0;
mFileSize = 0;
mCachedSize = 0;
mLoaded = FALSE;
mSentRequest = UNSENT;
mDecoded = FALSE;
mWritten = FALSE;
- FREE_MEM(LLImageBase::getPrivatePool(), mBuffer);
- mBuffer = NULL;
- mBufferSize = 0;
+ if (mHttpBufferArray)
+ {
+ mHttpBufferArray->release();
+ mHttpBufferArray = NULL;
+ }
mHaveAllData = FALSE;
clearPackets(); // TODO: Shouldn't be necessary
mCacheReadHandle = LLTextureCache::nullHandle();
@@ -904,6 +855,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == LOAD_FROM_TEXTURE_CACHE)
{
+ setPriority(0); // Set priority first since Responder may change it
if (mCacheReadHandle == LLTextureCache::nullHandle())
{
U32 cache_priority = mWorkPriority;
@@ -919,8 +871,6 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mUrl.compare(0, 7, "file://") == 0)
{
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
-
// read file from local disk
std::string filename = mUrl.substr(7, std::string::npos);
CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
@@ -929,8 +879,6 @@ bool LLTextureFetchWorker::doWork(S32 param)
}
else if (mUrl.empty())
{
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
-
CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
offset, size, responder);
@@ -942,12 +890,12 @@ bool LLTextureFetchWorker::doWork(S32 param)
// *TODO:?remove this warning
llwarns << "Unknown URL Type: " << mUrl << llendl;
}
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = SEND_HTTP_REQ;
}
else
{
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = LOAD_FROM_NETWORK;
}
}
@@ -959,6 +907,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
{
mCacheReadHandle = LLTextureCache::nullHandle();
mState = CACHE_POST;
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
// fall through
}
else
@@ -982,6 +931,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
llassert_always(mFormattedImage->getDataSize() > 0);
mLoadedDiscard = mDesiredDiscard;
mState = DECODE_IMAGE;
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mWriteToCacheState = NOT_WRITE ;
LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize()
<< " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight())
@@ -999,6 +949,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
else
{
LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL;
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = LOAD_FROM_NETWORK;
}
// fall through
@@ -1009,6 +960,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
{
static LLCachedControl<bool> use_http(gSavedSettings,"ImagePipelineUseHTTP");
+ setPriority(0);
// if (mHost != LLHost::invalid) get_url = false;
if ( use_http && mCanUseHTTP && mUrl.empty())//get http url.
{
@@ -1040,8 +992,8 @@ bool LLTextureFetchWorker::doWork(S32 param)
}
if (mCanUseHTTP && !mUrl.empty())
{
- mState = LLTextureFetchWorker::SEND_HTTP_REQ;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ mState = SEND_HTTP_REQ;
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
if(mWriteToCacheState != NOT_WRITE)
{
mWriteToCacheState = CAN_WRITE ;
@@ -1057,14 +1009,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
mRequestedDiscard = mDesiredDiscard;
mSentRequest = QUEUED;
mFetcher->addToNetworkQueue(this);
- if (! mMetricsStartTime)
- {
- mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
- }
- LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
- false,
- LLImageBase::TYPE_AVATAR_BAKE == mType);
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ recordTextureStart(false);
return false;
}
@@ -1074,12 +1019,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
//llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
// Make certain this is in the network queue
//mFetcher->addToNetworkQueue(this);
- //if (! mMetricsStartTime)
- //{
- // mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
- //}
- //LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false,
- // LLImageBase::TYPE_AVATAR_BAKE == mType);
+ //recordTextureStart(false);
//setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return false;
}
@@ -1087,6 +1027,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == LOAD_FROM_SIMULATOR)
{
+ setPriority(0);
if (mFormattedImage.isNull())
{
mFormattedImage = new LLImageJ2C;
@@ -1101,39 +1042,22 @@ bool LLTextureFetchWorker::doWork(S32 param)
// llwarns << "processSimulatorPackets() failed to load buffer" << llendl;
return true; // failed
}
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = DECODE_IMAGE;
mWriteToCacheState = SHOULD_WRITE;
-
- if (mMetricsStartTime)
- {
- LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
- false,
- LLImageBase::TYPE_AVATAR_BAKE == mType,
- LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime);
- mMetricsStartTime = 0;
- }
- LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
- false,
- LLImageBase::TYPE_AVATAR_BAKE == mType);
+ recordTextureDone(false);
}
else
{
mFetcher->addToNetworkQueue(this); // failsafe
- if (! mMetricsStartTime)
- {
- mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
- }
- LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
- false,
- LLImageBase::TYPE_AVATAR_BAKE == mType);
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ recordTextureStart(false);
}
return false;
}
if (mState == SEND_HTTP_REQ)
{
+ setPriority(0);
if(mCanUseHTTP)
{
//NOTE:
@@ -1159,6 +1083,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
{
// We already have all the data, just decode it
mLoadedDiscard = mFormattedImage->getDiscardLevel();
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = DECODE_IMAGE;
return false;
}
@@ -1171,44 +1096,48 @@ bool LLTextureFetchWorker::doWork(S32 param)
mRequestedSize = mDesiredSize;
mRequestedDiscard = mDesiredDiscard;
mRequestedSize -= cur_size;
- S32 offset = cur_size;
- mBufferSize = cur_size; // This will get modified by callbackHttpGet()
+ mRequestedOffset = cur_size;
- bool res = false;
+ mHttpHandle = LLCORE_HTTP_HANDLE_INVALID;
if (!mUrl.empty())
{
mLoaded = FALSE;
- mGetStatus = 0;
+ mGetStatus = LLCore::HttpStatus();
mGetReason.clear();
- LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset
+ LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset
<< " Bytes: " << mRequestedSize
<< " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth
<< LL_ENDL;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- mState = WAIT_HTTP_REQ;
-
- mFetcher->addToHTTPQueue(mID);
- if (! mMetricsStartTime)
- {
- mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
- }
- LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
- true,
- LLImageBase::TYPE_AVATAR_BAKE == mType);
+// LL_WARNS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset
+// << " Bytes: " << mRequestedSize
+// << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth
+// << LL_ENDL;
// Will call callbackHttpGet when curl request completes
- std::vector<std::string> headers;
- headers.push_back("Accept: image/x-j2c");
- res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize,
- new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true));
+ // *FIXME: enable redirection follow
+ mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass,
+ mRequestedPriority,
+ mUrl,
+ mRequestedOffset,
+ mRequestedSize,
+ mFetcher->mHttpOptions,
+ mFetcher->mHttpHeaders,
+ this);
}
- if (!res)
+ if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle)
{
llwarns << "HTTP GET request failed for " << mID << llendl;
resetFormattedData();
++mHTTPFailCount;
return true; // failed
}
+
+ mHttpActive = true;
+ mFetcher->addToHTTPQueue(mID);
+ recordTextureStart(true);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
+ mState = WAIT_HTTP_REQ;
+
// fall through
}
else //can not use http fetch.
@@ -1219,27 +1148,28 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == WAIT_HTTP_REQ)
{
+ setPriority(0);
if (mLoaded)
{
S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
if (mRequestedSize < 0)
{
S32 max_attempts;
- if (mGetStatus == HTTP_NOT_FOUND)
+ if (mGetStatus == LLCore::HttpStatus(HTTP_NOT_FOUND, LLCore::HE_REPLY_ERROR))
{
mHTTPFailCount = max_attempts = 1; // Don't retry
llwarns << "Texture missing from server (404): " << mUrl << llendl;
//roll back to try UDP
- if(mCanUseNET)
+ if (mCanUseNET)
{
- mState = INIT ;
- mCanUseHTTP = false ;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- return false ;
+ mState = INIT;
+ mCanUseHTTP = false;
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ return false;
}
}
- else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE)
+ else if (mGetStatus == LLCore::HttpStatus(HTTP_SERVICE_UNAVAILABLE, LLCore::HE_REPLY_ERROR))
{
// *TODO: Should probably introduce a timer here to delay future HTTP requsts
// for a short time (~1s) to ease server load? Ideally the server would queue
@@ -1254,7 +1184,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
max_attempts = HTTP_MAX_RETRY_COUNT + 1;
++mHTTPFailCount;
llinfos << "HTTP GET failed for: " << mUrl
- << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'"
+ << " Status: " << mGetStatus.toULong() << " Reason: '" << mGetReason << "'"
<< " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl;
}
@@ -1264,12 +1194,14 @@ bool LLTextureFetchWorker::doWork(S32 param)
{
// Use available data
mLoadedDiscard = mFormattedImage->getDiscardLevel();
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = DECODE_IMAGE;
return false;
}
else
{
resetFormattedData();
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = DONE;
return true; // failed
}
@@ -1281,17 +1213,25 @@ bool LLTextureFetchWorker::doWork(S32 param)
}
}
- llassert_always(mBufferSize == cur_size + mRequestedSize);
- if(!mBufferSize)//no data received.
+ if (! mHttpBufferArray || ! mHttpBufferArray->size())
{
- FREE_MEM(LLImageBase::getPrivatePool(), mBuffer);
- mBuffer = NULL;
+ // no data received.
+ if (mHttpBufferArray)
+ {
+ mHttpBufferArray->release();
+ mHttpBufferArray = NULL;
+ }
- //abort.
+ // abort.
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = DONE;
return true;
}
+ const S32 append_size(mHttpBufferArray->size());
+ const S32 total_size(cur_size + append_size);
+ llassert_always(append_size == mRequestedSize);
+
if (mFormattedImage.isNull())
{
// For now, create formatted image based on extension
@@ -1305,49 +1245,52 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded.
{
- mFileSize = mBufferSize;
+ mFileSize = total_size;
}
else //the file size is unknown.
{
- mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded.
+ mFileSize = total_size + 1 ; //flag the file is not fully loaded.
}
- U8* buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), mBufferSize);
+ U8 * buffer = (U8 *) ALLOCATE_MEM(LLImageBase::getPrivatePool(), total_size);
if (cur_size > 0)
{
memcpy(buffer, mFormattedImage->getData(), cur_size);
}
- memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append
+ mHttpBufferArray->seek(0);
+ mHttpBufferArray->read((char *) buffer + cur_size, append_size);
+
// NOTE: setData releases current data and owns new data (buffer)
- mFormattedImage->setData(buffer, mBufferSize);
- // delete temp data
- FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); // Note: not 'buffer' (assigned in setData())
- mBuffer = NULL;
- mBufferSize = 0;
+ mFormattedImage->setData(buffer, total_size);
+
+ // Done with buffer array
+ mHttpBufferArray->release();
+ mHttpBufferArray = NULL;
+
mLoadedDiscard = mRequestedDiscard;
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = DECODE_IMAGE;
- if(mWriteToCacheState != NOT_WRITE)
+ if (mWriteToCacheState != NOT_WRITE)
{
mWriteToCacheState = SHOULD_WRITE ;
}
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
return false;
}
else
{
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return false;
}
}
if (mState == DECODE_IMAGE)
{
+ setPriority(0);
static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled");
- if(textures_decode_disabled)
+ if (textures_decode_disabled)
{
// for debug use, don't decode
mState = DONE;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
return true;
}
@@ -1355,7 +1298,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
{
// We aborted, don't decode
mState = DONE;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
return true;
}
@@ -1365,7 +1308,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
//abort, don't decode
mState = DONE;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
return true;
}
if (mLoadedDiscard < 0)
@@ -1374,10 +1317,10 @@ bool LLTextureFetchWorker::doWork(S32 param)
//abort, don't decode
mState = DONE;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
return true;
}
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+
mRawImage = NULL;
mAuxImage = NULL;
llassert_always(mFormattedImage.notNull());
@@ -1394,6 +1337,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == DECODE_IMAGE_UPDATE)
{
+ setPriority(0);
if (mDecoded)
{
if (mDecodedDiscard < 0)
@@ -1406,13 +1350,14 @@ bool LLTextureFetchWorker::doWork(S32 param)
llassert_always(mDecodeHandle == 0);
mFormattedImage = NULL;
++mRetryAttempt;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
mState = INIT;
return false;
}
else
{
// llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl;
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = DONE; // failed
}
}
@@ -1421,7 +1366,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
llassert_always(mRawImage.notNull());
LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard
<< " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = WRITE_TO_CACHE;
}
// fall through
@@ -1434,10 +1379,12 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == WRITE_TO_CACHE)
{
+ setPriority(0);
if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull())
{
// If we're in a local cache or we didn't actually receive any new data,
// or we failed to load anything, skip
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = DONE;
return false;
}
@@ -1457,6 +1404,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
U32 cache_priority = mWorkPriority;
mWritten = FALSE;
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = WAIT_ON_WRITE;
CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID);
mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority,
@@ -1467,8 +1415,10 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == WAIT_ON_WRITE)
{
+ setPriority(0);
if (writeToCacheComplete())
{
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
mState = DONE;
// fall through
}
@@ -1487,16 +1437,15 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == DONE)
{
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard)
{
// More data was requested, return to INIT
mState = INIT;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
return false;
}
else
{
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return true;
}
}
@@ -1504,6 +1453,71 @@ bool LLTextureFetchWorker::doWork(S32 param)
return false;
}
+// virtual
+void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)
+{
+ static LLCachedControl<bool> log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog");
+ static LLCachedControl<bool> log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator");
+ static LLCachedControl<bool> log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ;
+
+ mHttpActive = false;
+
+ if (log_to_viewer_log || log_to_sim)
+ {
+ U64 timeNow = LLTimer::getTotalTime();
+ mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime);
+ mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+ mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize);
+ mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset);
+ mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
+ }
+
+ bool success = true;
+ bool partial = false;
+ LLCore::HttpStatus status(response->getStatus());
+
+ lldebugs << "HTTP COMPLETE: " << mID
+ << " status: " << status.toULong() << " '" << status.toString() << "'"
+ << llendl;
+ unsigned int offset(0), length(0);
+ response->getRange(&offset, &length);
+// llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle
+// << " status: " << status.toULong() << " '" << status.toString() << "'"
+// << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize
+// << " offset: " << offset << " length: " << length
+// << llendl;
+
+ if (! status)
+ {
+ success = false;
+ std::string reason(status.toString());
+ setGetStatus(status, reason);
+ llwarns << "CURL GET FAILED, status:" << status.toULong() << " reason:" << reason << llendl;
+ }
+ else
+ {
+ static const LLCore::HttpStatus par_status(LLCore::HttpStatus(HTTP_PARTIAL_CONTENT, LLCore::HE_SUCCESS));
+
+ partial = (par_status == status);
+ }
+
+ S32 data_size = callbackHttpGet(response, partial, success);
+
+ if (log_texture_traffic && data_size > 0)
+ {
+ LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID);
+ if (tex)
+ {
+ gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ;
+ }
+ }
+
+ mFetcher->removeFromHTTPQueue(mID);
+
+ recordTextureDone(true);
+}
+
+
// Called from MAIN thread
void LLTextureFetchWorker::endWork(S32 param, bool aborted)
{
@@ -1537,6 +1551,14 @@ void LLTextureFetchWorker::finishWork(S32 param, bool completed)
bool LLTextureFetchWorker::deleteOK()
{
bool delete_ok = true;
+
+ if (mHttpActive)
+ {
+ // HTTP library has a pointer to this worker
+ // and will dereference it to do notification.
+ delete_ok = false;
+ }
+
// Allow any pending reads or writes to complete
if (mCacheReadHandle != LLTextureCache::nullHandle())
{
@@ -1642,9 +1664,8 @@ bool LLTextureFetchWorker::processSimulatorPackets()
//////////////////////////////////////////////////////////////////////////////
-S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer,
- bool partial, bool success)
+S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response,
+ bool partial, bool success)
{
S32 data_size = 0 ;
@@ -1664,15 +1685,22 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
if (success)
{
// get length of stream:
- data_size = buffer->countAfter(channels.in(), NULL);
-
+ LLCore::BufferArray * body(response->getBody());
+ data_size = body ? body->size() : 0;
+
LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL;
if (data_size > 0)
{
// *TODO: set the formatted image data here directly to avoid the copy
- mBuffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size);
- buffer->readAfter(channels.in(), NULL, mBuffer, data_size);
- mBufferSize += data_size;
+ // *FIXME: deal with actual offset and actual datasize, don't assume
+ // server gave exactly what was asked for.
+
+ llassert_always(NULL == mHttpBufferArray);
+
+ // Hold on to body for later copy
+ body->addRef();
+ mHttpBufferArray = body;
+
if (data_size < mRequestedSize && mRequestedDiscard == 0)
{
mHaveAllData = TRUE;
@@ -1684,7 +1712,6 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
mHaveAllData = TRUE;
llassert_always(mDecodeHandle == 0);
mFormattedImage = NULL; // discard any previous data we had
- mBufferSize = data_size;
}
}
else
@@ -1700,7 +1727,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
mRequestedSize = -1; // error
}
mLoaded = TRUE;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
return data_size ;
}
@@ -1729,7 +1756,7 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima
}
}
mLoaded = TRUE;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
}
void LLTextureFetchWorker::callbackCacheWrite(bool success)
@@ -1741,7 +1768,7 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success)
return;
}
mWritten = TRUE;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
}
//////////////////////////////////////////////////////////////////////////////
@@ -1779,7 +1806,7 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag
}
mDecoded = TRUE;
// llinfos << mID << " : DECODE COMPLETE " << llendl;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ setPriority(LLWorkerThread::PRIORITY_HIGH);
}
//////////////////////////////////////////////////////////////////////////////
@@ -1806,6 +1833,34 @@ bool LLTextureFetchWorker::writeToCacheComplete()
}
+void LLTextureFetchWorker::recordTextureStart(bool is_http)
+{
+ if (! mMetricsStartTime)
+ {
+ mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+ }
+ LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
+ is_http,
+ LLImageBase::TYPE_AVATAR_BAKE == mType);
+}
+
+
+void LLTextureFetchWorker::recordTextureDone(bool is_http)
+{
+ if (mMetricsStartTime)
+ {
+ LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
+ is_http,
+ LLImageBase::TYPE_AVATAR_BAKE == mType,
+ LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime);
+ mMetricsStartTime = 0;
+ }
+ LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
+ is_http,
+ LLImageBase::TYPE_AVATAR_BAKE == mType);
+}
+
+
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// public
@@ -1823,17 +1878,26 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
mTextureBandwidth(0),
mHTTPTextureBits(0),
mTotalHTTPRequests(0),
- mCurlGetRequest(NULL),
- mQAMode(qa_mode)
+ mQAMode(qa_mode),
+ mHttpRequest(NULL),
+ mHttpOptions(NULL),
+ mHttpHeaders(NULL)
{
mCurlPOSTRequestCount = 0;
mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
+
+ mHttpRequest = new LLCore::HttpRequest;
+ mHttpOptions = new LLCore::HttpOptions;
+ mHttpHeaders = new LLCore::HttpHeaders;
+ mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c");
}
LLTextureFetch::~LLTextureFetch()
{
- clearDeleteList() ;
+ cancelHttpRequests();
+
+ clearDeleteList();
while (! mCommands.empty())
{
@@ -1841,7 +1905,22 @@ LLTextureFetch::~LLTextureFetch()
mCommands.erase(mCommands.begin());
delete req;
}
-
+
+ if (mHttpOptions)
+ {
+ mHttpOptions->release();
+ mHttpOptions = NULL;
+ }
+
+ if (mHttpHeaders)
+ {
+ mHttpHeaders->release();
+ mHttpHeaders = NULL;
+ }
+
+ delete mHttpRequest;
+ mHttpRequest = NULL;
+
// ~LLQueuedThread() called here
}
@@ -1984,6 +2063,28 @@ void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size)
mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits
}
+void LLTextureFetch::cancelHttpRequests()
+{
+ for (queue_t::iterator iter(mHTTPTextureQueue.begin());
+ mHTTPTextureQueue.end() != iter;
+ ++iter)
+ {
+ LLTextureFetchWorker* worker = getWorker(*iter);
+ if (worker && worker->mHttpActive)
+ {
+ mHttpRequest->requestCancel(worker->mHttpHandle, NULL);
+ }
+ }
+
+ // *FIXME: Do this better with less time wasting.
+ int tries(10);
+ while (! mHTTPTextureQueue.empty() && --tries)
+ {
+ mHttpRequest->update(100);
+ ms_sleep(100);
+ }
+}
+
void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel)
{
lockQueue() ;
@@ -2194,11 +2295,21 @@ void LLTextureFetch::commonUpdate()
cmdDoWork();
// Update Curl on same thread as mCurlGetRequest was constructed
- S32 processed = mCurlGetRequest->process();
+ LLCore::HttpStatus status = mHttpRequest->update(200);
+ if (! status)
+ {
+ LL_INFOS_ONCE("Texture") << "Problem during HTTP servicing. Reason: "
+ << status.toString()
+ << LL_ENDL;
+ }
+
+#if 0
+ // *FIXME: maybe implement this another way...
if (processed > 0)
{
lldebugs << "processed: " << processed << " messages." << llendl;
}
+#endif
}
@@ -2256,22 +2367,22 @@ void LLTextureFetch::shutDownImageDecodeThread()
// WORKER THREAD
void LLTextureFetch::startThread()
{
- // Construct mCurlGetRequest from Worker Thread
- mCurlGetRequest = new LLCurlRequest();
}
// WORKER THREAD
+//
+// This detaches the texture fetch thread from the LLCore
+// HTTP library but doesn't stop the thread running in that
+// library...
void LLTextureFetch::endThread()
{
- // Destroy mCurlGetRequest from Worker Thread
- delete mCurlGetRequest;
- mCurlGetRequest = NULL;
+ cancelHttpRequests();
}
// WORKER THREAD
void LLTextureFetch::threadedUpdate()
{
- llassert_always(mCurlGetRequest);
+ llassert_always(mHttpRequest);
// Limit update frequency
const F32 PROCESS_TIME = 0.05f;
@@ -2579,7 +2690,7 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8
llassert_always(totalbytes > 0);
llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize);
res = worker->insertPacket(0, data, data_size);
- worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+ worker->setPriority(LLWorkerThread::PRIORITY_HIGH);
worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
worker->unlockWorkMutex();
return res;
@@ -2623,7 +2734,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1
if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) ||
(worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK))
{
- worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+ worker->setPriority(LLWorkerThread::PRIORITY_HIGH);
worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
}
else
@@ -2730,6 +2841,14 @@ void LLTextureFetch::dump()
<< " STATE: " << worker->sStateDescs[worker->mState]
<< llendl;
}
+
+ llinfos << "LLTextureFetch ACTIVE_HTTP:" << llendl;
+ for (queue_t::const_iterator iter(mHTTPTextureQueue.begin());
+ mHTTPTextureQueue.end() != iter;
+ ++iter)
+ {
+ llinfos << " ID: " << (*iter) << llendl;
+ }
}
//////////////////////////////////////////////////////////////////////////////
@@ -2942,6 +3061,8 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
if (! mCapsURL.empty())
{
LLCurlRequest::headers_t headers;
+#if 0
+ // *FIXME: Going to need a post op after all...
fetcher->getCurlRequest().post(mCapsURL,
headers,
merged_llsd,
@@ -2950,6 +3071,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
report_sequence,
LLTextureFetch::svMetricsDataBreak,
reporting_started));
+#endif
}
else
{
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 35df7d816f..402b198246 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * 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
@@ -31,9 +31,11 @@
#include "llimage.h"
#include "lluuid.h"
#include "llworkerthread.h"
-#include "llcurl.h"
#include "lltextureinfo.h"
#include "llapr.h"
+#include "httprequest.h"
+#include "httpoptions.h"
+#include "httpheaders.h"
class LLViewerTexture;
class LLTextureFetchWorker;
@@ -98,7 +100,7 @@ public:
LLViewerAssetStats * main_stats);
void commandDataBreak();
- LLCurlRequest & getCurlRequest() { return *mCurlGetRequest; }
+ LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; }
bool isQAMode() const { return mQAMode; }
@@ -112,7 +114,8 @@ protected:
void addToHTTPQueue(const LLUUID& id);
void removeFromHTTPQueue(const LLUUID& id, S32 received_size = 0);
void removeRequest(LLTextureFetchWorker* worker, bool cancel);
-
+ void cancelHttpRequests();
+
// Overrides from the LLThread tree
bool runCondition();
@@ -166,7 +169,6 @@ private:
LLTextureCache* mTextureCache;
LLImageDecodeThread* mImageDecodeThread;
- LLCurlRequest* mCurlGetRequest;
// Map of all requests by UUID
typedef std::map<LLUUID,LLTextureFetchWorker*> map_t;
@@ -203,6 +205,13 @@ private:
// use the LLCurl module's request counter as it isn't thread compatible.
// *NOTE: Don't mix Atomic and static, apr_initialize must be called first.
LLAtomic32<S32> mCurlPOSTRequestCount;
+
+ // Interfaces and objects into the core http library used
+ // to make our HTTP requests. These replace the various
+ // LLCurl interfaces used in the past.
+ LLCore::HttpRequest * mHttpRequest;
+ LLCore::HttpOptions * mHttpOptions;
+ LLCore::HttpHeaders * mHttpHeaders;
public:
// A probabilistically-correct indicator that the current