aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmessage/llcurl.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:45:34 -0500
committerJacek Antonelli2008-08-15 23:45:34 -0500
commitcd17687f01420952712a500107e0f93e7ab8d5f8 (patch)
treece48c2b706f2c1176290e39fb555fbdf6648ce01 /linden/indra/llmessage/llcurl.cpp
parentSecond Life viewer sources 1.19.0.5 (diff)
downloadmeta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.zip
meta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.tar.gz
meta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.tar.bz2
meta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.tar.xz
Second Life viewer sources 1.19.1.0
Diffstat (limited to '')
-rw-r--r--linden/indra/llmessage/llcurl.cpp962
1 files changed, 799 insertions, 163 deletions
diff --git a/linden/indra/llmessage/llcurl.cpp b/linden/indra/llmessage/llcurl.cpp
index 3969d18..badda64 100644
--- a/linden/indra/llmessage/llcurl.cpp
+++ b/linden/indra/llmessage/llcurl.cpp
@@ -2,7 +2,7 @@
2 * @file llcurl.h 2 * @file llcurl.h
3 * @author Zero / Donovan 3 * @author Zero / Donovan
4 * @date 2006-10-15 4 * @date 2006-10-15
5 * @brief Curl wrapper 5 * @brief Implementation of wrapper around libcurl.
6 * 6 *
7 * $LicenseInfo:firstyear=2006&license=viewergpl$ 7 * $LicenseInfo:firstyear=2006&license=viewergpl$
8 * 8 *
@@ -31,13 +31,29 @@
31 * $/LicenseInfo$ 31 * $/LicenseInfo$
32 */ 32 */
33 33
34#if LL_WINDOWS
35#define SAFE_SSL 1
36#elif LL_DARWIN
37#define SAFE_SSL 1
38#else
39#define SAFE_SSL 1
40#endif
41
34#include "linden_common.h" 42#include "linden_common.h"
35 43
36#include "llcurl.h" 44#include "llcurl.h"
37 45
46#include <algorithm>
38#include <iomanip> 47#include <iomanip>
48#include <curl/curl.h>
49#if SAFE_SSL
50#include <openssl/crypto.h>
51#endif
39 52
53#include "llbufferstream.h"
54#include "llstl.h"
40#include "llsdserialize.h" 55#include "llsdserialize.h"
56#include "llthread.h"
41 57
42////////////////////////////////////////////////////////////////////////////// 58//////////////////////////////////////////////////////////////////////////////
43/* 59/*
@@ -55,40 +71,112 @@
55 do this. 71 do this.
56 */ 72 */
57 73
58using namespace std; 74//////////////////////////////////////////////////////////////////////////////
59 75
76static const S32 EASY_HANDLE_POOL_SIZE = 5;
77static const S32 MULTI_PERFORM_CALL_REPEAT = 5;
78static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds
79static const S32 MAX_ACTIVE_REQUEST_COUNT = 100;
80
81// DEBUG //
82S32 gCurlEasyCount = 0;
83S32 gCurlMultiCount = 0;
84
85//////////////////////////////////////////////////////////////////////////////
86
87//static
88std::vector<LLMutex*> LLCurl::sSSLMutex;
89std::string LLCurl::sCAPath;
90std::string LLCurl::sCAFile;
91
92//static
93void LLCurl::setCAPath(const std::string& path)
94{
95 sCAPath = path;
96}
97
98//static
99void LLCurl::setCAFile(const std::string& file)
100{
101 sCAFile = file;
102}
103
104//////////////////////////////////////////////////////////////////////////////
105
60LLCurl::Responder::Responder() 106LLCurl::Responder::Responder()
61 : mReferenceCount(0) 107 : mReferenceCount(0)
62{ 108{
63} 109}
110
64LLCurl::Responder::~Responder() 111LLCurl::Responder::~Responder()
65{ 112{
66} 113}
67 114
68// virtual 115// virtual
69void LLCurl::Responder::error(U32 status, const std::stringstream& content) 116void LLCurl::Responder::error(U32 status, const std::string& reason)
70{ 117{
71 llinfos << "LLCurl::Responder::error " << status << ": " << content.str() << llendl; 118 llinfos << status << ": " << reason << llendl;
72} 119}
73 120
74// virtual 121// virtual
75void LLCurl::Responder::result(const std::stringstream& content) 122void LLCurl::Responder::result(const LLSD& content)
76{ 123{
124 llwarns << "Virtual Function not implemented" << llendl;
77} 125}
78 126
79// virtual 127// virtual
80void LLCurl::Responder::completed(U32 status, const std::stringstream& content) 128void LLCurl::Responder::completedRaw(U32 status, const std::string& reason,
129 const LLChannelDescriptors& channels,
130 const LLIOPipe::buffer_ptr_t& buffer)
81{ 131{
82 if (200 <= status && status < 300) 132 if (isGoodStatus(status))
133 {
134 LLSD content;
135 LLBufferStream istr(channels, buffer.get());
136 LLSDSerialize::fromXML(content, istr);
137/*
138 const S32 parseError = -1;
139 if(LLSDSerialize::fromXML(content, istr) == parseError)
140 {
141 mStatus = 498;
142 mReason = "Client Parse Error";
143 }
144*/
145 completed(status, reason, content);
146 }
147 else if (status == 400)
148 {
149 // Get reason from buffer
150 char tbuf[4096];
151 S32 len = 4096;
152 buffer->readAfter(channels.in(), NULL, (U8*)tbuf, len);
153 tbuf[len] = 0;
154 completed(status, std::string(tbuf), LLSD());
155 }
156 else
157 {
158 completed(status, reason, LLSD());
159 }
160}
161
162// virtual
163void LLCurl::Responder::completed(U32 status, const std::string& reason, const LLSD& content)
164{
165 if (isGoodStatus(status))
83 { 166 {
84 result(content); 167 result(content);
85 } 168 }
86 else 169 else
87 { 170 {
88 error(status, content); 171 error(status, reason);
89 } 172 }
90} 173}
91 174
175//virtual
176void LLCurl::Responder::completedHeader(U32 status, const std::string& reason, const LLSD& content)
177{
178
179}
92 180
93namespace boost 181namespace boost
94{ 182{
@@ -106,226 +194,456 @@ namespace boost
106 } 194 }
107}; 195};
108 196
197
109////////////////////////////////////////////////////////////////////////////// 198//////////////////////////////////////////////////////////////////////////////
110 199
111size_t 200
112curlOutputCallback(void* data, size_t size, size_t nmemb, void* user_data) 201class LLCurl::Easy
113{ 202{
114 stringstream& output = *(stringstream*)user_data; 203 LOG_CLASS(Easy);
204
205private:
206 Easy();
115 207
116 size_t n = size * nmemb; 208public:
117 output.write((const char*)data, n); 209 static Easy* getEasy();
118 if (!((istream&)output).good()) { 210 ~Easy();
119 std::cerr << "WHAT!?!?!? istream side bad" << std::endl;
120 }
121 if (!((ostream&)output).good()) {
122 std::cerr << "WHAT!?!?!? ostream side bad" << std::endl;
123 }
124 211
125 return n; 212 CURL* getCurlHandle() const { return mCurlEasyHandle; }
126}
127 213
128// Only used if request contained a body (post or put), Not currently implemented. 214 void setErrorBuffer();
129// size_t 215 void setCA();
130// curlRequestCallback(void* data, size_t size, size_t nmemb, void* user_data)
131// {
132// stringstream& request = *(stringstream*)user_data;
133 216
134// size_t n = size * nmemb; 217 void setopt(CURLoption option, S32 value);
135// request.read((char*)data, n); 218 // These assume the setter does not free value!
136// return request.gcount(); 219 void setopt(CURLoption option, void* value);
137// } 220 void setopt(CURLoption option, char* value);
138 221 // Copies the string so that it is gauranteed to stick around
222 void setoptString(CURLoption option, const std::string& value);
223
224 void slist_append(const char* str);
225 void setHeaders();
226
227 U32 report(CURLcode);
228 void getTransferInfo(LLCurl::TransferInfo* info);
139 229
230 void prepRequest(const std::string& url, ResponderPtr, bool post = false);
231
232 const char* getErrorBuffer();
140 233
234 std::stringstream& getInput() { return mInput; }
235 std::stringstream& getHeaderOutput() { return mHeaderOutput; }
236 LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; }
237 const LLChannelDescriptors& getChannels() { return mChannels; }
238
239 void resetState();
141 240
241private:
242 CURL* mCurlEasyHandle;
243 struct curl_slist* mHeaders;
244
245 std::stringstream mRequest;
246 LLChannelDescriptors mChannels;
247 LLIOPipe::buffer_ptr_t mOutput;
248 std::stringstream mInput;
249 std::stringstream mHeaderOutput;
250 char mErrorBuffer[CURL_ERROR_SIZE];
251
252 // Note: char*'s not strings since we pass pointers to curl
253 std::vector<char*> mStrings;
254
255 ResponderPtr mResponder;
256};
142 257
143LLCurl::Easy::Easy() 258LLCurl::Easy::Easy()
259 : mHeaders(NULL),
260 mCurlEasyHandle(NULL)
144{ 261{
145 mHeaders = 0; 262 mErrorBuffer[0] = 0;
146 mHeaders = curl_slist_append(mHeaders, "Connection: keep-alive"); 263}
147 mHeaders = curl_slist_append(mHeaders, "Keep-alive: 300"); 264
148 mHeaders = curl_slist_append(mHeaders, "Content-Type: application/xml"); 265LLCurl::Easy* LLCurl::Easy::getEasy()
149 // FIXME: shouldn't be there for GET/DELETE 266{
150 // FIXME: should have ACCEPT headers 267 Easy* easy = new Easy();
151 268 easy->mCurlEasyHandle = curl_easy_init();
152 mHandle = curl_easy_init(); 269 if (!easy->mCurlEasyHandle)
270 {
271 // this can happen if we have too many open files (fails in c-ares/ares_init.c)
272 llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl;
273 delete easy;
274 return NULL;
275 }
276 ++gCurlEasyCount;
277 return easy;
153} 278}
154 279
155LLCurl::Easy::~Easy() 280LLCurl::Easy::~Easy()
156{ 281{
157 curl_easy_cleanup(mHandle); 282 curl_easy_cleanup(mCurlEasyHandle);
283 --gCurlEasyCount;
158 curl_slist_free_all(mHeaders); 284 curl_slist_free_all(mHeaders);
285 for_each(mStrings.begin(), mStrings.end(), DeletePointer());
159} 286}
160 287
161void 288void LLCurl::Easy::resetState()
162LLCurl::Easy::get(const string& url, ResponderPtr responder)
163{ 289{
164 prep(url, responder); 290 curl_easy_reset(mCurlEasyHandle);
165 curl_easy_setopt(mHandle, CURLOPT_HTTPGET, 1); 291
292 if (mHeaders)
293 {
294 curl_slist_free_all(mHeaders);
295 mHeaders = NULL;
296 }
297
298 mRequest.str("");
299 mRequest.clear();
300
301 mOutput.reset();
302
303 mInput.str("");
304 mInput.clear();
305
306 mErrorBuffer[0] = 0;
307
308 mHeaderOutput.str("");
309 mHeaderOutput.clear();
166} 310}
167 311
168void 312void LLCurl::Easy::setErrorBuffer()
169LLCurl::Easy::getByteRange(const string& url, S32 offset, S32 length, ResponderPtr responder)
170{ 313{
171 mRange = llformat("Range: bytes=%d-%d", offset,offset+length-1); 314 setopt(CURLOPT_ERRORBUFFER, &mErrorBuffer);
172 mHeaders = curl_slist_append(mHeaders, mRange.c_str());
173 prep(url, responder);
174 curl_easy_setopt(mHandle, CURLOPT_HTTPGET, 1);
175} 315}
176 316
177void 317const char* LLCurl::Easy::getErrorBuffer()
178LLCurl::Easy::perform()
179{ 318{
180 report(curl_easy_perform(mHandle)); 319 return mErrorBuffer;
181} 320}
182 321
183void 322void LLCurl::Easy::setCA()
184LLCurl::Easy::prep(const std::string& url, ResponderPtr responder)
185{ 323{
186#if !LL_DARWIN 324 if(!sCAPath.empty())
187 curl_easy_reset(mHandle); // SJB: doesn't exisit on OSX 10.3.9 325 {
188#else 326 setoptString(CURLOPT_CAPATH, sCAPath);
189 // SJB: equivalent? fast?
190 curl_easy_cleanup(mHandle);
191 mHandle = curl_easy_init();
192#endif
193
194 curl_easy_setopt(mHandle, CURLOPT_PRIVATE, this);
195
196// curl_easy_setopt(mHandle, CURLOPT_VERBOSE, 1); // usefull for debugging
197 curl_easy_setopt(mHandle, CURLOPT_NOSIGNAL, 1);
198 curl_easy_setopt(mHandle, CURLOPT_WRITEFUNCTION, &curlOutputCallback);
199 curl_easy_setopt(mHandle, CURLOPT_WRITEDATA, &mOutput);
200#if 1 // For debug
201 curl_easy_setopt(mHandle, CURLOPT_HEADERFUNCTION, &curlOutputCallback);
202 curl_easy_setopt(mHandle, CURLOPT_HEADERDATA, &mHeaderOutput);
203#endif
204 curl_easy_setopt(mHandle, CURLOPT_ERRORBUFFER, &mErrorBuffer);
205 curl_easy_setopt(mHandle, CURLOPT_ENCODING, "");
206 curl_easy_setopt(mHandle, CURLOPT_SSL_VERIFYPEER, false);
207 curl_easy_setopt(mHandle, CURLOPT_HTTPHEADER, mHeaders);
208
209 mOutput.str("");
210 if (!((istream&)mOutput).good()) {
211 std::cerr << "WHAT!?!?!? istream side bad" << std::endl;
212 } 327 }
213 if (!((ostream&)mOutput).good()) { 328 if(!sCAFile.empty())
214 std::cerr << "WHAT!?!?!? ostream side bad" << std::endl; 329 {
330 setoptString(CURLOPT_CAINFO, sCAFile);
215 } 331 }
332}
216 333
217 mURL = url; 334void LLCurl::Easy::setHeaders()
218 curl_easy_setopt(mHandle, CURLOPT_URL, mURL.c_str()); 335{
336 setopt(CURLOPT_HTTPHEADER, mHeaders);
337}
219 338
220 mResponder = responder; 339void LLCurl::Easy::getTransferInfo(LLCurl::TransferInfo* info)
340{
341 curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SIZE_DOWNLOAD, &info->mSizeDownload);
342 curl_easy_getinfo(mCurlEasyHandle, CURLINFO_TOTAL_TIME, &info->mTotalTime);
343 curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SPEED_DOWNLOAD, &info->mSpeedDownload);
221} 344}
222 345
223void 346U32 LLCurl::Easy::report(CURLcode code)
224LLCurl::Easy::report(CURLcode code)
225{ 347{
226 if (!mResponder) return; 348 U32 responseCode = 0;
227 349 std::string responseReason;
228 long responseCode;
229 350
230 if (code == CURLE_OK) 351 if (code == CURLE_OK)
231 { 352 {
232 curl_easy_getinfo(mHandle, CURLINFO_RESPONSE_CODE, &responseCode); 353 curl_easy_getinfo(mCurlEasyHandle, CURLINFO_RESPONSE_CODE, &responseCode);
354 //*TODO: get reason from first line of mHeaderOutput
233 } 355 }
234 else 356 else
235 { 357 {
236 responseCode = 499; 358 responseCode = 499;
359 responseReason = strerror(code) + " : " + mErrorBuffer;
360 }
361
362 if (mResponder)
363 {
364 mResponder->completedRaw(responseCode, responseReason, mChannels, mOutput);
365 mResponder = NULL;
237 } 366 }
238 367
239 mResponder->completed(responseCode, mOutput); 368 resetState();
240 mResponder = NULL; 369 return responseCode;
241} 370}
242 371
372// Note: these all assume the caller tracks the value (i.e. keeps it persistant)
373void LLCurl::Easy::setopt(CURLoption option, S32 value)
374{
375 curl_easy_setopt(mCurlEasyHandle, option, value);
376}
243 377
378void LLCurl::Easy::setopt(CURLoption option, void* value)
379{
380 curl_easy_setopt(mCurlEasyHandle, option, value);
381}
244 382
383void LLCurl::Easy::setopt(CURLoption option, char* value)
384{
385 curl_easy_setopt(mCurlEasyHandle, option, value);
386}
245 387
388// Note: this copies the string so that the caller does not have to keep it around
389void LLCurl::Easy::setoptString(CURLoption option, const std::string& value)
390{
391 char* tstring = new char[value.length()+1];
392 strcpy(tstring, value.c_str());
393 mStrings.push_back(tstring);
394 curl_easy_setopt(mCurlEasyHandle, option, tstring);
395}
246 396
397void LLCurl::Easy::slist_append(const char* str)
398{
399 mHeaders = curl_slist_append(mHeaders, str);
400}
247 401
248LLCurl::Multi::Multi() 402size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data)
249{ 403{
250 mHandle = curl_multi_init(); 404 LLCurl::Easy* easy = (LLCurl::Easy*)user_data;
405
406 S32 n = size * nmemb;
407 S32 startpos = easy->getInput().tellg();
408 easy->getInput().seekg(0, std::ios::end);
409 S32 endpos = easy->getInput().tellg();
410 easy->getInput().seekg(startpos, std::ios::beg);
411 S32 maxn = endpos - startpos;
412 n = llmin(n, maxn);
413 easy->getInput().read((char*)data, n);
414
415 return n;
251} 416}
252 417
253LLCurl::Multi::~Multi() 418size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data)
254{ 419{
255 // FIXME: should clean up excess handles in mFreeEasy 420 LLCurl::Easy* easy = (LLCurl::Easy*)user_data;
256 curl_multi_cleanup(mHandle); 421
422 S32 n = size * nmemb;
423 easy->getOutput()->append(easy->getChannels().in(), (const U8*)data, n);
424
425 return n;
257} 426}
258 427
428size_t curlHeaderCallback(void* data, size_t size, size_t nmemb, void* user_data)
429{
430 LLCurl::Easy* easy = (LLCurl::Easy*)user_data;
431
432 size_t n = size * nmemb;
433 easy->getHeaderOutput().write((const char*)data, n);
259 434
260void 435 return n;
261LLCurl::Multi::get(const std::string& url, ResponderPtr responder) 436}
437
438void LLCurl::Easy::prepRequest(const std::string& url, ResponderPtr responder, bool post)
262{ 439{
263 LLCurl::Easy* easy = easyAlloc(); 440 resetState();
264 easy->get(url, responder); 441
265 curl_multi_add_handle(mHandle, easy->mHandle); 442 if (post) setoptString(CURLOPT_ENCODING, "");
443
444// setopt(CURLOPT_VERBOSE, 1); // usefull for debugging
445 setopt(CURLOPT_NOSIGNAL, 1);
446
447 mOutput.reset(new LLBufferArray);
448 setopt(CURLOPT_WRITEFUNCTION, (void*)&curlWriteCallback);
449 setopt(CURLOPT_WRITEDATA, (void*)this);
450
451 setopt(CURLOPT_READFUNCTION, (void*)&curlReadCallback);
452 setopt(CURLOPT_READDATA, (void*)this);
453
454 setopt(CURLOPT_HEADERFUNCTION, (void*)&curlHeaderCallback);
455 setopt(CURLOPT_HEADERDATA, (void*)this);
456
457 setErrorBuffer();
458 setCA();
459
460 setopt(CURLOPT_SSL_VERIFYPEER, true);
461 setopt(CURLOPT_TIMEOUT, CURL_REQUEST_TIMEOUT);
462
463 setoptString(CURLOPT_URL, url);
464
465 mResponder = responder;
466
467 if (!post)
468 {
469 slist_append("Connection: keep-alive");
470 slist_append("Keep-alive: 300");
471 }
472 // *FIX: should have ACCEPT headers
266} 473}
474
475////////////////////////////////////////////////////////////////////////////
476
477class LLCurl::Multi
478{
479 LOG_CLASS(Multi);
480public:
481
482 Multi();
483 ~Multi();
484
485 Easy* allocEasy();
486 bool addEasy(Easy* easy);
267 487
268void 488 void removeEasy(Easy* easy);
269LLCurl::Multi::getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder) 489
490 S32 process();
491 S32 perform();
492
493 CURLMsg* info_read(S32* msgs_in_queue);
494
495 S32 mQueued;
496 S32 mErrorCount;
497
498private:
499 void easyFree(Easy*);
500
501 CURLM* mCurlMultiHandle;
502
503 typedef std::set<Easy*> easy_active_list_t;
504 easy_active_list_t mEasyActiveList;
505 typedef std::map<CURL*, Easy*> easy_active_map_t;
506 easy_active_map_t mEasyActiveMap;
507 typedef std::set<Easy*> easy_free_list_t;
508 easy_free_list_t mEasyFreeList;
509};
510
511LLCurl::Multi::Multi()
512 : mQueued(0),
513 mErrorCount(0)
270{ 514{
271 LLCurl::Easy* easy = easyAlloc(); 515 mCurlMultiHandle = curl_multi_init();
272 easy->getByteRange(url, offset, length, responder); 516 if (!mCurlMultiHandle)
273 curl_multi_add_handle(mHandle, easy->mHandle); 517 {
518 llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl;
519 mCurlMultiHandle = curl_multi_init();
520 }
521 llassert_always(mCurlMultiHandle);
522 ++gCurlMultiCount;
274} 523}
524
525LLCurl::Multi::~Multi()
526{
527 // Clean up active
528 for(easy_active_list_t::iterator iter = mEasyActiveList.begin();
529 iter != mEasyActiveList.end(); ++iter)
530 {
531 Easy* easy = *iter;
532 curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle());
533 delete easy;
534 }
535 mEasyActiveList.clear();
536 mEasyActiveMap.clear();
275 537
276void 538 // Clean up freed
277LLCurl::Multi::process() 539 for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer());
540 mEasyFreeList.clear();
541
542 curl_multi_cleanup(mCurlMultiHandle);
543 --gCurlMultiCount;
544}
545
546CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue)
278{ 547{
279 int count; 548 CURLMsg* curlmsg = curl_multi_info_read(mCurlMultiHandle, msgs_in_queue);
280 for (int call_count = 0; call_count < 5; call_count += 1) 549 return curlmsg;
550}
551
552
553S32 LLCurl::Multi::perform()
554{
555 S32 q = 0;
556 for (S32 call_count = 0;
557 call_count < MULTI_PERFORM_CALL_REPEAT;
558 call_count += 1)
281 { 559 {
282 if (CURLM_CALL_MULTI_PERFORM != curl_multi_perform(mHandle, &count)) 560 CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q);
561 if (CURLM_CALL_MULTI_PERFORM != code || q == 0)
283 { 562 {
284 break; 563 break;
285 } 564 }
286 } 565 }
287 566 mQueued = q;
567 return q;
568}
569
570S32 LLCurl::Multi::process()
571{
572 perform();
573
288 CURLMsg* msg; 574 CURLMsg* msg;
289 int msgs_in_queue; 575 int msgs_in_queue;
290 while ((msg = curl_multi_info_read(mHandle, &msgs_in_queue))) 576
577 S32 processed = 0;
578 while ((msg = info_read(&msgs_in_queue)))
291 { 579 {
292 if (msg->msg != CURLMSG_DONE) continue; 580 ++processed;
293 Easy* easy = 0; 581 if (msg->msg == CURLMSG_DONE)
294 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &easy); 582 {
295 if (!easy) continue; 583 U32 response = 0;
296 easy->report(msg->data.result); 584 easy_active_map_t::iterator iter = mEasyActiveMap.find(msg->easy_handle);
297 585 if (iter != mEasyActiveMap.end())
298 curl_multi_remove_handle(mHandle, easy->mHandle); 586 {
299 easyFree(easy); 587 Easy* easy = iter->second;
588 response = easy->report(msg->data.result);
589 removeEasy(easy);
590 }
591 else
592 {
593 response = 499;
594 //*TODO: change to llwarns
595 llerrs << "cleaned up curl request completed!" << llendl;
596 }
597 if (response >= 400)
598 {
599 // failure of some sort, inc mErrorCount for debugging and flagging multi for destruction
600 ++mErrorCount;
601 }
602 }
300 } 603 }
604 return processed;
301} 605}
302 606
303 607LLCurl::Easy* LLCurl::Multi::allocEasy()
304
305LLCurl::Easy*
306LLCurl::Multi::easyAlloc()
307{ 608{
308 Easy* easy = 0; 609 Easy* easy = 0;
309 610
310 if (mFreeEasy.empty()) 611 if (mEasyFreeList.empty())
311 { 612 {
312 easy = new Easy(); 613 easy = Easy::getEasy();
313 } 614 }
314 else 615 else
315 { 616 {
316 easy = mFreeEasy.back(); 617 easy = *(mEasyFreeList.begin());
317 mFreeEasy.pop_back(); 618 mEasyFreeList.erase(easy);
619 }
620 if (easy)
621 {
622 mEasyActiveList.insert(easy);
623 mEasyActiveMap[easy->getCurlHandle()] = easy;
318 } 624 }
319
320 return easy; 625 return easy;
321} 626}
322 627
323void 628bool LLCurl::Multi::addEasy(Easy* easy)
324LLCurl::Multi::easyFree(Easy* easy)
325{ 629{
326 if (mFreeEasy.size() < 5) 630 CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle());
631 if (mcode != CURLM_OK)
327 { 632 {
328 mFreeEasy.push_back(easy); 633 llwarns << "Curl Error: " << curl_multi_strerror(mcode) << llendl;
634 return false;
635 }
636 return true;
637}
638
639void LLCurl::Multi::easyFree(Easy* easy)
640{
641 mEasyActiveList.erase(easy);
642 mEasyActiveMap.erase(easy->getCurlHandle());
643 if (mEasyFreeList.size() < EASY_HANDLE_POOL_SIZE)
644 {
645 easy->resetState();
646 mEasyFreeList.insert(easy);
329 } 647 }
330 else 648 else
331 { 649 {
@@ -333,53 +651,371 @@ LLCurl::Multi::easyFree(Easy* easy)
333 } 651 }
334} 652}
335 653
654void LLCurl::Multi::removeEasy(Easy* easy)
655{
656 curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle());
657 easyFree(easy);
658}
659
660//static
661std::string LLCurl::strerror(CURLcode errorcode)
662{
663#if LL_DARWIN
664 // 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...
665 // There's a problem with the custom curl headers in our build that keeps me from #ifdefing this on the libcurl version number
666 // (the correct check would be #if LIBCURL_VERSION_NUM >= 0x070c00). We'll fix the header problem soon, but for now
667 // just punt and print the numeric error code on the Mac.
668 return llformat("%d", errorcode);
669#else // LL_DARWIN
670 return std::string(curl_easy_strerror(errorcode));
671#endif // LL_DARWIN
672}
673
674////////////////////////////////////////////////////////////////////////////
675// For generating a simple request for data
676// using one multi and one easy per request
677
678LLCurlRequest::LLCurlRequest()
679 : mActiveMulti(NULL)
680{
681}
682
683LLCurlRequest::~LLCurlRequest()
684{
685 for_each(mMultiSet.begin(), mMultiSet.end(), DeletePointer());
686}
687
688void LLCurlRequest::addMulti()
689{
690 LLCurl::Multi* multi = new LLCurl::Multi();
691 mMultiSet.insert(multi);
692 mActiveMulti = multi;
693 mActiveRequestCount = 0;
694}
695
696LLCurl::Easy* LLCurlRequest::allocEasy()
697{
698 if (!mActiveMulti ||
699 mActiveRequestCount >= MAX_ACTIVE_REQUEST_COUNT ||
700 mActiveMulti->mErrorCount > 0)
701 {
702 addMulti();
703 }
704 llassert_always(mActiveMulti);
705 ++mActiveRequestCount;
706 LLCurl::Easy* easy = mActiveMulti->allocEasy();
707 return easy;
708}
709
710bool LLCurlRequest::addEasy(LLCurl::Easy* easy)
711{
712 llassert_always(mActiveMulti);
713 bool res = mActiveMulti->addEasy(easy);
714 return res;
715}
336 716
717void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder)
718{
719 getByteRange(url, 0, -1, responder);
720}
721
722bool LLCurlRequest::getByteRange(const std::string& url, S32 offset, S32 length, LLCurl::ResponderPtr responder)
723{
724 LLCurl::Easy* easy = allocEasy();
725 if (!easy)
726 {
727 return false;
728 }
729 easy->prepRequest(url, responder);
730 easy->setopt(CURLOPT_HTTPGET, 1);
731 if (length > 0)
732 {
733 std::string range = llformat("Range: bytes=%d-%d", offset,offset+length-1);
734 easy->slist_append(range.c_str());
735 }
736 easy->setHeaders();
737 bool res = addEasy(easy);
738 return res;
739}
337 740
338namespace 741bool LLCurlRequest::post(const std::string& url, const LLSD& data, LLCurl::ResponderPtr responder)
339{ 742{
340 static LLCurl::Multi* sMainMulti = 0; 743 LLCurl::Easy* easy = allocEasy();
744 if (!easy)
745 {
746 return false;
747 }
748 easy->prepRequest(url, responder);
749
750 LLSDSerialize::toXML(data, easy->getInput());
751 S32 bytes = easy->getInput().str().length();
752
753 easy->setopt(CURLOPT_POST, 1);
754 easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL);
755 easy->setopt(CURLOPT_POSTFIELDSIZE, bytes);
756
757 easy->slist_append("Content-Type: application/xml");
758 easy->setHeaders();
759
760 lldebugs << "POSTING: " << bytes << " bytes." << llendl;
761 bool res = addEasy(easy);
762 return res;
763}
341 764
342 LLCurl::Multi* 765// Note: call once per frame
343 mainMulti() 766S32 LLCurlRequest::process()
767{
768 S32 res = 0;
769 for (curlmulti_set_t::iterator iter = mMultiSet.begin();
770 iter != mMultiSet.end(); )
344 { 771 {
345 if (!sMainMulti) { 772 curlmulti_set_t::iterator curiter = iter++;
346 sMainMulti = new LLCurl::Multi(); 773 LLCurl::Multi* multi = *curiter;
774 S32 tres = multi->process();
775 res += tres;
776 if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0)
777 {
778 mMultiSet.erase(curiter);
779 delete multi;
347 } 780 }
348 return sMainMulti;
349 } 781 }
782 return res;
783}
350 784
351 void freeMulti() 785S32 LLCurlRequest::getQueued()
786{
787 S32 queued = 0;
788 for (curlmulti_set_t::iterator iter = mMultiSet.begin();
789 iter != mMultiSet.end(); )
352 { 790 {
353 delete sMainMulti; 791 curlmulti_set_t::iterator curiter = iter++;
354 sMainMulti = NULL; 792 LLCurl::Multi* multi = *curiter;
793 queued += multi->mQueued;
355 } 794 }
795 return queued;
356} 796}
357 797
358void 798////////////////////////////////////////////////////////////////////////////
359LLCurl::get(const std::string& url, ResponderPtr responder) 799// For generating one easy request
800// associated with a single multi request
801
802LLCurlEasyRequest::LLCurlEasyRequest()
803 : mRequestSent(false),
804 mResultReturned(false)
360{ 805{
361 mainMulti()->get(url, responder); 806 mMulti = new LLCurl::Multi();
807 mEasy = mMulti->allocEasy();
808 if (mEasy)
809 {
810 mEasy->setErrorBuffer();
811 mEasy->setCA();
812 }
362} 813}
363 814
364void 815LLCurlEasyRequest::~LLCurlEasyRequest()
365LLCurl::getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder)
366{ 816{
367 mainMulti()->getByteRange(url, offset, length, responder); 817 delete mMulti;
368} 818}
369 819
370void LLCurl::initClass() 820void LLCurlEasyRequest::setopt(CURLoption option, S32 value)
371{ 821{
372 curl_global_init(CURL_GLOBAL_ALL); 822 if (mEasy)
823 {
824 mEasy->setopt(option, value);
825 }
373} 826}
374 827
375void 828void LLCurlEasyRequest::setoptString(CURLoption option, const std::string& value)
376LLCurl::process()
377{ 829{
378 mainMulti()->process(); 830 if (mEasy)
831 {
832 mEasy->setoptString(option, value);
833 }
379} 834}
380 835
381void LLCurl::cleanup() 836void LLCurlEasyRequest::setPost(char* postdata, S32 size)
382{ 837{
383 freeMulti(); 838 if (mEasy)
839 {
840 mEasy->setopt(CURLOPT_POST, 1);
841 mEasy->setopt(CURLOPT_POSTFIELDS, postdata);
842 mEasy->setopt(CURLOPT_POSTFIELDSIZE, size);
843 }
844}
845
846void LLCurlEasyRequest::setHeaderCallback(curl_header_callback callback, void* userdata)
847{
848 if (mEasy)
849 {
850 mEasy->setopt(CURLOPT_HEADERFUNCTION, (void*)callback);
851 mEasy->setopt(CURLOPT_HEADERDATA, userdata); // aka CURLOPT_WRITEHEADER
852 }
853}
854
855void LLCurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userdata)
856{
857 if (mEasy)
858 {
859 mEasy->setopt(CURLOPT_WRITEFUNCTION, (void*)callback);
860 mEasy->setopt(CURLOPT_WRITEDATA, userdata);
861 }
862}
863
864void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata)
865{
866 if (mEasy)
867 {
868 mEasy->setopt(CURLOPT_READFUNCTION, (void*)callback);
869 mEasy->setopt(CURLOPT_READDATA, userdata);
870 }
871}
872
873void LLCurlEasyRequest::slist_append(const char* str)
874{
875 if (mEasy)
876 {
877 mEasy->slist_append(str);
878 }
879}
880
881void LLCurlEasyRequest::sendRequest(const std::string& url)
882{
883 llassert_always(!mRequestSent);
884 mRequestSent = true;
885 if (mEasy)
886 {
887 mEasy->setHeaders();
888 mEasy->setoptString(CURLOPT_URL, url);
889 mMulti->addEasy(mEasy);
890 }
891}
892
893void LLCurlEasyRequest::requestComplete()
894{
895 llassert_always(mRequestSent);
896 mRequestSent = false;
897 if (mEasy)
898 {
899 mMulti->removeEasy(mEasy);
900 }
901}
902
903S32 LLCurlEasyRequest::perform()
904{
905 return mMulti->perform();
906}
907
908// Usage: Call getRestult until it returns false (no more messages)
909bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info)
910{
911 if (!mEasy)
912 {
913 // Special case - we failed to initialize a curl_easy (can happen if too many open files)
914 // Act as though the request failed to connect
915 if (mResultReturned)
916 {
917 return false;
918 }
919 else
920 {
921 *result = CURLE_FAILED_INIT;
922 mResultReturned = true;
923 return true;
924 }
925 }
926 // In theory, info_read might return a message with a status other than CURLMSG_DONE
927 // In practice for all messages returned, msg == CURLMSG_DONE
928 // Ignore other messages just in case
929 while(1)
930 {
931 S32 q;
932 CURLMsg* curlmsg = info_read(&q, info);
933 if (curlmsg)
934 {
935 if (curlmsg->msg == CURLMSG_DONE)
936 {
937 *result = curlmsg->data.result;
938 return true;
939 }
940 // else continue
941 }
942 else
943 {
944 return false;
945 }
946 }
947}
948
949// private
950CURLMsg* LLCurlEasyRequest::info_read(S32* q, LLCurl::TransferInfo* info)
951{
952 if (mEasy)
953 {
954 CURLMsg* curlmsg = mMulti->info_read(q);
955 if (curlmsg && curlmsg->msg == CURLMSG_DONE)
956 {
957 if (info)
958 {
959 mEasy->getTransferInfo(info);
960 }
961 }
962 return curlmsg;
963 }
964 return NULL;
965}
966
967std::string LLCurlEasyRequest::getErrorString()
968{
969 return mEasy ? std::string(mEasy->getErrorBuffer()) : std::string();
970}
971
972////////////////////////////////////////////////////////////////////////////
973
974#if SAFE_SSL
975//static
976void LLCurl::ssl_locking_callback(int mode, int type, const char *file, int line)
977{
978 if (mode & CRYPTO_LOCK)
979 {
980 LLCurl::sSSLMutex[type]->lock();
981 }
982 else
983 {
984 LLCurl::sSSLMutex[type]->unlock();
985 }
986}
987
988//static
989unsigned long LLCurl::ssl_thread_id(void)
990{
991 return LLThread::currentID();
992}
993#endif
994
995void LLCurl::initClass()
996{
997 // Do not change this "unless you are familiar with and mean to control
998 // internal operations of libcurl"
999 // - http://curl.haxx.se/libcurl/c/curl_global_init.html
1000 curl_global_init(CURL_GLOBAL_ALL);
1001
1002#if SAFE_SSL
1003 S32 mutex_count = CRYPTO_num_locks();
1004 for (S32 i=0; i<mutex_count; i++)
1005 {
1006 sSSLMutex.push_back(new LLMutex(gAPRPoolp));
1007 }
1008 CRYPTO_set_id_callback(&LLCurl::ssl_thread_id);
1009 CRYPTO_set_locking_callback(&LLCurl::ssl_locking_callback);
1010#endif
1011}
1012
1013void LLCurl::cleanupClass()
1014{
1015#if SAFE_SSL
1016 CRYPTO_set_locking_callback(NULL);
1017 for_each(sSSLMutex.begin(), sSSLMutex.end(), DeletePointer());
1018#endif
384 curl_global_cleanup(); 1019 curl_global_cleanup();
385} 1020}
1021