diff --git a/indra/aistatemachine/aicurl.cpp b/indra/aistatemachine/aicurl.cpp index c20983f16..453098801 100644 --- a/indra/aistatemachine/aicurl.cpp +++ b/indra/aistatemachine/aicurl.cpp @@ -56,8 +56,22 @@ #include "llproxy.h" #include "llhttpstatuscodes.h" #include "aihttpheaders.h" +#include "aihttptimeoutpolicy.h" +#include "aicurleasyrequeststatemachine.h" #ifdef CWDEBUG #include +#include +#define DoutCurlEasy(x) do { \ + using namespace libcwd; \ + std::ostringstream marker; \ + marker << (void*)this->get_lockobj(); \ + libcw_do.push_marker(); \ + libcw_do.marker().assign(marker.str().data(), marker.str().size()); \ + Dout(dc::curl, x); \ + libcw_do.pop_marker(); \ + } while(0) +#else +#define DoutCurlEasy(x) Dout(dc::curl, x << " [" << (void*)this->get_lockobj() << ']') #endif //================================================================================== @@ -446,6 +460,11 @@ void Responder::setURL(std::string const& url) mURL = url; } +AIHTTPTimeoutPolicy const& Responder::getHTTPTimeoutPolicy(void) const +{ + return AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout(); +} + // Called with HTML header. // virtual void Responder::completedHeaders(U32, std::string const&, AIHTTPHeaders const&) @@ -778,9 +797,9 @@ DEFINE_FUNCTION_SETOPT1(curl_debug_callback, CURLOPT_DEBUGFUNCTION) DEFINE_FUNCTION_SETOPT4(curl_write_callback, CURLOPT_HEADERFUNCTION, CURLOPT_WRITEFUNCTION, CURLOPT_INTERLEAVEFUNCTION, CURLOPT_READFUNCTION) //DEFINE_FUNCTION_SETOPT1(curl_read_callback, CURLOPT_READFUNCTION) DEFINE_FUNCTION_SETOPT1(curl_ssl_ctx_callback, CURLOPT_SSL_CTX_FUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_progress_callback, CURLOPT_PROGRESSFUNCTION) DEFINE_FUNCTION_SETOPT3(curl_conv_callback, CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPT_CONV_FROM_UTF8_FUNCTION) #if 0 // Not used by the viewer. -DEFINE_FUNCTION_SETOPT1(curl_progress_callback, CURLOPT_PROGRESSFUNCTION) DEFINE_FUNCTION_SETOPT1(curl_seek_callback, CURLOPT_SEEKFUNCTION) DEFINE_FUNCTION_SETOPT1(curl_ioctl_callback, CURLOPT_IOCTLFUNCTION) DEFINE_FUNCTION_SETOPT1(curl_sockopt_callback, CURLOPT_SOCKOPTFUNCTION) @@ -805,7 +824,7 @@ void CurlEasyRequest::setPost(AIPostFieldPtr const& postdata, U32 size) { llassert_always(postdata->data()); - Dout(dc::curl, "POST size is " << size << " bytes: \"" << libcwd::buf2str(postdata->data(), size) << "\"."); + DoutCurlEasy("POST size is " << size << " bytes: \"" << libcwd::buf2str(postdata->data(), size) << "\"."); setPostField(postdata); // Make sure the data stays around until we don't need it anymore. setPost_raw(size, postdata->data()); @@ -816,7 +835,7 @@ void CurlEasyRequest::setPost_raw(U32 size, char const* data) if (!data) { // data == NULL when we're going to read the data using CURLOPT_READFUNCTION. - Dout(dc::curl, "POST size is " << size << " bytes."); + DoutCurlEasy("POST size is " << size << " bytes."); } // Accept everything (send an Accept-Encoding header containing all encodings we support (zlib and gzip)). @@ -906,6 +925,24 @@ void CurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* us setopt(CURLOPT_SSL_CTX_DATA, this); } +//static +int CurlEasyRequest::progressCallback(void* userdata, double dltotal, double dlnow, double ultotal, double ulnow) +{ + CurlEasyRequest* self = static_cast(userdata); + ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj(); + AICurlEasyRequest_wat lock_self(*lockobj); + return self->mProgressCallback(self->mProgressCallbackUserData, dltotal, dlnow, ultotal, ulnow); +} + +void CurlEasyRequest::setProgressCallback(curl_progress_callback callback, void* userdata) +{ + mProgressCallback = callback; + mProgressCallbackUserData = userdata; + setopt(CURLOPT_PROGRESSFUNCTION, callback ? &CurlEasyRequest::progressCallback : NULL); + setopt(CURLOPT_PROGRESSDATA, userdata ? this : NULL); + setopt(CURLOPT_NOPROGRESS, callback ? 0L: 1L); +} + #define llmaybewarns lllog(LLApp::isExiting() ? LLError::LEVEL_INFO : LLError::LEVEL_WARN, NULL, NULL, false, true) static size_t noHeaderCallback(char* ptr, size_t size, size_t nmemb, void* userdata) @@ -932,12 +969,19 @@ static CURLcode noSSLCtxCallback(CURL* curl, void* sslctx, void* parm) return CURLE_ABORTED_BY_CALLBACK; } +static int noProgressCallback(void* userdata, double dltotal, double dlnow, double ultotal, double ulnow) +{ + llmaybewarns << "Calling noProgressCallback(); curl session aborted." << llendl; + return CURLE_ABORTED_BY_CALLBACK; +} + void CurlEasyRequest::revokeCallbacks(void) { if (mHeaderCallback == &noHeaderCallback && mWriteCallback == &noWriteCallback && mReadCallback == &noReadCallback && - mSSLCtxCallback == &noSSLCtxCallback) + mSSLCtxCallback == &noSSLCtxCallback && + mProgressCallback == &noProgressCallback) { // Already revoked. return; @@ -946,6 +990,7 @@ void CurlEasyRequest::revokeCallbacks(void) mWriteCallback = &noWriteCallback; mReadCallback = &noReadCallback; mSSLCtxCallback = &noSSLCtxCallback; + mProgressCallback = &noProgressCallback; if (active() && !no_warning()) { llwarns << "Revoking callbacks on a still active CurlEasyRequest object!" << llendl; @@ -954,6 +999,7 @@ void CurlEasyRequest::revokeCallbacks(void) curl_easy_setopt(getEasyHandle(), CURLOPT_WRITEHEADER, &noWriteCallback); curl_easy_setopt(getEasyHandle(), CURLOPT_READFUNCTION, &noReadCallback); curl_easy_setopt(getEasyHandle(), CURLOPT_SSL_CTX_FUNCTION, &noSSLCtxCallback); + curl_easy_setopt(getEasyHandle(), CURLOPT_PROGRESSFUNCTION, &noProgressCallback); } CurlEasyRequest::~CurlEasyRequest() @@ -1167,8 +1213,8 @@ void CurlEasyRequest::applyDefaultOptions(void) setoptString(CURLOPT_CAINFO, CertificateAuthority_r->file); setSSLCtxCallback(&curlCtxCallback, NULL); setopt(CURLOPT_NOSIGNAL, 1); - // The old code did this for the 'buffered' version, but I think it's nonsense. - //setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0); + // Cache DNS look ups an hour. If we set it smaller we risk frequent connect timeouts in cases where DNS look ups are slow. + setopt(CURLOPT_DNS_CACHE_TIMEOUT, 3600); // Disable SSL/TLS session caching; some servers (aka id.secondlife.com) refuse connections when session ids are enabled. setopt(CURLOPT_SSL_SESSIONID_CACHE, 0); // Set the CURL options for either SOCKS or HTTP proxy. @@ -1184,7 +1230,7 @@ void CurlEasyRequest::applyDefaultOptions(void) ); } -void CurlEasyRequest::finalizeRequest(std::string const& url) +void CurlEasyRequest::finalizeRequest(std::string const& url, AIHTTPTimeoutPolicy const& policy, AICurlEasyRequestStateMachine* state_machine) { llassert(!mRequestFinalized); mResult = CURLE_FAILED_INIT; // General error code; the final result code is stored here by MultiHandle::check_run_count when msg is CURLMSG_DONE. @@ -1207,6 +1253,8 @@ void CurlEasyRequest::finalizeRequest(std::string const& url) mRequestFinalized = true; setopt(CURLOPT_HTTPHEADER, mHeaders); setoptString(CURLOPT_URL, 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. // Of course we could increment the reference count, but that doesn't help much: if then this pointer would // get "lost" we'd have a memory leak. Either way we must make sure that it is impossible that this pointer @@ -1224,6 +1272,208 @@ void CurlEasyRequest::finalizeRequest(std::string const& url) setopt(CURLOPT_PRIVATE, get_lockobj()); } +//............................................................................. +// HTTP Timeout stuff + +// url must be of the form +// (see http://www.ietf.org/rfc/rfc3986.txt Appendix A for definitions not given here): +// +// url = sheme ":" hier-part [ "?" query ] [ "#" fragment ] +// hier-part = "//" authority path-abempty +// authority = [ userinfo "@" ] host [ ":" port ] +// path-abempty = *( "/" segment ) +// +// That is, a hier-part of the form '/ path-absolute', '/ path-rootless' or +// '/ path-empty' is NOT allowed here. This should be safe because we only +// call this function for curl access, any file access would use APR. +// +// However, as a special exception, this function allows: +// +// url = authority path-abempty +// +// without the 'sheme ":" "//"' parts. +// +// As follows from the ABNF (see RFC, Appendix A): +// - authority is either terminated by a '/' or by the end of the string because +// neither userinfo, host nor port may contain a '/'. +// - userinfo does not contain a '@', and if it exists, is always terminated by a '@'. +// - port does not contain a ':', and if it exists is always prepended by a ':'. +// +// Only called by CurlEasyRequest::timeout_add_easy_request. +static std::string extract_canonical_hostname(std::string 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); +#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'); + } +#endif + return hostname; +} + +// CURL-THREAD +// This is called when the easy handle is actually being added to the multi handle (thus after being queued). +void CurlEasyRequest::timeout_add_easy_request(void) +{ + char* eff_url; + getinfo(CURLINFO_EFFECTIVE_URL, &eff_url); // According to a discussion on IRC with a curl developer, we can rely on this returning the set CURLOPT_URL at this point. + setopt(CURLOPT_CONNECTTIMEOUT, mTimeoutPolicy->getConnectTimeout(extract_canonical_hostname(eff_url))); + setopt(CURLOPT_TIMEOUT, mTimeoutPolicy->getCurlTransaction()); + // We do NOT use CURLOPT_LOW_SPEED_TIME and CURLOPT_LOW_SPEED_LIMIT, + // instead use a progress meter callback. + setProgressCallback(&AICurlPrivate::CurlEasyRequest::timeout_progress, this); + // This boolean is valid (only) if we get a time out event from libcurl. + mTimeoutConnected = false; +} + +// CURL-THREAD +// This is called when the connection succeeded (thus after DNS lookup and connect). +void CurlEasyRequest::timeout_connected(void) +{ + DoutCurlEasy("Calling CurlEasyRequest::timeout_connected(): mTimeoutWaitingForReply = false"); + mTimeoutConnected = true; +#ifdef CWDEBUG + mTimeout_connect_time = get_clock_count(); +#endif + // Now that mTimeoutConnected is set we'll be calling timeout_progress(); initialize the variables used there. + mTimeout_dlnow = 0; + mTimeout_ulnow = 0; + mTimeout_progress_time = 0; + mTimeoutWaitingForReply = false; + mTimeoutNothingReceivedYet = true; +} + +// CURL-THREAD +// This is called when data was sent to the server socket. +void CurlEasyRequest::timeout_data_sent(size_t n) +{ + if (!mTimeoutConnected) + { + // If we can send data (for the first time) then that's our way to know we connected. + timeout_connected(); + } +} + +// CURL-THREAD +// This is called when data was received from the server. +void CurlEasyRequest::timeout_data_received(size_t n) +{ + if (n > 0) + { +#ifdef CWDEBUG + if (mTimeoutNothingReceivedYet) + DoutCurlEasy("CurlEasyRequest::timeout_data_received(): Received data from server: set mTimeoutWaitingForReply = false"); +#endif + // We received something, now switch to getLowSpeedLimit()/getLowSpeedTime(). + mTimeoutWaitingForReply = false; + mTimeoutNothingReceivedYet = false; + } +} + +// CURL_THREAD +//static +int CurlEasyRequest::timeout_progress(void* userdata, double dltotal, double dlnow, double ultotal, double ulnow) +{ + CurlEasyRequest* self = static_cast(userdata); + + //AIFIXME: There has to be a better way to determine this (because it feels fuzzy, I only allow it to SET the boolean): +#ifdef CWDEBUG + if (!self->mTimeoutWaitingForReply && (self->mTimeoutNothingReceivedYet && ultotal > 0 && ulnow == ultotal)) + Dout(dc::curl, "CurlEasyRequest::timeout_progress(): uploading data finished: set mTimeoutWaitingForReply = true [" << (void*)self->get_lockobj() << ']'); +#endif + llassert(ulnow == 0 || ultotal > 0); // Do we always know the total upload size? + self->mTimeoutWaitingForReply = self->mTimeoutWaitingForReply || (self->mTimeoutNothingReceivedYet && ultotal > 0 && ulnow == ultotal); + + return self->mTimeoutConnected ? self->timeout_progress(dlnow, ulnow) : 0; +} + +// CURL_THREAD +// dlnow is the number of bytes of the BODY of the message, received from the server. +// ulnow is the number of bytes of the BODY of the message, sent to the server so far. +// +// Note that the algorithm used here is basically the same as libcurl uses +// for CURLOPT_LOW_SPEED_LIMIT / CURLOPT_LOW_SPEED_TIME, but we can't use that +// because we need to change the variables involved during transfer, which isn't +// officially supported by libcurl. +// Libcurl does the progress callback at the exact same point as checking for +// low speed, and the same data is passed (except the time, unfortunately), so +// the functionality is the same. +int CurlEasyRequest::timeout_progress(double dlnow, double ulnow) +{ + static double const clock_frequency = calc_clock_frequency(); + U64 last_time = mTimeout_progress_time; + mTimeout_progress_time = get_clock_count(); + double transfer = (dlnow - mTimeout_dlnow) + (ulnow - mTimeout_ulnow); // Just combine up and download :p. + mTimeout_dlnow = dlnow; + mTimeout_ulnow = ulnow; + if (!last_time || mTimeout_progress_time == last_time) // Is this the first time this function is called (or are we called infinitely fast)? + { + DoutCurlEasy("timeout_progress(" << dlnow << ", " << ulnow << ") called at " << + ((mTimeout_progress_time - mTimeout_connect_time) / clock_frequency) << " seconds after connect" << + (last_time ? "" : " (first time)")); + // Start the timer: we need to have a too low transfer rate, for at least + // mTimeoutPolicy->getLowSpeedTime() seconds, counting from this moment. + mTransferOK = mTimeout_progress_time; + return 0; + } + transfer *= clock_frequency / (mTimeout_progress_time - last_time); // Bytes per second. + U64 timer = mTimeout_progress_time - mTransferOK; // Low speed timer in clock ticks. + U16 lowspeedtime = mTimeoutWaitingForReply ? mTimeoutPolicy->getReplyDelay() : mTimeoutPolicy->getLowSpeedTime(); + U32 lowspeedlimit = mTimeoutWaitingForReply ? (U32)1 : mTimeoutPolicy->getLowSpeedLimit(); + DoutCurlEasy("timeout_progress(" << dlnow << ", " << ulnow << ") called at " << ((mTimeout_progress_time - mTimeout_connect_time) / clock_frequency) << + " seconds after connect (timer = " << (timer / clock_frequency) << " s ; transfer = " << transfer << " bytes/s; lowspeedtime = " << lowspeedtime << + "; lowspeedlimit = " << lowspeedlimit << " (mTimeoutWaitingForReply == " << (mTimeoutWaitingForReply ? "true" : "false") << "))"); + if (transfer >= lowspeedlimit) + { + // Yay! Transfer rate is OK; restart timeout timer. + mTransferOK = mTimeout_progress_time; + } + else if (timer > clock_frequency * lowspeedtime) + { + // We haven't seen a high enough transfer rate for too long. Abort the transfer. + llwarns << +#ifdef CWDEBUG + (void*)get_lockobj() << ": " +#endif + "aborting slow connection (transfer rate below " << lowspeedlimit << + " for more than " << lowspeedtime << " second" << ((lowspeedtime == 1) ? "" : "s") << ")." << llendl; + return 1; + } + return 0; +} + +// CURL-THREAD +// This is called immediately before done() after curl finished, with code. +void CurlEasyRequest::timeout_done(CURLcode code) +{ + if (code == CURLE_OPERATION_TIMEDOUT && !mTimeoutConnected) + { + char* eff_url; + getinfo(CURLINFO_EFFECTIVE_URL, &eff_url); //AIFIXME: cache hostname, cause this might have changed. + AIHTTPTimeoutPolicy::connect_timed_out(extract_canonical_hostname(eff_url)); + // AIFIXME: use return value to change priority + } + // Abuse this boolean to tell any subsequent call to timeout_progress that this certainly can't timeout anymore. + mTimeoutConnected = false; +} + +// End of HTTP Timeout stuff. +//............................................................................. + void CurlEasyRequest::getTransferInfo(AICurlInterface::TransferInfo* info) { // Curl explicitly demands a double for these info's. @@ -1268,8 +1518,6 @@ void CurlEasyRequest::removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy // CurlResponderBuffer static int const HTTP_REDIRECTS_DEFAULT = 10; -static S32 const CURL_REQUEST_TIMEOUT = 30; // The minimum value of the CURLOPT_TIMEOUT option (which is the maximum - // time in seconds that we allow a libcurl transfer operation to take). LLChannelDescriptors const CurlResponderBuffer::sChannels; @@ -1334,7 +1582,7 @@ ThreadSafeBufferedCurlEasyRequest* CurlResponderBuffer::get_lockobj(void) return static_cast(AIThreadSafeSimple::wrapper_cast(this)); } -void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w, AIHTTPHeaders const& headers, AICurlInterface::ResponderPtr responder, S32 time_out) +void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w, AIHTTPHeaders const& headers, AICurlInterface::ResponderPtr responder) { mInput.reset(new LLBufferArray); mInput->setThreaded(true); @@ -1359,8 +1607,6 @@ void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w // Don't verify host name so urls with scrubbed host names will work (improves DNS performance). curl_easy_request_w->setopt(CURLOPT_SSL_VERIFYHOST, 0); - curl_easy_request_w->setopt(CURLOPT_TIMEOUT, llmax(time_out, CURL_REQUEST_TIMEOUT)); - // Keep responder alive. mResponder = responder; // Send header events to responder if needed. @@ -1387,7 +1633,8 @@ size_t CurlResponderBuffer::curlWriteCallback(char* data, size_t size, size_t nm // CurlResponderBuffer::setBodyLimit is never called, so buffer_w->mBodyLimit is infinite. //S32 bytes = llmin(size * nmemb, buffer_w->mBodyLimit); buffer_w->mBodyLimit -= bytes; buffer_w->getOutput()->append(sChannels.in(), (U8 const*)data, bytes); - buffer_w->mResponseTransferedBytes += bytes; // Accumulate data received from the server. + buffer_w->mResponseTransferedBytes += bytes; // Accumulate data received from the server. + buffered_easy_request_w->timeout_data_received(bytes); // Timeout administration. return bytes; } @@ -1403,7 +1650,8 @@ size_t CurlResponderBuffer::curlReadCallback(char* data, size_t size, size_t nme S32 bytes = size * nmemb; // The maximum amount to read. AICurlResponderBuffer_wat buffer_w(*lockobj); buffer_w->mLastRead = buffer_w->getInput()->readAfter(sChannels.out(), buffer_w->mLastRead, (U8*)data, bytes); - buffer_w->mRequestTransferedBytes += bytes; // Accumulate data sent to the server. + buffer_w->mRequestTransferedBytes += bytes; // Accumulate data sent to the server. + buffered_easy_request_w->timeout_data_sent(bytes); // Timeout administration. return bytes; // Return the amount actually read (might be lowered by readAfter()). } @@ -1420,6 +1668,7 @@ size_t CurlResponderBuffer::curlHeaderCallback(char* data, size_t size, size_t n char const* const header_line = static_cast(data); size_t const header_len = size * nmemb; + buffered_easy_request_w->timeout_data_received(header_len); // Timeout administration. if (!header_len) { @@ -1504,7 +1753,7 @@ void CurlResponderBuffer::finished(AICurlEasyRequest_wat& curl_easy_request_w) void CurlResponderBuffer::removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) { - Dout(dc::curl, "Calling CurlResponderBuffer::removed_from_multi_handle(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this); + DoutCurlEasy("Calling CurlResponderBuffer::removed_from_multi_handle(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this); // Lock self. ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj(); diff --git a/indra/aistatemachine/aicurl.h b/indra/aistatemachine/aicurl.h index e0296f865..e19ae15e0 100644 --- a/indra/aistatemachine/aicurl.h +++ b/indra/aistatemachine/aicurl.h @@ -57,6 +57,7 @@ class LLSD; class LLBufferArray; class LLChannelDescriptors; +class AIHTTPTimeoutPolicy; //----------------------------------------------------------------------------- // Exceptions. @@ -210,6 +211,9 @@ class Responder : public AICurlResponderBufferEvents { // Derived classes that implement completedHeaders() should return true here. virtual bool needsHeaders(void) const { return false; } + // Timeout policy to use. + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const = 0; + // Derived classes can override this to get the raw data of the body of the HTML message that was received. // The default is to interpret the content as LLSD and call completed(). virtual void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer); diff --git a/indra/aistatemachine/aicurleasyrequeststatemachine.cpp b/indra/aistatemachine/aicurleasyrequeststatemachine.cpp index 9060779ee..868419e91 100644 --- a/indra/aistatemachine/aicurleasyrequeststatemachine.cpp +++ b/indra/aistatemachine/aicurleasyrequeststatemachine.cpp @@ -30,6 +30,7 @@ #include "linden_common.h" #include "aicurleasyrequeststatemachine.h" +#include "aihttptimeoutpolicy.h" #include "llcontrol.h" enum curleasyrequeststatemachine_state_type { @@ -61,7 +62,7 @@ void AICurlEasyRequestStateMachine::initialize_impl(void) { { AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest); - llassert(curlEasyRequest_w->is_finalized()); // Call finalizeRequest(url) before calling run(). + llassert(curlEasyRequest_w->is_finalized()); // Call finalizeRequest() before calling run(). curlEasyRequest_w->send_events_to(this); } mAdded = false; @@ -116,13 +117,13 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void) // 3) AICurlEasyRequestStateMachine_finished (running) // 4) AICurlEasyRequestStateMachine_removed_after_finished (running) - if (mRequestTimeOut > 0.f) + if (mTotalDelayTimeout > 0.f) { // Set an inactivity timer. // This shouldn't really be necessary, except in the case of a bug // in libcurl; but lets be sure and set a timer for inactivity. mTimer = new AIPersistentTimer; // Do not delete timer upon expiration. - mTimer->setInterval(mRequestTimeOut); + mTimer->setInterval(mTotalDelayTimeout); mTimer->run(this, AICurlEasyRequestStateMachine_timedOut, false, false); } break; @@ -246,7 +247,7 @@ void AICurlEasyRequestStateMachine::finish_impl(void) } AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(bool buffered) : - mBuffered(buffered), mCurlEasyRequest(buffered), mTimer(NULL), mRequestTimeOut(sCurlRequestTimeOut) + mBuffered(buffered), mCurlEasyRequest(buffered), mTimer(NULL), mTotalDelayTimeout(AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout().getTotalDelay()) { Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(" << (buffered ? "true" : "false") << ") [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]"); if (!mBuffered) @@ -255,18 +256,9 @@ AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(bool buffered) : } } -//static -F32 AICurlEasyRequestStateMachine::sCurlRequestTimeOut = 40.f; - -//static -void AICurlEasyRequestStateMachine::setDefaultRequestTimeOut(F32 defaultRequestTimeOut) +void AICurlEasyRequestStateMachine::setTotalDelayTimeout(F32 totalDelayTimeout) { - sCurlRequestTimeOut = defaultRequestTimeOut; -} - -void AICurlEasyRequestStateMachine::setRequestTimeOut(F32 curlRequestTimeOut) -{ - mRequestTimeOut = curlRequestTimeOut; + mTotalDelayTimeout = totalDelayTimeout; } AICurlEasyRequestStateMachine::~AICurlEasyRequestStateMachine() diff --git a/indra/aistatemachine/aicurleasyrequeststatemachine.h b/indra/aistatemachine/aicurleasyrequeststatemachine.h index ec46c43bc..3878a597e 100644 --- a/indra/aistatemachine/aicurleasyrequeststatemachine.h +++ b/indra/aistatemachine/aicurleasyrequeststatemachine.h @@ -64,16 +64,11 @@ class AICurlEasyRequestStateMachine : public AIStateMachine, public AICurlEasyHa bool mFinished; // Set by the curl thread to signal it finished. bool mHandled; // Set when we processed the received data. AITimer* mTimer; // Expiration timer. - F32 mRequestTimeOut; // The time out value for mTimer. - - static F32 sCurlRequestTimeOut; // The default time out value for mTimer (CurlRequestTimeOut debug setting). + F32 mTotalDelayTimeout; // The time out value for mTimer. public: - // Called once to set a different timeout then the default of 40 seconds. - static void setDefaultRequestTimeOut(F32 defaultRequestTimeOut); - // Called to set a specific time out, instead of the default one. - void setRequestTimeOut(F32 requestTimeOut); + void setTotalDelayTimeout(F32 totalDelayTimeout); protected: // AICurlEasyRequest Events. diff --git a/indra/aistatemachine/aicurlprivate.h b/indra/aistatemachine/aicurlprivate.h index bef49e0f0..59c7d01dd 100644 --- a/indra/aistatemachine/aicurlprivate.h +++ b/indra/aistatemachine/aicurlprivate.h @@ -35,6 +35,8 @@ #include "llatomic.h" class AIHTTPHeaders; +class AIHTTPTimeoutPolicy; +class AICurlEasyRequestStateMachine; namespace AICurlPrivate { namespace curlthread { class MultiHandle; } @@ -87,9 +89,9 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven DECLARE_SETOPT(curl_write_callback); //DECLARE_SETOPT(curl_read_callback); Same type as curl_write_callback DECLARE_SETOPT(curl_ssl_ctx_callback); + DECLARE_SETOPT(curl_progress_callback); DECLARE_SETOPT(curl_conv_callback); #if 0 // Not used by the viewer. - DECLARE_SETOPT(curl_progress_callback); DECLARE_SETOPT(curl_seek_callback); DECLARE_SETOPT(curl_ioctl_callback); DECLARE_SETOPT(curl_sockopt_callback); @@ -225,6 +227,7 @@ class CurlEasyRequest : public CurlEasyHandle { static size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata); static size_t readCallback(char* ptr, size_t size, size_t nmemb, void* userdata); static CURLcode SSLCtxCallback(CURL* curl, void* sslctx, void* userdata); + static int progressCallback(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow); curl_write_callback mHeaderCallback; void* mHeaderCallbackUserData; @@ -234,12 +237,15 @@ class CurlEasyRequest : public CurlEasyHandle { void* mReadCallbackUserData; curl_ssl_ctx_callback mSSLCtxCallback; void* mSSLCtxCallbackUserData; + curl_progress_callback mProgressCallback; + void* mProgressCallbackUserData; public: void setHeaderCallback(curl_write_callback callback, void* userdata); void setWriteCallback(curl_write_callback callback, void* userdata); void setReadCallback(curl_read_callback callback, void* userdata); void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata); + void setProgressCallback(curl_progress_callback callback, void* userdata); // Call this if the set callbacks are about to be invalidated. void revokeCallbacks(void); @@ -260,7 +266,26 @@ class CurlEasyRequest : public CurlEasyHandle { // Prepare the request for adding it to a multi session, or calling perform. // This actually adds the headers that were collected with addHeader. - void finalizeRequest(std::string const& url); + void finalizeRequest(std::string const& url, AIHTTPTimeoutPolicy const& policy, AICurlEasyRequestStateMachine* state_machine); + + // Called by MultiHandle::add_easy_request when the easy handle is actually being added to the multi handle. + void timeout_add_easy_request(void); + + // Called when data was written the first time, meaning that the connection succeeded. + void timeout_connected(void); + + // Called when data is sent. + void timeout_data_sent(size_t n); + + // Called when data is received. + void timeout_data_received(size_t n); + + // Called immediately before done() after curl finished, with code. + void timeout_done(CURLcode code); + + // Progress meter callback. + static int timeout_progress(void* userdata, double dltotal, double dlnow, double ultotal, double ulnow); + int timeout_progress(double dlnow, double ulnow); // Called by MultiHandle::check_run_count() to store result code that is returned by getResult. void store_result(CURLcode result) { mResult = result; } @@ -280,12 +305,25 @@ class CurlEasyRequest : public CurlEasyHandle { AICurlEasyHandleEvents* mEventsTarget; CURLcode mResult; + // AIFIXME: put all timeout stuff in it's own class. + AIHTTPTimeoutPolicy const* mTimeoutPolicy; + bool mTimeoutConnected; // Set if we succeeded to connect and are transfering data. +#ifdef CWDEBUG + U64 mTimeout_connect_time; // Time at which mTimeoutConnected was set to true (for debugging purposes only). +#endif + bool mTimeoutWaitingForReply; // Set after we finished sending data to the server and are waiting for the reply. + bool mTimeoutNothingReceivedYet; // Set when connected, reset when the HTML reply header from the server is received. + U64 mTimeout_progress_time; // The (last) time timeout_progress() was called, in microseconds. + U64 mTransferOK; // The last time the transfer rate was OK. + double mTimeout_dlnow; // Number of downloaded bytes so far, as per last call to timeout_progress. + double mTimeout_ulnow; // Number of uploaded bytes so far, as per last call to timeout_progress. + private: // This class may only be created by constructing a ThreadSafeCurlEasyRequest. friend class ThreadSafeCurlEasyRequest; // Throws AICurlNoEasyHandle. CurlEasyRequest(void) : - mHeaders(NULL), mRequestFinalized(false), mEventsTarget(NULL), mResult(CURLE_FAILED_INIT) + mHeaders(NULL), mRequestFinalized(false), mEventsTarget(NULL), mResult(CURLE_FAILED_INIT), mTimeoutPolicy(NULL) { applyDefaultOptions(); } public: ~CurlEasyRequest(); @@ -327,7 +365,7 @@ class CurlResponderBuffer : protected AICurlResponderBufferEvents, protected AIC typedef AICurlInterface::Responder::buffer_ptr_t buffer_ptr_t; void resetState(AICurlEasyRequest_wat& curl_easy_request_w); - void prepRequest(AICurlEasyRequest_wat& buffered_curl_easy_request_w, AIHTTPHeaders const& headers, AICurlInterface::ResponderPtr responder, S32 time_out = 0); + void prepRequest(AICurlEasyRequest_wat& buffered_curl_easy_request_w, AIHTTPHeaders const& headers, AICurlInterface::ResponderPtr responder); buffer_ptr_t& getInput(void) { return mInput; } buffer_ptr_t& getOutput(void) { return mOutput; } diff --git a/indra/aistatemachine/aicurlthread.cpp b/indra/aistatemachine/aicurlthread.cpp index c29092271..c593ec910 100644 --- a/indra/aistatemachine/aicurlthread.cpp +++ b/indra/aistatemachine/aicurlthread.cpp @@ -1382,7 +1382,12 @@ char const* action_str(int action) //static int MultiHandle::socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp) { - DoutEntering(dc::curl, "MultiHandle::socket_callback((CURL*)" << (void*)easy << ", " << s << ", " << action_str(action) << ", " << (void*)userp << ", " << (void*)socketp << ")"); +#ifdef CWDEBUG + ThreadSafeCurlEasyRequest* lockobj = NULL; + curl_easy_getinfo(easy, CURLINFO_PRIVATE, &lockobj); + DoutEntering(dc::curl, "MultiHandle::socket_callback((CURL*)" << (void*)easy << ", " << s << + ", " << action_str(action) << ", " << (void*)userp << ", " << (void*)socketp << ") [CURLINFO_PRIVATE = " << (void*)lockobj << "]"); +#endif MultiHandle& self = *static_cast(userp); CurlSocketInfo* sock_info = static_cast(socketp); if (action == CURL_POLL_REMOVE) @@ -1449,6 +1454,7 @@ void MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request) CURLMcode ret; { AICurlEasyRequest_wat curl_easy_request_w(*easy_request); + curl_easy_request_w->timeout_add_easy_request(); ret = curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle); } if (ret == CURLM_OK) @@ -1456,7 +1462,7 @@ void MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request) mHandleAddedOrRemoved = true; std::pair res = mAddedEasyRequests.insert(easy_request); llassert(res.second); // May not have been added before. - Dout(dc::curl, "MultiHandle::add_easy_request: Added AICurlEasyRequest " << (void*)easy_request.get() << "; now processing " << mAddedEasyRequests.size() << " easy handles."); + Dout(dc::curl, "MultiHandle::add_easy_request: Added AICurlEasyRequest " << (void*)easy_request.get_ptr().get() << "; now processing " << mAddedEasyRequests.size() << " easy handles."); return; } } @@ -1506,7 +1512,7 @@ CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request } mAddedEasyRequests.erase(iter); mHandleAddedOrRemoved = true; - Dout(dc::curl, "MultiHandle::remove_easy_request: Removed AICurlEasyRequest " << (void*)easy_request.get() << "; now processing " << mAddedEasyRequests.size() << " easy handles."); + Dout(dc::curl, "MultiHandle::remove_easy_request: Removed AICurlEasyRequest " << (void*)easy_request.get_ptr().get() << "; now processing " << mAddedEasyRequests.size() << " easy handles."); // Attempt to add a queued request, if any. if (!mQueuedRequests.empty()) @@ -1541,8 +1547,10 @@ void MultiHandle::check_run_count(void) #ifdef CWDEBUG char* eff_url; curl_easy_request_w->getinfo(CURLINFO_EFFECTIVE_URL, &eff_url); - Dout(dc::curl, "Finished: " << eff_url << " (" << msg->data.result << ")"); + Dout(dc::curl, "Finished: " << eff_url << " (" << curl_easy_strerror(msg->data.result) << ") [CURLINFO_PRIVATE = " << (void*)ptr << "]"); #endif + // Update timeout administration. + curl_easy_request_w->timeout_done(msg->data.result); // Signal that this easy handle finished. curl_easy_request_w->done(curl_easy_request_w); } diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index 5e5010c64..f5e9a1932 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -53,9 +53,14 @@ LLPumpIO* gServicePump; BOOL gBreak = false; BOOL gSent = false; +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy crashLoggerResponder_timeout; + class LLCrashLoggerResponder : public LLHTTPClient::Responder { public: + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return crashLoggerResponder_timeout; } + LLCrashLoggerResponder() { } @@ -308,14 +313,14 @@ bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior) return true; } -bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout) +bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries) { gBreak = false; std::string status_message; for(int i = 0; i < retries; ++i) { status_message = llformat("%s, try %d...", msg.c_str(), i+1); - LLHTTPClient::post4(host, data, new LLCrashLoggerResponder(), timeout); + LLHTTPClient::post4(host, data, new LLCrashLoggerResponder); while(!gBreak) { updateApplication(status_message); @@ -350,12 +355,12 @@ bool LLCrashLogger::sendCrashLogs() // *TODO: Translate if(mCrashHost != "") { - sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3, 5); + sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3); } if(!sent) { - sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3, 5); + sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3); } mSentCrashLogs = sent; diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h index 72c1f3fb8..08e4a5f9f 100644 --- a/indra/llcrashlogger/llcrashlogger.h +++ b/indra/llcrashlogger/llcrashlogger.h @@ -40,6 +40,8 @@ #include "llsd.h" #include "llcontrol.h" +class AIHTTPTimeoutPolicy; + class LLCrashLogger : public LLApp { public: @@ -57,7 +59,7 @@ public: virtual bool cleanup() { return true; } void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; } S32 getCrashBehavior() { return mCrashBehavior; } - bool runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout); + bool runCrashLogPost(std::string host, LLSD data, std::string msg, int retries); protected: S32 mCrashBehavior; BOOL mCrashInPreviousExec; diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 0c0650330..ec697c4a3 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -21,6 +21,7 @@ include_directories( set(llmessage_SOURCE_FILES aihttpheaders.cpp + aihttptimeoutpolicy.cpp llhttpclient.cpp llares.cpp llareslistener.cpp @@ -106,6 +107,7 @@ set(llmessage_HEADER_FILES CMakeLists.txt aihttpheaders.h + aihttptimeoutpolicy.h llares.h llareslistener.h llassetstorage.h diff --git a/indra/llmessage/aihttptimeoutpolicy.cpp b/indra/llmessage/aihttptimeoutpolicy.cpp new file mode 100644 index 000000000..55a2e7ad4 --- /dev/null +++ b/indra/llmessage/aihttptimeoutpolicy.cpp @@ -0,0 +1,733 @@ +/** + * @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" +#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 = 1000; // AIFIXME: this should be 1 byte/s, set at 1000 now for debugging purposes. +U32 const ABS_max_low_speed_limit = 1000000; // This limit almost certainly higher than what 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 = PolicyOp()) : 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) +{ + // Register for changes to the base policy. + mBase->derived(this); +} + +//static +void AIHTTPTimeoutPolicy::setDefaultCurlTimeout(AIHTTPTimeoutPolicy const& timeout) +{ + sDebugSettingsCurlTimeout = timeout; + 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 int min(void) { return ABS_min_DNS_lookup; } + static int 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 int min(void) { return ABS_min_connect_time; } + static int 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 int min(void) { return ABS_min_reply_delay; } + static int 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 int min(void) { return ABS_min_low_speed_time; } + static int max(AIHTTPTimeoutPolicy const* policy) { return llmin(ABS_max_low_speed_time, (U16)(policy->mMaximumCurlTransaction / 2)); } + static int lmin(void) { return ABS_min_low_speed_limit; } + static int 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 int min(AIHTTPTimeoutPolicy const* policy) { return llmax((int)ABS_min_transaction, policy->mMaximumConnectTime + policy->mMaximumReplyDelay + 4 * policy->mLowSpeedTime); } + static int 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 int min(AIHTTPTimeoutPolicy const* policy) { return llmax((int)ABS_min_total_delay, policy->mMaximumCurlTransaction + 1); } + static int 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. + +// AIFIXME: update all policies whenever a CurlTimeout* settings is changed. + +// 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); + +// This used to be '5 seconds'. +AIHTTPTimeoutPolicyBase transfer_5s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(), + Transaction(5) + ); + +// This used to be '18 seconds'. +AIHTTPTimeoutPolicyBase transfer_18s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(), + Transaction(18) + ); + +// This used to be '300 seconds'. We derive this from the hardcoded result so users can't mess with it. +AIHTTPTimeoutPolicyBase transfer_300s(HTTPTimeoutPolicy_default, + Transaction(300) + ); + +// This used to be a call to setopt(CURLOPT_CONNECTTIMEOUT, 40L) with the remark 'Be a little impatient about establishing connections.' +AIHTTPTimeoutPolicyBase connect_40s(AIHTTPTimeoutPolicyBase::getDebugSettingsCurlTimeout(), + Connect(40)); + +// End of policy definitions. +//======================================================================================================= + +//======================================================================================================= +// 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(avatarNameResponder); +P2(baseCapabilitiesComplete, transfer_18s); +P(blockingGet); +P(blockingPost); +P(charactersResponder); +P(classifiedStatsResponder); +P(consoleResponder); +P2(crashLoggerResponder, transfer_5s); +P(createInventoryCategoryResponder); +P(emeraldDicDownloader); +P(environmentApplyResponder); +P(environmentRequestResponder); +P(estateChangeInfoResponder); +P(eventPollResponder); +P(eventResponder); +P(fetchInventoryResponder); +P(floaterRegionDebugConsole); +P(fnPtrResponder); +P2(groupProposalBallotResponder, transfer_300s); +P(homeLocationResponder); +P(HTTPGetResponder); +P(iamHereLogin); +P(iamHere); +P(iamHereVoice); +P2(inventoryModelFetchDescendentsResponder, transfer_300s); +P(inventoryModelFetchItemResponder); +P(lcl_responder); +P(mapLayerResponder); +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(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(wholeModelFeeResponder); +P(wholeModelUploadResponder); +P2(XMLRPCTransaction, connect_40s); + diff --git a/indra/llmessage/aihttptimeoutpolicy.h b/indra/llmessage/aihttptimeoutpolicy.h new file mode 100644 index 000000000..bb11b033a --- /dev/null +++ b/indra/llmessage/aihttptimeoutpolicy.h @@ -0,0 +1,114 @@ +/** + * @file aihttptimeoutpolicy.h + * @brief Store the policy on timing out a HTTP curl transaction. + * + * 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/09/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AIHTTPTIMEOUTPOLICY_H +#define AIHTTPTIMEOUTPOLICY_H + +#include "stdtypes.h" +#include + +class AIHTTPTimeoutPolicyBase; + +namespace AIHTTPTimeoutPolicyOperators { + +struct DNS; +struct Connect; +struct Reply; +struct Speed; +struct Transaction; +struct Total; + +} // namespace AIHTTPTimeoutPolicyOperators + +class AIHTTPTimeoutPolicy { + protected: + char const* const mName; // The name of this policy, for debugging purposes. + AIHTTPTimeoutPolicyBase* const mBase; // Policy this policy was based on. + static AIHTTPTimeoutPolicyBase sDebugSettingsCurlTimeout; // CurlTimeout* debug settings. + + private: + U16 mDNSLookupGrace; // Extra connect timeout the first time we connect to a host (this is to allow for DNS lookups). + U16 mMaximumConnectTime; // Connect timeouts any subsequent connects to the same host, assuming the DNS will be cached now. + U16 mMaximumReplyDelay; // Timeout for the period between sending data to the server and the HTTP header of the reply. + U16 mLowSpeedTime; // The time in seconds that a transfer should be below mLowSpeedLimit before to consider it too slow and abort. + U32 mLowSpeedLimit; // Transfer speed in bytes per second that a transfer should be below during mLowSpeedTime seconds to consider it too slow and abort. + U16 mMaximumCurlTransaction; // Timeout for the whole curl transaction (including connect and DNS lookup). + U16 mMaximumTotalDelay; // Timeout from moment of request (including the time a request is/was queued). + + friend struct AIHTTPTimeoutPolicyOperators::DNS; + friend struct AIHTTPTimeoutPolicyOperators::Connect; + friend struct AIHTTPTimeoutPolicyOperators::Reply; + friend struct AIHTTPTimeoutPolicyOperators::Speed; + friend struct AIHTTPTimeoutPolicyOperators::Transaction; + friend struct AIHTTPTimeoutPolicyOperators::Total; + + public: + // Construct a HTTP timeout policy object that mimics base, or Debug Settings if none given. + AIHTTPTimeoutPolicy( + char const* name, + AIHTTPTimeoutPolicyBase& base = sDebugSettingsCurlTimeout); + + // Construct a HTTP timeout policy with exact specifications. + 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); + + void sanity_checks(void) const; + + // Accessors. + char const* name(void) const { return mName; } + U16 getConnectTimeout(std::string const& hostname) const; + U16 getReplyDelay(void) const { return mMaximumReplyDelay; } + U16 getLowSpeedTime(void) const { return mLowSpeedTime; } + U32 getLowSpeedLimit(void) const { return mLowSpeedLimit; } + U16 getCurlTransaction(void) const { return mMaximumCurlTransaction; } + U16 getTotalDelay(void) const { return mMaximumTotalDelay; } + static AIHTTPTimeoutPolicy const& getDebugSettingsCurlTimeout(void); + + // Called once at start up of viewer to set a different default timeout policy than HTTPTimeoutPolicy_default. + static void setDefaultCurlTimeout(AIHTTPTimeoutPolicy const& defaultCurlTimeout); + + // Called when a connect to a hostname timed out. + static bool connect_timed_out(std::string const& hostname); + + protected: + // Used by AIHTTPTimeoutPolicyBase::AIHTTPTimeoutPolicyBase(AIHTTPTimeoutPolicyBase&). + AIHTTPTimeoutPolicy(AIHTTPTimeoutPolicy&); + // Abused assigned operator (called by AIHTTPTimeoutPolicyBase::operator=). + AIHTTPTimeoutPolicy& operator=(AIHTTPTimeoutPolicy const&); +}; + +#endif // AIHTTPTIMEOUTPOLICY_H diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 852b192ea..fb4eed79a 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -171,6 +171,9 @@ namespace LLAvatarNameCache */ +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy avatarNameResponder_timeout; + class LLAvatarNameResponder : public LLHTTPClient::Responder { private: @@ -182,6 +185,8 @@ private: AIHTTPHeaders mHeaders; public: + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return avatarNameResponder_timeout; } + LLAvatarNameResponder(const std::vector& agent_ids) : mAgentIDs(agent_ids) { } diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 585e4644b..9db6170c9 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -46,7 +46,6 @@ static const U32 EASY_HANDLE_POOL_SIZE = 5; static const S32 MULTI_PERFORM_CALL_REPEAT = 5; -static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds per operation static const S32 MAX_ACTIVE_REQUEST_COUNT = 100; //static diff --git a/indra/llmessage/llcurlrequest.cpp b/indra/llmessage/llcurlrequest.cpp index 9911335ee..72de85774 100644 --- a/indra/llmessage/llcurlrequest.cpp +++ b/indra/llmessage/llcurlrequest.cpp @@ -76,7 +76,7 @@ bool Request::getByteRange2(std::string const& url, AIHTTPHeaders const& headers buffered_easy_request_w->addHeader(range.c_str()); } - buffered_easy_request_w->finalizeRequest(url); + buffered_easy_request_w->finalizeRequest(url, responder->getHTTPTimeoutPolicy(), buffered_easy_request); } buffered_easy_request->run(); @@ -84,7 +84,7 @@ bool Request::getByteRange2(std::string const& url, AIHTTPHeaders const& headers return true; // We throw in case of problems. } -bool Request::post2(std::string const& url, AIHTTPHeaders const& headers, std::string const& data, ResponderPtr responder, S32 time_out) +bool Request::post2(std::string const& url, AIHTTPHeaders const& headers, std::string const& data, ResponderPtr responder) { DoutEntering(dc::curl, "Request::post(" << url << ", ...)"); @@ -95,7 +95,7 @@ bool Request::post2(std::string const& url, AIHTTPHeaders const& headers, std::s AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest); AICurlResponderBuffer_wat buffer_w(*buffered_easy_request->mCurlEasyRequest); - buffer_w->prepRequest(buffered_easy_request_w, headers, responder, time_out); + buffer_w->prepRequest(buffered_easy_request_w, headers, responder); U32 bytes = data.size(); bool success = buffer_w->getInput()->append(buffer_w->sChannels.out(), (U8 const*)data.data(), bytes); @@ -106,7 +106,7 @@ bool Request::post2(std::string const& url, AIHTTPHeaders const& headers, std::s } buffered_easy_request_w->setPost(bytes); buffered_easy_request_w->addHeader("Content-Type: application/octet-stream"); - buffered_easy_request_w->finalizeRequest(url); + buffered_easy_request_w->finalizeRequest(url, responder->getHTTPTimeoutPolicy(), buffered_easy_request); } buffered_easy_request->run(); @@ -114,7 +114,7 @@ bool Request::post2(std::string const& url, AIHTTPHeaders const& headers, std::s return true; // We throw in case of problems. } -bool Request::post3(std::string const& url, AIHTTPHeaders const& headers, LLSD const& data, ResponderPtr responder, S32 time_out) +bool Request::post3(std::string const& url, AIHTTPHeaders const& headers, LLSD const& data, ResponderPtr responder) { DoutEntering(dc::curl, "Request::post(" << url << ", ...)"); @@ -125,7 +125,7 @@ bool Request::post3(std::string const& url, AIHTTPHeaders const& headers, LLSD c AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest); AICurlResponderBuffer_wat buffer_w(*buffered_easy_request->mCurlEasyRequest); - buffer_w->prepRequest(buffered_easy_request_w, headers, responder, time_out); + buffer_w->prepRequest(buffered_easy_request_w, headers, responder); LLBufferStream buffer_stream(buffer_w->sChannels, buffer_w->getInput().get()); LLSDSerialize::toXML(data, buffer_stream); @@ -134,7 +134,7 @@ bool Request::post3(std::string const& url, AIHTTPHeaders const& headers, LLSD c S32 bytes = buffer_w->getInput()->countAfter(buffer_w->sChannels.out(), NULL); buffered_easy_request_w->setPost(bytes); buffered_easy_request_w->addHeader("Content-Type: application/llsd+xml"); - buffered_easy_request_w->finalizeRequest(url); + buffered_easy_request_w->finalizeRequest(url, responder->getHTTPTimeoutPolicy(), buffered_easy_request); lldebugs << "POSTING: " << bytes << " bytes." << llendl; } diff --git a/indra/llmessage/llcurlrequest.h b/indra/llmessage/llcurlrequest.h index 54bdb35ed..d30418abd 100644 --- a/indra/llmessage/llcurlrequest.h +++ b/indra/llmessage/llcurlrequest.h @@ -48,8 +48,8 @@ class Request { public: bool get2(std::string const& url, ResponderPtr responder); bool getByteRange2(std::string const& url, AIHTTPHeaders const& headers, S32 offset, S32 length, ResponderPtr responder); - bool post2(std::string const& url, AIHTTPHeaders const& headers, std::string const& data, ResponderPtr responder, S32 time_out = 0); - bool post3(std::string const& url, AIHTTPHeaders const& headers, LLSD const& data, ResponderPtr responder, S32 time_out = 0); + bool post2(std::string const& url, AIHTTPHeaders const& headers, std::string const& data, ResponderPtr responder); + bool post3(std::string const& url, AIHTTPHeaders const& headers, LLSD const& data, ResponderPtr responder); }; } // namespace AICurlInterface diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 077d3302d..5ec98b5e3 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -34,7 +34,10 @@ #include "llvfile.h" #include "llurlrequest.h" -F32 const HTTP_REQUEST_EXPIRY_SECS = 60.0f; +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy blockingGet_timeout; +extern AIHTTPTimeoutPolicy blockingPost_timeout; + //////////////////////////////////////////////////////////////////////////// class LLSDInjector : public Injector @@ -146,8 +149,7 @@ static void request( LLURLRequest::ERequestAction method, Injector* body_injector, LLCurl::ResponderPtr responder, - AIHTTPHeaders& headers, - F32 timeout = HTTP_REQUEST_EXPIRY_SECS) + AIHTTPHeaders& headers) { if (responder) { @@ -167,40 +169,39 @@ static void request( return ; } - req->setRequestTimeOut(timeout); req->run(); } -void LLHTTPClient::getByteRange4(std::string const& url, S32 offset, S32 bytes, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::getByteRange4(std::string const& url, S32 offset, S32 bytes, ResponderPtr responder, AIHTTPHeaders& headers) { if(offset > 0 || bytes > 0) { headers.addHeader("Range", llformat("bytes=%d-%d", offset, offset + bytes - 1)); } - request(url, LLURLRequest::HTTP_GET, NULL, responder, headers, timeout); + request(url, LLURLRequest::HTTP_GET, NULL, responder, headers); } -void LLHTTPClient::head4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::head4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers) { - request(url, LLURLRequest::HTTP_HEAD, NULL, responder, headers, timeout); + request(url, LLURLRequest::HTTP_HEAD, NULL, responder, headers); } -void LLHTTPClient::get4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::get4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers) { - request(url, LLURLRequest::HTTP_GET, NULL, responder, headers, timeout); + request(url, LLURLRequest::HTTP_GET, NULL, responder, headers); } -void LLHTTPClient::getHeaderOnly4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::getHeaderOnly4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers) { - request(url, LLURLRequest::HTTP_HEAD, NULL, responder, headers, timeout); + request(url, LLURLRequest::HTTP_HEAD, NULL, responder, headers); } -void LLHTTPClient::get4(std::string const& url, LLSD const& query, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::get4(std::string const& url, LLSD const& query, ResponderPtr responder, AIHTTPHeaders& headers) { LLURI uri; uri = LLURI::buildHTTP(url, LLSD::emptyArray(), query); - get4(uri.asString(), responder, headers, timeout); + get4(uri.asString(), responder, headers); } // A simple class for managing data returned from a curl http request. @@ -261,7 +262,7 @@ static LLSD blocking_request( LLURLRequest::ERequestAction method, LLSD const& body, AIHTTPHeaders& headers, - F32 timeout = 5) + AIHTTPTimeoutPolicy const& timeout) { lldebugs << "blockingRequest of " << url << llendl; @@ -347,49 +348,49 @@ static LLSD blocking_request( LLSD LLHTTPClient::blockingGet(const std::string& url) { AIHTTPHeaders empty_headers; - return blocking_request(url, LLURLRequest::HTTP_GET, LLSD(), empty_headers); + return blocking_request(url, LLURLRequest::HTTP_GET, LLSD(), empty_headers, blockingGet_timeout); } LLSD LLHTTPClient::blockingPost(const std::string& url, const LLSD& body) { AIHTTPHeaders empty_headers; - return blocking_request(url, LLURLRequest::HTTP_POST, body, empty_headers); + return blocking_request(url, LLURLRequest::HTTP_POST, body, empty_headers, blockingPost_timeout); } -void LLHTTPClient::put4(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::put4(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers) { - request(url, LLURLRequest::HTTP_PUT, new LLSDInjector(body), responder, headers, timeout); + request(url, LLURLRequest::HTTP_PUT, new LLSDInjector(body), responder, headers); } -void LLHTTPClient::post4(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::post4(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers) { - request(url, LLURLRequest::HTTP_POST, new LLSDInjector(body), responder, headers, timeout); + request(url, LLURLRequest::HTTP_POST, new LLSDInjector(body), responder, headers); } -void LLHTTPClient::postRaw4(std::string const& url, char const* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::postRaw4(std::string const& url, char const* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers) { - request(url, LLURLRequest::HTTP_POST, new RawInjector(data, size), responder, headers, timeout); + request(url, LLURLRequest::HTTP_POST, new RawInjector(data, size), responder, headers); } -void LLHTTPClient::postFile4(std::string const& url, std::string const& filename, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::postFile4(std::string const& url, std::string const& filename, ResponderPtr responder, AIHTTPHeaders& headers) { - request(url, LLURLRequest::HTTP_POST, new FileInjector(filename), responder, headers, timeout); + request(url, LLURLRequest::HTTP_POST, new FileInjector(filename), responder, headers); } -void LLHTTPClient::postFile4(std::string const& url, LLUUID const& uuid, LLAssetType::EType asset_type, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::postFile4(std::string const& url, LLUUID const& uuid, LLAssetType::EType asset_type, ResponderPtr responder, AIHTTPHeaders& headers) { - request(url, LLURLRequest::HTTP_POST, new VFileInjector(uuid, asset_type), responder, headers, timeout); + request(url, LLURLRequest::HTTP_POST, new VFileInjector(uuid, asset_type), responder, headers); } // static -void LLHTTPClient::del4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::del4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers) { - request(url, LLURLRequest::HTTP_DELETE, NULL, responder, headers, timeout); + request(url, LLURLRequest::HTTP_DELETE, NULL, responder, headers); } // static -void LLHTTPClient::move4(std::string const& url, std::string const& destination, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout) +void LLHTTPClient::move4(std::string const& url, std::string const& destination, ResponderPtr responder, AIHTTPHeaders& headers) { headers.addHeader("Destination", destination); - request(url, LLURLRequest::HTTP_MOVE, NULL, responder, headers, timeout); + request(url, LLURLRequest::HTTP_MOVE, NULL, responder, headers); } diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index 0f8cca0be..0dcd0767f 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -37,11 +37,12 @@ #include "llcurl.h" #include "aihttpheaders.h" -extern F32 const HTTP_REQUEST_EXPIRY_SECS; - class LLUUID; class LLPumpIO; class LLSD; +class AIHTTPTimeoutPolicy; + +extern AIHTTPTimeoutPolicy responderIgnore_timeout; class LLHTTPClient { @@ -51,54 +52,54 @@ public: typedef LLCurl::ResponderPtr ResponderPtr; // The default actually already ignores responses. - class ResponderIgnore : public Responder { }; + class ResponderIgnore : public Responder { virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return responderIgnore_timeout;} }; /** @name non-blocking API */ //@{ - static void head4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void head4(std::string const& url, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; head4(url, responder, headers, timeout); } + static void head4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers); + static void head4(std::string const& url, ResponderPtr responder) + { AIHTTPHeaders headers; head4(url, responder, headers); } - static void getByteRange4(std::string const& url, S32 offset, S32 bytes, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void getByteRange4(std::string const& url, S32 offset, S32 bytes, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; getByteRange4(url, offset, bytes, responder, headers, timeout); } + static void getByteRange4(std::string const& url, S32 offset, S32 bytes, ResponderPtr responder, AIHTTPHeaders& headers); + static void getByteRange4(std::string const& url, S32 offset, S32 bytes, ResponderPtr responder) + { AIHTTPHeaders headers; getByteRange4(url, offset, bytes, responder, headers); } - static void get4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void get4(std::string const& url, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; get4(url, responder, headers, timeout); } + static void get4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers); + static void get4(std::string const& url, ResponderPtr responder) + { AIHTTPHeaders headers; get4(url, responder, headers); } - static void get4(std::string const& url, LLSD const& query, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void get4(std::string const& url, LLSD const& query, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; get4(url, query, responder, headers, timeout); } + static void get4(std::string const& url, LLSD const& query, ResponderPtr responder, AIHTTPHeaders& headers); + static void get4(std::string const& url, LLSD const& query, ResponderPtr responder) + { AIHTTPHeaders headers; get4(url, query, responder, headers); } - static void put4(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void put4(std::string const& url, LLSD const& body, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; put4(url, body, responder, headers, timeout); } + static void put4(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers); + static void put4(std::string const& url, LLSD const& body, ResponderPtr responder) + { AIHTTPHeaders headers; put4(url, body, responder, headers); } - static void getHeaderOnly4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void getHeaderOnly4(std::string const& url, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; getHeaderOnly4(url, responder, headers, timeout); } + static void getHeaderOnly4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers); + static void getHeaderOnly4(std::string const& url, ResponderPtr responder) + { AIHTTPHeaders headers; getHeaderOnly4(url, responder, headers); } - static void post4(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void post4(std::string const& url, LLSD const& body, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; post4(url, body, responder, headers, timeout); } + static void post4(std::string const& url, LLSD const& body, ResponderPtr responder, AIHTTPHeaders& headers); + static void post4(std::string const& url, LLSD const& body, ResponderPtr responder) + { AIHTTPHeaders headers; post4(url, body, responder, headers); } /** Takes ownership of data and deletes it when sent */ - static void postRaw4(std::string const& url, const char* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void postRaw4(std::string const& url, const char* data, S32 size, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; postRaw4(url, data, size, responder, headers, timeout); } + static void postRaw4(std::string const& url, const char* data, S32 size, ResponderPtr responder, AIHTTPHeaders& headers); + static void postRaw4(std::string const& url, const char* data, S32 size, ResponderPtr responder) + { AIHTTPHeaders headers; postRaw4(url, data, size, responder, headers); } - static void postFile4(std::string const& url, std::string const& filename, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void postFile4(std::string const& url, std::string const& filename, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; postFile4(url, filename, responder, headers, timeout); } + static void postFile4(std::string const& url, std::string const& filename, ResponderPtr responder, AIHTTPHeaders& headers); + static void postFile4(std::string const& url, std::string const& filename, ResponderPtr responder) + { AIHTTPHeaders headers; postFile4(url, filename, responder, headers); } - static void postFile4(std::string const& url, const LLUUID& uuid, LLAssetType::EType asset_type, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void postFile4(std::string const& url, const LLUUID& uuid, LLAssetType::EType asset_type, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; postFile4(url, uuid, asset_type, responder, headers, timeout); } + static void postFile4(std::string const& url, const LLUUID& uuid, LLAssetType::EType asset_type, ResponderPtr responder, AIHTTPHeaders& headers); + static void postFile4(std::string const& url, const LLUUID& uuid, LLAssetType::EType asset_type, ResponderPtr responder) + { AIHTTPHeaders headers; postFile4(url, uuid, asset_type, responder, headers); } - static void del4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void del4(std::string const& url, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; del4(url, responder, headers, timeout); } + static void del4(std::string const& url, ResponderPtr responder, AIHTTPHeaders& headers); + static void del4(std::string const& url, ResponderPtr responder) + { AIHTTPHeaders headers; del4(url, responder, headers); } ///< sends a DELETE method, but we can't call it delete in c++ @@ -111,9 +112,9 @@ public: * @param headers A map of key:value headers to pass to the request * @param timeout The number of seconds to give the server to respond. */ - static void move4(std::string const& url, std::string const& destination, ResponderPtr responder, AIHTTPHeaders& headers, F32 timeout = HTTP_REQUEST_EXPIRY_SECS); - static void move4(std::string const& url, std::string const& destination, ResponderPtr responder, F32 timeout = HTTP_REQUEST_EXPIRY_SECS) - { AIHTTPHeaders headers; move4(url, destination, responder, headers, timeout); } + static void move4(std::string const& url, std::string const& destination, ResponderPtr responder, AIHTTPHeaders& headers); + static void move4(std::string const& url, std::string const& destination, ResponderPtr responder) + { AIHTTPHeaders headers; move4(url, destination, responder, headers); } //@} diff --git a/indra/llmessage/llregionpresenceverifier.h b/indra/llmessage/llregionpresenceverifier.h index 9b45e78b5..20dccb14c 100644 --- a/indra/llmessage/llregionpresenceverifier.h +++ b/indra/llmessage/llregionpresenceverifier.h @@ -34,6 +34,9 @@ #include class LLHTTPClientInterface; +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy regionResponder_timeout; +extern AIHTTPTimeoutPolicy verifiedDestinationResponder_timeout; class LLRegionPresenceVerifier { @@ -58,6 +61,8 @@ public: class RegionResponder : public LLHTTPClient::Responder { public: + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return regionResponder_timeout; } + RegionResponder(const std::string& uri, ResponsePtr data, S32 retry_count); virtual ~RegionResponder(); @@ -73,6 +78,8 @@ public: class VerifiedDestinationResponder : public LLHTTPClient::Responder { public: + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return verifiedDestinationResponder_timeout; } + VerifiedDestinationResponder(const std::string& uri, ResponsePtr data, const LLSD& content, S32 retry_count); virtual ~VerifiedDestinationResponder(); diff --git a/indra/llmessage/llsdmessage.cpp b/indra/llmessage/llsdmessage.cpp index a46c2835f..441355b3b 100644 --- a/indra/llmessage/llsdmessage.cpp +++ b/indra/llmessage/llsdmessage.cpp @@ -77,17 +77,18 @@ bool LLSDMessage::httpListener(const LLSD& request) out << "request event without 'url' key to '" << mEventPump.getName() << "'"; throw ArgError(out.str()); } +#if 0 // AIFIXME: ignore this for now // Establish default timeout. This test relies on LLSD::asReal() returning // exactly 0.0 for an undef value. if (! timeout) { timeout = HTTP_REQUEST_EXPIRY_SECS; } +#endif LLHTTPClient::post4(url, payload, new LLSDMessage::EventResponder(LLEventPumps::instance(), request, - url, "POST", reply, error), - (F32)timeout); + url, "POST", reply, error)); return false; } diff --git a/indra/llmessage/llsdmessage.h b/indra/llmessage/llsdmessage.h index 0d34847ff..3d1ae814c 100644 --- a/indra/llmessage/llsdmessage.h +++ b/indra/llmessage/llsdmessage.h @@ -37,6 +37,8 @@ #include class LLSD; +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy eventResponder_timeout; /** * Class managing the messaging API described in @@ -124,6 +126,8 @@ private: class EventResponder: public LLHTTPClient::Responder { public: + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return eventResponder_timeout; } + /** * LLHTTPClient::Responder that dispatches via named LLEventPump instances. * We bind LLEventPumps, even though it's an LLSingleton, for testability. diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index af22a1722..1ea9c02ad 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -293,14 +293,14 @@ LLIOPipe::EStatus LLURLRequest::handleError( LLIOPipe::EStatus status, LLPumpIO* pump) { - DoutEntering(dc::curl, "LLURLRequest::handleError(" << LLIOPipe::lookupStatusString(status) << ", " << (void*)pump << ")"); + DoutEntering(dc::curl, "LLURLRequest::handleError(" << LLIOPipe::lookupStatusString(status) << ", " << (void*)pump << ") [" << (void*)mCurlEasyRequest.get_ptr().get() << "]"); if (LL_LIKELY(!mDetail->mStateMachine->isBuffered())) // Currently always true. { // The last reference will be deleted when the pump that this chain belongs to // is removed from the running chains vector, upon returning from this function. // This keeps the CurlEasyRequest object alive until the curl thread cleanly removed it. - Dout(dc::curl, "Calling mDetail->mStateMachine->removeRequest()"); + Dout(dc::curl, "Calling mDetail->mStateMachine->removeRequest() [" << (void*)mCurlEasyRequest.get_ptr().get() << "]"); mDetail->mStateMachine->removeRequest(); } else if (!hasNotExpired()) @@ -546,7 +546,7 @@ bool LLURLRequest::configure(AICurlEasyRequest_wat const& curlEasyRequest_w) } if(rv) { - curlEasyRequest_w->finalizeRequest(mURL); + curlEasyRequest_w->finalizeRequest(mURL, mResponder->getHTTPTimeoutPolicy(), this); } } return rv; diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 1fe40f6bc..d082c248c 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -83,6 +83,9 @@ #include "llmemtype.h" #include "llpacketring.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy fnPtrResponder_timeout; + // Constants //const char* MESSAGE_LOG_FILENAME = "message.log"; static const F32 CIRCUIT_DUMP_TIMEOUT = 30.f; @@ -133,6 +136,8 @@ namespace if(NULL != mCallback) mCallback(mCallbackData, LL_ERR_NOERR); } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return fnPtrResponder_timeout; } + private: void (*mCallback)(void **,S32); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 7d9482424..2b7064536 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4227,16 +4227,82 @@ Value 256 - CurlRequestTimeOut + CurlTimeoutDNSLookup Comment - Max idle time of a curl request before killed (requires restart) + Extra time in seconds added to CurlTimeoutConnect for the initial connect to a host (requires restart) Persist 1 Type - F32 + U32 Value - 120.0 + 60 + + CurlTimeoutConnect + + Comment + Maximum time allowed until a connection is established (after adding the easy handle) until the server is considered unreachable and the connection is terminated (requires restart) + Persist + 1 + Type + U32 + Value + 10 + + CurlTimeoutReplyDelay + + Comment + Maximum time the viewer will wait between sending data to the server and receiving (a partial) reply (requires restart) + Persist + 1 + Type + U32 + Value + 60 + + CurlTimeoutLowSpeedLimit + + Comment + If a transfer speed drops below this value (in bytes/s) during CurlTimeoutLowSpeedTime seconds, the transfer is considered too slow and is terminated (requires restart) + Persist + 1 + Type + U32 + Value + 56000 + + CurlTimeoutLowSpeedTime + + Comment + If a transfer speed drops below CurlTimeoutLowSpeedLimit (in bytes/s) during this amount of seconds, the transfer is considered too slow and is terminated (requires restart) + Persist + 1 + Type + U32 + Value + 30 + + CurlTimeoutMaxTransaction + + Comment + Maximum total time of a curl transaction, from when the easy handle is added till the transaction has completed. This INCLUDES DNS lookups (CurlTimeoutConnect), connect time (CurlTimeoutConnect) and waiting for the first server reply (CurlTimeoutReply) as well as the actually time needed for data transfer (requires restart) + Persist + 1 + Type + U32 + Value + 300 + + CurlTimeoutMaxTotalDelay + + Comment + Maximum total time of a curl request, from when it is requested till the transaction has completed. This includes queuing due to connection throttling on top of the events covered by CurlTimeoutMaxTransaction (requires restart) + Persist + 1 + Type + U32 + Value + 600 Cursor3D diff --git a/indra/newview/floatervoicelicense.cpp b/indra/newview/floatervoicelicense.cpp index fd9a7a649..8badf0e30 100644 --- a/indra/newview/floatervoicelicense.cpp +++ b/indra/newview/floatervoicelicense.cpp @@ -52,6 +52,8 @@ #include "llvfile.h" #include "message.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy iamHereVoice_timeout; FloaterVoiceLicense::FloaterVoiceLicense(const LLSD& key) : LLModalDialog( std::string(" "), 100, 100 ), @@ -73,7 +75,6 @@ class LLIamHereVoice : public LLHTTPClient::Responder FloaterVoiceLicense* mParent; public: - static boost::intrusive_ptr< LLIamHereVoice > build( FloaterVoiceLicense* parent ) { return boost::intrusive_ptr< LLIamHereVoice >( new LLIamHereVoice( parent ) ); @@ -101,6 +102,8 @@ class LLIamHereVoice : public LLHTTPClient::Responder mParent->setSiteIsAlive( alive ); } }; + + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return iamHereVoice_timeout; } }; // this is global and not a class member to keep crud out of the header file diff --git a/indra/newview/hippogridmanager.cpp b/indra/newview/hippogridmanager.cpp index 73dd8d124..fac276e7a 100644 --- a/indra/newview/hippogridmanager.cpp +++ b/indra/newview/hippogridmanager.cpp @@ -19,7 +19,6 @@ #include "hipporestrequest.h" - // ******************************************************************** // Global Variables @@ -860,7 +859,6 @@ void HippoGridManager::loadFromFile() setCurrentGrid(last_grid); } - void HippoGridManager::parseUrl(const std::string url, bool mergeIfNewer) { llinfos << "Loading grid info from '" << url << "'." << llendl; diff --git a/indra/newview/hipporestrequest.cpp b/indra/newview/hipporestrequest.cpp index e36c081d7..107310759 100644 --- a/indra/newview/hipporestrequest.cpp +++ b/indra/newview/hipporestrequest.cpp @@ -319,7 +319,7 @@ int HippoRestRequest::getBlocking(const std::string &url, std::string *result) llassert_always(curlp); curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts - curl_easy_setopt(curlp, CURLOPT_TIMEOUT, 5); // seconds + curl_easy_setopt(curlp, CURLOPT_TIMEOUT, 30); // seconds (including DNS lookups) curl_easy_setopt(curlp, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str()); curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, curlWrite); diff --git a/indra/newview/lggdicdownload.cpp b/indra/newview/lggdicdownload.cpp index cc3e49c39..7bcaaac49 100644 --- a/indra/newview/lggdicdownload.cpp +++ b/indra/newview/lggdicdownload.cpp @@ -49,6 +49,8 @@ #include "llbufferstream.h" class lggDicDownloadFloater; +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy emeraldDicDownloader_timeout; class EmeraldDicDownloader : public LLHTTPClient::Responder { @@ -60,6 +62,7 @@ public: const std::string& reason, const LLChannelDescriptors& channels, const buffer_ptr_t& buffer); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return emeraldDicDownloader_timeout; } private: lggDicDownloadFloater* panel; std::string name; diff --git a/indra/newview/llaccountingcostmanager.cpp b/indra/newview/llaccountingcostmanager.cpp index 4971cdb9e..d35a80f8b 100644 --- a/indra/newview/llaccountingcostmanager.cpp +++ b/indra/newview/llaccountingcostmanager.cpp @@ -30,6 +30,9 @@ #include "llcurl.h" #include "llhttpclient.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy accountingCostResponder_timeout; + //=============================================================================== LLAccountingCostManager::LLAccountingCostManager() { @@ -85,6 +88,8 @@ public: clearPendingRequests(); } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return accountingCostResponder_timeout; } + private: //List of posted objects LLSD mObjectIDs; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c371ac072..99f900d3e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -119,6 +119,7 @@ #include "lldelayeduidelete.h" #include "llbuildnewviewsscheduler.h" #include "aicurleasyrequeststatemachine.h" +#include "aihttptimeoutpolicy.h" // // The files below handle dependencies from cleanup. #include "llcalc.h" @@ -642,7 +643,18 @@ bool LLAppViewer::init() mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling")); AIStateMachine::setMaxCount(gSavedSettings.getU32("StateMachineMaxTime")); - AICurlEasyRequestStateMachine::setDefaultRequestTimeOut(gSavedSettings.getF32("CurlRequestTimeOut")); + + AIHTTPTimeoutPolicy::setDefaultCurlTimeout( + AIHTTPTimeoutPolicy( + "CurlTimeout* Debug Settings", + gSavedSettings.getU32("CurlTimeoutDNSLookup"), + gSavedSettings.getU32("CurlTimeoutConnect"), + gSavedSettings.getU32("CurlTimeoutReplyDelay"), + gSavedSettings.getU32("CurlTimeoutLowSpeedTime"), + gSavedSettings.getU32("CurlTimeoutLowSpeedLimit"), + gSavedSettings.getU32("CurlTimeoutMaxTransaction"), + gSavedSettings.getU32("CurlTimeoutMaxTotalDelay") + )); initThreads(); LL_INFOS("InitInfo") << "Threads initialized." << LL_ENDL ; diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index 47f8eba37..c50557354 100644 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -192,8 +192,7 @@ void on_new_single_inventory_upload_complete( LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data, const LLUUID& vfile_id, LLAssetType::EType asset_type) -: LLHTTPClient::Responder(), - mPostData(post_data), +: mPostData(post_data), mVFileID(vfile_id), mAssetType(asset_type) { @@ -210,8 +209,7 @@ LLAssetUploadResponder::LLAssetUploadResponder( const LLSD &post_data, const std::string& file_name, LLAssetType::EType asset_type) -: LLHTTPClient::Responder(), - mPostData(post_data), +: mPostData(post_data), mFileName(file_name), mAssetType(asset_type) { diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h index 0236b08b5..6de99808d 100644 --- a/indra/newview/llassetuploadresponders.h +++ b/indra/newview/llassetuploadresponders.h @@ -36,6 +36,10 @@ #include "llhttpclient.h" #include "llinventory.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy assetUploadResponder_timeout; +extern AIHTTPTimeoutPolicy newAgentInventoryVariablePriceResponder_timeout; + void on_new_single_inventory_upload_complete(LLAssetType::EType asset_type, LLInventoryType::EType inventory_type, const std::string inventory_type_string, @@ -59,6 +63,7 @@ public: ~LLAssetUploadResponder(); virtual void error(U32 statusNum, const std::string& reason); virtual void result(const LLSD& content); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return assetUploadResponder_timeout; } virtual void uploadUpload(const LLSD& content); virtual void uploadComplete(const LLSD& content); virtual void uploadFailure(const LLSD& content); @@ -110,6 +115,7 @@ public: const std::string& reason, const LLSD& content); void result(const LLSD& content); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return newAgentInventoryVariablePriceResponder_timeout; } virtual void onApplicationLevelError( const LLSD& error); diff --git a/indra/newview/llcapabilitylistener.cpp b/indra/newview/llcapabilitylistener.cpp index 9ff6d9f8f..75934c156 100644 --- a/indra/newview/llcapabilitylistener.cpp +++ b/indra/newview/llcapabilitylistener.cpp @@ -95,12 +95,14 @@ bool LLCapabilityListener::capListener(const LLSD& request) << LL_ENDL; return false; // in case fatal-error function isn't } +#if 0 // AIFIXME: ignore this for now // Establish default timeout. This test relies on LLSD::asReal() returning // exactly 0.0 for an undef value. if (! timeout) { timeout = HTTP_REQUEST_EXPIRY_SECS; } +#endif // Look up the url for the requested capability name. std::string url = mProvider.getCapability(cap); if (! url.empty()) @@ -110,8 +112,7 @@ bool LLCapabilityListener::capListener(const LLSD& request) new LLSDMessage::EventResponder(LLEventPumps::instance(), request, mProvider.getDescription(), - cap, reply, error), - timeout); + cap, reply, error)); } else { diff --git a/indra/newview/llclassifiedstatsresponder.h b/indra/newview/llclassifiedstatsresponder.h index c4591df8d..f09245a66 100644 --- a/indra/newview/llclassifiedstatsresponder.h +++ b/indra/newview/llclassifiedstatsresponder.h @@ -37,6 +37,9 @@ #include "llview.h" #include "lluuid.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy classifiedStatsResponder_timeout; + class LLClassifiedStatsResponder : public LLHTTPClient::Responder { public: @@ -45,6 +48,7 @@ public: virtual void result(const LLSD& content); //If we get back an error (not found, etc...), handle it here virtual void error(U32 status, const std::string& reason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return classifiedStatsResponder_timeout; } protected: LLHandle mClassifiedPanelHandle; diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp index 3345eb301..e7442483f 100644 --- a/indra/newview/lleventpoll.cpp +++ b/indra/newview/lleventpoll.cpp @@ -46,6 +46,9 @@ #include "message.h" #include "lltrans.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy eventPollResponder_timeout; + namespace { // We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error. @@ -72,6 +75,7 @@ namespace void handleMessage(const LLSD& content); virtual void error(U32 status, const std::string& reason); virtual void result(const LLSD& content); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return eventPollResponder_timeout; } virtual void completedRaw(U32 status, const std::string& reason, diff --git a/indra/newview/llfloateractivespeakers.cpp b/indra/newview/llfloateractivespeakers.cpp index c4113c286..3b8bde610 100644 --- a/indra/newview/llfloateractivespeakers.cpp +++ b/indra/newview/llfloateractivespeakers.cpp @@ -57,6 +57,11 @@ #include "llavatarname.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy muteVoiceResponder_timeout; +extern AIHTTPTimeoutPolicy muteTextResponder_timeout; +extern AIHTTPTimeoutPolicy moderationModeResponder_timeout; + using namespace LLOldEvents; const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers @@ -882,6 +887,8 @@ void LLPanelActiveSpeakers::onModeratorMuteVoice(LLUICtrl* ctrl, void* user_data } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return muteVoiceResponder_timeout; } + private: LLUUID mSessionID; }; @@ -947,6 +954,8 @@ void LLPanelActiveSpeakers::onModeratorMuteText(LLUICtrl* ctrl, void* user_data) } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return muteTextResponder_timeout; } + private: LLUUID mSessionID; }; @@ -987,6 +996,7 @@ void LLPanelActiveSpeakers::onChangeModerationMode(LLUICtrl* ctrl, void* user_da { llwarns << status << ": " << reason << llendl; } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return moderationModeResponder_timeout; } }; LLHTTPClient::post4(url, data, new ModerationModeResponder()); diff --git a/indra/newview/llfloaterregiondebugconsole.cpp b/indra/newview/llfloaterregiondebugconsole.cpp index 1314a510a..ad3e873eb 100644 --- a/indra/newview/llfloaterregiondebugconsole.cpp +++ b/indra/newview/llfloaterregiondebugconsole.cpp @@ -37,6 +37,10 @@ #include "llviewerregion.h" #include "lluictrlfactory.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy asyncConsoleResponder_timeout; +extern AIHTTPTimeoutPolicy consoleResponder_timeout; + // Two versions of the sim console API are supported. // // SimConsole capability (deprecated): @@ -72,6 +76,7 @@ namespace // This responder handles the initial response. Unless error() is called // we assume that the simulator has received our request. Error will be // called if this request times out. + // class AsyncConsoleResponder : public LLHTTPClient::Responder { public: @@ -80,6 +85,7 @@ namespace { sConsoleReplySignal(UNABLE_TO_SEND_COMMAND); } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return asyncConsoleResponder_timeout; } }; class ConsoleResponder : public LLHTTPClient::Responder @@ -110,11 +116,14 @@ namespace } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return consoleResponder_timeout; } + LLTextEditor * mOutput; }; // This handles responses for console commands sent via the asynchronous // API. + class ConsoleResponseNode : public LLHTTPNode { public: diff --git a/indra/newview/llfloaterregiondebugconsole.h b/indra/newview/llfloaterregiondebugconsole.h index bc20ea9c4..2636b6426 100644 --- a/indra/newview/llfloaterregiondebugconsole.h +++ b/indra/newview/llfloaterregiondebugconsole.h @@ -34,6 +34,8 @@ #include "llhttpclient.h" class LLTextEditor; +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy floaterRegionDebugConsole_timeout; typedef boost::signals2::signal< void (const std::string& output)> console_reply_signal_t; @@ -44,6 +46,8 @@ public: LLFloaterRegionDebugConsole(); virtual ~LLFloaterRegionDebugConsole(); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return floaterRegionDebugConsole_timeout; } + // virtual BOOL postBuild(); void onClose(bool app_quitting); diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index ecfed8b2f..e7f6b805b 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -91,6 +91,9 @@ const S32 TERRAIN_TEXTURE_COUNT = 4; const S32 CORNER_COUNT = 4; +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy estateChangeInfoResponder_timeout; + ///---------------------------------------------------------------------------- /// Local class declaration ///---------------------------------------------------------------------------- @@ -2325,6 +2328,9 @@ public: llinfos << "LLEstateChangeInfoResponder::error " << status << ": " << reason << llendl; } + + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return estateChangeInfoResponder_timeout; } + private: LLPanelEstateInfo* mpPanel; }; diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 48cee545c..c11c95d18 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -96,6 +96,9 @@ const U32 INCLUDE_SCREENSHOT = 0x01 << 0; +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy userReportResponder_timeout; + //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- @@ -858,7 +861,7 @@ public: class LLUserReportResponder : public LLHTTPClient::Responder { public: - LLUserReportResponder(): LLHTTPClient::Responder() {} + LLUserReportResponder() { } void error(U32 status, const std::string& reason) { @@ -870,6 +873,7 @@ public: // we don't care about what the server returns LLUploadDialog::modalUploadFinished(); } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return userReportResponder_timeout; } }; void LLFloaterReporter::sendReportViaCaps(std::string url, std::string sshot_url, const LLSD& report) diff --git a/indra/newview/llfloaterteleport.cpp b/indra/newview/llfloaterteleport.cpp index ae4ef5a3a..adfa034b1 100644 --- a/indra/newview/llfloaterteleport.cpp +++ b/indra/newview/llfloaterteleport.cpp @@ -55,6 +55,8 @@ #include "llworld.h" #include "pipeline.h" // for gPipeline +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy placeAvatarTeleportResponder_timeout; // OGPX HTTP responder for PlaceAvatar cap used for Teleport // very similar to the responder in Login, but not as many fields are returned in the TP version @@ -218,6 +220,8 @@ public: // gViewerWindow->setShowProgress(FALSE); } + + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return placeAvatarTeleportResponder_timeout; } }; // Statics diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp index 3ae58c14f..a67d6aa43 100644 --- a/indra/newview/llfloatertos.cpp +++ b/indra/newview/llfloatertos.cpp @@ -54,6 +54,8 @@ #include "llvfile.h" #include "message.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy iamHere_timeout; // static LLFloaterTOS* LLFloaterTOS::sInstance = NULL; @@ -128,6 +130,8 @@ class LLIamHere : public LLHTTPClient::Responder mParent->setSiteIsAlive( alive ); } }; + + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return iamHere_timeout; } }; // this is global and not a class member to keep crud out of the header file diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index a8479e895..baf90fc7e 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -45,6 +45,9 @@ #include "llviewerwindow.h" #include "llhttpclient.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy mediaTypeResponder_timeout; + static LLFloaterURLEntry* sInstance = NULL; // Move this to its own file. @@ -85,6 +88,8 @@ public: if ( floater_url_entry ) floater_url_entry->headerFetchComplete( status, resolved_mime_type ); } + + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return mediaTypeResponder_timeout; } }; //----------------------------------------------------------------------------- diff --git a/indra/newview/llhomelocationresponder.h b/indra/newview/llhomelocationresponder.h index 3a1d8ebfe..67ba049fe 100644 --- a/indra/newview/llhomelocationresponder.h +++ b/indra/newview/llhomelocationresponder.h @@ -38,11 +38,15 @@ /* File Inclusions */ #include "llhttpclient.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy homeLocationResponder_timeout; + /* Typedef, Enum, Class, Struct, etc. */ class LLHomeLocationResponder : public LLHTTPClient::Responder { virtual void result( const LLSD& content ); virtual void error( U32 status, const std::string& reason ); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return homeLocationResponder_timeout; } }; #endif diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 909fa6aa6..2d5ee2293 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -84,6 +84,11 @@ #include "rlvhandler.h" // [/RLVa:KB] +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy startConferenceChatResponder_timeout; +extern AIHTTPTimeoutPolicy voiceCallCapResponder_timeout; +extern AIHTTPTimeoutPolicy sessionInviteResponder_timeout; + // // Constants // @@ -214,6 +219,8 @@ public: //the possible different language translations } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return startConferenceChatResponder_timeout; } + private: LLUUID mTempSessionID; LLUUID mCreatorID; @@ -301,6 +308,7 @@ public: virtual void error(U32 status, const std::string& reason); // called with bad status codes virtual void result(const LLSD& content); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return voiceCallCapResponder_timeout; } private: LLUUID mSessionID; @@ -1564,6 +1572,8 @@ public: //throw something back to the viewer here? } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return sessionInviteResponder_timeout; } + private: LLUUID mSessionID; }; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 628448301..e8c221414 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -77,6 +77,9 @@ #include "rlvhandler.h" // [/RLVa:KB] +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy viewerChatterBoxInvitationAcceptResponder_timeout; + // // Globals // @@ -91,7 +94,6 @@ LLIMMgr* gIMMgr = NULL; //{ // return (LLStringUtil::compareDict( a->mName, b->mName ) < 0); //} - class LLViewerChatterBoxInvitationAcceptResponder : public LLHTTPClient::Responder { @@ -175,6 +177,8 @@ public: } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return viewerChatterBoxInvitationAcceptResponder_timeout; } + private: LLUUID mSessionID; LLIMMgr::EInvitationType mInvitiationType; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 1122712bc..ab88171ef 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -60,6 +60,9 @@ #include "process.h" #endif +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy createInventoryCategoryResponder_timeout; + // Increment this if the inventory contents change in a non-backwards-compatible way. // For viewers with link items support, former caches are incorrect. const S32 LLInventoryModel::sCurrentInvCacheVersion = 2; @@ -511,6 +514,8 @@ public: } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return createInventoryCategoryResponder_timeout; } + private: void (*mCallback)(const LLSD&, void*); void* mData; diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index a7506e19a..c567c505d 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -43,8 +43,10 @@ #include #include -class LLInventoryObserver; +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy fetchInventoryResponder_timeout; +class LLInventoryObserver; class LLInventoryObject; class LLInventoryItem; class LLInventoryCategory; @@ -55,7 +57,6 @@ class LLViewerInventoryCategory; class LLMessageSystem; class LLInventoryCollectFunctor; - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // LLInventoryModel // @@ -88,6 +89,7 @@ public: fetchInventoryResponder(const LLSD& request_sd) : mRequestSD(request_sd) {}; void result(const LLSD& content); void error(U32 status, const std::string& reason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return fetchInventoryResponder_timeout; } protected: LLSD mRequestSD; }; diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 619722b70..03ca87137 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -38,6 +38,10 @@ #include "llviewerregion.h" #include "llviewerwindow.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy inventoryModelFetchDescendentsResponder_timeout; +extern AIHTTPTimeoutPolicy inventoryModelFetchItemResponder_timeout; + const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f; const S32 MAX_FETCH_RETRIES = 10; @@ -374,6 +378,7 @@ public: LLInventoryModelFetchItemResponder(const LLSD& request_sd) : LLInventoryModel::fetchInventoryResponder(request_sd) {}; void result(const LLSD& content); void error(U32 status, const std::string& reason); + AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return inventoryModelFetchItemResponder_timeout; } }; void LLInventoryModelFetchItemResponder::result( const LLSD& content ) @@ -388,7 +393,6 @@ void LLInventoryModelFetchItemResponder::error( U32 status, const std::string& r LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); } - class LLInventoryModelFetchDescendentsResponder: public LLHTTPClient::Responder { public: @@ -399,6 +403,8 @@ class LLInventoryModelFetchDescendentsResponder: public LLHTTPClient::Responder //LLInventoryModelFetchDescendentsResponder() {}; void result(const LLSD& content); void error(U32 status, const std::string& reason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return inventoryModelFetchDescendentsResponder_timeout; } + protected: BOOL getIsRecursive(const LLUUID& cat_id) const; private: @@ -699,14 +705,14 @@ void LLInventoryModelBackgroundFetch::bulkFetch() if (folder_request_body["folders"].size()) { LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body, recursive_cats); - LLHTTPClient::post4(url, folder_request_body, fetcher, 300.0); + LLHTTPClient::post4(url, folder_request_body, fetcher); } if (folder_request_body_lib["folders"].size()) { std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents2"); LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body_lib, recursive_cats); - LLHTTPClient::post4(url_lib, folder_request_body_lib, fetcher, 300.0); + LLHTTPClient::post4(url_lib, folder_request_body_lib, fetcher); } } if (item_count) diff --git a/indra/newview/llmapresponders.h b/indra/newview/llmapresponders.h index b6fb8e551..94843d0e9 100644 --- a/indra/newview/llmapresponders.h +++ b/indra/newview/llmapresponders.h @@ -35,9 +35,13 @@ #include "llhttpclient.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy mapLayerResponder_timeout; + class LLMapLayerResponder : public LLHTTPClient::Responder { virtual void result(const LLSD& content); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return mapLayerResponder_timeout; } }; #endif // LL_LLMAPLAYERRESPONDER_H diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index fa60a85e4..dfdd3e4ec 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -74,6 +74,15 @@ #include +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy meshHeaderResponder_timeout; +extern AIHTTPTimeoutPolicy meshLODResponder_timeout; +extern AIHTTPTimeoutPolicy meshSkinInfoResponder_timeout; +extern AIHTTPTimeoutPolicy meshDecompositionResponder_timeout; +extern AIHTTPTimeoutPolicy meshPhysicsShapeResponder_timeout; +extern AIHTTPTimeoutPolicy wholeModelFeeResponder_timeout; +extern AIHTTPTimeoutPolicy wholeModelUploadResponder_timeout; + LLMeshRepository gMeshRepo; const U32 MAX_MESH_REQUESTS_PER_SECOND = 100; @@ -221,6 +230,7 @@ public: const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return meshHeaderResponder_timeout; } }; class LLMeshLODResponder : public LLCurl::Responder @@ -246,6 +256,7 @@ public: const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return meshLODResponder_timeout; } }; class LLMeshSkinInfoResponder : public LLCurl::Responder @@ -264,6 +275,7 @@ public: const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return meshSkinInfoResponder_timeout; } }; class LLMeshDecompositionResponder : public LLCurl::Responder @@ -282,6 +294,7 @@ public: const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return meshDecompositionResponder_timeout; } }; class LLMeshPhysicsShapeResponder : public LLCurl::Responder @@ -300,6 +313,7 @@ public: const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return meshPhysicsShapeResponder_timeout; } }; #if MESH_IMPORT @@ -403,6 +417,7 @@ public: } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return wholeModelFeeResponder_timeout; } }; class LLWholeModelUploadResponder: public LLCurl::Responder @@ -459,6 +474,8 @@ public: } } } + + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return wholeModelUploadResponder_timeout; } }; #endif //MESH_IMPORT diff --git a/indra/newview/llpanelgroupvoting.cpp b/indra/newview/llpanelgroupvoting.cpp index b7a1ddd7e..978bddfda 100644 --- a/indra/newview/llpanelgroupvoting.cpp +++ b/indra/newview/llpanelgroupvoting.cpp @@ -52,6 +52,10 @@ #include "llviewerwindow.h" #include "llviewerregion.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy startGroupVoteResponder_timeout; +extern AIHTTPTimeoutPolicy groupProposalBallotResponder_timeout; + class LLPanelGroupVoting::impl { public: @@ -705,6 +709,10 @@ public: LLPanelGroupVoting::handleFailure(mGroupID); } + + //Return our timeout policy. + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return startGroupVoteResponder_timeout; } + private: LLUUID mGroupID; }; @@ -735,6 +743,10 @@ public: LLPanelGroupVoting::handleFailure(mGroupID); } + + //Return out timeout policy. + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return groupProposalBallotResponder_timeout; } + private: LLUUID mGroupID; }; @@ -781,8 +793,7 @@ void LLPanelGroupVoting::impl::sendStartGroupProposal() LLHTTPClient::post4( url, body, - new LLStartGroupVoteResponder(mGroupID), - 300); + new LLStartGroupVoteResponder(mGroupID)); } else { //DEPRECATED!!!!!!! This is a fallback just in case our backend cap is not there. Delete this block ASAP! @@ -828,8 +839,7 @@ void LLPanelGroupVoting::impl::sendGroupProposalBallot(const std::string& vote) LLHTTPClient::post4( url, body, - new LLGroupProposalBallotResponder(mGroupID), - 300); + new LLGroupProposalBallotResponder(mGroupID)); } else { //DEPRECATED!!!!!!! This is a fallback just in case our backend cap is not there. Delete this block ASAP! diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index c5163c3a5..a10e9f0cb 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -95,6 +95,9 @@ #define USE_VIEWER_AUTH 0 +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy iamHereLogin_timeout; + const S32 BLACK_BORDER_HEIGHT = 160; const S32 MAX_PASSWORD = 16; @@ -204,6 +207,8 @@ class LLIamHereLogin : public LLHTTPClient::Responder if ( mParent ) mParent->setSiteIsAlive( false ); }; + + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return iamHereLogin_timeout; } }; // this is global and not a class member to keep crud out of the header file diff --git a/indra/newview/llpathfindingmanager.cpp b/indra/newview/llpathfindingmanager.cpp index 0653111e3..c56f1b618 100644 --- a/indra/newview/llpathfindingmanager.cpp +++ b/indra/newview/llpathfindingmanager.cpp @@ -56,6 +56,15 @@ #include "llviewerregion.h" #include "llweb.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy navMeshStatusResponder_timeout; +extern AIHTTPTimeoutPolicy navMeshResponder_timeout; +extern AIHTTPTimeoutPolicy agentStateResponder_timeout; +extern AIHTTPTimeoutPolicy navMeshRebakeResponder_timeout; +extern AIHTTPTimeoutPolicy objectLinksetsResponder_timeout; +extern AIHTTPTimeoutPolicy terrainLinksetsResponder_timeout; +extern AIHTTPTimeoutPolicy charactersResponder_timeout; + #define CAP_SERVICE_RETRIEVE_NAVMESH "RetrieveNavMeshSrc" #define CAP_SERVICE_NAVMESH_STATUS "NavMeshGenerationStatus" @@ -109,6 +118,7 @@ public: virtual void result(const LLSD &pContent); virtual void error(U32 pStatus, const std::string& pReason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return navMeshStatusResponder_timeout; } protected: @@ -131,6 +141,7 @@ public: virtual void result(const LLSD &pContent); virtual void error(U32 pStatus, const std::string& pReason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return navMeshResponder_timeout; } protected: @@ -152,6 +163,7 @@ public: virtual void result(const LLSD &pContent); virtual void error(U32 pStatus, const std::string& pReason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return agentStateResponder_timeout; } protected: @@ -171,6 +183,7 @@ public: virtual void result(const LLSD &pContent); virtual void error(U32 pStatus, const std::string& pReason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return navMeshRebakeResponder_timeout; } protected: @@ -222,7 +235,6 @@ typedef boost::shared_ptr LinksetsResponderPtr; //--------------------------------------------------------------------------- // ObjectLinksetsResponder //--------------------------------------------------------------------------- - class ObjectLinksetsResponder : public LLHTTPClient::Responder { public: @@ -231,6 +243,7 @@ public: virtual void result(const LLSD &pContent); virtual void error(U32 pStatus, const std::string &pReason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return objectLinksetsResponder_timeout; } protected: @@ -242,7 +255,6 @@ private: //--------------------------------------------------------------------------- // TerrainLinksetsResponder //--------------------------------------------------------------------------- - class TerrainLinksetsResponder : public LLHTTPClient::Responder { public: @@ -251,6 +263,7 @@ public: virtual void result(const LLSD &pContent); virtual void error(U32 pStatus, const std::string &pReason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return terrainLinksetsResponder_timeout; } protected: @@ -262,7 +275,6 @@ private: //--------------------------------------------------------------------------- // CharactersResponder //--------------------------------------------------------------------------- - class CharactersResponder : public LLHTTPClient::Responder { public: @@ -271,6 +283,7 @@ public: virtual void result(const LLSD &pContent); virtual void error(U32 pStatus, const std::string &pReason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return charactersResponder_timeout; } protected: @@ -778,8 +791,7 @@ void LLAgentStateChangeNode::post(ResponsePtr pResponse, const LLSD &pContext, c // NavMeshStatusResponder //--------------------------------------------------------------------------- -NavMeshStatusResponder::NavMeshStatusResponder(const std::string &pCapabilityURL, LLViewerRegion *pRegion, bool pIsGetStatusOnly) - : LLHTTPClient::Responder(), +NavMeshStatusResponder::NavMeshStatusResponder(const std::string &pCapabilityURL, LLViewerRegion *pRegion, bool pIsGetStatusOnly) : mCapabilityURL(pCapabilityURL), mRegion(pRegion), mRegionUUID(), @@ -812,8 +824,7 @@ void NavMeshStatusResponder::error(U32 pStatus, const std::string& pReason) // NavMeshResponder //--------------------------------------------------------------------------- -NavMeshResponder::NavMeshResponder(const std::string &pCapabilityURL, U32 pNavMeshVersion, LLPathfindingNavMeshPtr pNavMeshPtr) - : LLHTTPClient::Responder(), +NavMeshResponder::NavMeshResponder(const std::string &pCapabilityURL, U32 pNavMeshVersion, LLPathfindingNavMeshPtr pNavMeshPtr) : mCapabilityURL(pCapabilityURL), mNavMeshVersion(pNavMeshVersion), mNavMeshPtr(pNavMeshPtr) @@ -838,9 +849,7 @@ void NavMeshResponder::error(U32 pStatus, const std::string& pReason) // AgentStateResponder //--------------------------------------------------------------------------- -AgentStateResponder::AgentStateResponder(const std::string &pCapabilityURL) -: LLHTTPClient::Responder() -, mCapabilityURL(pCapabilityURL) +AgentStateResponder::AgentStateResponder(const std::string &pCapabilityURL) : mCapabilityURL(pCapabilityURL) { } @@ -866,8 +875,7 @@ void AgentStateResponder::error(U32 pStatus, const std::string &pReason) //--------------------------------------------------------------------------- // navmesh rebake responder //--------------------------------------------------------------------------- -NavMeshRebakeResponder::NavMeshRebakeResponder(const std::string &pCapabilityURL, LLPathfindingManager::rebake_navmesh_callback_t pRebakeNavMeshCallback) - : LLHTTPClient::Responder(), +NavMeshRebakeResponder::NavMeshRebakeResponder(const std::string &pCapabilityURL, LLPathfindingManager::rebake_navmesh_callback_t pRebakeNavMeshCallback) : mCapabilityURL(pCapabilityURL), mRebakeNavMeshCallback(pRebakeNavMeshCallback) { @@ -973,8 +981,7 @@ void LinksetsResponder::sendCallback() // ObjectLinksetsResponder //--------------------------------------------------------------------------- -ObjectLinksetsResponder::ObjectLinksetsResponder(const std::string &pCapabilityURL, LinksetsResponderPtr pLinksetsResponsderPtr) - : LLHTTPClient::Responder(), +ObjectLinksetsResponder::ObjectLinksetsResponder(const std::string &pCapabilityURL, LinksetsResponderPtr pLinksetsResponsderPtr) : mCapabilityURL(pCapabilityURL), mLinksetsResponsderPtr(pLinksetsResponsderPtr) { @@ -998,8 +1005,7 @@ void ObjectLinksetsResponder::error(U32 pStatus, const std::string &pReason) // TerrainLinksetsResponder //--------------------------------------------------------------------------- -TerrainLinksetsResponder::TerrainLinksetsResponder(const std::string &pCapabilityURL, LinksetsResponderPtr pLinksetsResponsderPtr) - : LLHTTPClient::Responder(), +TerrainLinksetsResponder::TerrainLinksetsResponder(const std::string &pCapabilityURL, LinksetsResponderPtr pLinksetsResponsderPtr) : mCapabilityURL(pCapabilityURL), mLinksetsResponsderPtr(pLinksetsResponsderPtr) { @@ -1023,8 +1029,7 @@ void TerrainLinksetsResponder::error(U32 pStatus, const std::string &pReason) // CharactersResponder //--------------------------------------------------------------------------- -CharactersResponder::CharactersResponder(const std::string &pCapabilityURL, LLPathfindingManager::request_id_t pRequestId, LLPathfindingManager::object_request_callback_t pCharactersCallback) - : LLHTTPClient::Responder(), +CharactersResponder::CharactersResponder(const std::string &pCapabilityURL, LLPathfindingManager::request_id_t pRequestId, LLPathfindingManager::object_request_callback_t pCharactersCallback) : mCapabilityURL(pCapabilityURL), mRequestId(pRequestId), mCharactersCallback(pCharactersCallback) diff --git a/indra/newview/llproductinforequest.cpp b/indra/newview/llproductinforequest.cpp index 65f53822b..6a4b94a8d 100644 --- a/indra/newview/llproductinforequest.cpp +++ b/indra/newview/llproductinforequest.cpp @@ -39,6 +39,9 @@ #include "lltrans.h" #include "llviewerregion.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy productInfoRequestResponder_timeout; + class LLProductInfoRequestResponder : public LLHTTPClient::Responder { public: @@ -54,6 +57,8 @@ public: llwarns << "LLProductInfoRequest::error(" << status << ": " << reason << ")" << llendl; } + + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return productInfoRequestResponder_timeout; } }; LLProductInfoRequestManager::LLProductInfoRequestManager() : mSkuDescriptions() diff --git a/indra/newview/llremoteparcelrequest.h b/indra/newview/llremoteparcelrequest.h index c92ee3ff3..885b1d65c 100644 --- a/indra/newview/llremoteparcelrequest.h +++ b/indra/newview/llremoteparcelrequest.h @@ -38,6 +38,9 @@ #include "llhttpclient.h" #include "llpanel.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy remoteParcelRequestResponder_timeout; + class LLRemoteParcelRequestResponder : public LLHTTPClient::Responder { public: @@ -46,6 +49,7 @@ public: virtual void result(const LLSD& content); //If we get back an error (not found, etc...), handle it here virtual void error(U32 status, const std::string& reason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return remoteParcelRequestResponder_timeout; } protected: LLHandle mPlacePanelHandle; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 5c0f7935b..159110fc0 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -54,6 +54,10 @@ #include "llstartup.h" #include "llbuffer.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy HTTPGetResponder_timeout; +extern AIHTTPTimeoutPolicy lcl_responder_timeout; + ////////////////////////////////////////////////////////////////////////////// class LLTextureFetchWorker : public LLWorkerClass { @@ -288,7 +292,6 @@ private: }; ////////////////////////////////////////////////////////////////////////////// - class HTTPGetResponder : public LLCurl::Responder { LOG_CLASS(HTTPGetResponder); @@ -301,6 +304,8 @@ public: { } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return HTTPGetResponder_timeout; } + virtual void completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const buffer_ptr_t& buffer) @@ -3036,8 +3041,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) volatile const S32 & live_sequence, volatile bool & reporting_break, volatile bool & reporting_started) - : LLCurl::Responder(), - mFetcher(fetcher), + : mFetcher(fetcher), mExpectedSequence(expected_sequence), mLiveSequence(live_sequence), mReportingBreak(reporting_break), @@ -3071,6 +3075,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) mReportingStarted = true; } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return lcl_responder_timeout; } private: LLTextureFetch * mFetcher; diff --git a/indra/newview/lltranslate.h b/indra/newview/lltranslate.h index a646efa87..8b344ae0c 100644 --- a/indra/newview/lltranslate.h +++ b/indra/newview/lltranslate.h @@ -37,6 +37,9 @@ #include "llbufferstream.h" #include "json/reader.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy translationReceiver_timeout; + class LLTranslate { public : @@ -63,6 +66,8 @@ public : handleFailure(); } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return translationReceiver_timeout; } + virtual void completedRaw( U32 status, const std::string& reason, diff --git a/indra/newview/lluploadfloaterobservers.h b/indra/newview/lluploadfloaterobservers.h index 0301f2640..5d4c67788 100644 --- a/indra/newview/lluploadfloaterobservers.h +++ b/indra/newview/lluploadfloaterobservers.h @@ -37,6 +37,9 @@ #include "llhttpclient.h" #include "llui.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy uploadModelPremissionsResponder_timeout; + class LLUploadPermissionsObserver { public: @@ -105,6 +108,8 @@ public: void result(const LLSD& content); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return uploadModelPremissionsResponder_timeout; } + private: LLHandle mObserverHandle; }; diff --git a/indra/newview/llviewerdisplayname.cpp b/indra/newview/llviewerdisplayname.cpp index 3d8b5e017..a9b3a27f3 100644 --- a/indra/newview/llviewerdisplayname.cpp +++ b/indra/newview/llviewerdisplayname.cpp @@ -40,6 +40,9 @@ #include "llnotificationsutil.h" #include "llui.h" // getLanguage() +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy setDisplayNameResponder_timeout; + namespace LLViewerDisplayName { // Fired when viewer receives server response to display name change @@ -64,6 +67,8 @@ public: LLViewerDisplayName::sSetDisplayNameSignal(false, "", LLSD()); LLViewerDisplayName::sSetDisplayNameSignal.disconnect_all_slots(); } + + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return setDisplayNameResponder_timeout; } }; void LLViewerDisplayName::set(const std::string& display_name, const set_name_slot_t& slot) diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 750180608..093a3989b 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -59,6 +59,11 @@ #include "llfloateravatarinfo.h" // for getProfileURL() function //#include "viewerversion.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy mimeDiscoveryResponder_timeout; +extern AIHTTPTimeoutPolicy viewerMediaOpenIDResponder_timeout; +extern AIHTTPTimeoutPolicy viewerMediaWebProfileResponder_timeout; + // Merov: Temporary definitions while porting the new viewer media code to Snowglobe const int LEFT_BUTTON = 0; const int RIGHT_BUTTON = 1; @@ -99,6 +104,8 @@ public: } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return mimeDiscoveryResponder_timeout; } + public: viewer_media_t mMediaImpl; bool mInitialized; @@ -139,6 +146,7 @@ public: // We don't care about the content of the response, only the set-cookie header. } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return viewerMediaOpenIDResponder_timeout; } }; class LLViewerMediaWebProfileResponder : public LLHTTPClient::Responder @@ -168,7 +176,7 @@ public: } } - void completedRaw( + void completedRaw( U32 status, const std::string& reason, const LLChannelDescriptors& channels, @@ -178,6 +186,8 @@ public: // We don't care about the content of the response, only the set-cookie header. } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return viewerMediaWebProfileResponder_timeout; } + std::string mHost; }; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 74d0e17f2..7f472d32b 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -92,6 +92,10 @@ extern ImportTracker gImportTracker; void dialog_refresh_all(); +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy objectCostResponder_timeout; +extern AIHTTPTimeoutPolicy physicsFlagsResponder_timeout; + #define CULL_VIS //#define ORPHAN_SPAM //#define IGNORE_DEAD @@ -768,11 +772,12 @@ public: } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return objectCostResponder_timeout; } + private: LLSD mObjectIDs; }; - class LLPhysicsFlagsResponder : public LLCurl::Responder { public: @@ -864,6 +869,8 @@ public: } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return physicsFlagsResponder_timeout; } + private: LLSD mObjectIDs; }; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index adb3e25ad..ab9b89d13 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -82,13 +82,16 @@ #pragma warning(disable:4355) #endif +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy baseCapabilitiesComplete_timeout; +extern AIHTTPTimeoutPolicy simulatorFeaturesReceived_timeout; + const F32 WATER_TEXTURE_SCALE = 8.f; // Number of times to repeat the water texture across a region const S16 MAX_MAP_DIST = 10; // The server only keeps our pending agent info for 60 seconds. // We want to allow for seed cap retry, but its not useful after that 60 seconds. // Give it 3 chances, each at 18 seconds to give ourselves a few seconds to connect anyways if we give up. const S32 MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN = 3; -const F32 CAP_REQUEST_TIMEOUT = 18; // Even though we gave up on login, keep trying for caps after we are logged in: const S32 MAX_CAP_REQUEST_ATTEMPTS = 30; @@ -262,6 +265,8 @@ public: } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return baseCapabilitiesComplete_timeout; } + static boost::intrusive_ptr build( U64 region_handle, S32 id ) { return boost::intrusive_ptr( @@ -1662,8 +1667,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) S32 id = ++mImpl->mHttpResponderID; LLHTTPClient::post4(url, capabilityNames, - BaseCapabilitiesComplete::build(getHandle(), id), - CAP_REQUEST_TIMEOUT); + BaseCapabilitiesComplete::build(getHandle(), id)); } S32 LLViewerRegion::getNumSeedCapRetries() @@ -1698,8 +1702,7 @@ void LLViewerRegion::failedSeedCapability() S32 id = ++mImpl->mHttpResponderID; LLHTTPClient::post4(url, capabilityNames, - BaseCapabilitiesComplete::build(getHandle(), id), - CAP_REQUEST_TIMEOUT); + BaseCapabilitiesComplete::build(getHandle(), id)); } else { @@ -1736,6 +1739,8 @@ public: regionp->setSimulatorFeatures(content); } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return simulatorFeaturesReceived_timeout; } + private: void retry() { @@ -1743,7 +1748,7 @@ private: { mAttempt++; LL_WARNS2("AppInit", "SimulatorFeatures") << "Re-trying '" << mRetryURL << "'. Retry #" << mAttempt << LL_ENDL; - LLHTTPClient::get4(mRetryURL, new SimulatorFeaturesReceived(*this), CAP_REQUEST_TIMEOUT); + LLHTTPClient::get4(mRetryURL, new SimulatorFeaturesReceived(*this)); } } @@ -1769,7 +1774,7 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u else if (name == "SimulatorFeatures") { // kick off a request for simulator features - LLHTTPClient::get4(url, new SimulatorFeaturesReceived(url, getHandle()), CAP_REQUEST_TIMEOUT); + LLHTTPClient::get4(url, new SimulatorFeaturesReceived(url, getHandle())); } else { diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 8202bd3a8..799dc4262 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -63,6 +63,8 @@ #include "llmeshrepository.h" //for LLMeshRepository::sBytesReceived #include "sgmemstat.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy viewerStatsResponder_timeout; class StatAttributes { @@ -710,6 +712,8 @@ public: { llinfos << "ViewerStatsResponder::result" << llendl; } + + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return viewerStatsResponder_timeout; } }; /* diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 4b27f6a2a..67b1b2b10 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -113,6 +113,7 @@ // #include "llfloaterexploreanimations.h" #include "llimagemetadatareader.h" +#include "aihttptimeoutpolicy.h" // #include "llavatarname.h" diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index fe06343b7..de444efff 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -82,6 +82,10 @@ #define USE_SESSION_GROUPS 0 +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy viewerVoiceAccountProvisionResponder_timeout; +extern AIHTTPTimeoutPolicy voiceClientCapResponder_timeout; + static bool sConnectingToAgni = false; F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f; @@ -187,6 +191,8 @@ public: } } + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return viewerVoiceAccountProvisionResponder_timeout; } + private: int mRetries; }; @@ -1008,7 +1014,6 @@ static bool sMuteListListener_listening = false; static LLVoiceClientFriendsObserver *friendslist_listener = NULL; /////////////////////////////////////////////////////////////////////////////////////////////// - class LLVoiceClientCapResponder : public LLHTTPClient::Responder { public: @@ -1016,6 +1021,7 @@ public: virtual void error(U32 status, const std::string& reason); // called with bad status codes virtual void result(const LLSD& content); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return voiceClientCapResponder_timeout; } private: }; diff --git a/indra/newview/llwlhandlers.h b/indra/newview/llwlhandlers.h index 213bc7c7c..95a2f6c62 100644 --- a/indra/newview/llwlhandlers.h +++ b/indra/newview/llwlhandlers.h @@ -36,6 +36,10 @@ #include "llviewerprecompiledheaders.h" #include "llhttpclient.h" +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy environmentRequestResponder_timeout; +extern AIHTTPTimeoutPolicy environmentApplyResponder_timeout; + class LLEnvironmentRequest { LOG_CLASS(LLEnvironmentRequest); @@ -54,6 +58,7 @@ class LLEnvironmentRequestResponder: public LLHTTPClient::Responder public: virtual void result(const LLSD& content); virtual void error(U32 status, const std::string& reason); + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return environmentRequestResponder_timeout; } private: friend class LLEnvironmentRequest; @@ -97,6 +102,8 @@ public: virtual void error(U32 status, const std::string& reason); // non-200 errors only + virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return environmentApplyResponder_timeout; } + private: friend class LLEnvironmentApply; diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index b1c3c4269..f4cd188fe 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -49,6 +49,9 @@ #include #endif +class AIHTTPTimeoutPolicy; +extern AIHTTPTimeoutPolicy XMLRPCTransaction_timeout; + LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const { return LLXMLRPCValue(XMLRPC_VectorGetValueWithID(mV, id)); @@ -232,7 +235,7 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { try { - mCurlEasyRequestStateMachinePtr = new AICurlEasyRequestStateMachine(false); + mCurlEasyRequestStateMachinePtr = new AICurlEasyRequestStateMachine(false); // AIFIXME: This is the only unbuffered AICurlEasyRequestStateMachine left. } catch(AICurlNoEasyHandle const& error) { @@ -246,8 +249,6 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert); curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0); - // Be a little impatient about establishing connections. - curlEasyRequest_w->setopt(CURLOPT_CONNECTTIMEOUT, 40L); /* Setting the DNS cache timeout to -1 disables it completely. This might help with bug #503 */ @@ -271,7 +272,7 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) setStatus(StatusOtherError); } - curlEasyRequest_w->finalizeRequest(mURI); + curlEasyRequest_w->finalizeRequest(mURI, XMLRPCTransaction_timeout, mCurlEasyRequestStateMachinePtr); } if (mStatus == LLXMLRPCTransaction::StatusNotStarted) // It could be LLXMLRPCTransaction::StatusOtherError. { @@ -479,10 +480,13 @@ size_t LLXMLRPCTransaction::Impl::curlDownloadCallback( size_t n = size * nmemb; #ifdef CWDEBUG + void* lockobj = impl.mCurlEasyRequestStateMachinePtr ? impl.mCurlEasyRequestStateMachinePtr->mCurlEasyRequest.get_ptr().get() : NULL; if (n < 80) - Dout(dc::curl, "Entering LLXMLRPCTransaction::Impl::curlDownloadCallback(\"" << buf2str(data, n) << "\", " << size << ", " << nmemb << ", " << user_data << ")"); + Dout(dc::curl, "Entering LLXMLRPCTransaction::Impl::curlDownloadCallback(\"" << + buf2str(data, n) << "\", " << size << ", " << nmemb << ", " << user_data << ") [" << lockobj << ']'); else - Dout(dc::curl, "Entering LLXMLRPCTransaction::Impl::curlDownloadCallback(\"" << buf2str(data, 40) << "\"...\"" << buf2str(data + n - 40, 40) << "\", " << size << ", " << nmemb << ", " << user_data << ")"); + Dout(dc::curl, "Entering LLXMLRPCTransaction::Impl::curlDownloadCallback(\"" << + buf2str(data, 40) << "\"...\"" << buf2str(data + n - 40, 40) << "\", " << size << ", " << nmemb << ", " << user_data << ") [" << lockobj << ']'); #endif impl.mResponseText.append(data, n);