diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 6b2101a4d..99683e0c5 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -765,8 +765,8 @@ DEFINE_FUNCTION_SETOPT4(curl_write_callback, CURLOPT_HEADERFUNCTION, CURLOPT_WRI //DEFINE_FUNCTION_SETOPT1(curl_read_callback, CURLOPT_READFUNCTION) DEFINE_FUNCTION_SETOPT1(curl_ssl_ctx_callback, CURLOPT_SSL_CTX_FUNCTION) 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) +#if 0 // Not used by the viewer. DEFINE_FUNCTION_SETOPT1(curl_seek_callback, CURLOPT_SEEKFUNCTION) DEFINE_FUNCTION_SETOPT1(curl_ioctl_callback, CURLOPT_IOCTLFUNCTION) DEFINE_FUNCTION_SETOPT1(curl_sockopt_callback, CURLOPT_SOCKOPTFUNCTION) @@ -901,6 +901,23 @@ 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); + ThreadSafeBufferedCurlEasyRequest* 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); +} + #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) @@ -927,12 +944,19 @@ static CURLcode noSSLCtxCallback(CURL* curl, void* sslctx, void* parm) return CURLE_ABORTED_BY_CALLBACK; } +static int noProgressCallback(void* userdata, double, double, double, double) +{ + llmaybewarns << "Calling noProgressCallback(); curl session aborted." << llendl; + return -1; // Cause a CURLE_ABORTED_BY_CALLBACK +} + void CurlEasyRequest::revokeCallbacks(void) { if (mHeaderCallback == &noHeaderCallback && mWriteCallback == &noWriteCallback && mReadCallback == &noReadCallback && - mSSLCtxCallback == &noSSLCtxCallback) + mSSLCtxCallback == &noSSLCtxCallback && + mProgressCallback == &noProgressCallback) { // Already revoked. return; @@ -941,6 +965,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; @@ -949,6 +974,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() @@ -1077,6 +1103,8 @@ void CurlEasyRequest::applyDefaultOptions(void) setopt(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // Disable SSL/TLS session caching; some servers (aka id.secondlife.com) refuse connections when session ids are enabled. setopt(CURLOPT_SSL_SESSIONID_CACHE, 0); + // Call the progress callback funtion. + setopt(CURLOPT_NOPROGRESS, 0); // Set the CURL options for either SOCKS or HTTP proxy. applyProxySettings(); // Cause libcurl to print all it's I/O traffic on the debug channel. @@ -1410,6 +1438,7 @@ void BufferedCurlEasyRequest::prepRequest(AICurlEasyRequest_wat& curl_easy_reque curl_easy_request_w->setWriteCallback(&curlWriteCallback, lockobj); curl_easy_request_w->setReadCallback(&curlReadCallback, lockobj); curl_easy_request_w->setHeaderCallback(&curlHeaderCallback, lockobj); + curl_easy_request_w->setProgressCallback(&curlProgressCallback, lockobj); bool allow_cookies = headers.hasHeader("Cookie"); // Allow up to sixteen redirects. diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index f2230d179..be3d90e7d 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -83,8 +83,8 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven //DECLARE_SETOPT(curl_read_callback); Same type as curl_write_callback DECLARE_SETOPT(curl_ssl_ctx_callback); DECLARE_SETOPT(curl_conv_callback); -#if 0 // Not used by the viewer. DECLARE_SETOPT(curl_progress_callback); +#if 0 // Not used by the viewer. DECLARE_SETOPT(curl_seek_callback); DECLARE_SETOPT(curl_ioctl_callback); DECLARE_SETOPT(curl_sockopt_callback); @@ -235,6 +235,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* userdata, double, double, double, double); curl_write_callback mHeaderCallback; void* mHeaderCallbackUserData; @@ -244,12 +245,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); @@ -441,6 +445,7 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { static size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data); static size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data); static size_t curlHeaderCallback(char* data, size_t size, size_t nmemb, void* user_data); + static int curlProgressCallback(void* user_data, double dltotal, double dlnow, double ultotal, double ulnow); // Called from curlHeaderCallback. void setStatusAndReason(U32 status, std::string const& reason); diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 0375f8bc3..986b1d36b 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -2354,6 +2354,24 @@ size_t BufferedCurlEasyRequest::curlHeaderCallback(char* data, size_t size, size return header_len; } +//static +int BufferedCurlEasyRequest::curlProgressCallback(void* user_data, double dltotal, double dlnow, double ultotal, double ulnow) +{ + if (ultotal > 0) // Zero just means it isn't known yet. + { + ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast(user_data); + DoutEntering(dc::curl, "BufferedCurlEasyRequest::curlProgressCallback(" << (void*)lockobj << ", " << dltotal << ", " << dlnow << ", " << ultotal << ", " << ulnow << ")"); + + if (ulnow == ultotal) // Everything uploaded? + { + AICurlEasyRequest_wat self_w(*lockobj); + self_w->httptimeout()->upload_finished(); + } + } + + return 0; +} + #if defined(CWDEBUG) || defined(DEBUG_CURLIO) int debug_callback(CURL* handle, curl_infotype infotype, char* buf, size_t size, void* user_ptr) { diff --git a/indra/llmessage/aihttptimeout.cpp b/indra/llmessage/aihttptimeout.cpp index bfb0f767b..f79e2a450 100644 --- a/indra/llmessage/aihttptimeout.cpp +++ b/indra/llmessage/aihttptimeout.cpp @@ -157,10 +157,11 @@ void HTTPTimeout::upload_starting(void) // | void HTTPTimeout::upload_finished(void) { - // Disable this assert when there isn't enough debug output to do anything with it. -#if defined(CWDEBUG) || defined(DEBUG_CURLIO) - llassert(!mUploadFinished); // If we get here twice, then the 'upload finished' detection failed. -#endif + // This function can be called more than once. Ignore the second call. + if (mUploadFinished) + { + return; + } mUploadFinished = true; // Only accept a call to upload_starting() if being_redirected() is called after this point. mBeingRedirected = false;