Use host:port as key for the "PerHost" request queue, instead of just the hostname.
Rationale: LL is doing all throttling per service (host:port), not per service hostname. Also, textures and capabilities use the same host: the sim you are connected to. Splitting the queues up on a per-service basis will stop the textures from blocking a capability request.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<curlthread::HTTPTimeout> 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<curlthread::HTTPTimeout>& 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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user