diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 9df34518e..6ca834afe 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1114,7 +1114,7 @@ void CurlEasyRequest::finalizeRequest(std::string const& url, AIHTTPTimeoutPolic setopt(CURLOPT_HTTPHEADER, mHeaders); setoptString(CURLOPT_URL, url); llassert(!mPerHostPtr); - mLowercaseHostname = AIPerHostRequestQueue::extract_canonical_hostname(url); + mLowercaseServicename = AIPerHostRequestQueue::extract_canonical_servicename(url); mTimeoutPolicy = &policy; state_machine->setTotalDelayTimeout(policy.getTotalDelay()); // The following line is a bit tricky: we store a pointer to the object without increasing its reference count. @@ -1140,7 +1140,7 @@ void CurlEasyRequest::finalizeRequest(std::string const& url, AIHTTPTimeoutPolic // // get less connect time, while it still (also) has to wait for this DNS lookup. void CurlEasyRequest::set_timeout_opts(void) { - setopt(CURLOPT_CONNECTTIMEOUT, mTimeoutPolicy->getConnectTimeout(mLowercaseHostname)); + setopt(CURLOPT_CONNECTTIMEOUT, mTimeoutPolicy->getConnectTimeout(getLowercaseHostname())); setopt(CURLOPT_TIMEOUT, mTimeoutPolicy->getCurlTransaction()); } @@ -1241,9 +1241,9 @@ AIPerHostRequestQueuePtr CurlEasyRequest::getPerHostPtr(void) if (!mPerHostPtr) { // mPerHostPtr is really just a speed-up cache. - // The reason we can cache it is because mLowercaseHostname is only set + // The reason we can cache it is because mLowercaseServicename is only set // in finalizeRequest which may only be called once: it never changes. - mPerHostPtr = AIPerHostRequestQueue::instance(mLowercaseHostname); + mPerHostPtr = AIPerHostRequestQueue::instance(mLowercaseServicename); } return mPerHostPtr; } @@ -1254,6 +1254,11 @@ bool CurlEasyRequest::removeFromPerHostQueue(AICurlEasyRequest const& easy_reque return mPerHostPtr && PerHostRequestQueue_wat(*mPerHostPtr)->cancel(easy_request); } +std::string CurlEasyRequest::getLowercaseHostname(void) const +{ + return mLowercaseServicename.substr(0, mLowercaseServicename.find_last_of(':')); +} + //----------------------------------------------------------------------------- // BufferedCurlEasyRequest diff --git a/indra/llmessage/aicurlperhost.cpp b/indra/llmessage/aicurlperhost.cpp index 1d2f157a9..87d7131b4 100644 --- a/indra/llmessage/aicurlperhost.cpp +++ b/indra/llmessage/aicurlperhost.cpp @@ -92,40 +92,86 @@ using namespace AICurlPrivate; // - port does not contain a ':', and if it exists is always prepended by a ':'. // //static -std::string AIPerHostRequestQueue::extract_canonical_hostname(std::string const& url) +std::string AIPerHostRequestQueue::extract_canonical_servicename(std::string const& url) { - std::string::size_type pos; - std::string::size_type authority = 0; // Default if there is no sheme. - if ((pos = url.find("://")) != url.npos && pos < url.find('/')) authority = pos + 3; // Skip the "sheme://" if any, the second find is to avoid finding a "://" as part of path-abempty. - std::string::size_type host = authority; // Default if there is no userinfo. - if ((pos = url.find('@', authority)) != url.npos) host = pos + 1; // Skip the "userinfo@" if any. - authority = url.length() - 1; // Default last character of host if there is no path-abempty. - if ((pos = url.find('/', host)) != url.npos) authority = pos - 1; // Point to last character of host. - std::string::size_type len = url.find_last_not_of(":0123456789", authority) - host + 1; // Skip trailing ":port", if any. - std::string hostname(url, host, len); + char const* p = url.data(); + char const* const end = p + url.size(); + char const* sheme_colon = NULL; + char const* sheme_slash = NULL; + char const* first_ampersand = NULL; + char const* port_colon = NULL; + std::string servicename; + char const* hostname = p; // Default in the case there is no "sheme://userinfo@". + while (p < end) + { + int c = *p; + if (c == ':') + { + if (!port_colon && std::isdigit(p[1])) + { + port_colon = p; + } + else if (!sheme_colon && !sheme_slash && !first_ampersand && !port_colon) + { + // Found a colon before any slash or ampersand: this has to be the colon between the sheme and the hier-part. + sheme_colon = p; + } + } + else if (c == '/') + { + if (!sheme_slash && sheme_colon && sheme_colon == p - 1 && !first_ampersand && p[1] == '/') + { + // Found the first '/' in the first occurance of the sequence "://". + sheme_slash = p; + hostname = ++p + 1; // Point hostname to the start of the authority, the default when there is no "userinfo@" part. + servicename.clear(); // Remove the sheme. + } + else + { + // Found slash that is not part of the "sheme://" string. Signals end of authority. + // We're done. + break; + } + } + else if (c == '@') + { + if (!first_ampersand) + { + first_ampersand = p; + hostname = p + 1; + servicename.clear(); // Remove the "userinfo@" + } + } + if (p >= hostname) + { + // Convert hostname to lowercase in a way that we compare two hostnames equal iff libcurl does. #if APR_CHARSET_EBCDIC #error Not implemented #else - // Convert hostname to lowercase in a way that we compare two hostnames equal iff libcurl does. - for (std::string::iterator iter = hostname.begin(); iter != hostname.end(); ++iter) - { - int c = *iter; - if (c >= 'A' && c <= 'Z') - *iter = c + ('a' - 'A'); - } + if (c >= 'A' && c <= 'Z') + c += ('a' - 'A'); #endif - return hostname; + servicename += c; + } + ++p; + } + // Strip of any trailing ":80". + if (p - 3 == port_colon && p[-1] == '0' && p[-2] == '8') + { + return servicename.substr(0, p - hostname - 3); + } + return servicename; } //static -AIPerHostRequestQueuePtr AIPerHostRequestQueue::instance(std::string const& hostname) +AIPerHostRequestQueuePtr AIPerHostRequestQueue::instance(std::string const& servicename) { - llassert(!hostname.empty()); + llassert(!servicename.empty()); instance_map_wat instance_map_w(sInstanceMap); - AIPerHostRequestQueue::iterator iter = instance_map_w->find(hostname); + AIPerHostRequestQueue::iterator iter = instance_map_w->find(servicename); if (iter == instance_map_w->end()) { - iter = instance_map_w->insert(instance_map_type::value_type(hostname, new RefCountedThreadSafePerHostRequestQueue)).first; + iter = instance_map_w->insert(instance_map_type::value_type(servicename, new RefCountedThreadSafePerHostRequestQueue)).first; } // Note: the creation of AIPerHostRequestQueuePtr MUST be protected by the lock on sInstanceMap (see release()). return iter->second; diff --git a/indra/llmessage/aicurlperhost.h b/indra/llmessage/aicurlperhost.h index 7a90ca2ce..3e22e4fbe 100644 --- a/indra/llmessage/aicurlperhost.h +++ b/indra/llmessage/aicurlperhost.h @@ -91,8 +91,8 @@ class AIPerHostRequestQueue { typedef instance_map_type::iterator iterator; typedef instance_map_type::const_iterator const_iterator; - // Utility function; extract canonical (lowercase) hostname from url. - static std::string extract_canonical_hostname(std::string const& url); + // Utility function; extract canonical (lowercase) hostname and port from url. + static std::string extract_canonical_servicename(std::string const& url); // Return (possibly create) a unique instance for the given hostname. static AIPerHostRequestQueuePtr instance(std::string const& hostname); diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 9d8d51fb0..23e7e8b12 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -304,7 +304,7 @@ class CurlEasyRequest : public CurlEasyHandle { CURLcode mResult; //AIFIXME: this does not belong in the request object, but belongs in the response object. AIHTTPTimeoutPolicy const* mTimeoutPolicy; - std::string mLowercaseHostname; // Lowercase hostname (canonicalized) extracted from the url. + std::string mLowercaseServicename; // Lowercase hostname:port (canonicalized) extracted from the url. AIPerHostRequestQueuePtr mPerHostPtr; // Pointer to the corresponding AIPerHostRequestQueue. LLPointer mTimeout;// Timeout administration object associated with last created CurlSocketInfo. bool mTimeoutIsOrphan; // Set to true when mTimeout is not (yet) associated with a CurlSocketInfo. @@ -316,7 +316,8 @@ class CurlEasyRequest : public CurlEasyHandle { public: // These two are only valid after finalizeRequest. AIHTTPTimeoutPolicy const* getTimeoutPolicy(void) const { return mTimeoutPolicy; } - std::string const& getLowercaseHostname(void) const { return mLowercaseHostname; } + std::string const& getLowercaseServicename(void) const { return mLowercaseServicename; } + std::string getLowercaseHostname(void) const; // Called by CurlSocketInfo to allow access to the last (after a redirect) HTTPTimeout object related to this request. // This creates mTimeout (unless mTimeoutIsOrphan is set in which case it adopts the orphan). LLPointer& get_timeout_object(void); @@ -349,7 +350,7 @@ class CurlEasyRequest : public CurlEasyHandle { // PerHost API. AIPerHostRequestQueuePtr getPerHostPtr(void); // (Optionally create and) return a pointer to the unique - // AIPerHostRequestQueue corresponding to mLowercaseHostname. + // AIPerHostRequestQueue corresponding to mLowercaseServicename. bool removeFromPerHostQueue(AICurlEasyRequest const&) const; // Remove this request from the per-host queue, if queued at all. // Returns true if it was queued. protected: diff --git a/indra/llmessage/aihttptimeout.cpp b/indra/llmessage/aihttptimeout.cpp index 0f99e23d2..16ed7c170 100644 --- a/indra/llmessage/aihttptimeout.cpp +++ b/indra/llmessage/aihttptimeout.cpp @@ -75,6 +75,7 @@ namespace AICurlPrivate { class BufferedCurlEasyRequest { public: char const* getLowercaseHostname(void) const { return "hostname.com"; } + char const* getLowercaseServicename(void) const { return "hostname.com:12047"; } void getinfo(const int&, double* p) { *p = 0.1; } }; @@ -435,7 +436,7 @@ bool HTTPTimeout::maybe_upload_finished(void) void HTTPTimeout::print_diagnostics(CurlEasyRequest const* curl_easy_request, char const* eff_url) { #ifndef HTTPTIMEOUT_TESTSUITE - llwarns << "Request to \"" << curl_easy_request->getLowercaseHostname() << "\" timed out for " << curl_easy_request->getTimeoutPolicy()->name() << llendl; + llwarns << "Request to \"" << curl_easy_request->getLowercaseServicename() << "\" timed out for " << curl_easy_request->getTimeoutPolicy()->name() << llendl; llinfos << "Effective URL: \"" << eff_url << "\"." << llendl; double namelookup_time, connect_time, appconnect_time, pretransfer_time, starttransfer_time; curl_easy_request->getinfo(CURLINFO_NAMELOOKUP_TIME, &namelookup_time); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 6cdc3f703..dd634490e 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -798,12 +798,12 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, if (!mCanUseNET) { - // Probably a file://, but well; in that case hostname will be empty. - std::string hostname = AIPerHostRequestQueue::extract_canonical_hostname(mUrl); - if (!hostname.empty()) + // Probably a file://, but well; in that case servicename will be empty. + std::string servicename = AIPerHostRequestQueue::extract_canonical_servicename(mUrl); + if (!servicename.empty()) { // Make sure mPerHostPtr is up to date with mUrl. - mPerHostPtr = AIPerHostRequestQueue::instance(hostname); + mPerHostPtr = AIPerHostRequestQueue::instance(servicename); } } @@ -1162,7 +1162,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { mUrl = http_url + "/?texture_id=" + mID.asString().c_str(); mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id. - mPerHostPtr = AIPerHostRequestQueue::instance(AIPerHostRequestQueue::extract_canonical_hostname(http_url)); + mPerHostPtr = AIPerHostRequestQueue::instance(AIPerHostRequestQueue::extract_canonical_servicename(http_url)); } else {