summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/edit-me-to-trigger-new-build.txt2
-rw-r--r--indra/llcorehttp/_httplibcurl.cpp17
-rw-r--r--indra/llcorehttp/_httpoprequest.cpp14
-rw-r--r--indra/llcorehttp/examples/http_texture_load.cpp4
-rw-r--r--indra/llcorehttp/httpheaders.cpp141
-rw-r--r--indra/llcorehttp/httpheaders.h112
-rw-r--r--indra/llcorehttp/tests/test_httpheaders.hpp345
-rw-r--r--indra/llcorehttp/tests/test_httprequest.hpp563
-rwxr-xr-x[-rw-r--r--]indra/llmessage/CMakeLists.txt0
-rwxr-xr-x[-rw-r--r--]indra/llmessage/llhttpconstants.cpp36
-rwxr-xr-x[-rw-r--r--]indra/newview/CMakeLists.txt5
-rwxr-xr-xindra/newview/app_settings/settings.xml11
-rw-r--r--[-rwxr-xr-x]indra/newview/llagentwearables.cpp9
-rwxr-xr-xindra/newview/llappearancemgr.cpp531
-rwxr-xr-xindra/newview/llappearancemgr.h50
-rw-r--r--indra/newview/llfloatergesture.cpp3
-rw-r--r--indra/newview/llfloatersidepanelcontainer.cpp20
-rw-r--r--indra/newview/llfloatersidepanelcontainer.h2
-rwxr-xr-x[-rw-r--r--]indra/newview/llgesturemgr.cpp13
-rwxr-xr-xindra/newview/llhttpretrypolicy.cpp136
-rwxr-xr-xindra/newview/llhttpretrypolicy.h93
-rw-r--r--indra/newview/llinspectavatar.cpp5
-rwxr-xr-xindra/newview/llinventorymodel.cpp101
-rwxr-xr-x[-rw-r--r--]indra/newview/llinventorymodel.h6
-rwxr-xr-x[-rw-r--r--]indra/newview/llpaneleditwearable.cpp23
-rw-r--r--indra/newview/llpaneloutfitedit.cpp4
-rwxr-xr-x[-rw-r--r--]indra/newview/llpaneloutfitsinventory.cpp3
-rw-r--r--indra/newview/llsidepanelappearance.cpp90
-rw-r--r--indra/newview/llsidepanelappearance.h16
-rw-r--r--indra/newview/llspeakers.cpp5
-rwxr-xr-xindra/newview/lltexturefetch.cpp175
-rwxr-xr-x[-rw-r--r--]indra/newview/lltexturefetch.h9
-rwxr-xr-x[-rw-r--r--]indra/newview/llviewerinventory.cpp101
-rwxr-xr-x[-rw-r--r--]indra/newview/llviewerinventory.h6
-rw-r--r--indra/newview/llviewerregion.cpp1
-rw-r--r--[-rwxr-xr-x]indra/newview/llviewertexlayer.cpp0
-rwxr-xr-x[-rw-r--r--]indra/newview/llviewertexture.cpp62
-rwxr-xr-x[-rw-r--r--]indra/newview/llviewertexture.h12
-rwxr-xr-xindra/newview/llvoavatar.cpp12
-rwxr-xr-xindra/newview/llvoavatar.h2
-rwxr-xr-xindra/newview/llvoavatarself.cpp2
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml16
-rwxr-xr-xindra/newview/tests/llhttpretrypolicy_test.cpp321
-rwxr-xr-x[-rw-r--r--]indra/test/lltut.h10
44 files changed, 2393 insertions, 696 deletions
diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt
index 0f6a8b8a1d..48e8b566a6 100644
--- a/indra/edit-me-to-trigger-new-build.txt
+++ b/indra/edit-me-to-trigger-new-build.txt
@@ -1 +1 @@
-Wed Nov 7 00:25:19 UTC 2012
+Mon Apr 15 14:35:39 EDT 2013
diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp
index 9ea831a771..d49f615ac4 100644
--- a/indra/llcorehttp/_httplibcurl.cpp
+++ b/indra/llcorehttp/_httplibcurl.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, 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
@@ -359,12 +359,17 @@ int HttpLibcurl::getActiveCountInClass(int policy_class) 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;
- ++it)
+ const HttpHeaders::const_iterator end(headers->end());
+ for (HttpHeaders::const_iterator it(headers->begin()); end != it; ++it)
{
- slist = curl_slist_append(slist, (*it).c_str());
+ static const char sep[] = ": ";
+ std::string header;
+ header.reserve((*it).first.size() + (*it).second.size() + sizeof(sep));
+ header.append((*it).first);
+ header.append(sep);
+ header.append((*it).second);
+
+ slist = curl_slist_append(slist, header.c_str());
}
return slist;
}
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index 74fc5c393a..95e0f72c0b 100644
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, 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
@@ -610,7 +610,8 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
const size_t hdr_size(size * nmemb);
const char * hdr_data(static_cast<const char *>(data)); // Not null terminated
-
+ bool is_header(true);
+
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
@@ -621,8 +622,9 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
op->mStatus = HttpStatus();
if (op->mReplyHeaders)
{
- op->mReplyHeaders->mHeaders.clear();
+ op->mReplyHeaders->clear();
}
+ is_header = false;
}
// Nothing in here wants a final CR/LF combination. Remove
@@ -637,18 +639,18 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
}
// Save header if caller wants them in the response
- if (op->mProcFlags & PF_SAVE_HEADERS)
+ if (is_header && op->mProcFlags & PF_SAVE_HEADERS)
{
// Save headers in response
if (! op->mReplyHeaders)
{
op->mReplyHeaders = new HttpHeaders;
}
- op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_hdr_size));
+ op->mReplyHeaders->appendNormal(hdr_data, wanted_hdr_size);
}
// Detect and parse 'Content-Range' headers
- if (op->mProcFlags & PF_SCAN_RANGE_HEADER)
+ if (is_header && op->mProcFlags & PF_SCAN_RANGE_HEADER)
{
char hdr_buffer[128]; // Enough for a reasonable header
size_t frag_size((std::min)(wanted_hdr_size, sizeof(hdr_buffer) - 1));
diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp
index 40ad4f047d..909dc5b0cb 100644
--- a/indra/llcorehttp/examples/http_texture_load.cpp
+++ b/indra/llcorehttp/examples/http_texture_load.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, 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
@@ -328,7 +328,7 @@ WorkingSet::WorkingSet()
mTextures.reserve(30000);
mHeaders = new LLCore::HttpHeaders;
- mHeaders->mHeaders.push_back("Accept: image/x-j2c");
+ mHeaders->append("Accept", "image/x-j2c");
}
diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp
index 2832696271..23ebea361c 100644
--- a/indra/llcorehttp/httpheaders.cpp
+++ b/indra/llcorehttp/httpheaders.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, 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
@@ -26,6 +26,8 @@
#include "httpheaders.h"
+#include "llstring.h"
+
namespace LLCore
{
@@ -40,5 +42,142 @@ HttpHeaders::~HttpHeaders()
{}
+void
+HttpHeaders::clear()
+{
+ mHeaders.clear();
+}
+
+
+void HttpHeaders::append(const std::string & name, const std::string & value)
+{
+ mHeaders.push_back(value_type(name, value));
+}
+
+
+void HttpHeaders::append(const char * name, const char * value)
+{
+ mHeaders.push_back(value_type(name, value));
+}
+
+
+void HttpHeaders::appendNormal(const char * header, size_t size)
+{
+ std::string name;
+ std::string value;
+
+ int col_pos(0);
+ for (; col_pos < size; ++col_pos)
+ {
+ if (':' == header[col_pos])
+ break;
+ }
+
+ if (col_pos < size)
+ {
+ // Looks like a header, split it and normalize.
+ // Name is everything before the colon, may be zero-length.
+ name.assign(header, col_pos);
+
+ // Value is everything after the colon, may also be zero-length.
+ const size_t val_len(size - col_pos - 1);
+ if (val_len)
+ {
+ value.assign(header + col_pos + 1, val_len);
+ }
+
+ // Clean the strings
+ LLStringUtil::toLower(name);
+ LLStringUtil::trim(name);
+ LLStringUtil::trimHead(value);
+ }
+ else
+ {
+ // Uncertain what this is, we'll pack it as
+ // a name without a value. Won't clean as we don't
+ // know what it is...
+ name.assign(header, size);
+ }
+
+ mHeaders.push_back(value_type(name, value));
+}
+
+
+// Find from end to simulate a tradition of using single-valued
+// std::map for this in the past.
+const std::string * HttpHeaders::find(const char * name) const
+{
+ const_reverse_iterator iend(rend());
+ for (const_reverse_iterator iter(rbegin()); iend != iter; ++iter)
+ {
+ if ((*iter).first == name)
+ {
+ return &(*iter).second;
+ }
+ }
+ return NULL;
+}
+
+
+// Standard Iterators
+HttpHeaders::iterator HttpHeaders::begin()
+{
+ return mHeaders.begin();
+}
+
+
+HttpHeaders::const_iterator HttpHeaders::begin() const
+{
+ return mHeaders.begin();
+}
+
+
+HttpHeaders::iterator HttpHeaders::end()
+{
+ return mHeaders.end();
+}
+
+
+HttpHeaders::const_iterator HttpHeaders::end() const
+{
+ return mHeaders.end();
+}
+
+
+// Standard Reverse Iterators
+HttpHeaders::reverse_iterator HttpHeaders::rbegin()
+{
+ return mHeaders.rbegin();
+}
+
+
+HttpHeaders::const_reverse_iterator HttpHeaders::rbegin() const
+{
+ return mHeaders.rbegin();
+}
+
+
+HttpHeaders::reverse_iterator HttpHeaders::rend()
+{
+ return mHeaders.rend();
+}
+
+
+HttpHeaders::const_reverse_iterator HttpHeaders::rend() const
+{
+ return mHeaders.rend();
+}
+
+
+// Return the raw container to the caller.
+//
+// To be used FOR UNIT TESTS ONLY.
+//
+HttpHeaders::container_t & HttpHeaders::getContainerTESTONLY()
+{
+ return mHeaders;
+}
+
+
} // end namespace LLCore
diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h
index 3449daa3a1..f70cd898f3 100644
--- a/indra/llcorehttp/httpheaders.h
+++ b/indra/llcorehttp/httpheaders.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, 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
@@ -43,13 +43,26 @@ namespace LLCore
/// caller has asked that headers be returned (not the default
/// option).
///
-/// @note
-/// This is a minimally-functional placeholder at the moment
-/// to fill out the class hierarchy. The final class will be
-/// something else, probably more pair-oriented. It's also
-/// an area where shared values are desirable so refcounting is
-/// already specced and a copy-on-write scheme imagined.
-/// Expect changes here.
+/// Class is mostly a thin wrapper around a vector of pairs
+/// of strings. Methods provided are few and intended to
+/// reflect actual use patterns. These include:
+/// - Clearing the list
+/// - Appending a name/value pair to the vector
+/// - Processing a raw byte string into a normalized name/value
+/// pair and appending the result.
+/// - Simple case-sensitive find-last-by-name search
+/// - Forward and reverse iterators over all pairs
+///
+/// Container is ordered and multi-valued. Headers are
+/// written in the order in which they are appended and
+/// are stored in the order in which they're received from
+/// the wire. The same header may appear two or more times
+/// in any container. Searches using the simple find()
+/// interface will find only the last occurrence (somewhat
+/// simulates the use of std::map). Fuller searches require
+/// the use of an iterator. Headers received from the wire
+/// are only returned from the last request when redirections
+/// are involved.
///
/// Threading: Not intrinsically thread-safe. It *is* expected
/// that callers will build these objects and then share them
@@ -64,6 +77,16 @@ namespace LLCore
class HttpHeaders : public LLCoreInt::RefCounted
{
public:
+ typedef std::pair<std::string, std::string> header_t;
+ typedef std::vector<header_t> container_t;
+ typedef container_t::iterator iterator;
+ typedef container_t::const_iterator const_iterator;
+ typedef container_t::reverse_iterator reverse_iterator;
+ typedef container_t::const_reverse_iterator const_reverse_iterator;
+ typedef container_t::value_type value_type;
+ typedef container_t::size_type size_type;
+
+public:
/// @post In addition to the instance, caller has a refcount
/// to the instance. A call to @see release() will destroy
/// the instance.
@@ -76,7 +99,78 @@ protected:
void operator=(const HttpHeaders &); // Not defined
public:
- typedef std::vector<std::string> container_t;
+ // Empty the list of headers.
+ void clear();
+
+ // Append a name/value pair supplied as either std::strings
+ // or NUL-terminated char * to the header list. No normalization
+ // is performed on the strings. No conformance test is
+ // performed (names may contain spaces, colons, etc.).
+ //
+ void append(const std::string & name, const std::string & value);
+ void append(const char * name, const char * value);
+
+ // Extract a name/value pair from a raw byte array using
+ // the first colon character as a separator. Input string
+ // does not need to be NUL-terminated. Resulting name/value
+ // pair is appended to the header list.
+ //
+ // Normalization is performed on the name/value pair as
+ // follows:
+ // - name is lower-cased according to mostly ASCII rules
+ // - name is left- and right-trimmed of spaces and tabs
+ // - value is left-trimmed of spaces and tabs
+ // - either or both of name and value may be zero-length
+ //
+ // By convention, headers read from the wire will be normalized
+ // in this fashion prior to delivery to any HttpHandler code.
+ // Headers to be written to the wire are left as appended to
+ // the list.
+ void appendNormal(const char * header, size_t size);
+
+ // Perform a simple, case-sensitive search of the header list
+ // returning a pointer to the value of the last matching header
+ // in the header list. If none is found, a NULL pointer is returned.
+ //
+ // Any pointer returned references objects in the container itself
+ // and will have the same lifetime as this class. If you want
+ // the value beyond the lifetime of this instance, make a copy.
+ //
+ // @arg name C-style string giving the name of a header
+ // to search. The comparison is case-sensitive
+ // though list entries may have been normalized
+ // to lower-case.
+ //
+ // @return NULL if the header wasn't found otherwise
+ // a pointer to a std::string in the container.
+ // Pointer is valid only for the lifetime of
+ // the container or until container is modifed.
+ //
+ const std::string * find(const char * name) const;
+
+ // Count of headers currently in the list.
+ size_type size() const
+ {
+ return mHeaders.size();
+ }
+
+ // Standard std::vector-based forward iterators.
+ iterator begin();
+ const_iterator begin() const;
+ iterator end();
+ const_iterator end() const;
+
+ // Standard std::vector-based reverse iterators.
+ reverse_iterator rbegin();
+ const_reverse_iterator rbegin() const;
+ reverse_iterator rend();
+ const_reverse_iterator rend() const;
+
+public:
+ // For unit tests only - not a public API
+ container_t & getContainerTESTONLY();
+
+protected:
container_t mHeaders;
}; // end class HttpHeaders
diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp
index ce0d19b058..668c36dc66 100644
--- a/indra/llcorehttp/tests/test_httpheaders.hpp
+++ b/indra/llcorehttp/tests/test_httpheaders.hpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, 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
@@ -36,7 +36,6 @@
using namespace LLCoreInt;
-
namespace tut
{
@@ -63,7 +62,7 @@ void HttpHeadersTestObjectType::test<1>()
HttpHeaders * headers = new HttpHeaders();
ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1);
ensure("Memory being used", mMemTotal < GetMemTotal());
- ensure("Nothing in headers", 0 == headers->mHeaders.size());
+ ensure("Nothing in headers", 0 == headers->size());
// release the implicit reference, causing the object to be released
headers->release();
@@ -85,14 +84,340 @@ void HttpHeadersTestObjectType::test<2>()
{
// Append a few strings
- std::string str1("Pragma:");
- headers->mHeaders.push_back(str1);
- std::string str2("Accept: application/json");
- headers->mHeaders.push_back(str2);
+ std::string str1n("Pragma");
+ std::string str1v("");
+ headers->append(str1n, str1v);
+ std::string str2n("Accept");
+ std::string str2v("application/json");
+ headers->append(str2n, str2v);
+
+ ensure("Headers retained", 2 == headers->size());
+ HttpHeaders::container_t & c(headers->getContainerTESTONLY());
+
+ ensure("First name is first name", c[0].first == str1n);
+ ensure("First value is first value", c[0].second == str1v);
+ ensure("Second name is second name", c[1].first == str2n);
+ ensure("Second value is second value", c[1].second == str2v);
+ }
+
+ // release the implicit reference, causing the object to be released
+ headers->release();
+
+ // make sure we didn't leak any memory
+ ensure(mMemTotal == GetMemTotal());
+}
+
+template <> template <>
+void HttpHeadersTestObjectType::test<3>()
+{
+ set_test_name("HttpHeaders basic find");
+
+ // record the total amount of dynamically allocated memory
+ mMemTotal = GetMemTotal();
+
+ // create a new ref counted object with an implicit reference
+ HttpHeaders * headers = new HttpHeaders();
+
+ {
+ // Append a few strings
+ std::string str1n("Uno");
+ std::string str1v("1");
+ headers->append(str1n, str1v);
+ std::string str2n("doS");
+ std::string str2v("2-2-2-2");
+ headers->append(str2n, str2v);
+ std::string str3n("TRES");
+ std::string str3v("trois gymnopedie");
+ headers->append(str3n, str3v);
+
+ ensure("Headers retained", 3 == headers->size());
+
+ const std::string * result(NULL);
+
+ // Find a header
+ result = headers->find("TRES");
+ ensure("Found the last item", result != NULL);
+ ensure("Last item is a nice", result != NULL && str3v == *result);
+
+ // appends above are raw and find is case sensitive
+ result = headers->find("TReS");
+ ensure("Last item not found due to case", result == NULL);
+
+ result = headers->find("TRE");
+ ensure("Last item not found due to prefixing (1)", result == NULL);
+
+ result = headers->find("TRESS");
+ ensure("Last item not found due to prefixing (2)", result == NULL);
+ }
+
+ // release the implicit reference, causing the object to be released
+ headers->release();
+
+ // make sure we didn't leak any memory
+ ensure(mMemTotal == GetMemTotal());
+}
+
+template <> template <>
+void HttpHeadersTestObjectType::test<4>()
+{
+ set_test_name("HttpHeaders normalized header entry");
+
+ // record the total amount of dynamically allocated memory
+ mMemTotal = GetMemTotal();
+
+ // create a new ref counted object with an implicit reference
+ HttpHeaders * headers = new HttpHeaders();
+
+ {
+ static char line1[] = " AcCePT : image/yourfacehere";
+ static char line1v[] = "image/yourfacehere";
+ headers->appendNormal(line1, sizeof(line1) - 1);
+
+ ensure("First append worked in some fashion", 1 == headers->size());
+
+ const std::string * result(NULL);
+
+ // Find a header
+ result = headers->find("accept");
+ ensure("Found 'accept'", result != NULL);
+ ensure("accept value has face", result != NULL && *result == line1v);
+
+ // Left-clean on value
+ static char line2[] = " next : \t\tlinejunk \t";
+ headers->appendNormal(line2, sizeof(line2) - 1);
+ ensure("Second append worked", 2 == headers->size());
+ result = headers->find("next");
+ ensure("Found 'next'", result != NULL);
+ ensure("next value is left-clean", result != NULL &&
+ *result == "linejunk \t");
+
+ // First value unmolested
+ result = headers->find("accept");
+ ensure("Found 'accept' again", result != NULL);
+ ensure("accept value has face", result != NULL && *result == line1v);
+
+ // Colons in value are okay
+ static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
+ static char line3v[] = ":plop:-neuf-=vleem=";
+ headers->appendNormal(line3, sizeof(line3) - 1);
+ ensure("Third append worked", 3 == headers->size());
+ result = headers->find("fancy-pants");
+ ensure("Found 'fancy-pants'", result != NULL);
+ ensure("fancy-pants value has colons", result != NULL && *result == line3v);
+
+ // Zero-length value
+ static char line4[] = "all-talk-no-walk:";
+ headers->appendNormal(line4, sizeof(line4) - 1);
+ ensure("Fourth append worked", 4 == headers->size());
+ result = headers->find("all-talk-no-walk");
+ ensure("Found 'all-talk'", result != NULL);
+ ensure("al-talk value is zero-length", result != NULL && result->size() == 0);
+
+ // Zero-length name
+ static char line5[] = ":all-talk-no-walk";
+ static char line5v[] = "all-talk-no-walk";
+ headers->appendNormal(line5, sizeof(line5) - 1);
+ ensure("Fifth append worked", 5 == headers->size());
+ result = headers->find("");
+ ensure("Found no-name", result != NULL);
+ ensure("no-name value is something", result != NULL && *result == line5v);
+
+ // Lone colon is still something
+ headers->clear();
+ static char line6[] = " :";
+ headers->appendNormal(line6, sizeof(line6) - 1);
+ ensure("Sixth append worked", 1 == headers->size());
+ result = headers->find("");
+ ensure("Found 2nd no-name", result != NULL);
+ ensure("2nd no-name value is nothing", result != NULL && result->size() == 0);
+
+ // Line without colons is taken as-is and unstripped in name
+ static char line7[] = " \toskdgioasdghaosdghoowg28342908tg8902hg0hwedfhqew890v7qh0wdebv78q0wdevbhq>?M>BNM<ZV>?NZ? \t";
+ headers->appendNormal(line7, sizeof(line7) - 1);
+ ensure("Seventh append worked", 2 == headers->size());
+ result = headers->find(line7);
+ ensure("Found whatsit line", result != NULL);
+ ensure("Whatsit line has no value", result != NULL && result->size() == 0);
+
+ // Normaling interface heeds the byte count, doesn't look for NUL-terminator
+ static char line8[] = "binary:ignorestuffontheendofthis";
+ headers->appendNormal(line8, 13);
+ ensure("Eighth append worked", 3 == headers->size());
+ result = headers->find("binary");
+ ensure("Found 'binary'", result != NULL);
+ ensure("binary value was limited to 'ignore'", result != NULL &&
+ *result == "ignore");
+
+ }
- ensure("Headers retained", 2 == headers->mHeaders.size());
- ensure("First is first", headers->mHeaders[0] == str1);
- ensure("Second is second", headers->mHeaders[1] == str2);
+ // release the implicit reference, causing the object to be released
+ headers->release();
+
+ // make sure we didn't leak any memory
+ ensure(mMemTotal == GetMemTotal());
+}
+
+// Verify forward iterator finds everything as expected
+template <> template <>
+void HttpHeadersTestObjectType::test<5>()
+{
+ set_test_name("HttpHeaders iterator tests");
+
+ // record the total amount of dynamically allocated memory
+ mMemTotal = GetMemTotal();
+
+ // create a new ref counted object with an implicit reference
+ HttpHeaders * headers = new HttpHeaders();
+
+ HttpHeaders::iterator end(headers->end()), begin(headers->begin());
+ ensure("Empty container has equal begin/end const iterators", end == begin);
+ HttpHeaders::const_iterator cend(headers->end()), cbegin(headers->begin());
+ ensure("Empty container has equal rbegin/rend const iterators", cend == cbegin);
+
+ ensure("Empty container has equal begin/end iterators", headers->end() == headers->begin());
+
+ {
+ static char line1[] = " AcCePT : image/yourfacehere";
+ static char line1v[] = "image/yourfacehere";
+ headers->appendNormal(line1, sizeof(line1) - 1);
+
+ static char line2[] = " next : \t\tlinejunk \t";
+ static char line2v[] = "linejunk \t";
+ headers->appendNormal(line2, sizeof(line2) - 1);
+
+ static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
+ static char line3v[] = ":plop:-neuf-=vleem=";
+ headers->appendNormal(line3, sizeof(line3) - 1);
+
+ static char line4[] = "all-talk-no-walk:";
+ static char line4v[] = "";
+ headers->appendNormal(line4, sizeof(line4) - 1);
+
+ static char line5[] = ":all-talk-no-walk";
+ static char line5v[] = "all-talk-no-walk";
+ headers->appendNormal(line5, sizeof(line5) - 1);
+
+ static char line6[] = " :";
+ static char line6v[] = "";
+ headers->appendNormal(line6, sizeof(line6) - 1);
+
+ ensure("All entries accounted for", 6 == headers->size());
+
+ static char * values[] = {
+ line1v,
+ line2v,
+ line3v,
+ line4v,
+ line5v,
+ line6v
+ };
+
+ int i(0);
+ HttpHeaders::const_iterator cend(headers->end());
+ for (HttpHeaders::const_iterator it(headers->begin());
+ cend != it;
+ ++it, ++i)
+ {
+ std::ostringstream str;
+ str << "Const Iterator value # " << i << " was " << values[i];
+ ensure(str.str(), (*it).second == values[i]);
+ }
+
+ // Rewind, do non-consts
+ i = 0;
+ HttpHeaders::iterator end(headers->end());
+ for (HttpHeaders::iterator it(headers->begin());
+ end != it;
+ ++it, ++i)
+ {
+ std::ostringstream str;
+ str << "Const Iterator value # " << i << " was " << values[i];
+ ensure(str.str(), (*it).second == values[i]);
+ }
+ }
+
+ // release the implicit reference, causing the object to be released
+ headers->release();
+
+ // make sure we didn't leak any memory
+ ensure(mMemTotal == GetMemTotal());
+}
+
+// Reverse iterators find everything as expected
+template <> template <>
+void HttpHeadersTestObjectType::test<6>()
+{
+ set_test_name("HttpHeaders reverse iterator tests");
+
+ // record the total amount of dynamically allocated memory
+ mMemTotal = GetMemTotal();
+
+ // create a new ref counted object with an implicit reference
+ HttpHeaders * headers = new HttpHeaders();
+
+ HttpHeaders::reverse_iterator rend(headers->rend()), rbegin(headers->rbegin());
+ ensure("Empty container has equal rbegin/rend const iterators", rend == rbegin);
+ HttpHeaders::const_reverse_iterator crend(headers->rend()), crbegin(headers->rbegin());
+ ensure("Empty container has equal rbegin/rend const iterators", crend == crbegin);
+
+ {
+ static char line1[] = " AcCePT : image/yourfacehere";
+ static char line1v[] = "image/yourfacehere";
+ headers->appendNormal(line1, sizeof(line1) - 1);
+
+ static char line2[] = " next : \t\tlinejunk \t";
+ static char line2v[] = "linejunk \t";
+ headers->appendNormal(line2, sizeof(line2) - 1);
+
+ static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
+ static char line3v[] = ":plop:-neuf-=vleem=";
+ headers->appendNormal(line3, sizeof(line3) - 1);
+
+ static char line4[] = "all-talk-no-walk:";
+ static char line4v[] = "";
+ headers->appendNormal(line4, sizeof(line4) - 1);
+
+ static char line5[] = ":all-talk-no-walk";
+ static char line5v[] = "all-talk-no-walk";
+ headers->appendNormal(line5, sizeof(line5) - 1);
+
+ static char line6[] = " :";
+ static char line6v[] = "";
+ headers->appendNormal(line6, sizeof(line6) - 1);
+
+ ensure("All entries accounted for", 6 == headers->size());
+
+ static char * values[] = {
+ line6v,
+ line5v,
+ line4v,
+ line3v,
+ line2v,
+ line1v
+ };
+
+ int i(0);
+ HttpHeaders::const_reverse_iterator cend(headers->rend());
+ for (HttpHeaders::const_reverse_iterator it(headers->rbegin());
+ cend != it;
+ ++it, ++i)
+ {
+ std::ostringstream str;
+ str << "Const Iterator value # " << i << " was " << values[i];
+ ensure(str.str(), (*it).second == values[i]);
+ }
+
+ // Rewind, do non-consts
+ i = 0;
+ HttpHeaders::reverse_iterator end(headers->rend());
+ for (HttpHeaders::reverse_iterator it(headers->rbegin());
+ end != it;
+ ++it, ++i)
+ {
+ std::ostringstream str;
+ str << "Iterator value # " << i << " was " << values[i];
+ ensure(str.str(), (*it).second == values[i]);
+ }
}
// release the implicit reference, causing the object to be released
diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp
index e5488cf941..27d65f171e 100644
--- a/indra/llcorehttp/tests/test_httprequest.hpp
+++ b/indra/llcorehttp/tests/test_httprequest.hpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, 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
@@ -60,6 +60,8 @@ void usleep(unsigned long usec);
namespace tut
{
+typedef std::vector<std::pair<boost::regex, boost::regex> > regex_container_t;
+
struct HttpRequestTestData
{
// the test objects inherit from this so the member functions and variables
@@ -109,11 +111,17 @@ public:
for (int i(0); i < mHeadersRequired.size(); ++i)
{
bool found = false;
- for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin());
- header->mHeaders.end() != iter;
+ for (HttpHeaders::const_iterator iter(header->begin());
+ header->end() != iter;
++iter)
{
- if (boost::regex_match(*iter, mHeadersRequired[i]))
+ // std::cerr << "Header: " << (*iter).first
+ // << ": " << (*iter).second << std::endl;
+
+ if (boost::regex_match((*iter).first,
+ mHeadersRequired[i].first) &&
+ boost::regex_match((*iter).second,
+ mHeadersRequired[i].second))
{
found = true;
break;
@@ -129,11 +137,14 @@ public:
{
for (int i(0); i < mHeadersDisallowed.size(); ++i)
{
- for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin());
- header->mHeaders.end() != iter;
+ for (HttpHeaders::const_iterator iter(header->begin());
+ header->end() != iter;
++iter)
{
- if (boost::regex_match(*iter, mHeadersDisallowed[i]))
+ if (boost::regex_match((*iter).first,
+ mHeadersDisallowed[i].first) &&
+ boost::regex_match((*iter).second,
+ mHeadersDisallowed[i].second))
{
std::ostringstream str;
str << "Disallowed header # " << i << " not found in response";
@@ -159,8 +170,8 @@ public:
std::string mName;
HttpHandle mExpectHandle;
std::string mCheckContentType;
- std::vector<boost::regex> mHeadersRequired;
- std::vector<boost::regex> mHeadersDisallowed;
+ regex_container_t mHeadersRequired;
+ regex_container_t mHeadersDisallowed;
};
typedef test_group<HttpRequestTestData> HttpRequestTestGroupType;
@@ -1335,7 +1346,9 @@ void HttpRequestTestObjectType::test<13>()
// Issue a GET that succeeds
mStatus = HttpStatus(200);
- handler.mHeadersRequired.push_back(boost::regex("\\W*X-LL-Special:.*", boost::regex::icase));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(boost::regex("X-LL-Special", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
@@ -1702,18 +1715,54 @@ void HttpRequestTestObjectType::test<16>()
// Issue a GET that *can* connect
mStatus = HttpStatus(200);
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-connection", boost::regex::icase),
+ boost::regex("keep-alive", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept", boost::regex::icase),
+ boost::regex("\\*/\\*", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+ boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+ boost::regex("\\d+", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-host", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-cache-control", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-pragma", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-range", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-referer", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-type", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@@ -1735,23 +1784,60 @@ void HttpRequestTestObjectType::test<16>()
// Do a texture-style fetch
headers = new HttpHeaders;
- headers->mHeaders.push_back("Accept: image/x-j2c");
+ headers->append("Accept", "image/x-j2c");
mStatus = HttpStatus(200);
handler.mHeadersRequired.clear();
handler.mHeadersDisallowed.clear();
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*image/x-j2c", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-connection", boost::regex::icase),
+ boost::regex("keep-alive", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept", boost::regex::icase),
+ boost::regex("image/x-j2c", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+ boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+ boost::regex("\\d+", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-host", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("\\W*X-Reflect-range", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-cache-control", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-pragma", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-referer", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-type", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@@ -1892,20 +1978,63 @@ void HttpRequestTestObjectType::test<17>()
// Issue a default POST
mStatus = HttpStatus(200);
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-connection", boost::regex::icase),
+ boost::regex("keep-alive", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept", boost::regex::icase),
+ boost::regex("\\*/\\*", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+ boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+ boost::regex("\\d+", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-host", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-length", boost::regex::icase),
+ boost::regex("\\d+", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-type", boost::regex::icase),
+ boost::regex("application/x-www-form-urlencoded", boost::regex::icase)));
+
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-cache-control", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-pragma", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-range", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-referer", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-expect", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-transfer_encoding", boost::regex::icase),
+ boost::regex(".*chunked.*", boost::regex::icase)));
HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@@ -2052,20 +2181,64 @@ void HttpRequestTestObjectType::test<18>()
// Issue a default PUT
mStatus = HttpStatus(200);
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:.*", boost::regex::icase));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-connection", boost::regex::icase),
+ boost::regex("keep-alive", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept", boost::regex::icase),
+ boost::regex("\\*/\\*", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+ boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+ boost::regex("\\d+", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-host", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-length", boost::regex::icase),
+ boost::regex("\\d+", boost::regex::icase)));
+
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-cache-control", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-pragma", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-range", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-referer", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-expect", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+ boost::regex(".*chunked.*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-type", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+
HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@@ -2206,27 +2379,73 @@ void HttpRequestTestObjectType::test<19>()
// headers
headers = new HttpHeaders;
- headers->mHeaders.push_back("Keep-Alive: 120");
- headers->mHeaders.push_back("Accept-encoding: deflate");
- headers->mHeaders.push_back("Accept: text/plain");
+ headers->append("Keep-Alive", "120");
+ headers->append("Accept-encoding", "deflate");
+ headers->append("Accept", "text/plain");
// Issue a GET with modified headers
mStatus = HttpStatus(200);
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/plain", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*deflate", boost::regex::icase)); // close enough
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
- handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-connection", boost::regex::icase),
+ boost::regex("keep-alive", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept", boost::regex::icase),
+ boost::regex("text/plain", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+ boost::regex("deflate", boost::regex::icase))); // close enough
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+ boost::regex("120", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-host", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+ boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+ boost::regex("300", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept", boost::regex::icase),
+ boost::regex("\\*/\\*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-cache-control", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-pragma", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-range", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-referer", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-type", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@@ -2359,10 +2578,10 @@ void HttpRequestTestObjectType::test<20>()
// headers
headers = new HttpHeaders();
- headers->mHeaders.push_back("keep-Alive: 120");
- headers->mHeaders.push_back("Accept: text/html");
- headers->mHeaders.push_back("content-type: application/llsd+xml");
- headers->mHeaders.push_back("cache-control: no-store");
+ headers->append("keep-Alive", "120");
+ headers->append("Accept", "text/html");
+ headers->append("content-type", "application/llsd+xml");
+ headers->append("cache-control", "no-store");
// And a buffer array
const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>");
@@ -2371,23 +2590,76 @@ void HttpRequestTestObjectType::test<20>()
// Issue a default POST
mStatus = HttpStatus(200);
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/html", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("\\s*X-Reflect-cache-control:\\s*no-store", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-connection", boost::regex::icase),
+ boost::regex("keep-alive", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept", boost::regex::icase),
+ boost::regex("text/html", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+ boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+ boost::regex("120", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-host", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-length", boost::regex::icase),
+ boost::regex("\\d+", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-type", boost::regex::icase),
+ boost::regex("application/llsd\\+xml", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-cache-control", boost::regex::icase),
+ boost::regex("no-store", boost::regex::icase)));
+
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-type", boost::regex::icase),
+ boost::regex("application/x-www-form-urlencoded", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept", boost::regex::icase),
+ boost::regex("\\*/\\*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+ boost::regex("300", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-pragma", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-range", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-referer", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-expect", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+
HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
@@ -2529,9 +2801,9 @@ void HttpRequestTestObjectType::test<21>()
// headers
headers = new HttpHeaders;
- headers->mHeaders.push_back("content-type: text/plain");
- headers->mHeaders.push_back("content-type: text/html");
- headers->mHeaders.push_back("content-type: application/llsd+xml");
+ headers->append("content-type", "text/plain");
+ headers->append("content-type", "text/html");
+ headers->append("content-type", "application/llsd+xml");
// And a buffer array
const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>");
@@ -2540,22 +2812,71 @@ void HttpRequestTestObjectType::test<21>()
// Issue a default PUT
mStatus = HttpStatus(200);
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
- handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/plain", boost::regex::icase));
- handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/html", boost::regex::icase));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-connection", boost::regex::icase),
+ boost::regex("keep-alive", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept", boost::regex::icase),
+ boost::regex("\\*/\\*", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+ boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+ boost::regex("\\d+", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-host", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-length", boost::regex::icase),
+ boost::regex("\\d+", boost::regex::icase)));
+ handler.mHeadersRequired.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-type", boost::regex::icase),
+ boost::regex("application/llsd\\+xml", boost::regex::icase)));
+
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-cache-control", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-pragma", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-range", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-referer", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-expect", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+ boost::regex(".*", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-type", boost::regex::icase),
+ boost::regex("text/plain", boost::regex::icase)));
+ handler.mHeadersDisallowed.push_back(
+ regex_container_t::value_type(
+ boost::regex("X-Reflect-content-type", boost::regex::icase),
+ boost::regex("text/html", boost::regex::icase)));
HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt
index 6fa2669be6..6fa2669be6 100644..100755
--- a/indra/llmessage/CMakeLists.txt
+++ b/indra/llmessage/CMakeLists.txt
diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llmessage/llhttpconstants.cpp
index 1995fad1e5..016f1f1970 100644..100755
--- a/indra/llmessage/llhttpconstants.cpp
+++ b/indra/llmessage/llhttpconstants.cpp
@@ -2,31 +2,28 @@
* @file llhttpconstants.cpp
* @brief Implementation of the HTTP request / response constant lookups
*
- * $LicenseInfo:firstyear=2013&license=viewergpl$
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
*
* Copyright (c) 2013, Linden Research, Inc.
*
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2013, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * 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$
*/
@@ -220,7 +217,8 @@ bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_w
time_t date = curl_getdate(retry_after.c_str(), NULL );
if (-1 == date) return false;
- seconds_to_wait = (F32)date - (F32)LLTimer::getTotalSeconds();
+ seconds_to_wait = (F64)date - LLTimer::getTotalSeconds();
+
return true;
}
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 05736f6360..27dbe15005 100644..100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -299,6 +299,7 @@ set(viewer_SOURCE_FILES
llgroupmgr.cpp
llhints.cpp
llhomelocationresponder.cpp
+ llhttpretrypolicy.cpp
llhudeffect.cpp
llhudeffectbeam.cpp
llhudeffectlookat.cpp
@@ -877,6 +878,7 @@ set(viewer_HEADER_FILES
llgrouplist.h
llgroupmgr.h
llhints.h
+ llhttpretrypolicy.h
llhomelocationresponder.h
llhudeffect.h
llhudeffectbeam.h
@@ -2152,6 +2154,7 @@ if (LL_TESTS)
set(test_libs
${LLMESSAGE_LIBRARIES}
+ ${LLCOREHTTP_LIBRARIES}
${WINDOWS_LIBRARIES}
${LLVFS_LIBRARIES}
${LLMATH_LIBRARIES}
@@ -2197,6 +2200,8 @@ if (LL_TESTS)
"${test_libs}"
)
+ LL_ADD_INTEGRATION_TEST(llhttpretrypolicy "llhttpretrypolicy.cpp" "${test_libs}")
+
#ADD_VIEWER_BUILD_TEST(llmemoryview viewer)
#ADD_VIEWER_BUILD_TEST(llagentaccess viewer)
#ADD_VIEWER_BUILD_TEST(lltextureinfo viewer)
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index f66d8fca5b..18a33b3542 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -11130,6 +11130,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>TextureFetchFakeFailureRate</key>
+ <map>
+ <key>Comment</key>
+ <string>Simulate HTTP fetch failures for some server bake textures.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <integer>0.0</integer>
+ </map>
<key>TextureFetchSource</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index c88694ef76..b4c3e33e0e 100755..100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -69,7 +69,7 @@ void wear_and_edit_cb(const LLUUID& inv_item)
gAgentWearables.requestEditingWearable(inv_item);
// Wear it.
- LLAppearanceMgr::instance().wearItemOnAvatar(inv_item);
+ LLAppearanceMgr::instance().wearItemOnAvatar(inv_item,true);
}
///////////////////////////////////////////////////////////////////////////////
@@ -239,7 +239,7 @@ void LLAgentWearables::addWearableToAgentInventoryCallback::fire(const LLUUID& i
}
if (mTodo & CALL_RECOVERDONE)
{
- LLAppearanceMgr::instance().addCOFItemLink(inv_item,false);
+ LLAppearanceMgr::instance().addCOFItemLink(inv_item);
gAgentWearables.recoverMissingWearableDone();
}
/*
@@ -247,7 +247,7 @@ void LLAgentWearables::addWearableToAgentInventoryCallback::fire(const LLUUID& i
*/
if (mTodo & CALL_CREATESTANDARDDONE)
{
- LLAppearanceMgr::instance().addCOFItemLink(inv_item,false);
+ LLAppearanceMgr::instance().addCOFItemLink(inv_item);
gAgentWearables.createStandardWearablesDone(mType, mIndex);
}
if (mTodo & CALL_MAKENEWOUTFITDONE)
@@ -256,7 +256,8 @@ void LLAgentWearables::addWearableToAgentInventoryCallback::fire(const LLUUID& i
}
if (mTodo & CALL_WEARITEM)
{
- LLAppearanceMgr::instance().addCOFItemLink(inv_item, true, NULL, mDescription);
+ LLAppearanceMgr::instance().addCOFItemLink(inv_item,
+ new LLUpdateAppearanceAndEditWearableOnDestroy(inv_item), mDescription);
}
}
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index c2be472cbc..399cea676c 100755
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -52,6 +52,7 @@
#include "llwearablelist.h"
#include "llsdutil.h"
#include "llsdserialize.h"
+#include "llhttpretrypolicy.h"
#if LL_MSVC
// disable boost::lexical_cast warning
@@ -420,93 +421,25 @@ public:
}
};
-class LLCallAfterInventoryLinkMgr: public LLCallAfterInventoryBatchMgr
-{
-public:
- LLCallAfterInventoryLinkMgr(LLInventoryModel::item_array_t& src_items,
- const LLUUID& dst_cat_id,
- const std::string& phase_name,
- nullary_func_t on_completion_func,
- nullary_func_t on_failure_func = no_op,
- F32 retry_after = DEFAULT_RETRY_AFTER_INTERVAL,
- S32 max_retries = DEFAULT_MAX_RETRIES
- ):
- LLCallAfterInventoryBatchMgr(dst_cat_id, phase_name, on_completion_func, on_failure_func, retry_after, max_retries)
- {
- addItems(src_items);
- }
-
- virtual bool requestOperation(const LLUUID& item_id)
- {
- bool request_sent = false;
- LLViewerInventoryItem *item = gInventory.getItem(item_id);
- if (item)
- {
- if (item->getParentUUID() == mDstCatID)
- {
- LL_DEBUGS("Avatar") << "item " << item_id << " name " << item->getName() << " is already a child of " << mDstCatID << llendl;
- return false;
- }
- LL_DEBUGS("Avatar") << "linking item " << item_id << " name " << item->getName() << " to " << mDstCatID << llendl;
- // create an inventory item link.
- if (ll_frand() < gSavedSettings.getF32("InventoryDebugSimulateOpFailureRate"))
- {
- LL_DEBUGS("Avatar") << "simulating failure by not sending request for item " << item_id << llendl;
- return true;
- }
- link_inventory_item(gAgent.getID(),
- item->getLinkedUUID(),
- mDstCatID,
- item->getName(),
- item->getActualDescription(),
- LLAssetType::AT_LINK,
- new LLBoostFuncInventoryCallback(
- boost::bind(&LLCallAfterInventoryBatchMgr::onOp,this,item_id,_1,LLTimer())));
- return true;
- }
- else
- {
- // create a base outfit link if appropriate.
- LLViewerInventoryCategory *catp = gInventory.getCategory(item_id);
- if (!catp)
- {
- llwarns << "link request failed, id not found as inventory item or category " << item_id << llendl;
- return false;
- }
- const LLUUID cof = LLAppearanceMgr::instance().getCOF();
- std::string new_outfit_name = "";
-
- LLAppearanceMgr::instance().purgeBaseOutfitLink(cof);
-
- if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
- {
- if (ll_frand() < gSavedSettings.getF32("InventoryDebugSimulateOpFailureRate"))
- {
- LL_DEBUGS("Avatar") << "simulating failure by not sending request for item " << item_id << llendl;
- return true;
- }
- LL_DEBUGS("Avatar") << "linking folder " << item_id << " name " << catp->getName() << " to cof " << cof << llendl;
- link_inventory_item(gAgent.getID(), item_id, cof, catp->getName(), "",
- LLAssetType::AT_LINK_FOLDER,
- new LLBoostFuncInventoryCallback(
- boost::bind(&LLCallAfterInventoryBatchMgr::onOp,this,item_id,_1,LLTimer())));
- new_outfit_name = catp->getName();
- request_sent = true;
- }
-
- LLAppearanceMgr::instance().updatePanelOutfitName(new_outfit_name);
- }
- return request_sent;
- }
-};
-
-LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering):
+LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering,
+ bool enforce_item_restrictions):
mFireCount(0),
- mUpdateBaseOrder(update_base_outfit_ordering)
+ mUpdateBaseOrder(update_base_outfit_ordering),
+ mEnforceItemRestrictions(enforce_item_restrictions)
{
selfStartPhase("update_appearance_on_destroy");
}
+void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item)
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(inv_item);
+ const std::string item_name = item ? item->getName() : "ITEM NOT FOUND";
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LL_DEBUGS("Avatar") << self_av_string() << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << LL_ENDL;
+#endif
+ mFireCount++;
+}
+
LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
{
if (!LLApp::isExiting())
@@ -516,20 +449,39 @@ LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
selfStopPhase("update_appearance_on_destroy");
- LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder);
+ LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder, mEnforceItemRestrictions);
}
}
-void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item)
+LLUpdateAppearanceAndEditWearableOnDestroy::LLUpdateAppearanceAndEditWearableOnDestroy(const LLUUID& item_id):
+ mItemID(item_id)
{
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(inv_item);
- const std::string item_name = item ? item->getName() : "ITEM NOT FOUND";
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- LL_DEBUGS("Avatar") << self_av_string() << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << LL_ENDL;
-#endif
- mFireCount++;
}
+LLUpdateAppearanceAndEditWearableOnDestroy::~LLUpdateAppearanceAndEditWearableOnDestroy()
+{
+ if (!LLApp::isExiting())
+ {
+ LLAppearanceMgr::instance().updateAppearanceFromCOF();
+
+ // Start editing the item if previously requested.
+ gAgentWearables.editWearableIfRequested(mItemID);
+
+ // TODO: camera mode may not be changed if a debug setting is tweaked
+ if( gAgentCamera.cameraCustomizeAvatar() )
+ {
+ // If we're in appearance editing mode, the current tab may need to be refreshed
+ LLSidepanelAppearance *panel = dynamic_cast<LLSidepanelAppearance*>(
+ LLFloaterSidePanelContainer::getPanel("appearance"));
+ if (panel)
+ {
+ panel->showDefaultSubpart();
+ }
+ }
+ }
+}
+
+
struct LLFoundData
{
LLFoundData() :
@@ -1203,8 +1155,7 @@ const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink()
cat_array,
item_array,
false,
- is_category,
- false);
+ is_category);
for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
iter != item_array.end();
iter++)
@@ -1270,8 +1221,12 @@ void wear_on_avatar_cb(const LLUUID& inv_item, bool do_replace = false)
}
}
-bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace, LLPointer<LLInventoryCallback> cb)
+bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear,
+ bool do_update,
+ bool replace,
+ LLPointer<LLInventoryCallback> cb)
{
+
if (item_id_to_wear.isNull()) return false;
// *TODO: issue with multi-wearable should be fixed:
@@ -1310,15 +1265,22 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up
switch (item_to_wear->getType())
{
case LLAssetType::AT_CLOTHING:
- if (gAgentWearables.areWearablesLoaded())
+ if (gAgentWearables.areWearablesLoaded())
{
+ if (!cb && do_update)
+ {
+ cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear);
+ }
S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType());
if ((replace && wearable_count != 0) ||
(wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) )
{
- removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1));
+ LLUUID item_id = gAgentWearables.getWearableItemID(item_to_wear->getWearableType(),
+ wearable_count-1);
+ removeCOFItemLinks(item_id, cb);
}
- addCOFItemLink(item_to_wear, do_update, cb);
+
+ addCOFItemLink(item_to_wear, cb);
}
break;
case LLAssetType::AT_BODYPART:
@@ -1327,8 +1289,11 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up
// Remove the existing wearables of the same type.
// Remove existing body parts anyway because we must not be able to wear e.g. two skins.
removeCOFLinksOfType(item_to_wear->getWearableType());
-
- addCOFItemLink(item_to_wear, do_update, cb);
+ if (!cb && do_update)
+ {
+ cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear);
+ }
+ addCOFItemLink(item_to_wear, cb);
break;
case LLAssetType::AT_OBJECT:
rez_attachment(item_to_wear, NULL, replace);
@@ -1582,15 +1547,8 @@ bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id)
// static
bool LLAppearanceMgr::getCanRemoveFromCOF(const LLUUID& outfit_cat_id)
{
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
LLFindWearablesEx is_worn(/*is_worn=*/ true, /*include_body_parts=*/ false);
- gInventory.collectDescendentsIf(outfit_cat_id,
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- is_worn);
- return items.size() > 0;
+ return gInventory.hasMatchingDirectDescendent(outfit_cat_id, is_worn);
}
// static
@@ -1601,15 +1559,8 @@ bool LLAppearanceMgr::getCanAddToCOF(const LLUUID& outfit_cat_id)
return false;
}
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false);
- gInventory.collectDescendentsIf(outfit_cat_id,
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- not_worn);
- return items.size() > 0;
+ return gInventory.hasMatchingDirectDescendent(outfit_cat_id, not_worn);
}
bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id)
@@ -1627,18 +1578,11 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id)
}
// Check whether the outfit contains any wearables we aren't wearing already (STORM-702).
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
LLFindWearablesEx is_worn(/*is_worn=*/ false, /*include_body_parts=*/ true);
- gInventory.collectDescendentsIf(outfit_cat_id,
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- is_worn);
- return items.size() > 0;
+ return gInventory.hasMatchingDirectDescendent(outfit_cat_id, is_worn);
}
-void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category)
+void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category, LLPointer<LLInventoryCallback> cb)
{
LLInventoryModel::cat_array_t cats;
LLInventoryModel::item_array_t items;
@@ -1649,18 +1593,16 @@ void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category)
LLViewerInventoryItem *item = items.get(i);
if (item->getActualType() != LLAssetType::AT_LINK_FOLDER)
continue;
- if (item->getIsLinkType())
+ LLViewerInventoryCategory* catp = item->getLinkedCategory();
+ if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
{
- LLViewerInventoryCategory* catp = item->getLinkedCategory();
- if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
- {
- gInventory.purgeObject(item->getUUID());
- }
+ remove_inventory_item(item->getUUID(), cb);
}
}
}
-void LLAppearanceMgr::purgeCategory(const LLUUID& category, bool keep_outfit_links, LLInventoryModel::item_array_t* keep_items)
+void LLAppearanceMgr::removeCategoryContents(const LLUUID& category, bool keep_outfit_links,
+ LLPointer<LLInventoryCallback> cb)
{
LLInventoryModel::cat_array_t cats;
LLInventoryModel::item_array_t items;
@@ -1673,19 +1615,8 @@ void LLAppearanceMgr::purgeCategory(const LLUUID& category, bool keep_outfit_lin
continue;
if (item->getIsLinkType())
{
-#if 0
- if (keep_items && keep_items->find(item) != LLInventoryModel::item_array_t::FAIL)
- {
- llinfos << "preserved item" << llendl;
- }
- else
- {
- gInventory.purgeObject(item->getUUID());
- }
-#else
- gInventory.purgeObject(item->getUUID());
+ remove_inventory_item(item->getUUID(), cb);
}
-#endif
}
}
@@ -1737,6 +1668,18 @@ void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
}
}
+void LLAppearanceMgr::removeAll(LLInventoryModel::item_array_t& items_to_kill,
+ LLPointer<LLInventoryCallback> cb)
+{
+ for (LLInventoryModel::item_array_t::iterator it = items_to_kill.begin();
+ it != items_to_kill.end();
+ ++it)
+ {
+ LLViewerInventoryItem *item = *it;
+ remove_inventory_item(item->getUUID(), cb);
+ }
+}
+
void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
{
LLViewerInventoryCategory *pcat = gInventory.getCategory(category);
@@ -1748,7 +1691,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
if (!append)
{
LLInventoryModel::item_array_t gest_items;
- getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false);
+ getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE);
for(S32 i = 0; i < gest_items.count(); ++i)
{
LLViewerInventoryItem *gest_item = gest_items.get(i);
@@ -1765,8 +1708,8 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
// required parts are missing.
// Preserve body parts from COF if appending.
LLInventoryModel::item_array_t body_items;
- getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART, false);
- getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART, false);
+ getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART);
+ getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART);
if (append)
reverse(body_items.begin(), body_items.end());
// Reduce body items to max of one per type.
@@ -1776,8 +1719,8 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
// - Wearables: include COF contents only if appending.
LLInventoryModel::item_array_t wear_items;
if (append)
- getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING, false);
- getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING, false);
+ getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING);
+ getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING);
// Reduce wearables to max of one per type.
removeDuplicateItems(wear_items);
filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE);
@@ -1785,15 +1728,15 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
// - Attachments: include COF contents only if appending.
LLInventoryModel::item_array_t obj_items;
if (append)
- getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT, false);
- getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT, false);
+ getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT);
+ getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT);
removeDuplicateItems(obj_items);
// - Gestures: include COF contents only if appending.
LLInventoryModel::item_array_t gest_items;
if (append)
- getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false);
- getDescendentsOfAssetType(category, gest_items, LLAssetType::AT_GESTURE, false);
+ getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE);
+ getDescendentsOfAssetType(category, gest_items, LLAssetType::AT_GESTURE);
removeDuplicateItems(gest_items);
// Create links to new COF contents.
@@ -1818,8 +1761,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
// carried over (e.g. keeping old shape if the new outfit does not
// contain one)
bool keep_outfit_links = append;
- purgeCategory(cof, keep_outfit_links, &all_items);
- gInventory.notifyObservers();
+ removeCategoryContents(cof, keep_outfit_links, link_waiter);
LL_DEBUGS("Avatar") << self_av_string() << "waiting for LLUpdateAppearanceOnDestroy" << LL_ENDL;
}
@@ -1840,7 +1782,7 @@ void LLAppearanceMgr::createBaseOutfitLink(const LLUUID& category, LLPointer<LLI
LLViewerInventoryCategory* catp = gInventory.getCategory(category);
std::string new_outfit_name = "";
- purgeBaseOutfitLink(cof);
+ purgeBaseOutfitLink(cof, link_waiter);
if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
{
@@ -1937,7 +1879,7 @@ S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,
S32 to_kill_count = 0;
LLInventoryModel::item_array_t items;
- getDescendentsOfAssetType(cat_id, items, type, false);
+ getDescendentsOfAssetType(cat_id, items, type);
LLInventoryModel::item_array_t curr_items = items;
removeDuplicateItems(items);
if (max_items > 0)
@@ -1956,34 +1898,20 @@ S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,
return to_kill_count;
}
-
-void LLAppearanceMgr::enforceItemRestrictions()
-{
- S32 purge_count = 0;
- LLInventoryModel::item_array_t items_to_kill;
- purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_BODYPART,
- 1, items_to_kill);
- purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_CLOTHING,
- LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill);
- purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_OBJECT,
- -1, items_to_kill);
-
- if (items_to_kill.size()>0)
- {
- for (LLInventoryModel::item_array_t::iterator it = items_to_kill.begin();
- it != items_to_kill.end();
- ++it)
- {
- LLViewerInventoryItem *item = *it;
- LL_DEBUGS("Avatar") << self_av_string() << "purging duplicate or excess item " << item->getName() << LL_ENDL;
- gInventory.purgeObject(item->getUUID());
- }
- gInventory.notifyObservers();
- }
+void LLAppearanceMgr::findAllExcessOrDuplicateItems(const LLUUID& cat_id,
+ LLInventoryModel::item_array_t& items_to_kill)
+{
+ findExcessOrDuplicateItems(cat_id,LLAssetType::AT_BODYPART,
+ 1, items_to_kill);
+ findExcessOrDuplicateItems(cat_id,LLAssetType::AT_CLOTHING,
+ LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill);
+ findExcessOrDuplicateItems(cat_id,LLAssetType::AT_OBJECT,
+ -1, items_to_kill);
}
-void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
+void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering,
+ bool enforce_item_restrictions)
{
if (mIsInUpdateAppearanceFromCOF)
{
@@ -1996,14 +1924,31 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
LL_DEBUGS("Avatar") << self_av_string() << "starting" << LL_ENDL;
+ if (enforce_item_restrictions)
+ {
+ LLInventoryModel::item_array_t items_to_kill;
+ findAllExcessOrDuplicateItems(getCOF(), items_to_kill);
+ if (items_to_kill.size()>0)
+ {
+ // The point here is just to call
+ // updateAppearanceFromCOF() again after excess items
+ // have been removed. That time we will set
+ // enforce_item_restrictions to false so we don't get
+ // caught in a perpetual loop.
+ LLPointer<LLInventoryCallback> cb(
+ new LLUpdateAppearanceOnDestroy(update_base_outfit_ordering, false));
+
+ // Remove duplicate or excess wearables. Should normally be enforced at the UI level, but
+ // this should catch anything that gets through.
+ removeAll(items_to_kill, cb);
+ return;
+ }
+ }
+
//checking integrity of the COF in terms of ordering of wearables,
//checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state)
updateClothingOrderingInfo(LLUUID::null, update_base_outfit_ordering);
- // Remove duplicate or excess wearables. Should normally be enforced at the UI level, but
- // this should catch anything that gets through.
- enforceItemRestrictions();
-
// update dirty flag to see if the state of the COF matches
// the saved outfit stored as a folder link
updateIsDirty();
@@ -2019,7 +1964,6 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
//dumpCat(getCOF(),"COF, start");
- bool follow_folder_links = false;
LLUUID current_outfit_id = getCOF();
// Find all the wearables that are in the COF's subtree.
@@ -2027,7 +1971,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
LLInventoryModel::item_array_t wear_items;
LLInventoryModel::item_array_t obj_items;
LLInventoryModel::item_array_t gest_items;
- getUserDescendents(current_outfit_id, wear_items, obj_items, gest_items, follow_folder_links);
+ getUserDescendents(current_outfit_id, wear_items, obj_items, gest_items);
// Get rid of non-links in case somehow the COF was corrupted.
remove_non_link_items(wear_items);
remove_non_link_items(obj_items);
@@ -2123,8 +2067,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
void LLAppearanceMgr::getDescendentsOfAssetType(const LLUUID& category,
LLInventoryModel::item_array_t& items,
- LLAssetType::EType type,
- bool follow_folder_links)
+ LLAssetType::EType type)
{
LLInventoryModel::cat_array_t cats;
LLIsType is_of_type(type);
@@ -2132,15 +2075,13 @@ void LLAppearanceMgr::getDescendentsOfAssetType(const LLUUID& category,
cats,
items,
LLInventoryModel::EXCLUDE_TRASH,
- is_of_type,
- follow_folder_links);
+ is_of_type);
}
void LLAppearanceMgr::getUserDescendents(const LLUUID& category,
LLInventoryModel::item_array_t& wear_items,
LLInventoryModel::item_array_t& obj_items,
- LLInventoryModel::item_array_t& gest_items,
- bool follow_folder_links)
+ LLInventoryModel::item_array_t& gest_items)
{
LLInventoryModel::cat_array_t wear_cats;
LLFindWearables is_wearable;
@@ -2148,8 +2089,7 @@ void LLAppearanceMgr::getUserDescendents(const LLUUID& category,
wear_cats,
wear_items,
LLInventoryModel::EXCLUDE_TRASH,
- is_wearable,
- follow_folder_links);
+ is_wearable);
LLInventoryModel::cat_array_t obj_cats;
LLIsType is_object( LLAssetType::AT_OBJECT );
@@ -2157,8 +2097,7 @@ void LLAppearanceMgr::getUserDescendents(const LLUUID& category,
obj_cats,
obj_items,
LLInventoryModel::EXCLUDE_TRASH,
- is_object,
- follow_folder_links);
+ is_object);
// Find all gestures in this folder
LLInventoryModel::cat_array_t gest_cats;
@@ -2167,8 +2106,7 @@ void LLAppearanceMgr::getUserDescendents(const LLUUID& category,
gest_cats,
gest_items,
LLInventoryModel::EXCLUDE_TRASH,
- is_gesture,
- follow_folder_links);
+ is_gesture);
}
void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append)
@@ -2287,6 +2225,7 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego
LLAppearanceMgr::changeOutfit(TRUE, category->getUUID(), append);
}
+// FIXME do we really want to search entire inventory for matching name?
void LLAppearanceMgr::wearOutfitByName(const std::string& name)
{
LL_INFOS("Avatar") << self_av_string() << "Wearing category " << name << LL_ENDL;
@@ -2340,9 +2279,8 @@ bool areMatchingWearables(const LLViewerInventoryItem *a, const LLViewerInventor
class LLDeferredCOFLinkObserver: public LLInventoryObserver
{
public:
- LLDeferredCOFLinkObserver(const LLUUID& item_id, bool do_update, LLPointer<LLInventoryCallback> cb = NULL, std::string description = ""):
+ LLDeferredCOFLinkObserver(const LLUUID& item_id, LLPointer<LLInventoryCallback> cb, const std::string& description):
mItemID(item_id),
- mDoUpdate(do_update),
mCallback(cb),
mDescription(description)
{
@@ -2358,14 +2296,13 @@ public:
if (item)
{
gInventory.removeObserver(this);
- LLAppearanceMgr::instance().addCOFItemLink(item,mDoUpdate,mCallback);
+ LLAppearanceMgr::instance().addCOFItemLink(item, mCallback, mDescription);
delete this;
}
}
private:
const LLUUID mItemID;
- bool mDoUpdate;
std::string mDescription;
LLPointer<LLInventoryCallback> mCallback;
};
@@ -2373,42 +2310,26 @@ private:
// BAP - note that this runs asynchronously if the item is not already loaded from inventory.
// Dangerous if caller assumes link will exist after calling the function.
-void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id, bool do_update, LLPointer<LLInventoryCallback> cb, const std::string description)
+void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id,
+ LLPointer<LLInventoryCallback> cb,
+ const std::string description)
{
const LLInventoryItem *item = gInventory.getItem(item_id);
if (!item)
{
- LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, do_update, cb, description);
+ LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, cb, description);
gInventory.addObserver(observer);
}
else
{
- addCOFItemLink(item, do_update, cb, description);
+ addCOFItemLink(item, cb, description);
}
}
-void modified_cof_cb(const LLUUID& inv_item)
-{
- LLAppearanceMgr::instance().updateAppearanceFromCOF();
-
- // Start editing the item if previously requested.
- gAgentWearables.editWearableIfRequested(inv_item);
-
- // TODO: camera mode may not be changed if a debug setting is tweaked
- if( gAgentCamera.cameraCustomizeAvatar() )
- {
- // If we're in appearance editing mode, the current tab may need to be refreshed
- LLSidepanelAppearance *panel = dynamic_cast<LLSidepanelAppearance*>(LLFloaterSidePanelContainer::getPanel("appearance"));
- if (panel)
- {
- panel->showDefaultSubpart();
- }
- }
-}
-
-void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update, LLPointer<LLInventoryCallback> cb, const std::string description)
+void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item,
+ LLPointer<LLInventoryCallback> cb,
+ const std::string description)
{
- std::string link_description = description;
const LLViewerInventoryItem *vitem = dynamic_cast<const LLViewerInventoryItem*>(item);
if (!vitem)
{
@@ -2448,30 +2369,19 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update
++count;
if (is_body_part && inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type))
{
- gInventory.purgeObject(inv_item->getUUID());
+ remove_inventory_item(inv_item->getUUID(), cb);
}
else if (count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE)
{
// MULTI-WEARABLES: make sure we don't go over MAX_CLOTHING_PER_TYPE
- gInventory.purgeObject(inv_item->getUUID());
+ remove_inventory_item(inv_item->getUUID(), cb);
}
}
}
- if (linked_already)
- {
- if (do_update)
- {
- LLAppearanceMgr::updateAppearanceFromCOF();
- }
- return;
- }
- else
+ if (!linked_already)
{
- if(do_update && cb.isNull())
- {
- cb = new LLBoostFuncInventoryCallback(modified_cof_cb);
- }
+ std::string link_description = description;
if (vitem->getIsLinkType())
{
link_description = vitem->getActualDescription();
@@ -2484,7 +2394,6 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update
LLAssetType::AT_LINK,
cb);
}
- return;
}
LLInventoryModel::item_array_t LLAppearanceMgr::findCOFItemLinks(const LLUUID& item_id)
@@ -2524,8 +2433,7 @@ void LLAppearanceMgr::removeAllClothesFromAvatar()
dummy,
clothing_items,
LLInventoryModel::EXCLUDE_TRASH,
- is_clothing,
- false);
+ is_clothing);
uuid_vec_t item_ids;
for (LLInventoryModel::item_array_t::iterator it = clothing_items.begin();
it != clothing_items.end(); ++it)
@@ -2569,7 +2477,7 @@ void LLAppearanceMgr::removeAllAttachmentsFromAvatar()
removeItemsFromAvatar(ids_to_remove);
}
-void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id)
+void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, LLPointer<LLInventoryCallback> cb)
{
gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
@@ -2584,12 +2492,12 @@ void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id)
const LLInventoryItem* item = item_array.get(i).get();
if (item->getIsLinkType() && item->getLinkedUUID() == item_id)
{
- gInventory.purgeObject(item->getUUID());
+ remove_inventory_item(item->getUUID(), cb);
}
}
}
-void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type)
+void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, LLPointer<LLInventoryCallback> cb)
{
LLFindWearablesOfType filter_wearables_of_type(type);
LLInventoryModel::cat_array_t cats;
@@ -2602,7 +2510,7 @@ void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type)
const LLViewerInventoryItem* item = *it;
if (item->getIsLinkType()) // we must operate on links only
{
- gInventory.purgeObject(item->getUUID());
+ remove_inventory_item(item->getUUID(), cb);
}
}
}
@@ -2823,7 +2731,7 @@ bool LLAppearanceMgr::updateBaseOutfit()
updateClothingOrderingInfo();
// in a Base Outfit we do not remove items, only links
- purgeCategory(base_outfit_id, false);
+ removeCategoryContents(base_outfit_id, false, NULL);
LLPointer<LLInventoryCallback> dirty_state_updater =
new LLBoostFuncInventoryCallback(no_op_inventory_func, appearance_mgr_update_dirty_state);
@@ -2920,7 +2828,7 @@ void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, bool update_base
// COF is processed if cat_id is not specified
LLInventoryModel::item_array_t wear_items;
- getDescendentsOfAssetType(cat_id, wear_items, LLAssetType::AT_CLOTHING, false);
+ getDescendentsOfAssetType(cat_id, wear_items, LLAssetType::AT_CLOTHING);
wearables_by_type_t items_by_type(LLWearableType::WT_COUNT);
divvyWearablesByType(wear_items, items_by_type);
@@ -2957,78 +2865,6 @@ void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, bool update_base
if (inventory_changed) gInventory.notifyObservers();
}
-// This is intended for use with HTTP Clients/Responders, but is not
-// specifically coupled with those classes.
-class LLHTTPRetryPolicy: public LLThreadSafeRefCount
-{
-public:
- LLHTTPRetryPolicy() {}
- virtual ~LLHTTPRetryPolicy() {}
- virtual bool shouldRetry(S32 status, const LLSD& headers, F32& seconds_to_wait) = 0;
-};
-
-// Example of simplest possible policy, not necessarily recommended.
-// This would be a potentially dangerous policy to enable. Removing for now:
-#if 0
-class LLAlwaysRetryImmediatelyPolicy: public LLHTTPRetryPolicy
-{
-public:
- LLAlwaysRetryImmediatelyPolicy() {}
- bool shouldRetry(S32 status, const LLSD& headers, F32& seconds_to_wait)
- {
- seconds_to_wait = 0.0;
- return true;
- }
-};
-#endif
-
-// Very general policy with geometric back-off after failures,
-// up to a maximum delay, and maximum number of retries.
-class LLAdaptiveRetryPolicy: public LLHTTPRetryPolicy
-{
-public:
- LLAdaptiveRetryPolicy(F32 min_delay, F32 max_delay, F32 backoff_factor, U32 max_retries):
- mMinDelay(min_delay),
- mMaxDelay(max_delay),
- mBackoffFactor(backoff_factor),
- mMaxRetries(max_retries),
- mDelay(min_delay),
- mRetryCount(0)
- {
- }
-
- bool shouldRetry(S32 status, const LLSD& headers, F32& seconds_to_wait)
- {
-#if 0
- // *TODO: Test using status codes to only retry server errors.
- // Only server errors would potentially return a different result on retry.
- if (!isHttpServerErrorStatus(status)) return false;
-#endif
-
-#if 0
- // *TODO: Honor server Retry-After header.
- // Status 503 may ask us to wait for a certain amount of time before retrying.
- if (!headers.has(HTTP_IN_HEADER_RETRY_AFTER)
- || !getSecondsUntilRetryAfter(headers[HTTP_IN_HEADER_RETRY_AFTER].asStringRef(), seconds_to_wait))
-#endif
- {
- seconds_to_wait = mDelay;
- mDelay = llclamp(mDelay*mBackoffFactor,mMinDelay,mMaxDelay);
- }
-
- mRetryCount++;
- return (mRetryCount<=mMaxRetries);
- }
-
-private:
- F32 mMinDelay; // delay never less than this value
- F32 mMaxDelay; // delay never exceeds this value
- F32 mBackoffFactor; // delay increases by this factor after each retry, up to mMaxDelay.
- U32 mMaxRetries; // maximum number of times shouldRetry will return true.
- F32 mDelay; // current delay.
- U32 mRetryCount; // number of times shouldRetry has been called.
-};
-
class RequestAgentUpdateAppearanceResponder: public LLHTTPClient::Responder
{
LOG_CLASS(RequestAgentUpdateAppearanceResponder);
@@ -3084,7 +2920,8 @@ protected:
void onFailure()
{
F32 seconds_to_wait;
- if (mRetryPolicy->shouldRetry(getStatus(), getResponseHeaders(), seconds_to_wait))
+ mRetryPolicy->onFailure(getStatus(), getResponseHeaders());
+ if (mRetryPolicy->shouldRetry(seconds_to_wait))
{
llinfos << "retrying" << llendl;
doAfterInterval(boost::bind(&LLAppearanceMgr::requestServerAppearanceUpdate,
@@ -3281,7 +3118,6 @@ void LLAppearanceMgr::requestServerAppearanceUpdate(LLCurl::ResponderPtr respond
}
LL_DEBUGS("Avatar") << "request url " << url << " my_cof_version " << cof_version << llendl;
- //LLCurl::ResponderPtr responder_ptr;
if (!responder_ptr.get())
{
responder_ptr = new RequestAgentUpdateAppearanceResponder;
@@ -3327,7 +3163,8 @@ protected:
LL_WARNS("Avatar") << "While attempting to increment the agent's cof we got an error "
<< dumpResponse() << LL_ENDL;
F32 seconds_to_wait;
- if (mRetryPolicy->shouldRetry(getStatus(), getResponseHeaders(), seconds_to_wait))
+ mRetryPolicy->onFailure(getStatus(), getResponseHeaders());
+ if (mRetryPolicy->shouldRetry(seconds_to_wait))
{
llinfos << "retrying" << llendl;
doAfterInterval(boost::bind(&LLAppearanceMgr::incrementCofVersion,
@@ -3450,21 +3287,22 @@ void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove)
if (ids_to_remove.empty())
{
llwarns << "called with empty list, nothing to do" << llendl;
+ return;
}
+ LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
for (uuid_vec_t::const_iterator it = ids_to_remove.begin(); it != ids_to_remove.end(); ++it)
{
const LLUUID& id_to_remove = *it;
const LLUUID& linked_item_id = gInventory.getLinkedItemID(id_to_remove);
- removeCOFItemLinks(linked_item_id);
+ removeCOFItemLinks(linked_item_id, cb);
}
- updateAppearanceFromCOF();
}
void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove)
{
LLUUID linked_item_id = gInventory.getLinkedItemID(id_to_remove);
- removeCOFItemLinks(linked_item_id);
- updateAppearanceFromCOF();
+ LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
+ removeCOFItemLinks(linked_item_id, cb);
}
bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body)
@@ -3623,7 +3461,7 @@ void LLAppearanceMgr::registerAttachment(const LLUUID& item_id)
// we have to pass do_update = true to call LLAppearanceMgr::updateAppearanceFromCOF.
// it will trigger gAgentWariables.notifyLoadingFinished()
// But it is not acceptable solution. See EXT-7777
- LLAppearanceMgr::addCOFItemLink(item_id, false); // Add COF link for item.
+ LLAppearanceMgr::addCOFItemLink(item_id); // Add COF link for item.
}
else
{
@@ -3647,22 +3485,21 @@ void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id)
BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const
{
- return gInventory.isObjectDescendentOf(obj_id, getCOF());
+ const LLUUID& cof = getCOF();
+ if (obj_id == cof)
+ return TRUE;
+ const LLInventoryObject* obj = gInventory.getObject(obj_id);
+ if (obj && obj->getParentUUID() == cof)
+ return TRUE;
+ return FALSE;
}
// static
bool LLAppearanceMgr::isLinkInCOF(const LLUUID& obj_id)
{
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLLinkedItemIDMatches find_links(gInventory.getLinkedItemID(obj_id));
- gInventory.collectDescendentsIf(LLAppearanceMgr::instance().getCOF(),
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- find_links);
-
- return !items.empty();
+ const LLUUID& target_id = gInventory.getLinkedItemID(obj_id);
+ LLLinkedItemIDMatches find_links(target_id);
+ return gInventory.hasMatchingDirectDescendent(LLAppearanceMgr::instance().getCOF(), find_links);
}
BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const
@@ -3679,18 +3516,6 @@ BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const
// For now, don't allow direct deletion from the COF. Instead, force users
// to choose "Detach" or "Take Off".
return TRUE;
- /*
- const LLInventoryObject *obj = gInventory.getObject(obj_id);
- if (!obj) return FALSE;
-
- // Can't delete bodyparts, since this would be equivalent to removing the item.
- if (obj->getType() == LLAssetType::AT_BODYPART) return TRUE;
-
- // Can't delete the folder link, since this is saved for bookkeeping.
- if (obj->getActualType() == LLAssetType::AT_LINK_FOLDER) return TRUE;
-
- return FALSE;
- */
}
class CallAfterCategoryFetchStage2: public LLInventoryFetchItemsObserver
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index 46252afbde..2cc76c4b4c 100755
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -49,7 +49,8 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
public:
typedef std::vector<LLInventoryModel::item_array_t> wearables_by_type_t;
- void updateAppearanceFromCOF(bool update_base_outfit_ordering = false);
+ void updateAppearanceFromCOF(bool update_base_outfit_ordering = false,
+ bool enforce_item_restrictions = true);
bool needToSaveCOF();
void updateCOF(const LLUUID& category, bool append = false);
void wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append);
@@ -65,7 +66,8 @@ public:
LLAssetType::EType type,
S32 max_items,
LLInventoryModel::item_array_t& items_to_kill);
- void enforceItemRestrictions();
+ void findAllExcessOrDuplicateItems(const LLUUID& cat_id,
+ LLInventoryModel::item_array_t& items_to_kill);
// Copy all items and the src category itself.
void shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id,
@@ -105,12 +107,13 @@ public:
const LLUUID getBaseOutfitUUID();
// Wear/attach an item (from a user's inventory) on the agent
- bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true, bool replace = false, LLPointer<LLInventoryCallback> cb = NULL);
+ bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update, bool replace = false,
+ LLPointer<LLInventoryCallback> cb = NULL);
// Update the displayed outfit name in UI.
void updatePanelOutfitName(const std::string& name);
- void purgeBaseOutfitLink(const LLUUID& category);
+ void purgeBaseOutfitLink(const LLUUID& category, LLPointer<LLInventoryCallback> cb = NULL);
void createBaseOutfitLink(const LLUUID& category, LLPointer<LLInventoryCallback> link_waiter);
void updateAgentWearables(LLWearableHoldingPattern* holder, bool append);
@@ -129,16 +132,20 @@ public:
LLInventoryModel::item_array_t& items,
LLPointer<LLInventoryCallback> cb);
+ // And bulk removal.
+ void removeAll(LLInventoryModel::item_array_t& items,
+ LLPointer<LLInventoryCallback> cb);
+
// Add COF link to individual item.
- void addCOFItemLink(const LLUUID& item_id, bool do_update = true, LLPointer<LLInventoryCallback> cb = NULL, const std::string description = "");
- void addCOFItemLink(const LLInventoryItem *item, bool do_update = true, LLPointer<LLInventoryCallback> cb = NULL, const std::string description = "");
+ void addCOFItemLink(const LLUUID& item_id, LLPointer<LLInventoryCallback> cb = NULL, const std::string description = "");
+ void addCOFItemLink(const LLInventoryItem *item, LLPointer<LLInventoryCallback> cb = NULL, const std::string description = "");
// Find COF entries referencing the given item.
LLInventoryModel::item_array_t findCOFItemLinks(const LLUUID& item_id);
// Remove COF entries
- void removeCOFItemLinks(const LLUUID& item_id);
- void removeCOFLinksOfType(LLWearableType::EType type);
+ void removeCOFItemLinks(const LLUUID& item_id, LLPointer<LLInventoryCallback> cb = NULL);
+ void removeCOFLinksOfType(LLWearableType::EType type, LLPointer<LLInventoryCallback> cb = NULL);
void removeAllClothesFromAvatar();
void removeAllAttachmentsFromAvatar();
@@ -213,16 +220,15 @@ private:
void getDescendentsOfAssetType(const LLUUID& category,
LLInventoryModel::item_array_t& items,
- LLAssetType::EType type,
- bool follow_folder_links);
+ LLAssetType::EType type);
void getUserDescendents(const LLUUID& category,
LLInventoryModel::item_array_t& wear_items,
LLInventoryModel::item_array_t& obj_items,
- LLInventoryModel::item_array_t& gest_items,
- bool follow_folder_links);
+ LLInventoryModel::item_array_t& gest_items);
- void purgeCategory(const LLUUID& category, bool keep_outfit_links, LLInventoryModel::item_array_t* keep_items = NULL);
+ void removeCategoryContents(const LLUUID& category, bool keep_outfit_links,
+ LLPointer<LLInventoryCallback> cb);
static void onOutfitRename(const LLSD& notification, const LLSD& response);
void setOutfitLocked(bool locked);
@@ -256,15 +262,31 @@ public:
class LLUpdateAppearanceOnDestroy: public LLInventoryCallback
{
public:
- LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering = false);
+ LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering = false,
+ bool enforce_item_restrictions = true);
virtual ~LLUpdateAppearanceOnDestroy();
/* virtual */ void fire(const LLUUID& inv_item);
private:
U32 mFireCount;
bool mUpdateBaseOrder;
+ bool mEnforceItemRestrictions;
+};
+
+class LLUpdateAppearanceAndEditWearableOnDestroy: public LLInventoryCallback
+{
+public:
+ LLUpdateAppearanceAndEditWearableOnDestroy(const LLUUID& item_id);
+
+ /* virtual */ void fire(const LLUUID& item_id) {}
+
+ ~LLUpdateAppearanceAndEditWearableOnDestroy();
+
+private:
+ LLUUID mItemID;
};
+class
#define SUPPORT_ENSEMBLES 0
diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp
index 56051ff684..af5c11e12c 100644
--- a/indra/newview/llfloatergesture.cpp
+++ b/indra/newview/llfloatergesture.cpp
@@ -617,9 +617,10 @@ void LLFloaterGesture::addToCurrentOutFit()
uuid_vec_t ids;
getSelectedIds(ids);
LLAppearanceMgr* am = LLAppearanceMgr::getInstance();
+ LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
for(uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); it++)
{
- am->addCOFItemLink(*it);
+ am->addCOFItemLink(*it, cb);
}
}
diff --git a/indra/newview/llfloatersidepanelcontainer.cpp b/indra/newview/llfloatersidepanelcontainer.cpp
index 5f9556a870..4dd558c9c0 100644
--- a/indra/newview/llfloatersidepanelcontainer.cpp
+++ b/indra/newview/llfloatersidepanelcontainer.cpp
@@ -67,15 +67,21 @@ void LLFloaterSidePanelContainer::onClickCloseBtn()
if (parent == this )
{
LLSidepanelAppearance* panel_appearance = dynamic_cast<LLSidepanelAppearance*>(getPanel("appearance"));
- if ( panel_appearance )
- {
- panel_appearance->getWearable()->onClose();
- panel_appearance->showOutfitsInventoryPanel();
- }
+ panel_appearance->onClose(this);
}
+ else
+ {
+ LLFloater::onClickCloseBtn();
+ }
+ }
+ else
+ {
+ LLFloater::onClickCloseBtn();
}
-
- LLFloater::onClickCloseBtn();
+}
+void LLFloaterSidePanelContainer::close()
+{
+ LLFloater::onClickCloseBtn();
}
LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_name, const LLSD& params)
diff --git a/indra/newview/llfloatersidepanelcontainer.h b/indra/newview/llfloatersidepanelcontainer.h
index 491723471f..940673b643 100644
--- a/indra/newview/llfloatersidepanelcontainer.h
+++ b/indra/newview/llfloatersidepanelcontainer.h
@@ -55,6 +55,8 @@ public:
LLPanel* openChildPanel(const std::string& panel_name, const LLSD& params);
+ void close();
+
static void showPanel(const std::string& floater_name, const LLSD& key);
static void showPanel(const std::string& floater_name, const std::string& panel_name, const LLSD& key);
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp
index 9aa86297fc..5eaa83d872 100644..100755
--- a/indra/newview/llgesturemgr.cpp
+++ b/indra/newview/llgesturemgr.cpp
@@ -299,6 +299,12 @@ void LLGestureMgr::activateGestureWithAsset(const LLUUID& item_id,
}
+void notify_update_label(const LLUUID& base_item_id)
+{
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, base_item_id);
+ LLGestureMgr::instance().notifyObservers();
+}
+
void LLGestureMgr::deactivateGesture(const LLUUID& item_id)
{
const LLUUID& base_item_id = get_linked_uuid(item_id);
@@ -322,7 +328,6 @@ void LLGestureMgr::deactivateGesture(const LLUUID& item_id)
}
mActive.erase(it);
- gInventory.addChangedMask(LLInventoryObserver::LABEL, base_item_id);
// Inform the database of this change
LLMessageSystem* msg = gMessageSystem;
@@ -338,9 +343,11 @@ void LLGestureMgr::deactivateGesture(const LLUUID& item_id)
gAgent.sendReliableMessage();
- LLAppearanceMgr::instance().removeCOFItemLinks(base_item_id);
+ LLPointer<LLInventoryCallback> cb =
+ new LLBoostFuncInventoryCallback(no_op_inventory_func,
+ boost::bind(notify_update_label,base_item_id));
- notifyObservers();
+ LLAppearanceMgr::instance().removeCOFItemLinks(base_item_id, cb);
}
diff --git a/indra/newview/llhttpretrypolicy.cpp b/indra/newview/llhttpretrypolicy.cpp
new file mode 100755
index 0000000000..80d97e4362
--- /dev/null
+++ b/indra/newview/llhttpretrypolicy.cpp
@@ -0,0 +1,136 @@
+/**
+ * @file llhttpretrypolicy.h
+ * @brief Header for a retry policy class intended for use with http responders.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, 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 "llviewerprecompiledheaders.h"
+
+#include "llhttpretrypolicy.h"
+
+LLAdaptiveRetryPolicy::LLAdaptiveRetryPolicy(F32 min_delay, F32 max_delay, F32 backoff_factor, U32 max_retries):
+ mMinDelay(min_delay),
+ mMaxDelay(max_delay),
+ mBackoffFactor(backoff_factor),
+ mMaxRetries(max_retries)
+{
+ init();
+}
+
+void LLAdaptiveRetryPolicy::init()
+{
+ mDelay = mMinDelay;
+ mRetryCount = 0;
+ mShouldRetry = true;
+}
+
+bool LLAdaptiveRetryPolicy::getRetryAfter(const LLSD& headers, F32& retry_header_time)
+{
+ return (headers.has(HTTP_IN_HEADER_RETRY_AFTER)
+ && getSecondsUntilRetryAfter(headers[HTTP_IN_HEADER_RETRY_AFTER].asStringRef(), retry_header_time));
+}
+
+bool LLAdaptiveRetryPolicy::getRetryAfter(const LLCore::HttpHeaders *headers, F32& retry_header_time)
+{
+ if (headers)
+ {
+ const std::string *retry_value = headers->find(HTTP_IN_HEADER_RETRY_AFTER.c_str());
+ if (retry_value &&
+ getSecondsUntilRetryAfter(*retry_value, retry_header_time))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void LLAdaptiveRetryPolicy::onSuccess()
+{
+ init();
+}
+
+void LLAdaptiveRetryPolicy::onFailure(S32 status, const LLSD& headers)
+{
+ F32 retry_header_time;
+ bool has_retry_header_time = getRetryAfter(headers,retry_header_time);
+ onFailureCommon(status, has_retry_header_time, retry_header_time);
+}
+
+void LLAdaptiveRetryPolicy::onFailure(const LLCore::HttpResponse *response)
+{
+ F32 retry_header_time;
+ const LLCore::HttpHeaders *headers = response->getHeaders();
+ bool has_retry_header_time = getRetryAfter(headers,retry_header_time);
+ onFailureCommon(response->getStatus().mType, has_retry_header_time, retry_header_time);
+}
+
+void LLAdaptiveRetryPolicy::onFailureCommon(S32 status, bool has_retry_header_time, F32 retry_header_time)
+{
+ if (!mShouldRetry)
+ {
+ llinfos << "keep on failing" << llendl;
+ return;
+ }
+ if (mRetryCount > 0)
+ {
+ mDelay = llclamp(mDelay*mBackoffFactor,mMinDelay,mMaxDelay);
+ }
+ // Honor server Retry-After header.
+ // Status 503 may ask us to wait for a certain amount of time before retrying.
+ F32 wait_time = mDelay;
+ if (has_retry_header_time)
+ {
+ wait_time = retry_header_time;
+ }
+
+ if (mRetryCount>=mMaxRetries)
+ {
+ llinfos << "Too many retries " << mRetryCount << ", will not retry" << llendl;
+ mShouldRetry = false;
+ }
+ if (!isHttpServerErrorStatus(status))
+ {
+ llinfos << "Non-server error " << status << ", will not retry" << llendl;
+ mShouldRetry = false;
+ }
+ if (mShouldRetry)
+ {
+ llinfos << "Retry count " << mRetryCount << " should retry after " << wait_time << llendl;
+ mRetryTimer.reset();
+ mRetryTimer.setTimerExpirySec(wait_time);
+ }
+ mRetryCount++;
+}
+
+
+bool LLAdaptiveRetryPolicy::shouldRetry(F32& seconds_to_wait) const
+{
+ if (mRetryCount == 0)
+ {
+ // Called shouldRetry before any failure.
+ seconds_to_wait = F32_MAX;
+ return false;
+ }
+ seconds_to_wait = mShouldRetry ? mRetryTimer.getRemainingTimeF32() : F32_MAX;
+ return mShouldRetry;
+}
diff --git a/indra/newview/llhttpretrypolicy.h b/indra/newview/llhttpretrypolicy.h
new file mode 100755
index 0000000000..1fb0cac03f
--- /dev/null
+++ b/indra/newview/llhttpretrypolicy.h
@@ -0,0 +1,93 @@
+/**
+ * @file file llhttpretrypolicy.h
+ * @brief declarations for http retry policy class.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, 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 LL_RETRYPOLICY_H
+#define LL_RETRYPOLICY_H
+
+#include "lltimer.h"
+#include "llthread.h"
+
+#include "llhttpconstants.h"
+
+// For compatibility with new core http lib.
+#include "httpresponse.h"
+#include "httpheaders.h"
+
+// This is intended for use with HTTP Clients/Responders, but is not
+// specifically coupled with those classes.
+class LLHTTPRetryPolicy: public LLThreadSafeRefCount
+{
+public:
+ LLHTTPRetryPolicy() {}
+
+ virtual ~LLHTTPRetryPolicy() {}
+ // Call after a sucess to reset retry state.
+
+ virtual void onSuccess() = 0;
+ // Call once after an HTTP failure to update state.
+ virtual void onFailure(S32 status, const LLSD& headers) = 0;
+
+ virtual void onFailure(const LLCore::HttpResponse *response) = 0;
+
+ virtual bool shouldRetry(F32& seconds_to_wait) const = 0;
+};
+
+// Very general policy with geometric back-off after failures,
+// up to a maximum delay, and maximum number of retries.
+class LLAdaptiveRetryPolicy: public LLHTTPRetryPolicy
+{
+public:
+ LLAdaptiveRetryPolicy(F32 min_delay, F32 max_delay, F32 backoff_factor, U32 max_retries);
+
+ // virtual
+ void onSuccess();
+
+ // virtual
+ void onFailure(S32 status, const LLSD& headers);
+ // virtual
+ void onFailure(const LLCore::HttpResponse *response);
+ // virtual
+ bool shouldRetry(F32& seconds_to_wait) const;
+
+protected:
+ void init();
+ bool getRetryAfter(const LLSD& headers, F32& retry_header_time);
+ bool getRetryAfter(const LLCore::HttpHeaders *headers, F32& retry_header_time);
+ void onFailureCommon(S32 status, bool has_retry_header_time, F32 retry_header_time);
+
+private:
+
+ const F32 mMinDelay; // delay never less than this value
+ const F32 mMaxDelay; // delay never exceeds this value
+ const F32 mBackoffFactor; // delay increases by this factor after each retry, up to mMaxDelay.
+ const U32 mMaxRetries; // maximum number of times shouldRetry will return true.
+ F32 mDelay; // current default delay.
+ U32 mRetryCount; // number of times shouldRetry has been called.
+ LLTimer mRetryTimer; // time until next retry.
+ bool mShouldRetry; // Becomes false after too many retries, or the wrong sort of status received, etc.
+};
+
+#endif
diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp
index 0b3268eb54..6ff16742dd 100644
--- a/indra/newview/llinspectavatar.cpp
+++ b/indra/newview/llinspectavatar.cpp
@@ -292,10 +292,7 @@ void LLInspectAvatar::processAvatarData(LLAvatarData* data)
delete mPropertiesRequest;
mPropertiesRequest = NULL;
}
-/*
-prep#
- virtual void httpFailure()
- */
+
void LLInspectAvatar::updateVolumeSlider()
{
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 8d7478233a..a0b9e7b0ec 100755
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -593,6 +593,40 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
return id;
}
+// This is optimized for the case that we just want to know whether a
+// category has any immediate children meeting a condition, without
+// needing to recurse or build up any lists.
+bool LLInventoryModel::hasMatchingDirectDescendent(const LLUUID& cat_id,
+ LLInventoryCollectFunctor& filter)
+{
+ LLInventoryModel::cat_array_t *cats;
+ LLInventoryModel::item_array_t *items;
+ getDirectDescendentsOf(cat_id, cats, items);
+ if (cats)
+ {
+ for (LLInventoryModel::cat_array_t::const_iterator it = cats->begin();
+ it != cats->end(); ++it)
+ {
+ if (filter(*it,NULL))
+ {
+ return true;
+ }
+ }
+ }
+ if (items)
+ {
+ for (LLInventoryModel::item_array_t::const_iterator it = items->begin();
+ it != items->end(); ++it)
+ {
+ if (filter(NULL,*it))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
// Starting with the object specified, add it's descendents to the
// array provided, but do not add the inventory object specified by
// id. There is no guaranteed order. Neither array will be erased
@@ -624,8 +658,7 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
cat_array_t& cats,
item_array_t& items,
BOOL include_trash,
- LLInventoryCollectFunctor& add,
- BOOL follow_folder_links)
+ LLInventoryCollectFunctor& add)
{
// Start with categories
if(!include_trash)
@@ -652,36 +685,6 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
LLViewerInventoryItem* item = NULL;
item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id);
- // Follow folder links recursively. Currently never goes more
- // than one level deep (for current outfit support)
- // Note: if making it fully recursive, need more checking against infinite loops.
- if (follow_folder_links && item_array)
- {
- S32 count = item_array->count();
- for(S32 i = 0; i < count; ++i)
- {
- item = item_array->get(i);
- if (item && item->getActualType() == LLAssetType::AT_LINK_FOLDER)
- {
- LLViewerInventoryCategory *linked_cat = item->getLinkedCategory();
- if (linked_cat && linked_cat->getPreferredType() != LLFolderType::FT_OUTFIT)
- // BAP - was
- // LLAssetType::lookupIsEnsembleCategoryType(linked_cat->getPreferredType()))
- // Change back once ensemble typing is in place.
- {
- if(add(linked_cat,NULL))
- {
- // BAP should this be added here? May not
- // matter if it's only being used in current
- // outfit traversal.
- cats.put(LLPointer<LLViewerInventoryCategory>(linked_cat));
- }
- collectDescendentsIf(linked_cat->getUUID(), cats, items, include_trash, add, FALSE);
- }
- }
- }
- }
-
// Move onto items
if(item_array)
{
@@ -754,6 +757,10 @@ LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID
const LLUUID& start_folder_id)
{
item_array_t items;
+ const LLInventoryObject *obj = getObject(id);
+ if (!obj || obj->getIsLinkType())
+ return items;
+
LLInventoryModel::cat_array_t cat_array;
LLLinkedItemIDMatches is_linked_item_match(id);
collectDescendentsIf((start_folder_id == LLUUID::null ? gInventory.getRootFolderID() : start_folder_id),
@@ -1171,8 +1178,15 @@ void LLInventoryModel::deleteObject(const LLUUID& id)
mParentChildCategoryTree.erase(id);
}
addChangedMask(LLInventoryObserver::REMOVE, id);
+
+ // Can't have links to links, so there's no need for this update
+ // if the item removed is a link.
+ bool is_link_type = obj->getIsLinkType();
obj = NULL; // delete obj
- updateLinkedObjectsFromPurge(id);
+ if (!is_link_type)
+ {
+ updateLinkedObjectsFromPurge(id);
+ }
gInventory.notifyObservers();
}
@@ -1195,17 +1209,20 @@ void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id)
// REBUILD is expensive, so clear the current change list first else
// everything else on the changelist will also get rebuilt.
- gInventory.notifyObservers();
- for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
- iter != item_array.end();
- iter++)
+ if (item_array.size() > 0)
{
- const LLViewerInventoryItem *linked_item = (*iter);
- const LLUUID &item_id = linked_item->getUUID();
- if (item_id == baseobj_id) continue;
- addChangedMask(LLInventoryObserver::REBUILD, item_id);
+ gInventory.notifyObservers();
+ for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
+ iter != item_array.end();
+ iter++)
+ {
+ const LLViewerInventoryItem *linked_item = (*iter);
+ const LLUUID &item_id = linked_item->getUUID();
+ if (item_id == baseobj_id) continue;
+ addChangedMask(LLInventoryObserver::REBUILD, item_id);
+ }
+ gInventory.notifyObservers();
}
- gInventory.notifyObservers();
}
// This is a method which collects the descendents of the id
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index 3aa29bd91d..50b57ea08d 100644..100755
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -205,6 +205,9 @@ public:
EXCLUDE_TRASH = FALSE,
INCLUDE_TRASH = TRUE
};
+ // Simpler existence test if matches don't actually need to be collected.
+ bool hasMatchingDirectDescendent(const LLUUID& cat_id,
+ LLInventoryCollectFunctor& filter);
void collectDescendents(const LLUUID& id,
cat_array_t& categories,
item_array_t& items,
@@ -213,8 +216,7 @@ public:
cat_array_t& categories,
item_array_t& items,
BOOL include_trash,
- LLInventoryCollectFunctor& add,
- BOOL follow_folder_links = FALSE);
+ LLInventoryCollectFunctor& add);
// Collect all items in inventory that are linked to item_id.
// Assumes item_id is itself not a linked item.
diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp
index e71dba5cae..580e31591c 100644..100755
--- a/indra/newview/llpaneleditwearable.cpp
+++ b/indra/newview/llpaneleditwearable.cpp
@@ -1079,10 +1079,15 @@ void LLPanelEditWearable::saveChanges(bool force_save_as)
if (force_save_as)
{
- // the name of the wearable has changed, re-save wearable with new name
- LLAppearanceMgr::instance().removeCOFItemLinks(mWearablePtr->getItemID());
+ // FIXME race condition if removeCOFItemLinks does not
+ // complete immediately. Looks like we're counting on the
+ // fact that updateAppearanceFromCOF will get called after
+ // we exit customize mode.
+
+ // the name of the wearable has changed, re-save wearable with new name
+ LLAppearanceMgr::instance().removeCOFItemLinks(mWearablePtr->getItemID());
gAgentWearables.saveWearableAs(mWearablePtr->getType(), index, new_name, description, FALSE);
- mNameEditor->setText(mWearableItem->getName());
+ mNameEditor->setText(mWearableItem->getName());
}
else
{
@@ -1091,6 +1096,14 @@ void LLPanelEditWearable::saveChanges(bool force_save_as)
// version so texture baking service knows appearance has changed.
if (link_item)
{
+ // FIXME - two link-modifying calls here plus one
+ // inventory change request, none of which use a
+ // callback. When does a new appearance request go out
+ // and how is it synced with these changes? As above,
+ // we seem to be implicitly depending on
+ // updateAppearanceFromCOF() to be called when we
+ // exit customize mode.
+
// Create new link
link_inventory_item( gAgent.getID(),
link_item->getLinkedUUID(),
@@ -1100,9 +1113,9 @@ void LLPanelEditWearable::saveChanges(bool force_save_as)
LLAssetType::AT_LINK,
NULL);
// Remove old link
- gInventory.purgeObject(link_item->getUUID());
+ remove_inventory_item(link_item->getUUID(), NULL);
}
- gAgentWearables.saveWearable(mWearablePtr->getType(), index, TRUE, new_name);
+ gAgentWearables.saveWearable(mWearablePtr->getType(), index, TRUE, new_name);
}
diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp
index c09d4393c8..0e3057dcad 100644
--- a/indra/newview/llpaneloutfitedit.cpp
+++ b/indra/newview/llpaneloutfitedit.cpp
@@ -1184,12 +1184,12 @@ BOOL LLPanelOutfitEdit::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
* second argument is used to delay the appearance update until all dragged items
* are added to optimize user experience.
*/
- LLAppearanceMgr::instance().addCOFItemLink(item->getLinkedUUID(), false);
+ LLAppearanceMgr::instance().addCOFItemLink(item->getLinkedUUID());
}
else
{
// if asset id is not available for the item we must wear it immediately (attachments only)
- LLAppearanceMgr::instance().addCOFItemLink(item->getLinkedUUID(), true);
+ LLAppearanceMgr::instance().addCOFItemLink(item->getLinkedUUID(), new LLUpdateAppearanceAndEditWearableOnDestroy(item->getUUID()));
}
}
diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp
index f90236f6f2..d6c927ab58 100644..100755
--- a/indra/newview/llpaneloutfitsinventory.cpp
+++ b/indra/newview/llpaneloutfitsinventory.cpp
@@ -76,7 +76,8 @@ BOOL LLPanelOutfitsInventory::postBuild()
// Fetch your outfits folder so that the links are in memory.
// ( This is only necessary if we want to show a warning if a user deletes an item that has a
// a link in an outfit, see "ConfirmItemDeleteHasLinks". )
- const LLUUID &outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTFIT, false);
+
+ const LLUUID &outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false);
if (outfits_cat.notNull())
{
LLInventoryModelBackgroundFetch::instance().start(outfits_cat);
diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp
index 74fa5a87bb..77e9604460 100644
--- a/indra/newview/llsidepanelappearance.cpp
+++ b/indra/newview/llsidepanelappearance.cpp
@@ -48,6 +48,8 @@
#include "llviewerregion.h"
#include "llvoavatarself.h"
#include "llviewerwearable.h"
+#include "llnotificationsutil.h"
+#include "llfloatersidepanelcontainer.h"
static LLRegisterPanelClassWrapper<LLSidepanelAppearance> t_appearance("sidepanel_appearance");
@@ -70,13 +72,86 @@ private:
LLSidepanelAppearance *mPanel;
};
+bool LLSidepanelAppearance::callBackExitWithoutSaveViaBack(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if ( option == 0 )
+ {
+ LLAppearanceMgr::instance().setOutfitDirty( true );
+ showOutfitsInventoryPanel();
+ LLAppearanceMgr::getInstance()->wearBaseOutfit();
+ return true;
+ }
+ return false;
+}
+
+bool LLSidepanelAppearance::callBackExitWithoutSaveViaClose(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if ( option == 0 )
+ {
+ //revert curernt edits
+ mEditWearable->revertChanges();
+ toggleWearableEditPanel(FALSE);
+ LLVOAvatarSelf::onCustomizeEnd(FALSE);
+ mLLFloaterSidePanelContainer->close();
+ return true;
+ }
+ return false;
+}
+
+void LLSidepanelAppearance::onClickConfirmExitWithoutSaveViaClose()
+{
+ if ( LLAppearanceMgr::getInstance()->isOutfitDirty() && !LLAppearanceMgr::getInstance()->isOutfitLocked() )
+ {
+ LLSidepanelAppearance* pSelf = (LLSidepanelAppearance *)this;
+ LLNotificationsUtil::add("ConfirmExitWithoutSave", LLSD(), LLSD(), boost::bind(&LLSidepanelAppearance::callBackExitWithoutSaveViaClose,pSelf,_1,_2) );
+ }
+ else
+ {
+ showOutfitsInventoryPanel();
+ }
+}
+
+void LLSidepanelAppearance::onClickConfirmExitWithoutSaveViaBack()
+{
+ if ( LLAppearanceMgr::getInstance()->isOutfitDirty() && !mSidePanelJustOpened && !LLAppearanceMgr::getInstance()->isOutfitLocked() )
+ {
+ LLSidepanelAppearance* pSelf = (LLSidepanelAppearance *)this;
+ LLNotificationsUtil::add("ConfirmExitWithoutSave", LLSD(), LLSD(), boost::bind(&LLSidepanelAppearance::callBackExitWithoutSaveViaBack,pSelf,_1,_2) );
+ }
+ else
+ {
+ showOutfitsInventoryPanel();
+ }
+}
+
+void LLSidepanelAppearance::onClose(LLFloaterSidePanelContainer* obj)
+{
+ mLLFloaterSidePanelContainer = obj;
+ if ( LLAppearanceMgr::getInstance()->isOutfitDirty() &&
+ !LLAppearanceMgr::getInstance()->isOutfitLocked() ||
+ ( mEditWearable->isAvailable() && mEditWearable->isDirty() ) )
+ {
+ LLSidepanelAppearance* pSelf = (LLSidepanelAppearance *)this;
+ LLNotificationsUtil::add("ConfirmExitWithoutSave", LLSD(), LLSD(), boost::bind(&LLSidepanelAppearance::callBackExitWithoutSaveViaClose,pSelf,_1,_2) );
+ }
+ else
+ {
+ LLVOAvatarSelf::onCustomizeEnd(FALSE);
+ toggleWearableEditPanel(FALSE);
+ mLLFloaterSidePanelContainer->close();
+ }
+}
+
LLSidepanelAppearance::LLSidepanelAppearance() :
LLPanel(),
mFilterSubString(LLStringUtil::null),
mFilterEditor(NULL),
mOutfitEdit(NULL),
mCurrOutfitPanel(NULL),
- mOpened(false)
+ mOpened(false),
+ mSidePanelJustOpened(true)
{
LLOutfitObserver& outfit_observer = LLOutfitObserver::instance();
outfit_observer.addBOFReplacedCallback(boost::bind(&LLSidepanelAppearance::refreshCurrentOutfitName, this, ""));
@@ -85,6 +160,8 @@ LLSidepanelAppearance::LLSidepanelAppearance() :
gAgentWearables.addLoadingStartedCallback(boost::bind(&LLSidepanelAppearance::setWearablesLoading, this, true));
gAgentWearables.addLoadedCallback(boost::bind(&LLSidepanelAppearance::setWearablesLoading, this, false));
+
+
}
LLSidepanelAppearance::~LLSidepanelAppearance()
@@ -119,8 +196,8 @@ BOOL LLSidepanelAppearance::postBuild()
{
LLButton* back_btn = mOutfitEdit->getChild<LLButton>("back_btn");
if (back_btn)
- {
- back_btn->setClickedCallback(boost::bind(&LLSidepanelAppearance::showOutfitsInventoryPanel, this));
+ {
+ back_btn->setClickedCallback(boost::bind(&LLSidepanelAppearance::onClickConfirmExitWithoutSaveViaBack, this));
}
}
@@ -144,6 +221,7 @@ BOOL LLSidepanelAppearance::postBuild()
setVisibleCallback(boost::bind(&LLSidepanelAppearance::onVisibilityChange,this,_2));
+
return TRUE;
}
@@ -183,6 +261,12 @@ void LLSidepanelAppearance::onOpen(const LLSD& key)
void LLSidepanelAppearance::onVisibilityChange(const LLSD &new_visibility)
{
+ //handle leaving and subsequent user verification of discarding any unsaved data
+ if ( mSidePanelJustOpened )
+ {
+ mSidePanelJustOpened = false;
+ }
+
LLSD visibility;
visibility["visible"] = new_visibility.asBoolean();
visibility["reset_accordion"] = false;
diff --git a/indra/newview/llsidepanelappearance.h b/indra/newview/llsidepanelappearance.h
index 762f557a80..85e7734567 100644
--- a/indra/newview/llsidepanelappearance.h
+++ b/indra/newview/llsidepanelappearance.h
@@ -38,9 +38,11 @@ class LLCurrentlyWornFetchObserver;
class LLPanelEditWearable;
class LLViewerWearable;
class LLPanelOutfitsInventory;
+class LLFloaterSidePanelContainer;
class LLSidepanelAppearance : public LLPanel
-{
+{
+
LOG_CLASS(LLSidepanelAppearance);
public:
LLSidepanelAppearance();
@@ -48,6 +50,8 @@ public:
/*virtual*/ BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
+ void onClose(LLFloaterSidePanelContainer* obj);
+ void onClickCloseBtn();
void refreshCurrentOutfitName(const std::string& name = "");
@@ -65,6 +69,11 @@ public:
void updateScrollingPanelList();
void updateToVisibility( const LLSD& new_visibility );
LLPanelEditWearable* getWearable(){ return mEditWearable; }
+ bool callBackExitWithoutSaveViaBack(const LLSD& notification, const LLSD& response);
+ void onClickConfirmExitWithoutSaveViaBack();
+ bool callBackExitWithoutSaveViaClose(const LLSD& notification, const LLSD& response);
+ void onClickConfirmExitWithoutSaveViaClose();
+
private:
void onFilterEdit(const std::string& search_string);
@@ -85,6 +94,7 @@ private:
LLButton* mOpenOutfitBtn;
LLButton* mEditAppearanceBtn;
LLButton* mNewOutfitBtn;
+
LLPanel* mCurrOutfitPanel;
LLTextBox* mCurrentLookName;
@@ -99,6 +109,10 @@ private:
// Gets set to true when we're opened for the first time.
bool mOpened;
+ // Set to true if sidepanel has just been opened
+ bool mSidePanelJustOpened;
+ LLFloaterSidePanelContainer* mLLFloaterSidePanelContainer;
+
};
#endif //LL_LLSIDEPANELAPPEARANCE_H
diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp
index 3c3c699d17..bf209df863 100644
--- a/indra/newview/llspeakers.cpp
+++ b/indra/newview/llspeakers.cpp
@@ -855,10 +855,7 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update)
}
}
}
-/*prep#
- virtual void httpFailure()
- llwarns << dumpResponse() << llendl;
- */
+
void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id)
{
LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index cc6dc64626..f7fbb19bdc 100755
--- 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) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, 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
@@ -63,6 +63,8 @@
#include "bufferarray.h"
#include "bufferstream.h"
+#include "llhttpretrypolicy.h"
+
bool LLTextureFetchDebugger::sDebuggerEnabled = false ;
LLStat LLTextureFetch::sCacheHitRate("texture_cache_hits", 128);
LLStat LLTextureFetch::sCacheReadLatency("texture_cache_read_latency", 128);
@@ -244,6 +246,25 @@ static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; // Active level at whi
//////////////////////////////////////////////////////////////////////////////
+static const char* e_state_name[] =
+{
+ "INVALID",
+ "INIT",
+ "LOAD_FROM_TEXTURE_CACHE",
+ "CACHE_POST",
+ "LOAD_FROM_NETWORK",
+ "LOAD_FROM_SIMULATOR",
+ "WAIT_HTTP_RESOURCE",
+ "WAIT_HTTP_RESOURCE2",
+ "SEND_HTTP_REQ",
+ "WAIT_HTTP_REQ",
+ "DECODE_IMAGE",
+ "DECODE_IMAGE_UPDATE",
+ "WRITE_TO_CACHE",
+ "WAIT_ON_WRITE",
+ "DONE"
+};
+
class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler
{
@@ -382,12 +403,14 @@ public:
void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; }
bool getCanUseHTTP() const { return mCanUseHTTP; }
+ void setUrl(const std::string& url) { mUrl = url; }
+
LLTextureFetch & getFetcher() { return *mFetcher; }
// Inherited from LLCore::HttpHandler
// Threads: Ttf
virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
-
+
protected:
LLTextureFetchWorker(LLTextureFetch* fetcher, FTType f_type,
const std::string& url, const LLUUID& id, const LLHost& host,
@@ -547,6 +570,8 @@ private:
S32 mActiveCount;
LLCore::HttpStatus mGetStatus;
std::string mGetReason;
+ LLAdaptiveRetryPolicy mFetchRetryPolicy;
+
// Work Data
LLMutex mWorkMutex;
@@ -889,7 +914,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
mHttpHasResource(false),
mCacheReadCount(0U),
mCacheWriteCount(0U),
- mResourceWaitCount(0U)
+ mResourceWaitCount(0U),
+ mFetchRetryPolicy(10.0,3600.0,2.0,10)
{
mCanUseNET = mUrl.empty() ;
@@ -1148,6 +1174,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE
LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority)
<< " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
+
// fall through
}
@@ -1270,6 +1297,21 @@ bool LLTextureFetchWorker::doWork(S32 param)
if (mState == LOAD_FROM_NETWORK)
{
+ // Check for retries to previous server failures.
+ F32 wait_seconds;
+ if (mFetchRetryPolicy.shouldRetry(wait_seconds))
+ {
+ if (wait_seconds <= 0.0)
+ {
+ llinfos << mID << " retrying now" << llendl;
+ }
+ else
+ {
+ //llinfos << mID << " waiting to retry for " << wait_seconds << " seconds" << llendl;
+ return false;
+ }
+ }
+
static LLCachedControl<bool> use_http(gSavedSettings,"ImagePipelineUseHTTP");
// if (mHost != LLHost::invalid) get_url = false;
@@ -1286,7 +1328,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
std::string http_url = region->getHttpUrl() ;
if (!http_url.empty())
{
- mUrl = http_url + "/?texture_id=" + mID.asString().c_str();
+ setUrl(http_url + "/?texture_id=" + mID.asString().c_str());
mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id.
}
else
@@ -1482,12 +1524,14 @@ bool LLTextureFetchWorker::doWork(S32 param)
<< LL_ENDL;
// Will call callbackHttpGet when curl request completes
+ // Only server bake images use the returned headers currently, for getting retry-after field.
+ LLCore::HttpOptions *options = (mFTType == FTT_SERVER_BAKE) ? mFetcher->mHttpOptionsWithHeaders: mFetcher->mHttpOptions;
mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass,
mWorkPriority,
mUrl,
mRequestedOffset,
mRequestedSize,
- mFetcher->mHttpOptions,
+ options,
mFetcher->mHttpHeaders,
this);
}
@@ -1519,15 +1563,22 @@ bool LLTextureFetchWorker::doWork(S32 param)
{
if (http_not_found == mGetStatus)
{
- if(mWriteToCacheState == NOT_WRITE) //map tiles
+ if (mFTType != FTT_MAP_TILE)
+ {
+ llwarns << "Texture missing from server (404): " << mUrl << llendl;
+ }
+
+ if(mWriteToCacheState == NOT_WRITE) //map tiles or server bakes
{
setState(DONE);
releaseHttpSemaphore();
- LL_DEBUGS("Texture") << mID << " abort: WAIT_HTTP_REQ not found" << llendl;
- return true; // failed, means no map tile on the empty region.
+ if (mFTType != FTT_MAP_TILE)
+ {
+ LL_WARNS("Texture") << mID << " abort: WAIT_HTTP_REQ not found" << llendl;
+ }
+ return true;
}
- llwarns << "Texture missing from server (404): " << mUrl << llendl;
// roll back to try UDP
if (mCanUseNET)
@@ -1543,6 +1594,10 @@ bool LLTextureFetchWorker::doWork(S32 param)
else if (http_service_unavail == mGetStatus)
{
LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL;
+ llinfos << "503: HTTP GET failed for: " << mUrl
+ << " Status: " << mGetStatus.toHex()
+ << " Reason: '" << mGetReason << "'"
+ << llendl;
}
else if (http_not_sat == mGetStatus)
{
@@ -1551,7 +1606,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
}
else
{
- llinfos << "HTTP GET failed for: " << mUrl
+ llinfos << "other: HTTP GET failed for: " << mUrl
<< " Status: " << mGetStatus.toHex()
<< " Reason: '" << mGetReason << "'"
<< llendl;
@@ -1891,14 +1946,48 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe
mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
}
+ static LLCachedControl<F32> fake_failure_rate(gSavedSettings, "TextureFetchFakeFailureRate");
+ F32 rand_val = ll_frand();
+ F32 rate = fake_failure_rate;
+ if (mFTType == FTT_SERVER_BAKE && (fake_failure_rate > 0.0) && (rand_val < fake_failure_rate))
+ {
+ llwarns << mID << " for debugging, setting fake failure status for texture " << mID
+ << " (rand was " << rand_val << "/" << rate << ")" << llendl;
+ response->setStatus(LLCore::HttpStatus(503));
+ }
bool success = true;
bool partial = false;
LLCore::HttpStatus status(response->getStatus());
+ if (!status && (mFTType == FTT_SERVER_BAKE))
+ {
+ llinfos << mID << " state " << e_state_name[mState] << llendl;
+ mFetchRetryPolicy.onFailure(response);
+ F32 retry_after;
+ if (mFetchRetryPolicy.shouldRetry(retry_after))
+ {
+ llinfos << mID << " will retry after " << retry_after << " seconds, resetting state to LOAD_FROM_NETWORK" << llendl;
+ mFetcher->removeFromHTTPQueue(mID, 0);
+ std::string reason(status.toString());
+ setGetStatus(status, reason);
+ releaseHttpSemaphore();
+ setState(LOAD_FROM_NETWORK);
+ return;
+ }
+ else
+ {
+ llinfos << mID << " will not retry" << llendl;
+ }
+ }
+ else
+ {
+ mFetchRetryPolicy.onSuccess();
+ }
LL_DEBUGS("Texture") << "HTTP COMPLETE: " << mID
<< " status: " << status.toHex()
<< " '" << status.toString() << "'"
<< llendl;
+
// unsigned int offset(0), length(0), full_length(0);
// response->getRange(&offset, &length, &full_length);
// llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle
@@ -1907,13 +1996,16 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe
// << " offset: " << offset << " length: " << length
// << llendl;
+ std::string reason(status.toString());
+ setGetStatus(status, reason);
if (! status)
{
success = false;
- std::string reason(status.toString());
- setGetStatus(status, reason);
- llwarns << "CURL GET FAILED, status: " << status.toHex()
- << " reason: " << reason << llendl;
+ if (mFTType != FTT_MAP_TILE) // missing map tiles are normal, don't complain about them.
+ {
+ llwarns << mID << " CURL GET FAILED, status: " << status.toHex()
+ << " reason: " << reason << llendl;
+ }
}
else
{
@@ -2376,6 +2468,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
mQAMode(qa_mode),
mHttpRequest(NULL),
mHttpOptions(NULL),
+ mHttpOptionsWithHeaders(NULL),
mHttpHeaders(NULL),
mHttpMetricsHeaders(NULL),
mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
@@ -2406,11 +2499,13 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
mHttpRequest = new LLCore::HttpRequest;
mHttpOptions = new LLCore::HttpOptions;
+ mHttpOptionsWithHeaders = new LLCore::HttpOptions;
+ mHttpOptionsWithHeaders->setWantHeaders(true);
mHttpHeaders = new LLCore::HttpHeaders;
// *TODO: Should this be 'image/j2c' instead of 'image/x-j2c' ?
- mHttpHeaders->mHeaders.push_back(HTTP_OUT_HEADER_ACCEPT + ": " + HTTP_CONTENT_IMAGE_X_J2C);
+ mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C);
mHttpMetricsHeaders = new LLCore::HttpHeaders;
- mHttpMetricsHeaders->mHeaders.push_back(HTTP_OUT_HEADER_CONTENT_TYPE + ": " + HTTP_CONTENT_LLSD_XML);
+ mHttpMetricsHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML);
mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault();
}
@@ -2431,6 +2526,12 @@ LLTextureFetch::~LLTextureFetch()
mHttpOptions = NULL;
}
+ if (mHttpOptionsWithHeaders)
+ {
+ mHttpOptionsWithHeaders->release();
+ mHttpOptionsWithHeaders = NULL;
+ }
+
if (mHttpHeaders)
{
mHttpHeaders->release();
@@ -2465,7 +2566,11 @@ bool LLTextureFetch::createRequest(FTType f_type, const std::string& url, const
{
return false;
}
-
+
+ if (f_type == FTT_SERVER_BAKE)
+ {
+ LL_DEBUGS("Avatar") << " requesting " << id << " " << w << "x" << h << " discard " << desired_discard << llendl;
+ }
LLTextureFetchWorker* worker = getWorker(id) ;
if (worker)
{
@@ -2523,7 +2628,8 @@ bool LLTextureFetch::createRequest(FTType f_type, const std::string& url, const
worker->mNeedsAux = needs_aux;
worker->setImagePriority(priority);
worker->setDesiredDiscard(desired_discard, desired_size);
- worker->setCanUseHTTP(can_use_http) ;
+ worker->setCanUseHTTP(can_use_http);
+ worker->setUrl(url);
if (!worker->haveWork())
{
worker->setState(LLTextureFetchWorker::INIT);
@@ -2729,7 +2835,8 @@ LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id)
// Threads: T*
bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
- LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux)
+ LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux,
+ LLCore::HttpStatus& last_http_get_status)
{
bool res = false;
LLTextureFetchWorker* worker = getWorker(id);
@@ -2751,6 +2858,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
else if (worker->checkWork())
{
worker->lockWorkMutex(); // +Mw
+ last_http_get_status = worker->mGetStatus;
discard_level = worker->mDecodedDiscard;
raw = worker->mRawImage;
aux = worker->mAuxImage;
@@ -3221,25 +3329,14 @@ bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
void LLTextureFetchWorker::setState(e_state new_state)
{
- static const char* e_state_name[] =
- {
- "INVALID",
- "INIT",
- "LOAD_FROM_TEXTURE_CACHE",
- "CACHE_POST",
- "LOAD_FROM_NETWORK",
- "LOAD_FROM_SIMULATOR",
- "WAIT_HTTP_RESOURCE",
- "WAIT_HTTP_RESOURCE2",
- "SEND_HTTP_REQ",
- "WAIT_HTTP_REQ",
- "DECODE_IMAGE",
- "DECODE_IMAGE_UPDATE",
- "WRITE_TO_CACHE",
- "WAIT_ON_WRITE",
- "DONE"
- };
- LL_DEBUGS("Texture") << "id: " << mID << " FTType: " << mFTType << " disc: " << mDesiredDiscard << " sz: " << mDesiredSize << " state: " << e_state_name[mState] << " => " << e_state_name[new_state] << llendl;
+ if (mFTType == FTT_SERVER_BAKE)
+ {
+ // NOTE: turning on these log statements is a reliable way to get
+ // blurry images fairly frequently. Presumably this is an
+ // indication of some subtle timing or locking issue.
+
+// LL_INFOS("Texture") << "id: " << mID << " FTType: " << mFTType << " disc: " << mDesiredDiscard << " sz: " << mDesiredSize << " state: " << e_state_name[mState] << " => " << e_state_name[new_state] << llendl;
+ }
mState = new_state;
}
@@ -4043,7 +4140,7 @@ void LLTextureFetchDebugger::init()
{
mHttpHeaders = new LLCore::HttpHeaders;
// *TODO: Should this be 'image/j2c' instead of 'image/x-j2c' ?
- mHttpHeaders->mHeaders.push_back(HTTP_OUT_HEADER_ACCEPT + ": " + HTTP_CONTENT_IMAGE_X_J2C);
+ mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C);
}
}
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 902a3d7a25..237912cde7 100644..100755
--- 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) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, 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
@@ -95,7 +95,8 @@ public:
// Threads: T*
bool getRequestFinished(const LLUUID& id, S32& discard_level,
- LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux);
+ LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux,
+ LLCore::HttpStatus& last_http_get_status);
// Threads: T*
bool updateRequestPriority(const LLUUID& id, F32 priority);
@@ -351,6 +352,7 @@ private:
// LLCurl interfaces used in the past.
LLCore::HttpRequest * mHttpRequest; // Ttf
LLCore::HttpOptions * mHttpOptions; // Ttf
+ LLCore::HttpOptions * mHttpOptionsWithHeaders; // Ttf
LLCore::HttpHeaders * mHttpHeaders; // Ttf
LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf
LLCore::HttpRequest::policy_t mHttpPolicyClass; // T*
@@ -395,6 +397,9 @@ private:
e_tex_source mFetchSource;
e_tex_source mOriginFetchSource;
+ // Retry logic
+ //LLAdaptiveRetryPolicy mFetchRetryPolicy;
+
public:
//debug use
LLTextureFetchDebugger* getFetchDebugger() { return mFetchDebugger;}
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index fff9821e86..06efeb86d9 100644..100755
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -1179,6 +1179,107 @@ void move_inventory_item(
gAgent.sendReliableMessage();
}
+void handle_item_deletion(const LLUUID& item_id)
+{
+ LLPointer<LLViewerInventoryItem> obj = gInventory.getItem(item_id);
+ if(obj)
+ {
+ // From item removeFromServer()
+ LLInventoryModel::LLCategoryUpdate up(obj->getParentUUID(), -1);
+ gInventory.accountForUpdate(up);
+
+ // From purgeObject()
+ LLPreview::hide(item_id);
+ gInventory.deleteObject(item_id);
+ }
+}
+
+class RemoveItemResponder: public LLHTTPClient::Responder
+{
+public:
+ RemoveItemResponder(const LLUUID& item_id, LLPointer<LLInventoryCallback> callback):
+ mItemUUID(item_id),
+ mCallback(callback)
+ {
+ }
+ /* virtual */ void httpSuccess()
+ {
+ const LLSD& content = getContent();
+ if (!content.isMap())
+ {
+ failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);
+ return;
+ }
+ llinfos << "succeeded: " << ll_pretty_print_sd(content) << llendl;
+
+ handle_item_deletion(mItemUUID);
+
+ if (mCallback)
+ {
+ mCallback->fire(mItemUUID);
+ }
+ }
+ /*virtual*/ void httpFailure()
+ {
+ const LLSD& content = getContent();
+ if (!content.isMap())
+ {
+ failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);
+ return;
+ }
+ llwarns << "failed for " << mItemUUID << " content: " << ll_pretty_print_sd(content) << llendl;
+ }
+private:
+ LLPointer<LLInventoryCallback> mCallback;
+ const LLUUID mItemUUID;
+};
+
+void remove_inventory_item(
+ const LLUUID& item_id,
+ LLPointer<LLInventoryCallback> cb)
+{
+ llinfos << "item_id: [" << item_id << "] " << llendl;
+ LLPointer<LLViewerInventoryItem> obj = gInventory.getItem(item_id);
+ if(obj)
+ {
+ std::string cap;
+ if (gAgent.getRegion())
+ {
+ cap = gAgent.getRegion()->getCapability("InventoryAPIv3");
+ }
+ if (!cap.empty())
+ {
+ std::string url = cap + std::string("/item/") + item_id.asString();
+ llinfos << "url: " << url << llendl;
+ LLCurl::ResponderPtr responder_ptr = new RemoveItemResponder(item_id,cb);
+ LLHTTPClient::del(url,responder_ptr);
+ }
+ else // no cap
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RemoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ gAgent.sendReliableMessage();
+
+ // Update inventory and call callback immediately since
+ // message-based system has no callback mechanism (!)
+ handle_item_deletion(item_id);
+ if (cb)
+ {
+ cb->fire(item_id);
+ }
+ }
+ }
+ else
+ {
+ llwarns << "remove_inventory_item called for invalid or nonexistent item " << item_id << llendl;
+ }
+}
+
const LLUUID get_folder_by_itemtype(const LLInventoryItem *src)
{
LLUUID retval = LLUUID::null;
diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h
index 61b1b8d846..6b0e8ed4ef 100644..100755
--- a/indra/newview/llviewerinventory.h
+++ b/indra/newview/llviewerinventory.h
@@ -274,7 +274,7 @@ class LLBoostFuncInventoryCallback: public LLInventoryCallback
{
public:
- LLBoostFuncInventoryCallback(inventory_func_type fire_func,
+ LLBoostFuncInventoryCallback(inventory_func_type fire_func = no_op_inventory_func,
nullary_func_type destroy_func = no_op):
mFireFunc(fire_func),
mDestroyFunc(destroy_func)
@@ -365,6 +365,10 @@ void move_inventory_item(
const std::string& new_name,
LLPointer<LLInventoryCallback> cb);
+void remove_inventory_item(
+ const LLUUID& item_id,
+ LLPointer<LLInventoryCallback> cb);
+
const LLUUID get_folder_by_itemtype(const LLInventoryItem *src);
void copy_inventory_from_notecard(const LLUUID& destination_id,
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index f0d81c599c..e77b29aca4 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1641,6 +1641,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("FetchInventory2");
capabilityNames.append("FetchInventoryDescendents2");
capabilityNames.append("IncrementCOFVersion");
+ capabilityNames.append("InventoryAPIv3");
}
capabilityNames.append("GetDisplayNames");
diff --git a/indra/newview/llviewertexlayer.cpp b/indra/newview/llviewertexlayer.cpp
index 777e1f9c76..777e1f9c76 100755..100644
--- a/indra/newview/llviewertexlayer.cpp
+++ b/indra/newview/llviewertexlayer.cpp
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index eb6c453e76..45b402f0f6 100644..100755
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -1758,7 +1758,8 @@ bool LLViewerFetchedTexture::updateFetch()
if (mRawImage.notNull()) sRawCount--;
if (mAuxRawImage.notNull()) sAuxCount--;
- bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mRawImage, mAuxRawImage);
+ bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mRawImage, mAuxRawImage,
+ mLastHttpGetStatus);
if (mRawImage.notNull()) sRawCount++;
if (mAuxRawImage.notNull()) sAuxCount++;
if (finished)
@@ -1823,10 +1824,15 @@ bool LLViewerFetchedTexture::updateFetch()
// We finished but received no data
if (current_discard < 0)
{
- llwarns << "!mIsFetching, setting as missing, decode_priority " << decode_priority
- << " mRawDiscardLevel " << mRawDiscardLevel
- << " current_discard " << current_discard
- << llendl;
+ if (getFTType() != FTT_MAP_TILE)
+ {
+ llwarns << mID
+ << " Fetch failure, setting as missing, decode_priority " << decode_priority
+ << " mRawDiscardLevel " << mRawDiscardLevel
+ << " current_discard " << current_discard
+ << " stats " << mLastHttpGetStatus.toHex()
+ << llendl;
+ }
setIsMissingAsset();
desired_discard = -1;
}
@@ -2005,29 +2011,43 @@ void LLViewerFetchedTexture::forceToDeleteRequest()
mDesiredDiscardLevel = getMaxDiscardLevel() + 1;
}
-void LLViewerFetchedTexture::setIsMissingAsset()
+void LLViewerFetchedTexture::setIsMissingAsset(BOOL is_missing)
{
- if (mUrl.empty())
+ if (is_missing == mIsMissingAsset)
{
- llwarns << mID << ": Marking image as missing" << llendl;
+ return;
}
- else
+ if (is_missing)
{
- // This may or may not be an error - it is normal to have no
- // map tile on an empty region, but bad if we're failing on a
- // server bake texture.
- llwarns << mUrl << ": Marking image as missing" << llendl;
+ if (mUrl.empty())
+ {
+ llwarns << mID << ": Marking image as missing" << llendl;
+ }
+ else
+ {
+ // This may or may not be an error - it is normal to have no
+ // map tile on an empty region, but bad if we're failing on a
+ // server bake texture.
+ if (getFTType() != FTT_MAP_TILE)
+ {
+ llwarns << mUrl << ": Marking image as missing" << llendl;
+ }
+ }
+ if (mHasFetcher)
+ {
+ LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
+ mHasFetcher = FALSE;
+ mIsFetching = FALSE;
+ mLastPacketTimer.reset();
+ mFetchState = 0;
+ mFetchPriority = 0;
+ }
}
- if (mHasFetcher)
+ else
{
- LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
- mHasFetcher = FALSE;
- mIsFetching = FALSE;
- mLastPacketTimer.reset();
- mFetchState = 0;
- mFetchPriority = 0;
+ llinfos << mID << ": un-flagging missing asset" << llendl;
}
- mIsMissingAsset = TRUE;
+ mIsMissingAsset = is_missing;
}
void LLViewerFetchedTexture::setLoadedCallback( loaded_callback_func loaded_callback,
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index f2e1a90713..bf6aadd218 100644..100755
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -34,6 +34,7 @@
#include "llgltypes.h"
#include "llrender.h"
#include "llmetricperformancetester.h"
+#include "httpcommon.h"
#include <map>
#include <list>
@@ -120,7 +121,7 @@ public:
LLViewerTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps) ;
virtual S8 getType() const;
- virtual BOOL isMissingAsset()const ;
+ virtual BOOL isMissingAsset() const ;
virtual void dump(); // debug info to llinfos
/*virtual*/ bool bindDefaultImage(const S32 stage = 0) ;
@@ -338,8 +339,8 @@ public:
// more data.
/*virtual*/ void setKnownDrawSize(S32 width, S32 height);
- void setIsMissingAsset();
- /*virtual*/ BOOL isMissingAsset() const { return mIsMissingAsset; }
+ void setIsMissingAsset(BOOL is_missing = true);
+ /*virtual*/ BOOL isMissingAsset() const { return mIsMissingAsset; }
// returns dimensions of original image for local files (before power of two scaling)
// and returns 0 for all asset system images
@@ -446,10 +447,11 @@ protected:
S8 mIsRawImageValid;
S8 mHasFetcher; // We've made a fecth request
S8 mIsFetching; // Fetch request is active
- bool mCanUseHTTP ; //This texture can be fetched through http if true.
+ bool mCanUseHTTP; //This texture can be fetched through http if true.
+ LLCore::HttpStatus mLastHttpGetStatus; // Result of the most recently completed http request for this texture.
FTType mFTType; // What category of image is this - map tile, server bake, etc?
- mutable S8 mIsMissingAsset; // True if we know that there is no image asset with this image id in the database.
+ mutable BOOL mIsMissingAsset; // True if we know that there is no image asset with this image id in the database.
typedef std::list<LLLoadedCallbackEntry*> callback_list_t;
S8 mLoadedCallbackDesiredDiscardLevel;
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index bf6e28b93d..a56e09cde0 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -1884,6 +1884,10 @@ LLViewerFetchedTexture *LLVOAvatar::getBakedTextureImage(const U8 te, const LLUU
LL_DEBUGS("Avatar") << avString() << "from URL " << url << llendl;
result = LLViewerTextureManager::getFetchedTextureFromUrl(
url, FTT_SERVER_BAKE, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, uuid);
+ if (result->isMissingAsset())
+ {
+ result->setIsMissingAsset(false);
+ }
}
else
{
@@ -4410,7 +4414,7 @@ void LLVOAvatar::addLocalTextureStats( ETextureIndex idx, LLViewerFetchedTexture
}
const S32 MAX_TEXTURE_UPDATE_INTERVAL = 64 ; //need to call updateTextures() at least every 32 frames.
-const S32 MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL = S32_MAX ; //frames
+const S32 MAX_TEXTURE_VIRTUAL_SIZE_RESET_INTERVAL = S32_MAX ; //frames
void LLVOAvatar::checkTextureLoading()
{
static const F32 MAX_INVISIBLE_WAITING_TIME = 15.f ; //seconds
@@ -4473,11 +4477,11 @@ const F32 ADDITIONAL_PRI = 0.5f;
void LLVOAvatar::addBakedTextureStats( LLViewerFetchedTexture* imagep, F32 pixel_area, F32 texel_area_ratio, S32 boost_level)
{
//Note:
- //if this function is not called for the last MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL frames,
+ //if this function is not called for the last MAX_TEXTURE_VIRTUAL_SIZE_RESET_INTERVAL frames,
//the texture pipeline will stop fetching this texture.
imagep->resetTextureStats();
- imagep->setMaxVirtualSizeResetInterval(MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL);
+ imagep->setMaxVirtualSizeResetInterval(MAX_TEXTURE_VIRTUAL_SIZE_RESET_INTERVAL);
imagep->resetMaxVirtualSizeResetCounter() ;
mMaxPixelArea = llmax(pixel_area, mMaxPixelArea);
@@ -7300,7 +7304,7 @@ void LLVOAvatar::useBakedTexture( const LLUUID& id )
LLViewerTexture* image_baked = getImage( mBakedTextureDatas[i].mTextureIndex, 0 );
if (id == image_baked->getID())
{
- LL_DEBUGS("Avatar") << avString() << " i " << i << " id " << id << LL_ENDL;
+ //LL_DEBUGS("Avatar") << avString() << " i " << i << " id " << id << LL_ENDL;
mBakedTextureDatas[i].mIsLoaded = true;
mBakedTextureDatas[i].mLastTextureID = id;
mBakedTextureDatas[i].mIsUsed = true;
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 85f6f25009..98e8491cb1 100755
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -991,7 +991,7 @@ protected: // Shared with LLVOAvatarSelf
}; // LLVOAvatar
extern const F32 SELF_ADDITIONAL_PRI;
-extern const S32 MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL;
+extern const S32 MAX_TEXTURE_VIRTUAL_SIZE_RESET_INTERVAL;
std::string get_sequential_numbered_file_name(const std::string& prefix,
const std::string& suffix);
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index f89a72bc56..52c44e6e1b 100755
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -2587,7 +2587,7 @@ void LLVOAvatarSelf::addLocalTextureStats( ETextureIndex type, LLViewerFetchedTe
imagep->setBoostLevel(getAvatarBoostLevel());
imagep->setAdditionalDecodePriority(SELF_ADDITIONAL_PRI) ;
imagep->resetTextureStats();
- imagep->setMaxVirtualSizeResetInterval(MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL);
+ imagep->setMaxVirtualSizeResetInterval(MAX_TEXTURE_VIRTUAL_SIZE_RESET_INTERVAL);
imagep->addTextureStats( desired_pixels / texel_area_ratio );
imagep->forceUpdateBindStats() ;
if (imagep->getDiscardLevel() < 0)
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 105bef7321..1ff63c6def 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -10030,5 +10030,19 @@ Cannot create large prims that intersect other players. Please re-try when othe
name="okignore"
yestext="OK"/>
</notification>
-
+
+
+ <notification
+ icon="alertmodal.tga"
+ name="ConfirmExitWithoutSave"
+ type="alertmodal">
+ Closing this window will discard any changes you have made.
+ <tag>confirm</tag>
+ <usetemplate
+ name="okcancelignore"
+ notext="Cancel"
+ yestext="OK"
+ ignoretext="Don't show me this again."/>
+ </notification>
+
</notifications>
diff --git a/indra/newview/tests/llhttpretrypolicy_test.cpp b/indra/newview/tests/llhttpretrypolicy_test.cpp
new file mode 100755
index 0000000000..ed7f3ba326
--- /dev/null
+++ b/indra/newview/tests/llhttpretrypolicy_test.cpp
@@ -0,0 +1,321 @@
+/**
+ * @file llhttpretrypolicy_test.cpp
+ * @brief Header tests to exercise the LLHTTPRetryPolicy classes.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, 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 "../llviewerprecompiledheaders.h"
+#include "../llhttpretrypolicy.h"
+#include "lltut.h"
+
+namespace tut
+{
+struct TestData
+{
+};
+
+typedef test_group<TestData> RetryPolicyTestGroup;
+typedef RetryPolicyTestGroup::object RetryPolicyTestObject;
+RetryPolicyTestGroup retryPolicyTestGroup("retry_policy");
+
+template<> template<>
+void RetryPolicyTestObject::test<1>()
+{
+ LLAdaptiveRetryPolicy never_retry(1.0,1.0,1.0,0);
+ LLSD headers;
+ F32 wait_seconds;
+
+ // No retry until we've failed a try.
+ ensure("never retry 0", !never_retry.shouldRetry(wait_seconds));
+
+ // 0 retries max.
+ never_retry.onFailure(500,headers);
+ ensure("never retry 1", !never_retry.shouldRetry(wait_seconds));
+}
+
+template<> template<>
+void RetryPolicyTestObject::test<2>()
+{
+ LLAdaptiveRetryPolicy retry404(1.0,2.0,3.0,10);
+ LLSD headers;
+ F32 wait_seconds;
+
+ retry404.onFailure(404,headers);
+ ensure("no retry on 404", !retry404.shouldRetry(wait_seconds));
+}
+
+template<> template<>
+void RetryPolicyTestObject::test<3>()
+{
+ // Should retry after 1.0, 2.0, 3.0, 3.0 seconds.
+ LLAdaptiveRetryPolicy basic_retry(1.0,3.0,2.0,4);
+ LLSD headers;
+ F32 wait_seconds;
+ bool should_retry;
+ U32 frac_bits = 6;
+
+ // No retry until we've failed a try.
+ ensure("basic_retry 0", !basic_retry.shouldRetry(wait_seconds));
+
+ // Starting wait 1.0
+ basic_retry.onFailure(500,headers);
+ should_retry = basic_retry.shouldRetry(wait_seconds);
+ ensure("basic_retry 1", should_retry);
+ ensure_approximately_equals("basic_retry 1", wait_seconds, 1.0F, frac_bits);
+
+ // Double wait to 2.0
+ basic_retry.onFailure(500,headers);
+ should_retry = basic_retry.shouldRetry(wait_seconds);
+ ensure("basic_retry 2", should_retry);
+ ensure_approximately_equals("basic_retry 2", wait_seconds, 2.0F, frac_bits);
+
+ // Hit max wait of 3.0 (4.0 clamped to max 3)
+ basic_retry.onFailure(500,headers);
+ should_retry = basic_retry.shouldRetry(wait_seconds);
+ ensure("basic_retry 3", should_retry);
+ ensure_approximately_equals("basic_retry 3", wait_seconds, 3.0F, frac_bits);
+
+ // At max wait, should stay at 3.0
+ basic_retry.onFailure(500,headers);
+ should_retry = basic_retry.shouldRetry(wait_seconds);
+ ensure("basic_retry 4", should_retry);
+ ensure_approximately_equals("basic_retry 4", wait_seconds, 3.0F, frac_bits);
+
+ // Max retries, should fail now.
+ basic_retry.onFailure(500,headers);
+ should_retry = basic_retry.shouldRetry(wait_seconds);
+ ensure("basic_retry 5", !should_retry);
+
+ // Max retries, should fail now.
+ basic_retry.onFailure(500,headers);
+ should_retry = basic_retry.shouldRetry(wait_seconds);
+ ensure("basic_retry 5", !should_retry);
+
+ // After a success, should reset to the starting state.
+ basic_retry.onSuccess();
+
+ // No retry until we've failed a try.
+ ensure("basic_retry 6", !basic_retry.shouldRetry(wait_seconds));
+
+ // Starting wait 1.0
+ basic_retry.onFailure(500,headers);
+ should_retry = basic_retry.shouldRetry(wait_seconds);
+ ensure("basic_retry 7", should_retry);
+ ensure_approximately_equals("basic_retry 7", wait_seconds, 1.0F, frac_bits);
+
+ // Double wait to 2.0
+ basic_retry.onFailure(500,headers);
+ should_retry = basic_retry.shouldRetry(wait_seconds);
+ ensure("basic_retry 8", should_retry);
+ ensure_approximately_equals("basic_retry 8", wait_seconds, 2.0F, frac_bits);
+}
+
+// Retries should stop as soon as a non-5xx error is received.
+template<> template<>
+void RetryPolicyTestObject::test<4>()
+{
+ // Should retry after 1.0, 2.0, 3.0, 3.0 seconds.
+ LLAdaptiveRetryPolicy killer404(1.0,3.0,2.0,4);
+ LLSD headers;
+ F32 wait_seconds;
+ bool should_retry;
+ U32 frac_bits = 6;
+
+ // Starting wait 1.0
+ killer404.onFailure(500,headers);
+ should_retry = killer404.shouldRetry(wait_seconds);
+ ensure("killer404 1", should_retry);
+ ensure_approximately_equals("killer404 1", wait_seconds, 1.0F, frac_bits);
+
+ // Double wait to 2.0
+ killer404.onFailure(500,headers);
+ should_retry = killer404.shouldRetry(wait_seconds);
+ ensure("killer404 2", should_retry);
+ ensure_approximately_equals("killer404 2", wait_seconds, 2.0F, frac_bits);
+
+ // Should fail on non-5xx
+ killer404.onFailure(404,headers);
+ should_retry = killer404.shouldRetry(wait_seconds);
+ ensure("killer404 3", !should_retry);
+
+ // After a non-5xx, should keep failing.
+ killer404.onFailure(500,headers);
+ should_retry = killer404.shouldRetry(wait_seconds);
+ ensure("killer404 4", !should_retry);
+}
+
+// Test handling of "retry-after" header. If present, this header
+// value overrides the computed delay, but does not affect the
+// progression of delay values. For example, if the normal
+// progression of delays would be 1,2,4,8..., but the 2nd and 3rd calls
+// get a retry header of 33, the pattern would become 1,33,33,8...
+template<> template<>
+void RetryPolicyTestObject::test<5>()
+{
+ LLAdaptiveRetryPolicy policy(1.0,25.0,2.0,6);
+ LLSD headers_with_retry;
+ headers_with_retry[HTTP_IN_HEADER_RETRY_AFTER] = "666";
+ LLSD headers_without_retry;
+ F32 wait_seconds;
+ bool should_retry;
+ U32 frac_bits = 6;
+
+ policy.onFailure(500,headers_without_retry);
+ should_retry = policy.shouldRetry(wait_seconds);
+ ensure("retry header 1", should_retry);
+ ensure_approximately_equals("retry header 1", wait_seconds, 1.0F, frac_bits);
+
+ policy.onFailure(500,headers_without_retry);
+ should_retry = policy.shouldRetry(wait_seconds);
+ ensure("retry header 2", should_retry);
+ ensure_approximately_equals("retry header 2", wait_seconds, 2.0F, frac_bits);
+
+ policy.onFailure(500,headers_with_retry);
+ should_retry = policy.shouldRetry(wait_seconds);
+ ensure("retry header 3", should_retry);
+ // 4.0 overrides by header -> 666.0
+ ensure_approximately_equals("retry header 3", wait_seconds, 666.0F, frac_bits);
+
+ policy.onFailure(500,headers_with_retry);
+ should_retry = policy.shouldRetry(wait_seconds);
+ ensure("retry header 4", should_retry);
+ // 8.0 overrides by header -> 666.0
+ ensure_approximately_equals("retry header 4", wait_seconds, 666.0F, frac_bits);
+
+ policy.onFailure(500,headers_without_retry);
+ should_retry = policy.shouldRetry(wait_seconds);
+ ensure("retry header 5", should_retry);
+ ensure_approximately_equals("retry header 5", wait_seconds, 16.0F, frac_bits);
+
+ policy.onFailure(500,headers_without_retry);
+ should_retry = policy.shouldRetry(wait_seconds);
+ ensure("retry header 6", should_retry);
+ ensure_approximately_equals("retry header 6", wait_seconds, 25.0F, frac_bits);
+
+ policy.onFailure(500,headers_with_retry);
+ should_retry = policy.shouldRetry(wait_seconds);
+ ensure("retry header 7", !should_retry);
+}
+
+// Test getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait),
+// used by header parsing of the retry policy.
+template<> template<>
+void RetryPolicyTestObject::test<6>()
+{
+ F32 seconds_to_wait;
+ bool success;
+
+ std::string str1("0");
+ seconds_to_wait = F32_MAX;
+ success = getSecondsUntilRetryAfter(str1, seconds_to_wait);
+ ensure("parse 1", success);
+ ensure_equals("parse 1", seconds_to_wait, 0.0);
+
+ std::string str2("999.9");
+ seconds_to_wait = F32_MAX;
+ success = getSecondsUntilRetryAfter(str2, seconds_to_wait);
+ ensure("parse 2", success);
+ ensure_approximately_equals("parse 2", seconds_to_wait, 999.9F, 8);
+
+ time_t nowseconds;
+ time(&nowseconds);
+ std::string str3 = LLDate((F64)(nowseconds+44)).asRFC1123();
+ seconds_to_wait = F32_MAX;
+ success = getSecondsUntilRetryAfter(str3, seconds_to_wait);
+ std::cerr << " str3 [" << str3 << "]" << std::endl;
+ ensure("parse 3", success);
+ ensure_approximately_equals_range("parse 3", seconds_to_wait, 44.0F, 2.0F);
+}
+
+// Test retry-after field in both llmessage and CoreHttp headers.
+template<> template<>
+void RetryPolicyTestObject::test<7>()
+{
+ std::cerr << "7 starts" << std::endl;
+
+ LLSD sd_headers;
+ time_t nowseconds;
+ time(&nowseconds);
+ LLAdaptiveRetryPolicy policy(17.0,644.0,3.0,5);
+ F32 seconds_to_wait;
+ bool should_retry;
+
+ // No retry until we've failed a try.
+ ensure("header 0", !policy.shouldRetry(seconds_to_wait));
+
+ // no retry header, use default.
+ policy.onFailure(500,LLSD());
+ should_retry = policy.shouldRetry(seconds_to_wait);
+ ensure("header 1", should_retry);
+ ensure_approximately_equals("header 1", seconds_to_wait, 17.0F, 6);
+
+ // retry header should override, give delay of 0
+ std::string date_string = LLDate((F64)(nowseconds+7)).asRFC1123();
+ sd_headers[HTTP_IN_HEADER_RETRY_AFTER] = date_string;
+ policy.onFailure(503,sd_headers);
+ should_retry = policy.shouldRetry(seconds_to_wait);
+ ensure("header 2", should_retry);
+ ensure_approximately_equals_range("header 2", seconds_to_wait, 7.0F, 2.0F);
+
+ LLCore::HttpResponse *response;
+ LLCore::HttpHeaders *headers;
+
+ response = new LLCore::HttpResponse();
+ headers = new LLCore::HttpHeaders();
+ response->setStatus(503);
+ response->setHeaders(headers);
+ headers->append(HTTP_IN_HEADER_RETRY_AFTER, std::string("600"));
+ policy.onFailure(response);
+ should_retry = policy.shouldRetry(seconds_to_wait);
+ ensure("header 3",should_retry);
+ ensure_approximately_equals("header 3", seconds_to_wait, 600.0F, 6);
+ response->release();
+
+ response = new LLCore::HttpResponse();
+ headers = new LLCore::HttpHeaders();
+ response->setStatus(503);
+ response->setHeaders(headers);
+ time(&nowseconds);
+ date_string = LLDate((F64)(nowseconds+77)).asRFC1123();
+ std::cerr << "date_string [" << date_string << "]" << std::endl;
+ headers->append(HTTP_IN_HEADER_RETRY_AFTER,date_string);
+ policy.onFailure(response);
+ should_retry = policy.shouldRetry(seconds_to_wait);
+ ensure("header 4",should_retry);
+ ensure_approximately_equals_range("header 4", seconds_to_wait, 77.0F, 2.0F);
+ response->release();
+
+ // Timeout should be clamped at max.
+ policy.onFailure(500,LLSD());
+ should_retry = policy.shouldRetry(seconds_to_wait);
+ ensure("header 5", should_retry);
+ ensure_approximately_equals("header 5", seconds_to_wait, 644.0F, 6);
+
+ // No more retries.
+ policy.onFailure(500,LLSD());
+ should_retry = policy.shouldRetry(seconds_to_wait);
+ ensure("header 6", !should_retry);
+}
+
+}
+
diff --git a/indra/test/lltut.h b/indra/test/lltut.h
index 55d84bcaca..243e869be7 100644..100755
--- a/indra/test/lltut.h
+++ b/indra/test/lltut.h
@@ -65,6 +65,16 @@ namespace tut
ensure_approximately_equals(NULL, actual, expected, frac_bits);
}
+ inline void ensure_approximately_equals_range(const char *msg, F32 actual, F32 expected, F32 delta)
+ {
+ if (fabs(actual-expected)>delta)
+ {
+ std::stringstream ss;
+ ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected << " tolerance: " << delta;
+ throw tut::failure(ss.str().c_str());
+ }
+ }
+
inline void ensure_memory_matches(const char* msg,const void* actual, U32 actual_len, const void* expected,U32 expected_len)
{
if((expected_len != actual_len) ||