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 }