diff options
45 files changed, 2395 insertions, 698 deletions
diff --git a/autobuild.xml b/autobuild.xml index 80a44ec75d..95fb5a9629 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1290,9 +1290,9 @@              <key>archive</key>              <map>                <key>hash</key> -              <string>5bc44db15eb3cca021382e40e04a9a38</string> +              <string>419c3634dd982e8855785ce36f22892e</string>                <key>url</key> -              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearanceutility-source/rev/271972/arch/Linux/installer/llappearanceutility_source-0.1-linux-20130315.tar.bz2</string> +              <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearanceutility-source/rev/274704/arch/Linux/installer/llappearanceutility_source-0.1-linux-20130424.tar.bz2</string>              </map>              <key>name</key>              <string>linux</string> 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) ||   | 
