llcurl.cpp

Go to the documentation of this file.
00001 
00034 #include "linden_common.h"
00035 
00036 #include "llcurl.h"
00037 
00038 #include <iomanip>
00039 
00040 #include "llsdserialize.h"
00041 
00043 /*
00044         The trick to getting curl to do keep-alives is to reuse the
00045         same easy handle for the requests.  It appears that curl
00046         keeps a pool of connections alive for each easy handle, but
00047         doesn't share them between easy handles.  Therefore it is
00048         important to keep a pool of easy handles and reuse them,
00049         rather than create and destroy them with each request.  This
00050         code does this.
00051 
00052         Furthermore, it would behoove us to keep track of which
00053         hosts an easy handle was used for and pick an easy handle
00054         that matches the next request.  This code does not current
00055         do this.
00056  */
00057 
00058 using namespace std;
00059         
00060 LLCurl::Responder::Responder()
00061         : mReferenceCount(0)
00062 {
00063 }
00064 LLCurl::Responder::~Responder()
00065 {
00066 }
00067 
00068 // virtual
00069 void LLCurl::Responder::error(U32 status, const std::stringstream& content)
00070 {
00071         llinfos << "LLCurl::Responder::error " << status << ": " << content.str() << llendl;
00072 }
00073 
00074 // virtual
00075 void LLCurl::Responder::result(const std::stringstream& content)
00076 {
00077 }
00078 
00079 // virtual
00080 void LLCurl::Responder::completed(U32 status, const std::stringstream& content)
00081 {
00082         if (200 <= status &&  status < 300)
00083         {
00084                 result(content);
00085         }
00086         else
00087         {
00088                 error(status, content);
00089         }
00090 }
00091 
00092 
00093 namespace boost
00094 {
00095         void intrusive_ptr_add_ref(LLCurl::Responder* p)
00096         {
00097                 ++p->mReferenceCount;
00098         }
00099         
00100         void intrusive_ptr_release(LLCurl::Responder* p)
00101         {
00102                 if(p && 0 == --p->mReferenceCount)
00103                 {
00104                         delete p;
00105                 }
00106         }
00107 };
00108 
00110 
00111 size_t
00112 curlOutputCallback(void* data, size_t size, size_t nmemb, void* user_data)
00113 {
00114         stringstream& output = *(stringstream*)user_data;
00115         
00116         size_t n = size * nmemb;
00117         output.write((const char*)data, n);
00118         if (!((istream&)output).good()) {
00119                 std::cerr << "WHAT!?!?!? istream side bad" << std::endl;
00120         }
00121         if (!((ostream&)output).good()) {
00122                 std::cerr << "WHAT!?!?!? ostream side bad" << std::endl;
00123         }
00124 
00125         return n;
00126 }
00127 
00128 // Only used if request contained a body (post or put), Not currently implemented.
00129 // size_t
00130 // curlRequestCallback(void* data, size_t size, size_t nmemb, void* user_data)
00131 // {
00132 //      stringstream& request = *(stringstream*)user_data;
00133         
00134 //      size_t n = size * nmemb;
00135 //      request.read((char*)data, n);
00136 //      return request.gcount();
00137 // }
00138 
00139 
00140 
00141 
00142 
00143 LLCurl::Easy::Easy()
00144 {
00145         mHeaders = 0;
00146         mHeaders = curl_slist_append(mHeaders, "Connection: keep-alive");
00147         mHeaders = curl_slist_append(mHeaders, "Keep-alive: 300");
00148         mHeaders = curl_slist_append(mHeaders, "Content-Type: application/xml");
00149                 // FIXME: shouldn't be there for GET/DELETE
00150                 // FIXME: should have ACCEPT headers
00151                 
00152         mHandle = curl_easy_init();
00153 }
00154 
00155 LLCurl::Easy::~Easy()
00156 {
00157         curl_easy_cleanup(mHandle);
00158         curl_slist_free_all(mHeaders);
00159 }
00160 
00161 void
00162 LLCurl::Easy::get(const string& url, ResponderPtr responder)
00163 {
00164         prep(url, responder);
00165         curl_easy_setopt(mHandle, CURLOPT_HTTPGET, 1);
00166 }
00167 
00168 void
00169 LLCurl::Easy::getByteRange(const string& url, S32 offset, S32 length, ResponderPtr responder)
00170 {
00171         mRange = llformat("Range: bytes=%d-%d", offset,offset+length-1);
00172         mHeaders = curl_slist_append(mHeaders, mRange.c_str());
00173         prep(url, responder);
00174         curl_easy_setopt(mHandle, CURLOPT_HTTPGET, 1);
00175 }
00176 
00177 void
00178 LLCurl::Easy::perform()
00179 {
00180         report(curl_easy_perform(mHandle));
00181 }
00182 
00183 void
00184 LLCurl::Easy::prep(const std::string& url, ResponderPtr responder)
00185 {
00186 #if !LL_DARWIN
00187         curl_easy_reset(mHandle); // SJB: doesn't exisit on OSX 10.3.9
00188 #else
00189         // SJB: equivalent? fast?
00190         curl_easy_cleanup(mHandle);
00191         mHandle = curl_easy_init();
00192 #endif
00193         
00194         curl_easy_setopt(mHandle, CURLOPT_PRIVATE, this);
00195 
00196 //      curl_easy_setopt(mHandle, CURLOPT_VERBOSE, 1); // usefull for debugging
00197         curl_easy_setopt(mHandle, CURLOPT_NOSIGNAL, 1);
00198         curl_easy_setopt(mHandle, CURLOPT_WRITEFUNCTION, &curlOutputCallback);
00199         curl_easy_setopt(mHandle, CURLOPT_WRITEDATA, &mOutput);
00200 #if 1 // For debug
00201         curl_easy_setopt(mHandle, CURLOPT_HEADERFUNCTION, &curlOutputCallback);
00202         curl_easy_setopt(mHandle, CURLOPT_HEADERDATA, &mHeaderOutput);
00203 #endif
00204         curl_easy_setopt(mHandle, CURLOPT_ERRORBUFFER, &mErrorBuffer);
00205         curl_easy_setopt(mHandle, CURLOPT_ENCODING, "");
00206         curl_easy_setopt(mHandle, CURLOPT_SSL_VERIFYPEER, false);
00207         curl_easy_setopt(mHandle, CURLOPT_HTTPHEADER, mHeaders);
00208 
00209         mOutput.str("");
00210         if (!((istream&)mOutput).good()) {
00211                 std::cerr << "WHAT!?!?!? istream side bad" << std::endl;
00212         }
00213         if (!((ostream&)mOutput).good()) {
00214                 std::cerr << "WHAT!?!?!? ostream side bad" << std::endl;
00215         }
00216 
00217         mURL = url;
00218         curl_easy_setopt(mHandle, CURLOPT_URL, mURL.c_str());
00219 
00220         mResponder = responder;
00221 }
00222 
00223 void
00224 LLCurl::Easy::report(CURLcode code)
00225 {
00226         if (!mResponder) return;
00227         
00228         long responseCode;
00229         
00230         if (code == CURLE_OK)
00231         {
00232                 curl_easy_getinfo(mHandle, CURLINFO_RESPONSE_CODE, &responseCode);
00233         }
00234         else
00235         {
00236                 responseCode = 499;
00237         }
00238         
00239         mResponder->completed(responseCode, mOutput);
00240         mResponder = NULL;
00241 }
00242 
00243 
00244 
00245 
00246 
00247 
00248 LLCurl::Multi::Multi()
00249 {
00250         mHandle = curl_multi_init();
00251 }
00252 
00253 LLCurl::Multi::~Multi()
00254 {
00255         // FIXME: should clean up excess handles in mFreeEasy
00256         curl_multi_cleanup(mHandle);
00257 }
00258 
00259 
00260 void
00261 LLCurl::Multi::get(const std::string& url, ResponderPtr responder)
00262 {
00263         LLCurl::Easy* easy = easyAlloc();
00264         easy->get(url, responder);
00265         curl_multi_add_handle(mHandle, easy->mHandle);
00266 }
00267         
00268 void
00269 LLCurl::Multi::getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder)
00270 {
00271         LLCurl::Easy* easy = easyAlloc();
00272         easy->getByteRange(url, offset, length, responder);
00273         curl_multi_add_handle(mHandle, easy->mHandle);
00274 }
00275         
00276 void
00277 LLCurl::Multi::process()
00278 {
00279         int count;
00280         for (int call_count = 0; call_count < 5; call_count += 1)
00281         {
00282                 if (CURLM_CALL_MULTI_PERFORM != curl_multi_perform(mHandle, &count))
00283                 {
00284                         break;
00285                 }
00286         }
00287                 
00288         CURLMsg* msg;
00289         int msgs_in_queue;
00290         while ((msg = curl_multi_info_read(mHandle, &msgs_in_queue)))
00291         {
00292                 if (msg->msg != CURLMSG_DONE) continue;
00293                 Easy* easy = 0;
00294                 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &easy);
00295                 if (!easy) continue;
00296                 easy->report(msg->data.result);
00297                 
00298                 curl_multi_remove_handle(mHandle, easy->mHandle);
00299                 easyFree(easy);
00300         }
00301 }
00302 
00303 
00304 
00305 LLCurl::Easy*
00306 LLCurl::Multi::easyAlloc()
00307 {
00308         Easy* easy = 0;
00309         
00310         if (mFreeEasy.empty())
00311         {
00312                 easy = new Easy();
00313         }
00314         else
00315         {
00316                 easy = mFreeEasy.back();
00317                 mFreeEasy.pop_back();
00318         }
00319 
00320         return easy;
00321 }
00322 
00323 void
00324 LLCurl::Multi::easyFree(Easy* easy)
00325 {
00326         if (mFreeEasy.size() < 5)
00327         {
00328                 mFreeEasy.push_back(easy);
00329         }
00330         else
00331         {
00332                 delete easy;
00333         }
00334 }
00335 
00336 
00337 
00338 namespace
00339 {
00340         static LLCurl::Multi* sMainMulti = 0;
00341         
00342         LLCurl::Multi*
00343         mainMulti()
00344         {
00345                 if (!sMainMulti) {
00346                         sMainMulti = new LLCurl::Multi();
00347                 }
00348                 return sMainMulti;
00349         }
00350 
00351         void freeMulti()
00352         {
00353                 delete sMainMulti;
00354                 sMainMulti = NULL;
00355         }
00356 }
00357 
00358 void
00359 LLCurl::get(const std::string& url, ResponderPtr responder)
00360 {
00361         mainMulti()->get(url, responder);
00362 }
00363         
00364 void
00365 LLCurl::getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder)
00366 {
00367         mainMulti()->getByteRange(url, offset, length, responder);
00368 }
00369         
00370 void
00371 LLCurl::process()
00372 {
00373         mainMulti()->process();
00374 }
00375 
00376 void LLCurl::cleanup()
00377 {
00378         freeMulti();
00379         curl_global_cleanup();
00380 }

Generated on Thu Jul 1 06:08:23 2010 for Second Life Viewer by  doxygen 1.4.7