llcurl.cpp

Go to the documentation of this file.
00001 
00034 #if LL_WINDOWS
00035 #define SAFE_SSL 1
00036 #elif LL_DARWIN
00037 #define SAFE_SSL 1
00038 #else
00039 #define SAFE_SSL 1
00040 #endif
00041 
00042 #include "linden_common.h"
00043 
00044 #include "llcurl.h"
00045 
00046 #include <algorithm>
00047 #include <iomanip>
00048 #include <curl/curl.h>
00049 #if SAFE_SSL
00050 #include <openssl/crypto.h>
00051 #endif
00052 
00053 #include "llbufferstream.h"
00054 #include "llstl.h"
00055 #include "llsdserialize.h"
00056 #include "llthread.h"
00057 
00059 /*
00060         The trick to getting curl to do keep-alives is to reuse the
00061         same easy handle for the requests.  It appears that curl
00062         keeps a pool of connections alive for each easy handle, but
00063         doesn't share them between easy handles.  Therefore it is
00064         important to keep a pool of easy handles and reuse them,
00065         rather than create and destroy them with each request.  This
00066         code does this.
00067 
00068         Furthermore, it would behoove us to keep track of which
00069         hosts an easy handle was used for and pick an easy handle
00070         that matches the next request.  This code does not current
00071         do this.
00072  */
00073 
00075 
00076 static const S32 EASY_HANDLE_POOL_SIZE          = 5;
00077 static const S32 MULTI_PERFORM_CALL_REPEAT      = 5;
00078 static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds
00079 static const S32 MAX_ACTIVE_REQUEST_COUNT = 100;
00080 
00081 // DEBUG //
00082 S32 gCurlEasyCount = 0;
00083 S32 gCurlMultiCount = 0;
00084 
00086 
00087 //static
00088 std::vector<LLMutex*> LLCurl::sSSLMutex;
00089 std::string LLCurl::sCAPath;
00090 std::string LLCurl::sCAFile;
00091 
00092 //static
00093 void LLCurl::setCAPath(const std::string& path)
00094 {
00095         sCAPath = path;
00096 }
00097 
00098 //static
00099 void LLCurl::setCAFile(const std::string& file)
00100 {
00101         sCAFile = file;
00102 }
00103 
00105 
00106 LLCurl::Responder::Responder()
00107         : mReferenceCount(0)
00108 {
00109 }
00110 
00111 LLCurl::Responder::~Responder()
00112 {
00113 }
00114 
00115 // virtual
00116 void LLCurl::Responder::error(U32 status, const std::string& reason)
00117 {
00118         llinfos << status << ": " << reason << llendl;
00119 }
00120 
00121 // virtual
00122 void LLCurl::Responder::result(const LLSD& content)
00123 {
00124 }
00125 
00126 // virtual
00127 void LLCurl::Responder::completedRaw(U32 status, const std::string& reason,
00128                                                                          const LLChannelDescriptors& channels,
00129                                                                          const LLIOPipe::buffer_ptr_t& buffer)
00130 {
00131         if (isGoodStatus(status))
00132         {
00133                 LLSD content;
00134                 LLBufferStream istr(channels, buffer.get());
00135                 LLSDSerialize::fromXML(content, istr);
00136 /*
00137                 const S32 parseError = -1;
00138                 if(LLSDSerialize::fromXML(content, istr) == parseError)
00139                 {
00140                         mStatus = 498;
00141                         mReason = "Client Parse Error";
00142                 }
00143 */
00144                 completed(status, reason, content);
00145         }
00146         else if (status == 400)
00147         {
00148                 // Get reason from buffer
00149                 char tbuf[4096];
00150                 S32 len = 4096;
00151                 buffer->readAfter(channels.in(), NULL, (U8*)tbuf, len);
00152                 tbuf[len] = 0;
00153                 completed(status, std::string(tbuf), LLSD());
00154         }
00155         else
00156         {
00157                 completed(status, reason, LLSD());
00158         }
00159 }
00160 
00161 // virtual
00162 void LLCurl::Responder::completed(U32 status, const std::string& reason, const LLSD& content)
00163 {
00164         if (isGoodStatus(status))
00165         {
00166                 result(content);
00167         }
00168         else
00169         {
00170                 // *NOTE: This is kind of messed up. This should probably call
00171                 // the full error method which then provides a default impl
00172                 // which calls the thinner method.
00173                 error(status, reason);
00174         }
00175 }
00176 
00177 //virtual
00178 void LLCurl::Responder::completedHeader(U32 status, const std::string& reason, const LLSD& content)
00179 {
00180 
00181 }
00182 
00183 namespace boost
00184 {
00185         void intrusive_ptr_add_ref(LLCurl::Responder* p)
00186         {
00187                 ++p->mReferenceCount;
00188         }
00189         
00190         void intrusive_ptr_release(LLCurl::Responder* p)
00191         {
00192                 if(p && 0 == --p->mReferenceCount)
00193                 {
00194                         delete p;
00195                 }
00196         }
00197 };
00198 
00199 
00201 
00202 
00203 class LLCurl::Easy
00204 {
00205         LOG_CLASS(Easy);
00206 
00207 private:
00208         Easy();
00209         
00210 public:
00211         static Easy* getEasy();
00212         ~Easy();
00213 
00214         CURL* getCurlHandle() const { return mCurlEasyHandle; }
00215 
00216         void setErrorBuffer();
00217         void setCA();
00218         
00219         void setopt(CURLoption option, S32 value);
00220         // These assume the setter does not free value!
00221         void setopt(CURLoption option, void* value);
00222         void setopt(CURLoption option, char* value);
00223         // Copies the string so that it is gauranteed to stick around
00224         void setoptString(CURLoption option, const std::string& value);
00225         
00226         void slist_append(const char* str);
00227         void setHeaders();
00228         
00229         U32 report(CURLcode);
00230         void getTransferInfo(LLCurl::TransferInfo* info);
00231 
00232         void prepRequest(const std::string& url, ResponderPtr, bool post = false);
00233         
00234         const char* getErrorBuffer();
00235 
00236         std::stringstream& getInput() { return mInput; }
00237         std::stringstream& getHeaderOutput() { return mHeaderOutput; }
00238         LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; }
00239         const LLChannelDescriptors& getChannels() { return mChannels; }
00240         
00241         void resetState();
00242 
00243 private:        
00244         CURL*                           mCurlEasyHandle;
00245         struct curl_slist*      mHeaders;
00246         
00247         std::stringstream       mRequest;
00248         LLChannelDescriptors mChannels;
00249         LLIOPipe::buffer_ptr_t mOutput;
00250         std::stringstream       mInput;
00251         std::stringstream       mHeaderOutput;
00252         char                            mErrorBuffer[CURL_ERROR_SIZE];
00253 
00254         // Note: char*'s not strings since we pass pointers to curl
00255         std::vector<char*>      mStrings;
00256         
00257         ResponderPtr            mResponder;
00258 };
00259 
00260 LLCurl::Easy::Easy()
00261         : mHeaders(NULL),
00262           mCurlEasyHandle(NULL)
00263 {
00264         mErrorBuffer[0] = 0;
00265 }
00266 
00267 LLCurl::Easy* LLCurl::Easy::getEasy()
00268 {
00269         Easy* easy = new Easy();
00270         easy->mCurlEasyHandle = curl_easy_init();
00271         if (!easy->mCurlEasyHandle)
00272         {
00273                 // this can happen if we have too many open files (fails in c-ares/ares_init.c)
00274                 llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl;
00275                 delete easy;
00276                 return NULL;
00277         }
00278         ++gCurlEasyCount;
00279         return easy;
00280 }
00281 
00282 LLCurl::Easy::~Easy()
00283 {
00284         curl_easy_cleanup(mCurlEasyHandle);
00285         --gCurlEasyCount;
00286         curl_slist_free_all(mHeaders);
00287         for_each(mStrings.begin(), mStrings.end(), DeletePointer());
00288 }
00289 
00290 void LLCurl::Easy::resetState()
00291 {
00292         curl_easy_reset(mCurlEasyHandle);
00293 
00294         if (mHeaders)
00295         {
00296                 curl_slist_free_all(mHeaders);
00297                 mHeaders = NULL;
00298         }
00299 
00300         mRequest.str("");
00301         mRequest.clear();
00302 
00303         mOutput.reset();
00304         
00305         mInput.str("");
00306         mInput.clear();
00307         
00308         mErrorBuffer[0] = 0;
00309         
00310         mHeaderOutput.str("");
00311         mHeaderOutput.clear();
00312 }
00313 
00314 void LLCurl::Easy::setErrorBuffer()
00315 {
00316         setopt(CURLOPT_ERRORBUFFER, &mErrorBuffer);
00317 }
00318 
00319 const char* LLCurl::Easy::getErrorBuffer()
00320 {
00321         return mErrorBuffer;
00322 }
00323 
00324 void LLCurl::Easy::setCA()
00325 {
00326         if(!sCAPath.empty())
00327         {
00328                 setoptString(CURLOPT_CAPATH, sCAPath);
00329         }
00330         if(!sCAFile.empty())
00331         {
00332                 setoptString(CURLOPT_CAINFO, sCAFile);
00333         }
00334 }
00335 
00336 void LLCurl::Easy::setHeaders()
00337 {
00338         setopt(CURLOPT_HTTPHEADER, mHeaders);
00339 }
00340 
00341 void LLCurl::Easy::getTransferInfo(LLCurl::TransferInfo* info)
00342 {
00343         curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SIZE_DOWNLOAD, &info->mSizeDownload);
00344         curl_easy_getinfo(mCurlEasyHandle, CURLINFO_TOTAL_TIME, &info->mTotalTime);
00345         curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SPEED_DOWNLOAD, &info->mSpeedDownload);
00346 }
00347 
00348 U32 LLCurl::Easy::report(CURLcode code)
00349 {
00350         U32 responseCode = 0;   
00351         std::string responseReason;
00352         
00353         if (code == CURLE_OK)
00354         {
00355                 curl_easy_getinfo(mCurlEasyHandle, CURLINFO_RESPONSE_CODE, &responseCode);
00356                 //*TODO: get reason from first line of mHeaderOutput
00357         }
00358         else
00359         {
00360                 responseCode = 499;
00361                 responseReason = strerror(code) + " : " + mErrorBuffer;
00362         }
00363                 
00364         if (mResponder)
00365         {       
00366                 mResponder->completedRaw(responseCode, responseReason, mChannels, mOutput);
00367                 mResponder = NULL;
00368         }
00369         
00370         resetState();
00371         return responseCode;
00372 }
00373 
00374 // Note: these all assume the caller tracks the value (i.e. keeps it persistant)
00375 void LLCurl::Easy::setopt(CURLoption option, S32 value)
00376 {
00377         curl_easy_setopt(mCurlEasyHandle, option, value);
00378 }
00379 
00380 void LLCurl::Easy::setopt(CURLoption option, void* value)
00381 {
00382         curl_easy_setopt(mCurlEasyHandle, option, value);
00383 }
00384 
00385 void LLCurl::Easy::setopt(CURLoption option, char* value)
00386 {
00387         curl_easy_setopt(mCurlEasyHandle, option, value);
00388 }
00389 
00390 // Note: this copies the string so that the caller does not have to keep it around
00391 void LLCurl::Easy::setoptString(CURLoption option, const std::string& value)
00392 {
00393         char* tstring = new char[value.length()+1];
00394         strcpy(tstring, value.c_str());
00395         mStrings.push_back(tstring);
00396         curl_easy_setopt(mCurlEasyHandle, option, tstring);
00397 }
00398 
00399 void LLCurl::Easy::slist_append(const char* str)
00400 {
00401         mHeaders = curl_slist_append(mHeaders, str);
00402 }
00403 
00404 size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data)
00405 {
00406         LLCurl::Easy* easy = (LLCurl::Easy*)user_data;
00407         
00408         S32 n = size * nmemb;
00409         S32 startpos = easy->getInput().tellg();
00410         easy->getInput().seekg(0, std::ios::end);
00411         S32 endpos = easy->getInput().tellg();
00412         easy->getInput().seekg(startpos, std::ios::beg);
00413         S32 maxn = endpos - startpos;
00414         n = llmin(n, maxn);
00415         easy->getInput().read((char*)data, n);
00416 
00417         return n;
00418 }
00419 
00420 size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data)
00421 {
00422         LLCurl::Easy* easy = (LLCurl::Easy*)user_data;
00423         
00424         S32 n = size * nmemb;
00425         easy->getOutput()->append(easy->getChannels().in(), (const U8*)data, n);
00426 
00427         return n;
00428 }
00429 
00430 size_t curlHeaderCallback(void* data, size_t size, size_t nmemb, void* user_data)
00431 {
00432         LLCurl::Easy* easy = (LLCurl::Easy*)user_data;
00433         
00434         size_t n = size * nmemb;
00435         easy->getHeaderOutput().write((const char*)data, n);
00436 
00437         return n;
00438 }
00439 
00440 void LLCurl::Easy::prepRequest(const std::string& url, ResponderPtr responder, bool post)
00441 {
00442         resetState();
00443         
00444         if (post) setoptString(CURLOPT_ENCODING, "");
00445 
00446 //      setopt(CURLOPT_VERBOSE, 1); // usefull for debugging
00447         setopt(CURLOPT_NOSIGNAL, 1);
00448 
00449         mOutput.reset(new LLBufferArray);
00450         setopt(CURLOPT_WRITEFUNCTION, (void*)&curlWriteCallback);
00451         setopt(CURLOPT_WRITEDATA, (void*)this);
00452 
00453         setopt(CURLOPT_READFUNCTION, (void*)&curlReadCallback);
00454         setopt(CURLOPT_READDATA, (void*)this);
00455         
00456         setopt(CURLOPT_HEADERFUNCTION, (void*)&curlHeaderCallback);
00457         setopt(CURLOPT_HEADERDATA, (void*)this);
00458 
00459         setErrorBuffer();
00460         setCA();
00461 
00462         setopt(CURLOPT_SSL_VERIFYPEER, true);
00463         setopt(CURLOPT_TIMEOUT, CURL_REQUEST_TIMEOUT);
00464 
00465         setoptString(CURLOPT_URL, url);
00466 
00467         mResponder = responder;
00468 
00469         if (!post)
00470         {
00471                 slist_append("Connection: keep-alive");
00472                 slist_append("Keep-alive: 300");
00473         }
00474         // *FIX: should have ACCEPT headers
00475 }
00476 
00478 
00479 class LLCurl::Multi
00480 {
00481         LOG_CLASS(Multi);
00482 public:
00483         
00484         Multi();
00485         ~Multi();
00486 
00487         Easy* allocEasy();
00488         bool addEasy(Easy* easy);
00489         
00490         void removeEasy(Easy* easy);
00491 
00492         S32 process();
00493         S32 perform();
00494         
00495         CURLMsg* info_read(S32* msgs_in_queue);
00496 
00497         S32 mQueued;
00498         S32 mErrorCount;
00499         
00500 private:
00501         void easyFree(Easy*);
00502         
00503         CURLM* mCurlMultiHandle;
00504 
00505         typedef std::set<Easy*> easy_active_list_t;
00506         easy_active_list_t mEasyActiveList;
00507         typedef std::map<CURL*, Easy*> easy_active_map_t;
00508         easy_active_map_t mEasyActiveMap;
00509         typedef std::set<Easy*> easy_free_list_t;
00510         easy_free_list_t mEasyFreeList;
00511 };
00512 
00513 LLCurl::Multi::Multi()
00514         : mQueued(0),
00515           mErrorCount(0)
00516 {
00517         mCurlMultiHandle = curl_multi_init();
00518         if (!mCurlMultiHandle)
00519         {
00520                 llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl;
00521                 mCurlMultiHandle = curl_multi_init();
00522         }
00523         llassert_always(mCurlMultiHandle);
00524         ++gCurlMultiCount;
00525 }
00526 
00527 LLCurl::Multi::~Multi()
00528 {
00529         // Clean up active
00530         for(easy_active_list_t::iterator iter = mEasyActiveList.begin();
00531                 iter != mEasyActiveList.end(); ++iter)
00532         {
00533                 Easy* easy = *iter;
00534                 curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle());
00535                 delete easy;
00536         }
00537         mEasyActiveList.clear();
00538         mEasyActiveMap.clear();
00539         
00540         // Clean up freed
00541         for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer());  
00542         mEasyFreeList.clear();
00543 
00544         curl_multi_cleanup(mCurlMultiHandle);
00545         --gCurlMultiCount;
00546 }
00547 
00548 CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue)
00549 {
00550         CURLMsg* curlmsg = curl_multi_info_read(mCurlMultiHandle, msgs_in_queue);
00551         return curlmsg;
00552 }
00553 
00554 
00555 S32 LLCurl::Multi::perform()
00556 {
00557         S32 q = 0;
00558         for (S32 call_count = 0;
00559                  call_count < MULTI_PERFORM_CALL_REPEAT;
00560                  call_count += 1)
00561         {
00562                 CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q);
00563                 if (CURLM_CALL_MULTI_PERFORM != code || q == 0)
00564                 {
00565                         break;
00566                 }
00567         }
00568         mQueued = q;
00569         return q;
00570 }
00571 
00572 S32 LLCurl::Multi::process()
00573 {
00574         perform();
00575         
00576         CURLMsg* msg;
00577         int msgs_in_queue;
00578 
00579         S32 processed = 0;
00580         while ((msg = info_read(&msgs_in_queue)))
00581         {
00582                 ++processed;
00583                 if (msg->msg == CURLMSG_DONE)
00584                 {
00585                         U32 response = 0;
00586                         easy_active_map_t::iterator iter = mEasyActiveMap.find(msg->easy_handle);
00587                         if (iter != mEasyActiveMap.end())
00588                         {
00589                                 Easy* easy = iter->second;
00590                                 response = easy->report(msg->data.result);
00591                                 removeEasy(easy);
00592                         }
00593                         else
00594                         {
00595                                 response = 499;
00596                                 //*TODO: change to llwarns
00597                                 llerrs << "cleaned up curl request completed!" << llendl;
00598                         }
00599                         if (response >= 400)
00600                         {
00601                                 // failure of some sort, inc mErrorCount for debugging and flagging multi for destruction
00602                                 ++mErrorCount;
00603                         }
00604                 }
00605         }
00606         return processed;
00607 }
00608 
00609 LLCurl::Easy* LLCurl::Multi::allocEasy()
00610 {
00611         Easy* easy = 0;
00612 
00613         if (mEasyFreeList.empty())
00614         {
00615                 easy = Easy::getEasy();
00616         }
00617         else
00618         {
00619                 easy = *(mEasyFreeList.begin());
00620                 mEasyFreeList.erase(easy);
00621         }
00622         if (easy)
00623         {
00624                 mEasyActiveList.insert(easy);
00625                 mEasyActiveMap[easy->getCurlHandle()] = easy;
00626         }
00627         return easy;
00628 }
00629 
00630 bool LLCurl::Multi::addEasy(Easy* easy)
00631 {
00632         CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle());
00633         if (mcode != CURLM_OK)
00634         {
00635                 llwarns << "Curl Error: " << curl_multi_strerror(mcode) << llendl;
00636                 return false;
00637         }
00638         return true;
00639 }
00640 
00641 void LLCurl::Multi::easyFree(Easy* easy)
00642 {
00643         mEasyActiveList.erase(easy);
00644         mEasyActiveMap.erase(easy->getCurlHandle());
00645         if (mEasyFreeList.size() < EASY_HANDLE_POOL_SIZE)
00646         {
00647                 easy->resetState();
00648                 mEasyFreeList.insert(easy);
00649         }
00650         else
00651         {
00652                 delete easy;
00653         }
00654 }
00655 
00656 void LLCurl::Multi::removeEasy(Easy* easy)
00657 {
00658         curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle());
00659         easyFree(easy);
00660 }
00661 
00662 //static
00663 std::string LLCurl::strerror(CURLcode errorcode)
00664 {
00665 #if LL_DARWIN
00666         // curl_easy_strerror was added in libcurl 7.12.0.  Unfortunately, the version in the Mac OS X 10.3.9 SDK is 7.10.2...
00667         // There's a problem with the custom curl headers in our build that keeps me from #ifdefing this on the libcurl version number
00668         // (the correct check would be #if LIBCURL_VERSION_NUM >= 0x070c00).  We'll fix the header problem soon, but for now
00669         // just punt and print the numeric error code on the Mac.
00670         return llformat("%d", errorcode);
00671 #else // LL_DARWIN
00672         return std::string(curl_easy_strerror(errorcode));
00673 #endif // LL_DARWIN
00674 }
00675 
00677 // For generating a simple request for data
00678 // using one multi and one easy per request 
00679 
00680 LLCurlRequest::LLCurlRequest() :
00681         mActiveMulti(NULL),
00682         mActiveRequestCount(0)
00683 {
00684 }
00685 
00686 LLCurlRequest::~LLCurlRequest()
00687 {
00688         for_each(mMultiSet.begin(), mMultiSet.end(), DeletePointer());
00689 }
00690 
00691 void LLCurlRequest::addMulti()
00692 {
00693         LLCurl::Multi* multi = new LLCurl::Multi();
00694         mMultiSet.insert(multi);
00695         mActiveMulti = multi;
00696         mActiveRequestCount = 0;
00697 }
00698 
00699 LLCurl::Easy* LLCurlRequest::allocEasy()
00700 {
00701         if (!mActiveMulti ||
00702                 mActiveRequestCount     >= MAX_ACTIVE_REQUEST_COUNT ||
00703                 mActiveMulti->mErrorCount > 0)
00704         {
00705                 addMulti();
00706         }
00707         llassert_always(mActiveMulti);
00708         ++mActiveRequestCount;
00709         LLCurl::Easy* easy = mActiveMulti->allocEasy();
00710         return easy;
00711 }
00712 
00713 bool LLCurlRequest::addEasy(LLCurl::Easy* easy)
00714 {
00715         llassert_always(mActiveMulti);
00716         bool res = mActiveMulti->addEasy(easy);
00717         return res;
00718 }
00719 
00720 void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder)
00721 {
00722         getByteRange(url, 0, -1, responder);
00723 }
00724         
00725 bool LLCurlRequest::getByteRange(const std::string& url, S32 offset, S32 length, LLCurl::ResponderPtr responder)
00726 {
00727         LLCurl::Easy* easy = allocEasy();
00728         if (!easy)
00729         {
00730                 return false;
00731         }
00732         easy->prepRequest(url, responder);
00733         easy->setopt(CURLOPT_HTTPGET, 1);
00734         if (length > 0)
00735         {
00736                 std::string range = llformat("Range: bytes=%d-%d", offset,offset+length-1);
00737                 easy->slist_append(range.c_str());
00738         }
00739         easy->setHeaders();
00740         bool res = addEasy(easy);
00741         return res;
00742 }
00743 
00744 bool LLCurlRequest::post(const std::string& url, const LLSD& data, LLCurl::ResponderPtr responder)
00745 {
00746         LLCurl::Easy* easy = allocEasy();
00747         if (!easy)
00748         {
00749                 return false;
00750         }
00751         easy->prepRequest(url, responder);
00752 
00753         LLSDSerialize::toXML(data, easy->getInput());
00754         S32 bytes = easy->getInput().str().length();
00755         
00756         easy->setopt(CURLOPT_POST, 1);
00757         easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL);
00758         easy->setopt(CURLOPT_POSTFIELDSIZE, bytes);
00759 
00760         easy->slist_append("Content-Type: application/xml");
00761         easy->setHeaders();
00762 
00763         lldebugs << "POSTING: " << bytes << " bytes." << llendl;
00764         bool res = addEasy(easy);
00765         return res;
00766 }
00767         
00768 // Note: call once per frame
00769 S32 LLCurlRequest::process()
00770 {
00771         S32 res = 0;
00772         for (curlmulti_set_t::iterator iter = mMultiSet.begin();
00773                  iter != mMultiSet.end(); )
00774         {
00775                 curlmulti_set_t::iterator curiter = iter++;
00776                 LLCurl::Multi* multi = *curiter;
00777                 S32 tres = multi->process();
00778                 res += tres;
00779                 if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0)
00780                 {
00781                         mMultiSet.erase(curiter);
00782                         delete multi;
00783                 }
00784         }
00785         return res;
00786 }
00787 
00788 S32 LLCurlRequest::getQueued()
00789 {
00790         S32 queued = 0;
00791         for (curlmulti_set_t::iterator iter = mMultiSet.begin();
00792                  iter != mMultiSet.end(); )
00793         {
00794                 curlmulti_set_t::iterator curiter = iter++;
00795                 LLCurl::Multi* multi = *curiter;
00796                 queued += multi->mQueued;
00797         }
00798         return queued;
00799 }
00800 
00802 // For generating one easy request
00803 // associated with a single multi request
00804 
00805 LLCurlEasyRequest::LLCurlEasyRequest()
00806         : mRequestSent(false),
00807           mResultReturned(false)
00808 {
00809         mMulti = new LLCurl::Multi();
00810         mEasy = mMulti->allocEasy();
00811         if (mEasy)
00812         {
00813                 mEasy->setErrorBuffer();
00814                 mEasy->setCA();
00815         }
00816 }
00817 
00818 LLCurlEasyRequest::~LLCurlEasyRequest()
00819 {
00820         delete mMulti;
00821 }
00822         
00823 void LLCurlEasyRequest::setopt(CURLoption option, S32 value)
00824 {
00825         if (mEasy)
00826         {
00827                 mEasy->setopt(option, value);
00828         }
00829 }
00830 
00831 void LLCurlEasyRequest::setoptString(CURLoption option, const std::string& value)
00832 {
00833         if (mEasy)
00834         {
00835                 mEasy->setoptString(option, value);
00836         }
00837 }
00838 
00839 void LLCurlEasyRequest::setPost(char* postdata, S32 size)
00840 {
00841         if (mEasy)
00842         {
00843                 mEasy->setopt(CURLOPT_POST, 1);
00844                 mEasy->setopt(CURLOPT_POSTFIELDS, postdata);
00845                 mEasy->setopt(CURLOPT_POSTFIELDSIZE, size);
00846         }
00847 }
00848 
00849 void LLCurlEasyRequest::setHeaderCallback(curl_header_callback callback, void* userdata)
00850 {
00851         if (mEasy)
00852         {
00853                 mEasy->setopt(CURLOPT_HEADERFUNCTION, (void*)callback);
00854                 mEasy->setopt(CURLOPT_HEADERDATA, userdata); // aka CURLOPT_WRITEHEADER
00855         }
00856 }
00857 
00858 void LLCurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userdata)
00859 {
00860         if (mEasy)
00861         {
00862                 mEasy->setopt(CURLOPT_WRITEFUNCTION, (void*)callback);
00863                 mEasy->setopt(CURLOPT_WRITEDATA, userdata);
00864         }
00865 }
00866 
00867 void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata)
00868 {
00869         if (mEasy)
00870         {
00871                 mEasy->setopt(CURLOPT_READFUNCTION, (void*)callback);
00872                 mEasy->setopt(CURLOPT_READDATA, userdata);
00873         }
00874 }
00875 
00876 void LLCurlEasyRequest::slist_append(const char* str)
00877 {
00878         if (mEasy)
00879         {
00880                 mEasy->slist_append(str);
00881         }
00882 }
00883 
00884 void LLCurlEasyRequest::sendRequest(const std::string& url)
00885 {
00886         llassert_always(!mRequestSent);
00887         mRequestSent = true;
00888         lldebugs << url << llendl;
00889         if (mEasy)
00890         {
00891                 mEasy->setHeaders();
00892                 mEasy->setoptString(CURLOPT_URL, url);
00893                 mMulti->addEasy(mEasy);
00894         }
00895 }
00896 
00897 void LLCurlEasyRequest::requestComplete()
00898 {
00899         llassert_always(mRequestSent);
00900         mRequestSent = false;
00901         if (mEasy)
00902         {
00903                 mMulti->removeEasy(mEasy);
00904         }
00905 }
00906 
00907 S32 LLCurlEasyRequest::perform()
00908 {
00909         return mMulti->perform();
00910 }
00911 
00912 // Usage: Call getRestult until it returns false (no more messages)
00913 bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info)
00914 {
00915         if (!mEasy)
00916         {
00917                 // Special case - we failed to initialize a curl_easy (can happen if too many open files)
00918                 //  Act as though the request failed to connect
00919                 if (mResultReturned)
00920                 {
00921                         return false;
00922                 }
00923                 else
00924                 {
00925                         *result = CURLE_FAILED_INIT;
00926                         mResultReturned = true;
00927                         return true;
00928                 }
00929         }
00930         // In theory, info_read might return a message with a status other than CURLMSG_DONE
00931         // In practice for all messages returned, msg == CURLMSG_DONE
00932         // Ignore other messages just in case
00933         while(1)
00934         {
00935                 S32 q;
00936                 CURLMsg* curlmsg = info_read(&q, info);
00937                 if (curlmsg)
00938                 {
00939                         if (curlmsg->msg == CURLMSG_DONE)
00940                         {
00941                                 *result = curlmsg->data.result;                 
00942                                 return true;
00943                         }
00944                         // else continue
00945                 }
00946                 else
00947                 {
00948                         return false;
00949                 }
00950         }
00951 }
00952 
00953 // private
00954 CURLMsg* LLCurlEasyRequest::info_read(S32* q, LLCurl::TransferInfo* info)
00955 {
00956         if (mEasy)
00957         {
00958                 CURLMsg* curlmsg = mMulti->info_read(q);
00959                 if (curlmsg && curlmsg->msg == CURLMSG_DONE)
00960                 {
00961                         if (info)
00962                         {
00963                                 mEasy->getTransferInfo(info);
00964                         }
00965                 }
00966                 return curlmsg;
00967         }
00968         return NULL;
00969 }
00970 
00971 std::string LLCurlEasyRequest::getErrorString()
00972 {
00973         return mEasy ? std::string(mEasy->getErrorBuffer()) : std::string();
00974 }
00975 
00977 
00978 #if SAFE_SSL
00979 //static
00980 void LLCurl::ssl_locking_callback(int mode, int type, const char *file, int line)
00981 {
00982         if (mode & CRYPTO_LOCK)
00983         {
00984                 LLCurl::sSSLMutex[type]->lock();
00985         }
00986         else
00987         {
00988                 LLCurl::sSSLMutex[type]->unlock();
00989         }
00990 }
00991 
00992 //static
00993 unsigned long LLCurl::ssl_thread_id(void)
00994 {
00995         return LLThread::currentID();
00996 }
00997 #endif
00998 
00999 void LLCurl::initClass()
01000 {
01001         // Do not change this "unless you are familiar with and mean to control 
01002         // internal operations of libcurl"
01003         // - http://curl.haxx.se/libcurl/c/curl_global_init.html
01004         curl_global_init(CURL_GLOBAL_ALL);
01005         
01006 #if SAFE_SSL
01007         S32 mutex_count = CRYPTO_num_locks();
01008         for (S32 i=0; i<mutex_count; i++)
01009         {
01010                 sSSLMutex.push_back(new LLMutex(gAPRPoolp));
01011         }
01012         CRYPTO_set_id_callback(&LLCurl::ssl_thread_id);
01013         CRYPTO_set_locking_callback(&LLCurl::ssl_locking_callback);
01014 #endif
01015 }
01016 
01017 void LLCurl::cleanupClass()
01018 {
01019 #if SAFE_SSL
01020         CRYPTO_set_locking_callback(NULL);
01021         for_each(sSSLMutex.begin(), sSSLMutex.end(), DeletePointer());
01022 #endif
01023         curl_global_cleanup();
01024 }
01025 

Generated on Fri May 16 08:32:23 2008 for SecondLife by  doxygen 1.5.5