diff options
author | Jacek Antonelli | 2008-08-15 23:45:34 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:45:34 -0500 |
commit | cd17687f01420952712a500107e0f93e7ab8d5f8 (patch) | |
tree | ce48c2b706f2c1176290e39fb555fbdf6648ce01 /linden/indra/llmessage/llcurl.cpp | |
parent | Second Life viewer sources 1.19.0.5 (diff) | |
download | meta-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.cpp | 962 |
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 | ||
58 | using namespace std; | 74 | ////////////////////////////////////////////////////////////////////////////// |
59 | 75 | ||
76 | static const S32 EASY_HANDLE_POOL_SIZE = 5; | ||
77 | static const S32 MULTI_PERFORM_CALL_REPEAT = 5; | ||
78 | static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds | ||
79 | static const S32 MAX_ACTIVE_REQUEST_COUNT = 100; | ||
80 | |||
81 | // DEBUG // | ||
82 | S32 gCurlEasyCount = 0; | ||
83 | S32 gCurlMultiCount = 0; | ||
84 | |||
85 | ////////////////////////////////////////////////////////////////////////////// | ||
86 | |||
87 | //static | ||
88 | std::vector<LLMutex*> LLCurl::sSSLMutex; | ||
89 | std::string LLCurl::sCAPath; | ||
90 | std::string LLCurl::sCAFile; | ||
91 | |||
92 | //static | ||
93 | void LLCurl::setCAPath(const std::string& path) | ||
94 | { | ||
95 | sCAPath = path; | ||
96 | } | ||
97 | |||
98 | //static | ||
99 | void LLCurl::setCAFile(const std::string& file) | ||
100 | { | ||
101 | sCAFile = file; | ||
102 | } | ||
103 | |||
104 | ////////////////////////////////////////////////////////////////////////////// | ||
105 | |||
60 | LLCurl::Responder::Responder() | 106 | LLCurl::Responder::Responder() |
61 | : mReferenceCount(0) | 107 | : mReferenceCount(0) |
62 | { | 108 | { |
63 | } | 109 | } |
110 | |||
64 | LLCurl::Responder::~Responder() | 111 | LLCurl::Responder::~Responder() |
65 | { | 112 | { |
66 | } | 113 | } |
67 | 114 | ||
68 | // virtual | 115 | // virtual |
69 | void LLCurl::Responder::error(U32 status, const std::stringstream& content) | 116 | void 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 |
75 | void LLCurl::Responder::result(const std::stringstream& content) | 122 | void LLCurl::Responder::result(const LLSD& content) |
76 | { | 123 | { |
124 | llwarns << "Virtual Function not implemented" << llendl; | ||
77 | } | 125 | } |
78 | 126 | ||
79 | // virtual | 127 | // virtual |
80 | void LLCurl::Responder::completed(U32 status, const std::stringstream& content) | 128 | void 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 | ||
163 | void 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 | ||
176 | void LLCurl::Responder::completedHeader(U32 status, const std::string& reason, const LLSD& content) | ||
177 | { | ||
178 | |||
179 | } | ||
92 | 180 | ||
93 | namespace boost | 181 | namespace boost |
94 | { | 182 | { |
@@ -106,226 +194,456 @@ namespace boost | |||
106 | } | 194 | } |
107 | }; | 195 | }; |
108 | 196 | ||
197 | |||
109 | ////////////////////////////////////////////////////////////////////////////// | 198 | ////////////////////////////////////////////////////////////////////////////// |
110 | 199 | ||
111 | size_t | 200 | |
112 | curlOutputCallback(void* data, size_t size, size_t nmemb, void* user_data) | 201 | class LLCurl::Easy |
113 | { | 202 | { |
114 | stringstream& output = *(stringstream*)user_data; | 203 | LOG_CLASS(Easy); |
204 | |||
205 | private: | ||
206 | Easy(); | ||
115 | 207 | ||
116 | size_t n = size * nmemb; | 208 | public: |
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 | ||
241 | private: | ||
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 | ||
143 | LLCurl::Easy::Easy() | 258 | LLCurl::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"); | 265 | LLCurl::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 | ||
155 | LLCurl::Easy::~Easy() | 280 | LLCurl::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 | ||
161 | void | 288 | void LLCurl::Easy::resetState() |
162 | LLCurl::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 | ||
168 | void | 312 | void LLCurl::Easy::setErrorBuffer() |
169 | LLCurl::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 | ||
177 | void | 317 | const char* LLCurl::Easy::getErrorBuffer() |
178 | LLCurl::Easy::perform() | ||
179 | { | 318 | { |
180 | report(curl_easy_perform(mHandle)); | 319 | return mErrorBuffer; |
181 | } | 320 | } |
182 | 321 | ||
183 | void | 322 | void LLCurl::Easy::setCA() |
184 | LLCurl::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; | 334 | void 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; | 339 | void 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 | ||
223 | void | 346 | U32 LLCurl::Easy::report(CURLcode code) |
224 | LLCurl::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) | ||
373 | void LLCurl::Easy::setopt(CURLoption option, S32 value) | ||
374 | { | ||
375 | curl_easy_setopt(mCurlEasyHandle, option, value); | ||
376 | } | ||
243 | 377 | ||
378 | void LLCurl::Easy::setopt(CURLoption option, void* value) | ||
379 | { | ||
380 | curl_easy_setopt(mCurlEasyHandle, option, value); | ||
381 | } | ||
244 | 382 | ||
383 | void 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 | ||
389 | void 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 | ||
397 | void LLCurl::Easy::slist_append(const char* str) | ||
398 | { | ||
399 | mHeaders = curl_slist_append(mHeaders, str); | ||
400 | } | ||
247 | 401 | ||
248 | LLCurl::Multi::Multi() | 402 | size_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 | ||
253 | LLCurl::Multi::~Multi() | 418 | size_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 | ||
428 | size_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 | ||
260 | void | 435 | return n; |
261 | LLCurl::Multi::get(const std::string& url, ResponderPtr responder) | 436 | } |
437 | |||
438 | void 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 | |||
477 | class LLCurl::Multi | ||
478 | { | ||
479 | LOG_CLASS(Multi); | ||
480 | public: | ||
481 | |||
482 | Multi(); | ||
483 | ~Multi(); | ||
484 | |||
485 | Easy* allocEasy(); | ||
486 | bool addEasy(Easy* easy); | ||
267 | 487 | ||
268 | void | 488 | void removeEasy(Easy* easy); |
269 | LLCurl::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 | |||
498 | private: | ||
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 | |||
511 | LLCurl::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 | |||
525 | LLCurl::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 | ||
276 | void | 538 | // Clean up freed |
277 | LLCurl::Multi::process() | 539 | for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer()); |
540 | mEasyFreeList.clear(); | ||
541 | |||
542 | curl_multi_cleanup(mCurlMultiHandle); | ||
543 | --gCurlMultiCount; | ||
544 | } | ||
545 | |||
546 | CURLMsg* 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 | |||
553 | S32 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 | |||
570 | S32 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 | 607 | LLCurl::Easy* LLCurl::Multi::allocEasy() | |
304 | |||
305 | LLCurl::Easy* | ||
306 | LLCurl::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 | ||
323 | void | 628 | bool LLCurl::Multi::addEasy(Easy* easy) |
324 | LLCurl::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 | |||
639 | void 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 | ||
654 | void LLCurl::Multi::removeEasy(Easy* easy) | ||
655 | { | ||
656 | curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle()); | ||
657 | easyFree(easy); | ||
658 | } | ||
659 | |||
660 | //static | ||
661 | std::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 | |||
678 | LLCurlRequest::LLCurlRequest() | ||
679 | : mActiveMulti(NULL) | ||
680 | { | ||
681 | } | ||
682 | |||
683 | LLCurlRequest::~LLCurlRequest() | ||
684 | { | ||
685 | for_each(mMultiSet.begin(), mMultiSet.end(), DeletePointer()); | ||
686 | } | ||
687 | |||
688 | void LLCurlRequest::addMulti() | ||
689 | { | ||
690 | LLCurl::Multi* multi = new LLCurl::Multi(); | ||
691 | mMultiSet.insert(multi); | ||
692 | mActiveMulti = multi; | ||
693 | mActiveRequestCount = 0; | ||
694 | } | ||
695 | |||
696 | LLCurl::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 | |||
710 | bool LLCurlRequest::addEasy(LLCurl::Easy* easy) | ||
711 | { | ||
712 | llassert_always(mActiveMulti); | ||
713 | bool res = mActiveMulti->addEasy(easy); | ||
714 | return res; | ||
715 | } | ||
336 | 716 | ||
717 | void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) | ||
718 | { | ||
719 | getByteRange(url, 0, -1, responder); | ||
720 | } | ||
721 | |||
722 | bool 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 | ||
338 | namespace | 741 | bool 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() | 766 | S32 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() | 785 | S32 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 | ||
358 | void | 798 | //////////////////////////////////////////////////////////////////////////// |
359 | LLCurl::get(const std::string& url, ResponderPtr responder) | 799 | // For generating one easy request |
800 | // associated with a single multi request | ||
801 | |||
802 | LLCurlEasyRequest::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 | ||
364 | void | 815 | LLCurlEasyRequest::~LLCurlEasyRequest() |
365 | LLCurl::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 | ||
370 | void LLCurl::initClass() | 820 | void 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 | ||
375 | void | 828 | void LLCurlEasyRequest::setoptString(CURLoption option, const std::string& value) |
376 | LLCurl::process() | ||
377 | { | 829 | { |
378 | mainMulti()->process(); | 830 | if (mEasy) |
831 | { | ||
832 | mEasy->setoptString(option, value); | ||
833 | } | ||
379 | } | 834 | } |
380 | 835 | ||
381 | void LLCurl::cleanup() | 836 | void 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 | |||
846 | void 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 | |||
855 | void 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 | |||
864 | void 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 | |||
873 | void LLCurlEasyRequest::slist_append(const char* str) | ||
874 | { | ||
875 | if (mEasy) | ||
876 | { | ||
877 | mEasy->slist_append(str); | ||
878 | } | ||
879 | } | ||
880 | |||
881 | void 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 | |||
893 | void LLCurlEasyRequest::requestComplete() | ||
894 | { | ||
895 | llassert_always(mRequestSent); | ||
896 | mRequestSent = false; | ||
897 | if (mEasy) | ||
898 | { | ||
899 | mMulti->removeEasy(mEasy); | ||
900 | } | ||
901 | } | ||
902 | |||
903 | S32 LLCurlEasyRequest::perform() | ||
904 | { | ||
905 | return mMulti->perform(); | ||
906 | } | ||
907 | |||
908 | // Usage: Call getRestult until it returns false (no more messages) | ||
909 | bool 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 | ||
950 | CURLMsg* 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 | |||
967 | std::string LLCurlEasyRequest::getErrorString() | ||
968 | { | ||
969 | return mEasy ? std::string(mEasy->getErrorBuffer()) : std::string(); | ||
970 | } | ||
971 | |||
972 | //////////////////////////////////////////////////////////////////////////// | ||
973 | |||
974 | #if SAFE_SSL | ||
975 | //static | ||
976 | void 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 | ||
989 | unsigned long LLCurl::ssl_thread_id(void) | ||
990 | { | ||
991 | return LLThread::currentID(); | ||
992 | } | ||
993 | #endif | ||
994 | |||
995 | void 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 | |||
1013 | void 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 | |||