diff options
Diffstat (limited to 'indra/llmessage/llcurl.cpp')
-rw-r--r-- | indra/llmessage/llcurl.cpp | 290 |
1 files changed, 166 insertions, 124 deletions
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 7c8b7e3584..330028c926 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -1,5 +1,5 @@ /** - * @file llcurl.h + * @file llcurl.cpp * @author Zero / Donovan * @date 2006-10-15 * @brief Implementation of wrapper around libcurl. @@ -46,8 +46,9 @@ #endif #include "llbufferstream.h" -#include "llstl.h" +#include "llproxy.h" #include "llsdserialize.h" +#include "llstl.h" #include "llthread.h" #include "lltimer.h" @@ -74,6 +75,7 @@ static const S32 MULTI_PERFORM_CALL_REPEAT = 5; static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds static const S32 MAX_ACTIVE_REQUEST_COUNT = 100; +static // DEBUG // S32 gCurlEasyCount = 0; S32 gCurlMultiCount = 0; @@ -85,6 +87,9 @@ std::vector<LLMutex*> LLCurl::sSSLMutex; std::string LLCurl::sCAPath; std::string LLCurl::sCAFile; +bool LLCurl::sMultiThreaded = false; +static U32 sMainThreadID = 0; + void check_curl_code(CURLcode code) { if (code != CURLE_OK) @@ -204,7 +209,7 @@ namespace boost void intrusive_ptr_release(LLCurl::Responder* p) { - if(p && 0 == --p->mReferenceCount) + if (p && 0 == --p->mReferenceCount) { delete p; } @@ -214,77 +219,10 @@ namespace boost ////////////////////////////////////////////////////////////////////////////// - -class LLCurl::Easy -{ - LOG_CLASS(Easy); - -private: - Easy(); - -public: - static Easy* getEasy(); - ~Easy(); - - CURL* getCurlHandle() const { return mCurlEasyHandle; } - - void setErrorBuffer(); - void setCA(); - - void setopt(CURLoption option, S32 value); - // These assume the setter does not free value! - void setopt(CURLoption option, void* value); - void setopt(CURLoption option, char* value); - // Copies the string so that it is gauranteed to stick around - void setoptString(CURLoption option, const std::string& value); - - void slist_append(const char* str); - void setHeaders(); - - U32 report(CURLcode); - void getTransferInfo(LLCurl::TransferInfo* info); - - void prepRequest(const std::string& url, const std::vector<std::string>& headers, ResponderPtr, bool post = false); - - const char* getErrorBuffer(); - - std::stringstream& getInput() { return mInput; } - std::stringstream& getHeaderOutput() { return mHeaderOutput; } - LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; } - const LLChannelDescriptors& getChannels() { return mChannels; } - - void resetState(); - - static CURL* allocEasyHandle(); - static void releaseEasyHandle(CURL* handle); - -private: - friend class LLCurl; - - CURL* mCurlEasyHandle; - struct curl_slist* mHeaders; - - std::stringstream mRequest; - LLChannelDescriptors mChannels; - LLIOPipe::buffer_ptr_t mOutput; - std::stringstream mInput; - std::stringstream mHeaderOutput; - char mErrorBuffer[CURL_ERROR_SIZE]; - - // Note: char*'s not strings since we pass pointers to curl - std::vector<char*> mStrings; - - ResponderPtr mResponder; - - static std::set<CURL*> sFreeHandles; - static std::set<CURL*> sActiveHandles; - static LLMutex* sHandleMutex; -}; - std::set<CURL*> LLCurl::Easy::sFreeHandles; std::set<CURL*> LLCurl::Easy::sActiveHandles; LLMutex* LLCurl::Easy::sHandleMutex = NULL; - +LLMutex* LLCurl::Easy::sMultiMutex = NULL; //static CURL* LLCurl::Easy::allocEasyHandle() @@ -404,11 +342,11 @@ const char* LLCurl::Easy::getErrorBuffer() void LLCurl::Easy::setCA() { - if(!sCAPath.empty()) + if (!sCAPath.empty()) { setoptString(CURLOPT_CAPATH, sCAPath); } - if(!sCAFile.empty()) + if (!sCAFile.empty()) { setoptString(CURLOPT_CAINFO, sCAFile); } @@ -525,15 +463,18 @@ size_t curlHeaderCallback(void* data, size_t size, size_t nmemb, void* user_data void LLCurl::Easy::prepRequest(const std::string& url, const std::vector<std::string>& headers, - ResponderPtr responder, bool post) + ResponderPtr responder, S32 time_out, bool post) { resetState(); if (post) setoptString(CURLOPT_ENCODING, ""); - //setopt(CURLOPT_VERBOSE, 1); // usefull for debugging + //setopt(CURLOPT_VERBOSE, 1); // useful for debugging setopt(CURLOPT_NOSIGNAL, 1); + // Set the CURL options for either Socks or HTTP proxy + LLProxy::getInstance()->applyProxySettings(this); + mOutput.reset(new LLBufferArray); setopt(CURLOPT_WRITEFUNCTION, (void*)&curlWriteCallback); setopt(CURLOPT_WRITEDATA, (void*)this); @@ -545,7 +486,7 @@ void LLCurl::Easy::prepRequest(const std::string& url, setopt(CURLOPT_HEADERDATA, (void*)this); // Allow up to five redirects - if(responder && responder->followRedir()) + if (responder && responder->followRedir()) { setopt(CURLOPT_FOLLOWLOCATION, 1); setopt(CURLOPT_MAXREDIRS, MAX_REDIRECTS); @@ -558,7 +499,7 @@ void LLCurl::Easy::prepRequest(const std::string& url, //don't verify host name so urls with scrubbed host names will work (improves DNS performance) setopt(CURLOPT_SSL_VERIFYHOST, 0); - setopt(CURLOPT_TIMEOUT, CURL_REQUEST_TIMEOUT); + setopt(CURLOPT_TIMEOUT, llmax(time_out, CURL_REQUEST_TIMEOUT)); setoptString(CURLOPT_URL, url); @@ -579,44 +520,24 @@ void LLCurl::Easy::prepRequest(const std::string& url, //////////////////////////////////////////////////////////////////////////// -class LLCurl::Multi +LLCurl::Multi::Multi() + : LLThread("Curl Multi"), + mQueued(0), + mErrorCount(0), + mPerformState(PERFORM_STATE_READY) { - LOG_CLASS(Multi); -public: - - Multi(); - ~Multi(); - - Easy* allocEasy(); - bool addEasy(Easy* easy); - - void removeEasy(Easy* easy); + mQuitting = false; - S32 process(); - S32 perform(); - - CURLMsg* info_read(S32* msgs_in_queue); - - S32 mQueued; - S32 mErrorCount; - -private: - void easyFree(Easy*); - - CURLM* mCurlMultiHandle; - - typedef std::set<Easy*> easy_active_list_t; - easy_active_list_t mEasyActiveList; - typedef std::map<CURL*, Easy*> easy_active_map_t; - easy_active_map_t mEasyActiveMap; - typedef std::set<Easy*> easy_free_list_t; - easy_free_list_t mEasyFreeList; -}; + mThreaded = LLCurl::sMultiThreaded && LLThread::currentID() == sMainThreadID; + if (mThreaded) + { + mSignal = new LLCondition(NULL); + } + else + { + mSignal = NULL; + } -LLCurl::Multi::Multi() - : mQueued(0), - mErrorCount(0) -{ mCurlMultiHandle = curl_multi_init(); if (!mCurlMultiHandle) { @@ -630,6 +551,16 @@ LLCurl::Multi::Multi() LLCurl::Multi::~Multi() { + llassert(isStopped()); + + if (LLCurl::sMultiThreaded) + { + LLCurl::Easy::sMultiMutex->lock(); + } + + delete mSignal; + mSignal = NULL; + // Clean up active for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); iter != mEasyActiveList.end(); ++iter) @@ -647,6 +578,11 @@ LLCurl::Multi::~Multi() check_curl_multi_code(curl_multi_cleanup(mCurlMultiHandle)); --gCurlMultiCount; + + if (LLCurl::sMultiThreaded) + { + LLCurl::Easy::sMultiMutex->unlock(); + } } CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue) @@ -655,13 +591,43 @@ CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue) return curlmsg; } +void LLCurl::Multi::perform() +{ + if (mThreaded) + { + if (mPerformState == PERFORM_STATE_READY) + { + mSignal->signal(); + } + } + else + { + doPerform(); + } +} + +void LLCurl::Multi::run() +{ + llassert(mThreaded); + + while (!mQuitting) + { + mSignal->wait(); + mPerformState = PERFORM_STATE_PERFORMING; + if (!mQuitting) + { + LLMutexLock lock(LLCurl::Easy::sMultiMutex); + doPerform(); + } + } +} -S32 LLCurl::Multi::perform() +void LLCurl::Multi::doPerform() { S32 q = 0; for (S32 call_count = 0; - call_count < MULTI_PERFORM_CALL_REPEAT; - call_count += 1) + call_count < MULTI_PERFORM_CALL_REPEAT; + call_count += 1) { CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); if (CURLM_CALL_MULTI_PERFORM != code || q == 0) @@ -672,13 +638,18 @@ S32 LLCurl::Multi::perform() } mQueued = q; - return q; + mPerformState = PERFORM_STATE_COMPLETED; } S32 LLCurl::Multi::process() { perform(); - + + if (mPerformState != PERFORM_STATE_COMPLETED) + { + return 0; + } + CURLMsg* msg; int msgs_in_queue; @@ -709,6 +680,8 @@ S32 LLCurl::Multi::process() } } } + + mPerformState = PERFORM_STATE_READY; return processed; } @@ -787,6 +760,21 @@ LLCurlRequest::LLCurlRequest() : LLCurlRequest::~LLCurlRequest() { llassert_always(mThreadID == LLThread::currentID()); + + //stop all Multi handle background threads + for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) + { + LLCurl::Multi* multi = *iter; + multi->mQuitting = true; + if (multi->mThreaded) + { + while (!multi->isStopped()) + { + multi->mSignal->signal(); + apr_sleep(1000); + } + } + } for_each(mMultiSet.begin(), mMultiSet.end(), DeletePointer()); } @@ -794,6 +782,10 @@ void LLCurlRequest::addMulti() { llassert_always(mThreadID == LLThread::currentID()); LLCurl::Multi* multi = new LLCurl::Multi(); + if (multi->mThreaded) + { + multi->start(); + } mMultiSet.insert(multi); mActiveMulti = multi; mActiveRequestCount = 0; @@ -855,14 +847,14 @@ bool LLCurlRequest::getByteRange(const std::string& url, bool LLCurlRequest::post(const std::string& url, const headers_t& headers, const LLSD& data, - LLCurl::ResponderPtr responder) + LLCurl::ResponderPtr responder, S32 time_out) { LLCurl::Easy* easy = allocEasy(); if (!easy) { return false; } - easy->prepRequest(url, headers, responder); + easy->prepRequest(url, headers, responder, time_out); LLSDSerialize::toXML(data, easy->getInput()); S32 bytes = easy->getInput().str().length(); @@ -882,14 +874,14 @@ bool LLCurlRequest::post(const std::string& url, bool LLCurlRequest::post(const std::string& url, const headers_t& headers, const std::string& data, - LLCurl::ResponderPtr responder) + LLCurl::ResponderPtr responder, S32 time_out) { LLCurl::Easy* easy = allocEasy(); if (!easy) { return false; } - easy->prepRequest(url, headers, responder); + easy->prepRequest(url, headers, responder, time_out); easy->getInput().write(data.data(), data.size()); S32 bytes = easy->getInput().str().length(); @@ -923,6 +915,16 @@ S32 LLCurlRequest::process() if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) { mMultiSet.erase(curiter); + multi->mQuitting = true; + if (multi->mThreaded) + { + while (!multi->isStopped()) + { + multi->mSignal->signal(); + apr_sleep(1000); + } + } + delete multi; } } @@ -940,6 +942,10 @@ S32 LLCurlRequest::getQueued() curlmulti_set_t::iterator curiter = iter++; LLCurl::Multi* multi = *curiter; queued += multi->mQueued; + if (multi->mPerformState != LLCurl::Multi::PERFORM_STATE_READY) + { + ++queued; + } } return queued; } @@ -953,16 +959,31 @@ LLCurlEasyRequest::LLCurlEasyRequest() mResultReturned(false) { mMulti = new LLCurl::Multi(); + if (mMulti->mThreaded) + { + mMulti->start(); + } mEasy = mMulti->allocEasy(); if (mEasy) { mEasy->setErrorBuffer(); mEasy->setCA(); + // Set proxy settings if configured to do so. + LLProxy::getInstance()->applyProxySettings(mEasy); } } LLCurlEasyRequest::~LLCurlEasyRequest() { + mMulti->mQuitting = true; + if (mMulti->mThreaded) + { + while (!mMulti->isStopped()) + { + mMulti->mSignal->signal(); + apr_sleep(1000); + } + } delete mMulti; } @@ -1059,14 +1080,20 @@ void LLCurlEasyRequest::requestComplete() } } -S32 LLCurlEasyRequest::perform() +void LLCurlEasyRequest::perform() { - return mMulti->perform(); + mMulti->perform(); } // Usage: Call getRestult until it returns false (no more messages) bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info) { + if (mMulti->mPerformState != LLCurl::Multi::PERFORM_STATE_COMPLETED) + { //we're busy, try again later + return false; + } + mMulti->mPerformState = LLCurl::Multi::PERFORM_STATE_READY; + if (!mEasy) { // Special case - we failed to initialize a curl_easy (can happen if too many open files) @@ -1151,8 +1178,10 @@ unsigned long LLCurl::ssl_thread_id(void) } #endif -void LLCurl::initClass() +void LLCurl::initClass(bool multi_threaded) { + sMainThreadID = LLThread::currentID(); + sMultiThreaded = multi_threaded; // Do not change this "unless you are familiar with and mean to control // internal operations of libcurl" // - http://curl.haxx.se/libcurl/c/curl_global_init.html @@ -1161,6 +1190,7 @@ void LLCurl::initClass() check_curl_code(code); Easy::sHandleMutex = new LLMutex(NULL); + Easy::sMultiMutex = new LLMutex(NULL); #if SAFE_SSL S32 mutex_count = CRYPTO_num_locks(); @@ -1182,6 +1212,8 @@ void LLCurl::cleanupClass() delete Easy::sHandleMutex; Easy::sHandleMutex = NULL; + delete Easy::sMultiMutex; + Easy::sMultiMutex = NULL; for (std::set<CURL*>::iterator iter = Easy::sFreeHandles.begin(); iter != Easy::sFreeHandles.end(); ++iter) { @@ -1195,3 +1227,13 @@ void LLCurl::cleanupClass() } const unsigned int LLCurl::MAX_REDIRECTS = 5; + +// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace. +void LLCurlFF::check_easy_code(CURLcode code) +{ + check_curl_code(code); +} +void LLCurlFF::check_multi_code(CURLMcode code) +{ + check_curl_multi_code(code); +} |