/** * @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 . * * 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 //! // 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 = 56000; // 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; 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 mDerived; // Policies derived from this one. 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) { } // Derive base from base. AIHTTPTimeoutPolicyBase(AIHTTPTimeoutPolicyBase& rhs, PolicyOp const& op) : AIHTTPTimeoutPolicy(rhs) { op.perform(this); } // Called for every derived policy. void derived(AIHTTPTimeoutPolicy* derived) { mDerived.push_back(derived); } // 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); }; AIHTTPTimeoutPolicy::AIHTTPTimeoutPolicy(AIHTTPTimeoutPolicy& base) : mName(NULL), mBase(static_cast(&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); } //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 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 ; // 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. 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: Broken compiler doesn't allow as to use temporaries for the Operator ojects, // so they are instantiated separately. // This used to be '5 seconds'. Transaction transactionOp5s(5); AIHTTPTimeoutPolicyBase transfer_5s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(), transactionOp5s ); // This used to be '18 seconds'. Transaction transactionOp18s(18); AIHTTPTimeoutPolicyBase transfer_18s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(), transactionOp18s ); // This used to be '30 seconds'. Transaction transactionOp30s(30); AIHTTPTimeoutPolicyBase transfer_30s(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 ); // 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(assetUploadResponder); P(asyncConsoleResponder); P(avatarPickerResponder); P(authHandler); P(avatarNameResponder); P2(baseCapabilitiesComplete, transfer_18s); P(blockingLLSDPost); P(blockingLLSDGet); P(blockingRawGet); P(charactersResponder); P(classifiedStatsResponder); P(consoleResponder); P2(crashLoggerResponder, transfer_5s); P(createInventoryCategoryResponder); P(emeraldDicDownloader); P(environmentApplyResponder); P(environmentRequestResponder); P(estateChangeInfoResponder); P(eventPollResponder); P(fetchInventoryResponder); P(fetchScriptLimitsAttachmentInfoResponder); P(fetchScriptLimitsRegionDetailsResponder); P(fetchScriptLimitsRegionInfoResponder); P(fetchScriptLimitsRegionSummaryResponder); P(fnPtrResponder); P2(gamingDataReceived, transfer_18s); P2(groupMemberDataResponder, transfer_300s); P2(groupProposalBallotResponder, transfer_300s); P(homeLocationResponder); P(HTTPGetResponder); P(iamHereLogin); P(iamHere); P(iamHereVoice); P2(inventoryModelFetchDescendentsResponder, transfer_300s); P(inventoryModelFetchItemResponder); P(lcl_responder); P(MPImportGetResponder); P(MPImportPostResponder); P(mapLayerResponder); P2(maturityPreferences, transfer_30s); P(mediaTypeResponder); P(meshDecompositionResponder); P(meshHeaderResponder); P(meshLODResponder); P(meshPhysicsShapeResponder); P(meshSkinInfoResponder); P(mimeDiscoveryResponder); P(moderationModeResponder); P(muteTextResponder); P(muteVoiceResponder); 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_18s); 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(viewerVoiceAccountProvisionResponder); P(voiceCallCapResponder); P(voiceClientCapResponder); P(webProfileResponders); P(wholeModelFeeResponder); P(wholeModelUploadResponder); P2(XMLRPCResponder, connect_40s);