00001 
00034 #include "linden_common.h"
00035 
00036 #include "llcurl.h"
00037 
00038 #include <iomanip>
00039 
00040 #include "llsdserialize.h"
00041 
00043 
00044 
00045 
00046 
00047 
00048 
00049 
00050 
00051 
00052 
00053 
00054 
00055 
00056 
00057 
00058 using namespace std;
00059         
00060 LLCurl::Responder::Responder()
00061         : mReferenceCount(0)
00062 {
00063 }
00064 LLCurl::Responder::~Responder()
00065 {
00066 }
00067 
00068 
00069 void LLCurl::Responder::error(U32 status, const std::stringstream& content)
00070 {
00071         llinfos << "LLCurl::Responder::error " << status << ": " << content.str() << llendl;
00072 }
00073 
00074 
00075 void LLCurl::Responder::result(const std::stringstream& content)
00076 {
00077 }
00078 
00079 
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 
00129 
00130 
00131 
00132 
00133         
00134 
00135 
00136 
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                 
00150                 
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); 
00188 #else
00189         
00190         curl_easy_cleanup(mHandle);
00191         mHandle = curl_easy_init();
00192 #endif
00193         
00194         curl_easy_setopt(mHandle, CURLOPT_PRIVATE, this);
00195 
00196 
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         
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 }