diff --git a/indra/aistatemachine/aicurl.cpp b/indra/aistatemachine/aicurl.cpp index 6563bbdb8..58e2eaf37 100644 --- a/indra/aistatemachine/aicurl.cpp +++ b/indra/aistatemachine/aicurl.cpp @@ -458,7 +458,7 @@ AIHTTPTimeoutPolicy const& Responder::getHTTPTimeoutPolicy(void) const // Called with HTML header. // virtual -void Responder::completedHeaders(U32, std::string const&, AIHTTPHeaders const&) +void Responder::completedHeaders(U32, std::string const&, AIHTTPReceivedHeaders const&) { // This should not be called unless a derived class implemented it. llerrs << "Unexpected call to completedHeaders()." << llendl; diff --git a/indra/aistatemachine/aicurl.h b/indra/aistatemachine/aicurl.h index 655f6ecaa..f2ed94347 100644 --- a/indra/aistatemachine/aicurl.h +++ b/indra/aistatemachine/aicurl.h @@ -212,7 +212,7 @@ class Responder : public AICurlResponderBufferEvents { std::string mURL; // Headers received from the server. - AIHTTPHeaders mReceivedHeaders; + AIHTTPReceivedHeaders mReceivedHeaders; public: // Called to set the URL of the current request for this Responder, @@ -245,7 +245,7 @@ class Responder : public AICurlResponderBufferEvents { // Derived classes can override this to get the HTML headers that were received, when the message is completed. // The default does nothing. - virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPHeaders const& headers); + virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers); public: // Derived classes that implement completedHeaders() should return true here. diff --git a/indra/llmessage/aihttpheaders.cpp b/indra/llmessage/aihttpheaders.cpp index 1c6c6bc07..0c6009cad 100644 --- a/indra/llmessage/aihttpheaders.cpp +++ b/indra/llmessage/aihttpheaders.cpp @@ -35,10 +35,6 @@ #include "debug_libcurl.h" #endif -AIHTTPHeaders::AIHTTPHeaders(void) -{ -} - AIHTTPHeaders::AIHTTPHeaders(std::string const& key, std::string const& value) : mContainer(new Container) { addHeader(key, value); @@ -105,3 +101,53 @@ std::ostream& operator<<(std::ostream& os, AIHTTPHeaders const& headers) return os; } +void AIHTTPReceivedHeaders::addHeader(std::string const& key, std::string const& value) +{ + if (!mContainer) + { + mContainer = new Container; + } + mContainer->mKeyValuePairs.insert(container_t::value_type(key, value)); +} + +bool AIHTTPReceivedHeaders::hasHeader(std::string const& key) const +{ + return !mContainer ? false : (mContainer->mKeyValuePairs.find(key) != mContainer->mKeyValuePairs.end()); +} + +bool AIHTTPReceivedHeaders::getFirstValue(std::string const& key, std::string& value_out) const +{ + AIHTTPReceivedHeaders::container_t::const_iterator iter; + if (!mContainer || (iter = mContainer->mKeyValuePairs.find(key)) == mContainer->mKeyValuePairs.end()) + return false; + value_out = iter->second; + return true; +} + +bool AIHTTPReceivedHeaders::getValues(std::string const& key, range_type& value_out) const +{ + if (!mContainer) + return false; + value_out = mContainer->mKeyValuePairs.equal_range(key); + return value_out.first != value_out.second; +} + +std::ostream& operator<<(std::ostream& os, AIHTTPReceivedHeaders const& headers) +{ + os << '{'; + if (headers.mContainer) + { + bool first = true; + AIHTTPReceivedHeaders::container_t::const_iterator const end = headers.mContainer->mKeyValuePairs.end(); + for (AIHTTPReceivedHeaders::container_t::const_iterator iter = headers.mContainer->mKeyValuePairs.begin(); iter != end; ++iter) + { + if (!first) + os << ", "; + os << '"' << iter->first << ": " << iter->second << '"'; + first = false; + } + } + os << '}'; + return os; +} + diff --git a/indra/llmessage/aihttpheaders.h b/indra/llmessage/aihttpheaders.h index 3fdce0582..95766c3e9 100644 --- a/indra/llmessage/aihttpheaders.h +++ b/indra/llmessage/aihttpheaders.h @@ -26,6 +26,8 @@ * * 15/08/2012 * Initial version, written by Aleric Inglewood @ SL + * 21/10/2012 + * Added AIHTTPReceivedHeaders */ #ifndef AIHTTPHEADERS_H @@ -34,6 +36,7 @@ #include #include #include +#include #include "llpointer.h" #include "llthread.h" // LLThreadSafeRefCount @@ -49,7 +52,7 @@ class AIHTTPHeaders { }; // Construct an empty container. - AIHTTPHeaders(void); + AIHTTPHeaders(void) { } // Construct a container with a single header. AIHTTPHeaders(std::string const& key, std::string const& value); @@ -86,4 +89,70 @@ class AIHTTPHeaders { LLPointer mContainer; }; +// Functor that returns true if c1 is less than c2, ignoring bit 5. +// The effect is that characters in the range a-z equivalent ordering with A-Z. +// This function assumes UTF-8 or ASCII encoding! +// +// Note that other characters aren't important in the case of HTTP header keys; +// however if one considers all printable ASCII characters, then this functor +// also compares "@[\]^" equal to "`{|}~" (any other is either not printable or +// would be equal to a not printable character). +struct AIHTTPReceivedHeadersCharCompare { + bool operator()(std::string::value_type c1, std::string::value_type c2) const + { + static std::string::value_type const bit5 = 0x20; + return (c1 | bit5) < (c2 | bit5); + } +}; + +// Functor to lexiographically compare two HTTP header keys using the above predicate. +// This means that for example "Content-Type" and "content-type" will have equivalent ordering. +struct AIHTTPReceivedHeadersCompare { + bool operator()(std::string const& h1, std::string const& h2) const + { + static AIHTTPReceivedHeadersCharCompare const predicate; + return std::lexicographical_compare(h1.begin(), h1.end(), h2.begin(), h2.end(), predicate); + } +}; + +class AIHTTPReceivedHeaders { + private: + typedef std::multimap container_t; + + public: + typedef container_t::const_iterator iterator_type; + typedef std::pair range_type; + + // Construct an empty container. + AIHTTPReceivedHeaders(void) { } + + // Clear all headers. + void clear(void) { if (mContainer) mContainer->mKeyValuePairs.clear(); } + + // Add a header. + void addHeader(std::string const& key, std::string const& value); + + // Return true if there are no headers associated with this object. + bool empty(void) const { return !mContainer || mContainer->mKeyValuePairs.empty(); } + + // Return true if the header exists. + bool hasHeader(std::string const& key) const; + + // Return true if key exists and fill value_out with the value. Return false otherwise. + bool getFirstValue(std::string const& key, std::string& value_out) const; + + // Return true if key exists and fill value_out with all values. Return false otherwise. + bool getValues(std::string const& key, range_type& value_out) const; + + // For debug purposes. + friend std::ostream& operator<<(std::ostream& os, AIHTTPReceivedHeaders const& headers); + + private: + struct Container : public LLThreadSafeRefCount { + container_t mKeyValuePairs; + }; + + LLPointer mContainer; +}; + #endif // AIHTTPHEADERS_H diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index fb4eed79a..57affabfe 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -129,7 +129,7 @@ namespace LLAvatarNameCache // Erase expired names from cache void eraseUnrefreshed(); - bool expirationFromCacheControl(AIHTTPHeaders const& headers, F64* expires); + bool expirationFromCacheControl(AIHTTPReceivedHeaders const& headers, F64* expires); } /* Sample response: @@ -182,7 +182,7 @@ private: std::vector mAgentIDs; // Need the headers to look up Expires: and Retry-After: - AIHTTPHeaders mHeaders; + AIHTTPReceivedHeaders mHeaders; public: virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return avatarNameResponder_timeout; } @@ -191,7 +191,7 @@ public: : mAgentIDs(agent_ids) { } - /*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPHeaders const& headers) + /*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) { mHeaders = headers; } @@ -791,42 +791,33 @@ void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_na sCache[agent_id] = av_name; } -F64 LLAvatarNameCache::nameExpirationFromHeaders(AIHTTPHeaders const& headers) +F64 LLAvatarNameCache::nameExpirationFromHeaders(AIHTTPReceivedHeaders const& headers) { - F64 expires = 0.0; - if (expirationFromCacheControl(headers, &expires)) - { - return expires; - } - else - { - // With no expiration info, default to an hour - const F64 DEFAULT_EXPIRES = 60.0 * 60.0; - F64 now = LLFrameTimer::getTotalSeconds(); - return now + DEFAULT_EXPIRES; - } + F64 expires; + expirationFromCacheControl(headers, &expires); + return expires; } -bool LLAvatarNameCache::expirationFromCacheControl(AIHTTPHeaders const& headers, F64* expires) +bool LLAvatarNameCache::expirationFromCacheControl(AIHTTPReceivedHeaders const& headers, F64* expires) { bool fromCacheControl = false; + S32 max_age = 3600; // With no expiration info, default to an hour. F64 now = LLFrameTimer::getTotalSeconds(); // Allow the header to override the default std::string cache_control; - if (headers.getValue("cache-control", cache_control)) + if (headers.getFirstValue("cache-control", cache_control)) { - S32 max_age = 0; if (max_age_from_cache_control(cache_control, &max_age)) { - *expires = now + (F64)max_age; fromCacheControl = true; } } + *expires = now + (F64)max_age; LL_DEBUGS("AvNameCache") << ( fromCacheControl ? "expires based on cache control " : "default expiration " ) << "in " << *expires - now << " seconds" << LL_ENDL; - + return fromCacheControl; } diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index 268133b02..75f613e74 100644 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -33,7 +33,7 @@ #include class LLUUID; -class AIHTTPHeaders; +class AIHTTPReceivedHeaders; namespace LLAvatarNameCache { @@ -98,7 +98,7 @@ namespace LLAvatarNameCache // Compute name expiration time from HTTP Cache-Control header, // or return default value, in seconds from epoch. - F64 nameExpirationFromHeaders(AIHTTPHeaders const& headers); + F64 nameExpirationFromHeaders(AIHTTPReceivedHeaders const& headers); void addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb); } diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index baf90fc7e..f43619553 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -64,10 +64,10 @@ public: virtual bool needsHeaders(void) const { return true; } - virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPHeaders const& headers) + virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) { std::string media_type; - bool content_type_found = headers.getValue("content-type", media_type); + bool content_type_found = headers.getFirstValue("content-type", media_type); llassert_always(content_type_found); std::string::size_type idx1 = media_type.find_first_of(";"); std::string mime_type = media_type.substr(0, idx1); diff --git a/indra/newview/llviewerdisplayname.cpp b/indra/newview/llviewerdisplayname.cpp index a9b3a27f3..e5590de87 100644 --- a/indra/newview/llviewerdisplayname.cpp +++ b/indra/newview/llviewerdisplayname.cpp @@ -184,7 +184,7 @@ class LLDisplayNameUpdate : public LLHTTPNode // *TODO: get actual headers out of ResponsePtr //LLSD headers = response->mHeaders; av_name.mExpires = - LLAvatarNameCache::nameExpirationFromHeaders(AIHTTPHeaders()); + LLAvatarNameCache::nameExpirationFromHeaders(AIHTTPReceivedHeaders()); LLAvatarNameCache::insert(agent_id, av_name); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 093a3989b..25acb0b83 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -82,10 +82,10 @@ public: virtual bool needsHeaders(void) const { return true; } - virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPHeaders const& headers) + virtual void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) { std::string media_type; - bool content_type_found = headers.getValue("content-type", media_type); + bool content_type_found = headers.getFirstValue("content-type", media_type); llassert_always(content_type_found); std::string::size_type idx1 = media_type.find_first_of(";"); std::string mime_type = media_type.substr(0, idx1); @@ -125,14 +125,15 @@ public: /* virtual */ bool needsHeaders(void) const { return true; } - /* virtual */ void completedHeaders(U32 status, std::string const& reason, AIHTTPHeaders const& headers) + /* virtual */ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) { LL_DEBUGS("MediaAuth") << "status = " << status << ", reason = " << reason << LL_ENDL; LL_DEBUGS("MediaAuth") << headers << LL_ENDL; - std::string cookie; - if (headers.getValue("set-cookie", cookie)) + AIHTTPReceivedHeaders::range_type cookies; + if (headers.getValues("set-cookie", cookies)) { - LLViewerMedia::openIDCookieResponse(cookie); + for (AIHTTPReceivedHeaders::iterator_type cookie = cookies.first; cookie != cookies.second; ++cookie) + LLViewerMedia::openIDCookieResponse(cookie->second); } } @@ -164,15 +165,16 @@ public: /* virtual */ bool needsHeaders(void) const { return true; } - /* virtual */ void completedHeaders(U32 status, std::string const& reason, AIHTTPHeaders const& headers) + /* virtual */ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) { LL_INFOS("MediaAuth") << "status = " << status << ", reason = " << reason << LL_ENDL; LL_INFOS("MediaAuth") << headers << LL_ENDL; - std::string cookie; - if (headers.getValue("set-cookie", cookie)) + AIHTTPReceivedHeaders::range_type cookies; + if (headers.getValues("set-cookie", cookies)) { - LLViewerMedia::getCookieStore()->setCookiesFromHost(cookie, mHost); + for (AIHTTPReceivedHeaders::iterator_type cookie = cookies.first; cookie != cookies.second; ++cookie) + LLViewerMedia::getCookieStore()->setCookiesFromHost(cookie->second, mHost); } }