Files
SingularityViewer/indra/llmessage/aihttptimeoutpolicy.cpp
Latif Khalifa c87f7b0576 Breakpad3: WIP, minidump files created
TODO: re-work sending crash logs
get rid of standalone loggers
2013-10-01 13:43:45 +02:00

995 lines
38 KiB
C++

/**
* @file aihttptimeoutpolicy.cpp
* @brief Implementation of AIHTTPTimeoutPolicy
*
* Copyright (c) 2012, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* 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.
*
* CHANGELOG
* and additional copyright holders.
*
* 24/08/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#include "sys.h"
#include "aihttptimeoutpolicy.h"
#define NOMINMAX
#include "llerror.h"
#include "lldefs.h"
#include "v3math.h"
#include <vector>
//!
// Timing of a HTML connection.
//
// Request call
// |
// v ... <--low speed time--> ... ... <--low speed time--> ...
// <--request queued--><--DNS lookup--><--connect margin--><--data transfer to server--><--reply delay--><--data transfer from server-->
// <------------------------------------------curl transaction----------------------------------------------------->
// <--------------------------------------------------------------total delay---------------------------------------------------------->
// |
// v
// finished
// For now, low speed limit is the same for up and download: usually download is (much) higher, but we have to take into account that
// there might also be multiple downloads at the same time, more than simultaneous uploads.
// Absolute maxima (min/max range):
// These values are intuitively determined and rather arbitrary.
namespace {
U16 const ABS_min_DNS_lookup = 0; // Rationale: If a FAST roundtrip is demanded, then setting the DNS lookup grace time
// at 0 seconds will not make a connection fail when the lookup takes 1 second, it
// just means that no EXTRA time is added to the connect time.
U16 const ABS_max_DNS_lookup = 300; // Waiting longer than 5 minutes never makes sense.
U16 const ABS_min_connect_time = 1; // Can't demand 0 seconds, and we deal with integer numbers here.
U16 const ABS_max_connect_time = 30; // Making a TCP/IP connection should REALLY succeed within 30 seconds or we rather try again.
U16 const ABS_min_reply_delay = 1; // Can't demand 0 seconds, and we deal with integer numbers here.
U16 const ABS_max_reply_delay = 120; // If the server needs more than 2 minutes to find the reply then something just HAS to be wrong :/.
U16 const ABS_min_low_speed_time = 4; // Intuitively, I think it makes no sense to average a download speed over less than 4 seconds.
U16 const ABS_max_low_speed_time = 120; // Averaging it over a time considerably larger than the normal timeout periods makes no sense either.
U32 const ABS_min_low_speed_limit = 1; // In case you don't want to timeout when there is any data received at all.
U32 const ABS_max_low_speed_limit = 1000000; // This limit is almost certainly higher than the maximum speed you get from the server.
U16 const ABS_min_transaction = 60; // This is an absurd low value for experimentation. In reality, you should control
// termination of really slow connections through the low_speed settings.
U16 const ABS_max_transaction = 1200; // Insane long time. Downloads a texture of 4 MB at 3.5 kB/s. Textures are compressed though ;).
U16 const ABS_min_total_delay = 60; // This is an absurd low value for experimentation. In reality, you should control
// termination of really slow connections through the low_speed settings.
U16 const ABS_max_total_delay = 3000; // Insane long time, for when someone wants to be ABSOLUTELY sure this isn't the bottleneck.
using namespace AIHTTPTimeoutPolicyOperators;
// Default policy values.
U16 const AITP_default_DNS_lookup_grace = 60; // Allow for 60 seconds long DNS look ups.
U16 const AITP_default_maximum_connect_time = 10; // Allow the SSL/TLS connection through a proxy, including handshakes, to take up to 10 seconds.
U16 const AITP_default_maximum_reply_delay = 60; // Allow the server 60 seconds to do whatever it has to do before starting to send data.
U16 const AITP_default_low_speed_time = 30; // If a transfer speed drops below AITP_default_low_speed_limit bytes/s for 30 seconds, terminate the transfer.
U32 const AITP_default_low_speed_limit = 7000; // In bytes per second (use for CURLOPT_LOW_SPEED_LIMIT).
U16 const AITP_default_maximum_curl_transaction = 300; // Allow large files to be transfered over slow connections.
U16 const AITP_default_maximum_total_delay = 600; // Avoid "leaking" by terminating anything that wasn't completed after 10 minutes.
} // namespace
AIHTTPTimeoutPolicy& AIHTTPTimeoutPolicy::operator=(AIHTTPTimeoutPolicy const& rhs)
{
// You're not allowed to assign to a policy that is based on another policy.
llassert(!mBase);
mDNSLookupGrace = rhs.mDNSLookupGrace;
mMaximumConnectTime = rhs.mMaximumConnectTime;
mMaximumReplyDelay = rhs.mMaximumReplyDelay;
mLowSpeedTime = rhs.mLowSpeedTime;
mLowSpeedLimit = rhs.mLowSpeedLimit;
mMaximumCurlTransaction = rhs.mMaximumCurlTransaction;
mMaximumTotalDelay = rhs.mMaximumTotalDelay;
changed();
return *this;
}
AIHTTPTimeoutPolicy::AIHTTPTimeoutPolicy(char const* name,
U16 dns_lookup_grace, U16 subsequent_connects, U16 reply_delay,
U16 low_speed_time, U32 low_speed_limit,
U16 curl_transaction, U16 total_delay) :
mName(name),
mBase(NULL),
mDNSLookupGrace(dns_lookup_grace),
mMaximumConnectTime(subsequent_connects),
mMaximumReplyDelay(reply_delay),
mLowSpeedTime(low_speed_time),
mLowSpeedLimit(low_speed_limit),
mMaximumCurlTransaction(curl_transaction),
mMaximumTotalDelay(total_delay)
{
sanity_checks();
}
struct PolicyOp {
PolicyOp* mNext;
PolicyOp(void) : mNext(NULL) { }
PolicyOp(PolicyOp& op) : mNext(&op) { }
virtual void perform(AIHTTPTimeoutPolicy* policy) const { }
void nextOp(AIHTTPTimeoutPolicy* policy) const { if (mNext) mNext->perform(policy); }
};
class AIHTTPTimeoutPolicyBase : public AIHTTPTimeoutPolicy {
private:
std::vector<AIHTTPTimeoutPolicy*> mDerived; // Policies derived from this one.
PolicyOp const* mOp; // Operator we applied to base to get ourselves.
public:
AIHTTPTimeoutPolicyBase(U16 dns_lookup_grace, U16 subsequent_connects, U16 reply_delay,
U16 low_speed_time, U32 low_speed_limit,
U16 curl_transaction, U16 total_delay) :
AIHTTPTimeoutPolicy(NULL, dns_lookup_grace, subsequent_connects, reply_delay, low_speed_time, low_speed_limit, curl_transaction, total_delay),
mOp(NULL) { }
// Derive base from base.
AIHTTPTimeoutPolicyBase(AIHTTPTimeoutPolicyBase& rhs, PolicyOp& op) : AIHTTPTimeoutPolicy(rhs), mOp(&op) { rhs.derived(this); mOp->perform(this); }
// Called for every derived policy.
void derived(AIHTTPTimeoutPolicy* derived) { mDerived.push_back(derived); }
// Called when our base changed.
/*virtual*/ void base_changed(void);
// Called when we ourselves changed.
/*virtual*/ void changed(void);
// Provide public acces to sDebugSettingsCurlTimeout for this compilation unit.
static AIHTTPTimeoutPolicyBase& getDebugSettingsCurlTimeout(void) { return sDebugSettingsCurlTimeout; }
protected:
friend void AIHTTPTimeoutPolicy::setDefaultCurlTimeout(AIHTTPTimeoutPolicy const& timeout);
AIHTTPTimeoutPolicyBase& operator=(AIHTTPTimeoutPolicy const& rhs);
};
void AIHTTPTimeoutPolicyBase::base_changed(void)
{
AIHTTPTimeoutPolicy::base_changed();
if (mOp)
mOp->perform(this);
changed();
}
void AIHTTPTimeoutPolicyBase::changed(void)
{
for (std::vector<AIHTTPTimeoutPolicy*>::iterator iter = mDerived.begin(); iter != mDerived.end(); ++iter)
(*iter)->base_changed();
}
void AIHTTPTimeoutPolicy::changed(void)
{
Dout(dc::notice, "Policy \"" << mName << "\" changed into: DNSLookup: " << mDNSLookupGrace << "; Connect: " << mMaximumConnectTime <<
"; ReplyDelay: " << mMaximumReplyDelay << "; LowSpeedTime: " << mLowSpeedTime << "; LowSpeedLimit: " << mLowSpeedLimit <<
"; MaxTransaction: " << mMaximumCurlTransaction << "; MaxTotalDelay:" << mMaximumTotalDelay);
}
AIHTTPTimeoutPolicy::AIHTTPTimeoutPolicy(AIHTTPTimeoutPolicy& base) :
mName(NULL),
mBase(static_cast<AIHTTPTimeoutPolicyBase*>(&base)),
mDNSLookupGrace(base.mDNSLookupGrace),
mMaximumConnectTime(base.mMaximumConnectTime),
mMaximumReplyDelay(base.mMaximumReplyDelay),
mLowSpeedTime(base.mLowSpeedTime),
mLowSpeedLimit(base.mLowSpeedLimit),
mMaximumCurlTransaction(base.mMaximumCurlTransaction),
mMaximumTotalDelay(base.mMaximumTotalDelay)
{
}
AIHTTPTimeoutPolicyBase& AIHTTPTimeoutPolicyBase::operator=(AIHTTPTimeoutPolicy const& rhs)
{
AIHTTPTimeoutPolicy::operator=(rhs);
return *this;
}
AIHTTPTimeoutPolicy::AIHTTPTimeoutPolicy(char const* name, AIHTTPTimeoutPolicyBase& base) :
mName(name),
mBase(&base),
mDNSLookupGrace(mBase->mDNSLookupGrace),
mMaximumConnectTime(mBase->mMaximumConnectTime),
mMaximumReplyDelay(mBase->mMaximumReplyDelay),
mLowSpeedTime(mBase->mLowSpeedTime),
mLowSpeedLimit(mBase->mLowSpeedLimit),
mMaximumCurlTransaction(mBase->mMaximumCurlTransaction),
mMaximumTotalDelay(mBase->mMaximumTotalDelay)
{
sNameMap.insert(namemap_t::value_type(name, this));
// Register for changes to the base policy.
mBase->derived(this);
}
void AIHTTPTimeoutPolicy::base_changed(void)
{
// The same as *this = *mBase; but can't use operator= because of an assert that checks that mBase is not set.
mDNSLookupGrace = mBase->mDNSLookupGrace;
mMaximumConnectTime = mBase->mMaximumConnectTime;
mMaximumReplyDelay = mBase->mMaximumReplyDelay;
mLowSpeedTime = mBase->mLowSpeedTime;
mLowSpeedLimit = mBase->mLowSpeedLimit;
mMaximumCurlTransaction = mBase->mMaximumCurlTransaction;
mMaximumTotalDelay = mBase->mMaximumTotalDelay;
changed();
}
//static
void AIHTTPTimeoutPolicy::setDefaultCurlTimeout(AIHTTPTimeoutPolicy const& timeout)
{
sDebugSettingsCurlTimeout = timeout;
llinfos << "CurlTimeout Debug Settings now"
": DNSLookup: " << sDebugSettingsCurlTimeout.mDNSLookupGrace <<
"; Connect: " << sDebugSettingsCurlTimeout.mMaximumConnectTime <<
"; ReplyDelay: " << sDebugSettingsCurlTimeout.mMaximumReplyDelay <<
"; LowSpeedTime: " << sDebugSettingsCurlTimeout.mLowSpeedTime <<
"; LowSpeedLimit: " << sDebugSettingsCurlTimeout.mLowSpeedLimit <<
"; MaxTransaction: " << sDebugSettingsCurlTimeout.mMaximumCurlTransaction <<
"; MaxTotalDelay: " << sDebugSettingsCurlTimeout.mMaximumTotalDelay << llendl;
if (sDebugSettingsCurlTimeout.mDNSLookupGrace < AITP_default_DNS_lookup_grace)
{
llwarns << "CurlTimeoutDNSLookup (" << sDebugSettingsCurlTimeout.mDNSLookupGrace << ") is lower than the built-in default value (" << AITP_default_DNS_lookup_grace << ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mMaximumConnectTime < AITP_default_maximum_connect_time)
{
llwarns << "CurlTimeoutConnect (" << sDebugSettingsCurlTimeout.mMaximumConnectTime << ") is lower than the built-in default value (" << AITP_default_maximum_connect_time << ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mMaximumReplyDelay < AITP_default_maximum_reply_delay)
{
llwarns << "CurlTimeoutReplyDelay (" << sDebugSettingsCurlTimeout.mMaximumReplyDelay << ") is lower than the built-in default value (" << AITP_default_maximum_reply_delay << ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mLowSpeedTime < AITP_default_low_speed_time)
{
llwarns << "CurlTimeoutLowSpeedTime (" << sDebugSettingsCurlTimeout.mLowSpeedTime << ") is lower than the built-in default value (" << AITP_default_low_speed_time << ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mLowSpeedLimit > AITP_default_low_speed_limit)
{
llwarns << "CurlTimeoutLowSpeedLimit (" << sDebugSettingsCurlTimeout.mLowSpeedLimit << ") is higher than the built-in default value (" << AITP_default_low_speed_limit << ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mMaximumCurlTransaction < AITP_default_maximum_curl_transaction)
{
llwarns << "CurlTimeoutMaxTransaction (" << sDebugSettingsCurlTimeout.mMaximumCurlTransaction << ") is lower than the built-in default value (" << AITP_default_maximum_curl_transaction<< ")." << llendl;
}
if (sDebugSettingsCurlTimeout.mMaximumTotalDelay < AITP_default_maximum_total_delay)
{
llwarns << "CurlTimeoutMaxTotalDelay (" << sDebugSettingsCurlTimeout.mMaximumTotalDelay << ") is lower than the built-in default value (" << AITP_default_maximum_total_delay << ")." << llendl;
}
}
//static
AIHTTPTimeoutPolicy const& AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout(void)
{
return sDebugSettingsCurlTimeout;
}
#ifdef SHOW_ASSERT
#include "aithreadid.h"
static AIThreadID curlthread(AIThreadID::none); // Initialized by getConnectTimeout.
#endif
static std::set<std::string> gSeenHostnames;
U16 AIHTTPTimeoutPolicy::getConnectTimeout(std::string const& hostname) const
{
#ifdef SHOW_ASSERT
// Only the CURL-THREAD may access gSeenHostnames.
if (curlthread.is_no_thread())
curlthread.reset();
llassert(curlthread.equals_current_thread());
#endif
U16 connect_timeout = mMaximumConnectTime;
// Add the hostname to the list of seen hostnames, if not already there.
if (gSeenHostnames.insert(hostname).second)
connect_timeout += mDNSLookupGrace; // If the host is not in the list, increase the connect timeout with mDNSLookupGrace.
return connect_timeout;
}
//static
bool AIHTTPTimeoutPolicy::connect_timed_out(std::string const& hostname)
{
llassert(curlthread.equals_current_thread());
// This is called when a connect to hostname timed out on connect.
// If the hostname is currently in the list, remove it and return true
// so that subsequent connects will get more time to connect.
// Otherwise return false.
return gSeenHostnames.erase(hostname) > 0;
}
//=======================================================================================================
// Start of policy operation definitions.
namespace AIHTTPTimeoutPolicyOperators {
// Note: Policies are applied in the order First(x, Second(y, Third(z))) etc,
// where the last (Third) has the highest priority.
// For example: Transaction(5, Connect(40)) would first enforce a transaction time of 5 seconds,
// and then a connect time of 40 seconds, even if that would mean increasing the transaction
// time again.
struct DNS : PolicyOp {
int mSeconds;
DNS(int seconds) : mSeconds(seconds) { }
DNS(int seconds, PolicyOp& op) : PolicyOp(op), mSeconds(seconds) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(void) { return ABS_min_DNS_lookup; }
static U16 max(void) { return ABS_max_DNS_lookup; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
struct Connect : PolicyOp {
int mSeconds;
Connect(int seconds) : mSeconds(seconds) { }
Connect(int seconds, PolicyOp& op) : PolicyOp(op), mSeconds(seconds) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(void) { return ABS_min_connect_time; }
static U16 max(void) { return ABS_max_connect_time; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
struct Reply : PolicyOp {
int mSeconds;
Reply(int seconds) : mSeconds(seconds) { }
Reply(int seconds, PolicyOp& op) : PolicyOp(op), mSeconds(seconds) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(void) { return ABS_min_reply_delay; }
static U16 max(void) { return ABS_max_reply_delay; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
struct Speed : PolicyOp {
int mSeconds;
int mRate;
Speed(int seconds, int rate) : mSeconds(seconds), mRate(rate) { }
Speed(int seconds, int rate, PolicyOp& op) : PolicyOp(op), mSeconds(seconds), mRate(rate) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(void) { return ABS_min_low_speed_time; }
static U16 max(AIHTTPTimeoutPolicy const* policy) { return llmin(ABS_max_low_speed_time, (U16)(policy->mMaximumCurlTransaction / 2)); }
static U32 lmin(void) { return ABS_min_low_speed_limit; }
static U32 lmax(void) { return ABS_max_low_speed_limit; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
struct Transaction : PolicyOp {
int mSeconds;
Transaction(int seconds) : mSeconds(seconds) { }
Transaction(int seconds, PolicyOp& op) : PolicyOp(op), mSeconds(seconds) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(AIHTTPTimeoutPolicy const* policy) { return llmax(ABS_min_transaction, (U16)(policy->mMaximumConnectTime + policy->mMaximumReplyDelay + 4 * policy->mLowSpeedTime)); }
static U16 max(void) { return ABS_max_transaction; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
struct Total : PolicyOp {
int mSeconds;
Total(int seconds) : mSeconds(seconds) { }
Total(int seconds, PolicyOp& op) : PolicyOp(op), mSeconds(seconds) { }
static void fix(AIHTTPTimeoutPolicy* policy);
static U16 min(AIHTTPTimeoutPolicy const* policy) { return llmax(ABS_min_total_delay, (U16)(policy->mMaximumCurlTransaction + 1)); }
static U16 max(void) { return ABS_max_total_delay; }
virtual void perform(AIHTTPTimeoutPolicy* policy) const;
};
void DNS::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mDNSLookupGrace = mSeconds;
fix(policy);
nextOp(policy);
}
void Connect::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mMaximumConnectTime = mSeconds;
fix(policy);
nextOp(policy);
}
void Reply::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mMaximumReplyDelay = mSeconds;
fix(policy);
nextOp(policy);
}
void Speed::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mLowSpeedTime = mSeconds;
policy->mLowSpeedLimit = mRate;
fix(policy);
nextOp(policy);
}
void Transaction::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mMaximumCurlTransaction = mSeconds;
fix(policy);
nextOp(policy);
}
void Total::perform(AIHTTPTimeoutPolicy* policy) const
{
policy->mMaximumTotalDelay = mSeconds;
fix(policy);
nextOp(policy);
}
void DNS::fix(AIHTTPTimeoutPolicy* policy)
{
if (policy->mDNSLookupGrace > max())
{
policy->mDNSLookupGrace = max();
}
else if (policy->mDNSLookupGrace < min())
{
policy->mDNSLookupGrace = min();
}
}
void Connect::fix(AIHTTPTimeoutPolicy* policy)
{
bool changed = false;
if (policy->mMaximumConnectTime > max())
{
policy->mMaximumConnectTime = max();
changed = true;
}
else if (policy->mMaximumConnectTime < min())
{
policy->mMaximumConnectTime = min();
changed = true;
}
if (changed)
{
// Transaction limits depend on Connect.
Transaction::fix(policy);
}
}
void Reply::fix(AIHTTPTimeoutPolicy* policy)
{
bool changed = false;
if (policy->mMaximumReplyDelay > max())
{
policy->mMaximumReplyDelay = max();
changed = true;
}
else if (policy->mMaximumReplyDelay < min())
{
policy->mMaximumReplyDelay = min();
changed = true;
}
if (changed)
{
// Transaction limits depend on Reply.
Transaction::fix(policy);
}
}
void Speed::fix(AIHTTPTimeoutPolicy* policy)
{
bool changed = false;
if (policy->mLowSpeedTime > ABS_max_low_speed_time)
{
policy->mLowSpeedTime = ABS_max_low_speed_time;
changed = true;
}
else if (policy->mLowSpeedTime != 0 && policy->mLowSpeedTime < min())
{
policy->mLowSpeedTime = min();
changed = true;
}
if (changed)
{
// Transaction limits depend on Speed time.
Transaction::fix(policy);
}
if (policy->mLowSpeedTime > max(policy))
{
policy->mLowSpeedTime = max(policy);
}
if (policy->mLowSpeedLimit > lmax())
{
policy->mLowSpeedLimit = lmax();
}
else if (policy->mLowSpeedLimit != 0 && policy->mLowSpeedLimit < lmin())
{
policy->mLowSpeedLimit = lmin();
}
}
void Transaction::fix(AIHTTPTimeoutPolicy* policy)
{
bool changed = false;
if (policy->mMaximumCurlTransaction > max())
{
policy->mMaximumCurlTransaction = max();
changed = true;
}
else if (policy->mMaximumCurlTransaction < ABS_min_transaction)
{
policy->mMaximumCurlTransaction = ABS_min_transaction;
changed = true;
}
if (changed)
{
// Totals minimum limit depends on Transaction.
Total::fix(policy);
// Transaction limits depend on Connect, Reply and Speed time.
if (policy->mMaximumCurlTransaction < min(policy))
{
// We need to achieve the following (from Transaction::min()):
// policy->mMaximumCurlTransaction >= policy->mMaximumConnectTime + policy->mMaximumReplyDelay + 4 * policy->mLowSpeedTime
// There isn't a single way to fix this, so we just do something randomly intuitive.
// We consider the vector space <connect_time, reply_delay, low_speed_time>;
// In other words, we need to compare with the dot product of <1, 1, 4>.
LLVector3 const ref(1, 1, 4);
// The shortest allowed vector is:
LLVector3 const vec_min(ABS_min_connect_time, ABS_min_reply_delay, ABS_min_low_speed_time);
// Initialize the result vector to (0, 0, 0) (in the vector space with shifted origin).
LLVector3 vec_res;
// Check if there is a solution at all:
if (policy->mMaximumCurlTransaction > ref * vec_min) // Is vec_min small enough?
{
// The current point is:
LLVector3 vec_cur(policy->mMaximumConnectTime, policy->mMaximumReplyDelay, policy->mLowSpeedTime);
// The default point is:
LLVector3 vec_def(AITP_default_maximum_connect_time, AITP_default_maximum_reply_delay, AITP_default_low_speed_time);
// Move the origin.
vec_cur -= vec_min;
vec_def -= vec_min;
// Normalize the default vector (we only need it's direction).
vec_def.normalize();
// Project the current vector onto the default vector (dp = default projection):
LLVector3 vec_dp = vec_def * (vec_cur * vec_def);
// Check if the projection is a solution and choose the vectors between which the result lays.
LLVector3 a; // vec_min is too small (a = (0, 0, 0) which corresponds to vec_min).
LLVector3 b = vec_cur; // vec_cur is too large.
if (policy->mMaximumCurlTransaction > ref * (vec_dp + vec_min)) // Is vec_dp small enough too?
{
a = vec_dp; // New lower bound.
}
else
{
b = vec_dp; // New upper bound.
}
// Find vec_res = a + lambda * (b - a), where 0 <= lambda <= 1, such that
// policy->mMaximumCurlTransaction == ref * (vec_res + vec_min).
//
// Note that ref * (b - a) must be non-zero because if it wasn't then changing lambda wouldn't have
// any effect on right-hand side of the equation (ref * (vec_res + vec_min)) which in contradiction
// with the fact that a is a solution and b is not.
F32 lambda = (policy->mMaximumCurlTransaction - ref * (a + vec_min)) / (ref * (b - a));
vec_res = a + lambda * (b - a);
}
// Shift origin back and fill in the result.
vec_res += vec_min;
policy->mMaximumConnectTime = vec_res[VX];
policy->mMaximumReplyDelay = vec_res[VY];
policy->mLowSpeedTime = vec_res[VZ];
}
}
if (policy->mMaximumCurlTransaction < min(policy))
{
policy->mMaximumCurlTransaction = min(policy);
}
}
void Total::fix(AIHTTPTimeoutPolicy* policy)
{
bool changed = false;
if (policy->mMaximumTotalDelay > max())
{
policy->mMaximumTotalDelay = max();
changed = true;
}
else if (policy->mMaximumTotalDelay < ABS_min_total_delay)
{
policy->mMaximumTotalDelay = ABS_min_total_delay;
changed = true;
}
if (changed)
{
// Totals minimum limit depends on Transaction.
// We have to correct mMaximumCurlTransaction such that (from Total::min)
// mMaximumTotalDelay >= llmax((int)ABS_min_total_delay, policy->mMaximumCurlTransaction + 1)
if (policy->mMaximumTotalDelay < policy->mMaximumCurlTransaction + 1)
{
policy->mMaximumCurlTransaction = policy->mMaximumTotalDelay - 1;
}
}
if (policy->mMaximumTotalDelay < min(policy))
{
policy->mMaximumTotalDelay = min(policy);
}
}
} // namespace AIHTTPTimeoutPolicyOperators
void AIHTTPTimeoutPolicy::sanity_checks(void) const
{
// Sanity checks.
llassert( DNS::min() <= mDNSLookupGrace && mDNSLookupGrace <= DNS::max());
llassert( Connect::min() <= mMaximumConnectTime && mMaximumConnectTime <= Connect::max());
llassert( Reply::min() <= mMaximumReplyDelay && mMaximumReplyDelay <= Reply::max());
llassert(mLowSpeedTime == 0 ||
(Speed::min() <= mLowSpeedTime && mLowSpeedTime <= Speed::max(this)));
llassert(mLowSpeedLimit == 0 ||
(Speed::lmin() <= mLowSpeedLimit && mLowSpeedLimit <= Speed::lmax()));
llassert(Transaction::min(this) <= mMaximumCurlTransaction && mMaximumCurlTransaction <= Transaction::max());
llassert( Total::min(this) <= mMaximumTotalDelay && mMaximumTotalDelay <= Total::max());
}
//=======================================================================================================
// Start of policy definitions.
// Policy with hardcoded default values.
AIHTTPTimeoutPolicyBase HTTPTimeoutPolicy_default(
AITP_default_DNS_lookup_grace,
AITP_default_maximum_connect_time,
AITP_default_maximum_reply_delay,
AITP_default_low_speed_time,
AITP_default_low_speed_limit,
AITP_default_maximum_curl_transaction,
AITP_default_maximum_total_delay);
//static. Initialized here, but shortly overwritten by Debug Settings (except for the crash logger, in which case these are the actual values).
AIHTTPTimeoutPolicyBase AIHTTPTimeoutPolicy::sDebugSettingsCurlTimeout(
AITP_default_DNS_lookup_grace,
AITP_default_maximum_connect_time,
AITP_default_maximum_reply_delay,
AITP_default_low_speed_time,
AITP_default_low_speed_limit,
AITP_default_maximum_curl_transaction,
AITP_default_maximum_total_delay);
// Note: All operator objects (Transaction, Connect, etc) must be globals (not temporaries)!
// To enforce this they are passes as reference to non-const (but will never be changed).
// Used for baseCapabilitiesComplete - which is retried many times, and needs to be rather "fast".
// Most connection succeed under 5 seconds.
Connect connectOp5s(5);
AIHTTPTimeoutPolicyBase connect_5s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
connectOp5s
);
// Some services connects rather slow. We need to enforce that connect timeouts never drop below 10 seconds for the policies that shorten the transaction time.
Connect connectOp10s(10);
AIHTTPTimeoutPolicyBase connect_10s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
connectOp10s
);
// The texture/mesh service suffers often from timeouts, give them a much longer connect timeout than normal.
Connect connectOp30s(30);
AIHTTPTimeoutPolicyBase connect_30s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
connectOp30s
);
// Extreme measures.
Connect connectOp60s(60);
AIHTTPTimeoutPolicyBase connect_60s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
connectOp60s
);
// This used to be '18 seconds'. Pass connectOp5s or it would become 3 seconds (which is working already pretty good actually).
Transaction transactionOp18s(18, connectOp5s);
AIHTTPTimeoutPolicyBase transfer_18s_connect_5s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
transactionOp18s
);
// This used to be '18 seconds'. Pass connectOp10s or it would become 3 seconds.
Transaction transactionOp22s(22, connectOp10s);
AIHTTPTimeoutPolicyBase transfer_22s_connect_10s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
transactionOp22s
);
// This used to be '30 seconds'. Pass connectOp10s or it would become 3 seconds.
Transaction transactionOp30s(30, connectOp10s);
AIHTTPTimeoutPolicyBase transfer_30s_connect_10s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
transactionOp30s
);
// This used to be '300 seconds'. We derive this from the hardcoded result so users can't mess with it.
Transaction transactionOp300s(300);
AIHTTPTimeoutPolicyBase transfer_300s(HTTPTimeoutPolicy_default,
transactionOp300s
);
// This used to be a call to setopt(CURLOPT_CONNECTTIMEOUT, 40L) with the remark 'Be a little impatient about establishing connections.'
Connect connectOp40s(40);
AIHTTPTimeoutPolicyBase connect_40s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
connectOp40s
);
// This used to be FETCHING_TIMEOUT (for HTTP textures), being a 15 second timeout from start of request till finishing receiving all data.
// That seems way to demanding however; lets use a 15 second reply delay demand instead.
Reply replyOp15s(15);
AIHTTPTimeoutPolicyBase reply_15s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
replyOp15s
);
// The LLEventPollResponder needs a reply delay of at least 60 seconds, and although that is the
// default -- enforce it to be 60 seconds and not depend on CurlTimeoutReplyDelay because changing
// it makes no sense or will break stuff.
Reply replyOp60s(60);
AIHTTPTimeoutPolicyBase reply_60s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(),
replyOp60s
);
// End of policy definitions.
//=======================================================================================================
bool validateCurlTimeoutDNSLookup(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::DNS op(new_value);
op.perform(&timeout);
return timeout.getDNSLookup() == new_value;
}
bool handleCurlTimeoutDNSLookup(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::DNS op(newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutConnect(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Connect op(new_value);
op.perform(&timeout);
return timeout.getConnect() == new_value;
}
bool handleCurlTimeoutConnect(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Connect op(newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutReplyDelay(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Reply op(new_value);
op.perform(&timeout);
return timeout.getReplyDelay() == new_value;
}
bool handleCurlTimeoutReplyDelay(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Reply op(newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutLowSpeedLimit(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Speed op(timeout.getLowSpeedTime(), new_value);
op.perform(&timeout);
return timeout.getLowSpeedLimit() == new_value;
}
bool handleCurlTimeoutLowSpeedLimit(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Speed op(timeout.getLowSpeedTime(), newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutLowSpeedTime(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Speed op(new_value, timeout.getLowSpeedLimit());
op.perform(&timeout);
return timeout.getLowSpeedTime() == new_value;
}
bool handleCurlTimeoutLowSpeedTime(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Speed op(newvalue.asInteger(), timeout.getLowSpeedLimit());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutMaxTransaction(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Transaction op(new_value);
op.perform(&timeout);
return timeout.getCurlTransaction() == new_value;
}
bool handleCurlTimeoutMaxTransaction(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Transaction op(newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
bool validateCurlTimeoutMaxTotalDelay(LLSD const& newvalue)
{
U32 new_value = newvalue.asInteger();
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Total op(new_value);
op.perform(&timeout);
return timeout.getTotalDelay() == new_value;
}
bool handleCurlTimeoutMaxTotalDelay(LLSD const& newvalue)
{
AIHTTPTimeoutPolicyBase timeout = AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout();
AIHTTPTimeoutPolicyOperators::Total op(newvalue.asInteger());
op.perform(&timeout);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(timeout);
return true;
}
//static
AIHTTPTimeoutPolicy::namemap_t AIHTTPTimeoutPolicy::sNameMap;
//static
AIHTTPTimeoutPolicy const* AIHTTPTimeoutPolicy::getTimeoutPolicyByName(std::string const& name)
{
namemap_t::iterator iter = sNameMap.find(name);
if (iter == sNameMap.end())
{
if (!name.empty())
{
llwarns << "Cannot find AIHTTPTimeoutPolicy with name \"" << name << "\"." << llendl;
}
return &sDebugSettingsCurlTimeout;
}
return iter->second;
}
//=======================================================================================================
// Start of Responder timeout policy list.
// Note: to find the actual responder class back, search for the name listed here but with upper case first character.
// For example, if the actual responder class is LLAccountingCostResponder then the name used here is accountingCostResponder.
#undef P
#define P(n) AIHTTPTimeoutPolicy n##_timeout(#n)
#define P2(n, b) AIHTTPTimeoutPolicy n##_timeout(#n, b)
// Policy name Policy
P(accountingCostResponder);
P(agentStateResponder);
P(appearanceChangeMetricsResponder);
P(assetUploadResponder);
P(assetReportHandler);
P(asyncConsoleResponder);
P(avatarPickerResponder);
P(authHandler);
P(avatarNameResponder);
P2(baseCapabilitiesComplete, transfer_18s_connect_5s);
P(blockingLLSDPost);
P(blockingLLSDGet);
P(blockingRawGet);
P(charactersResponder);
P(checkAgentAppearanceServiceResponder);
P(classifiedStatsResponder);
P(consoleResponder);
P(createInventoryCategoryResponder);
P(emeraldDicDownloader);
P(environmentApplyResponder);
P(environmentRequestResponder);
P(estateChangeInfoResponder);
P2(eventPollResponder, reply_60s);
P(fetchInventoryResponder);
P(fetchScriptLimitsAttachmentInfoResponder);
P(fetchScriptLimitsRegionDetailsResponder);
P(fetchScriptLimitsRegionInfoResponder);
P(fetchScriptLimitsRegionSummaryResponder);
P(fnPtrResponder);
P2(gamingDataReceived, transfer_22s_connect_10s);
P2(groupMemberDataResponder, transfer_300s);
P2(groupProposalBallotResponder, transfer_300s);
P(homeLocationResponder);
P2(HTTPGetResponder, reply_15s);
P(iamHere);
P(iamHereVoice);
P2(inventoryModelFetchDescendentsResponder, transfer_300s);
P(inventoryModelFetchItemResponder);
P(lcl_responder);
P(MPImportGetResponder);
P(MPImportPostResponder);
P(mapLayerResponder);
P(materialsResponder);
P2(maturityPreferences, transfer_30s_connect_10s);
P(mediaDataClientResponder);
P(mediaTypeResponder);
P2(meshDecompositionResponder, connect_30s);
P2(meshHeaderResponder, connect_30s);
P2(meshLODResponder, connect_30s);
P2(meshPhysicsShapeResponder, connect_30s);
P2(meshSkinInfoResponder, connect_30s);
P(mimeDiscoveryResponder);
P(moderationResponder);
P(navMeshRebakeResponder);
P(navMeshResponder);
P(navMeshStatusResponder);
P(newAgentInventoryVariablePriceResponder);
P(objectCostResponder);
P(objectLinksetsResponder);
P(physicsFlagsResponder);
P(placeAvatarTeleportResponder);
P(productInfoRequestResponder);
P(regionResponder);
P(remoteParcelRequestResponder);
P(requestAgentUpdateAppearance);
P(responderIgnore);
P(sessionInviteResponder);
P(setDisplayNameResponder);
P2(simulatorFeaturesReceived, transfer_22s_connect_10s);
P(startConferenceChatResponder);
P2(startGroupVoteResponder, transfer_300s);
P(terrainLinksetsResponder);
P(translationReceiver);
P(uploadModelPremissionsResponder);
P(userReportResponder);
P(verifiedDestinationResponder);
P(viewerChatterBoxInvitationAcceptResponder);
P(viewerMediaOpenIDResponder);
P(viewerMediaWebProfileResponder);
P(viewerStatsResponder);
P(vivoxVoiceAccountProvisionResponder);
P(vivoxVoiceClientCapResponder);
P(voiceCallCapResponder);
P(webProfileResponders);
P(wholeModelFeeResponder);
P(wholeModelUploadResponder);
P2(XMLRPCResponder, connect_40s);
P2(crashLoggerResponder, transfer_300s);