aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmessage/llurlrequest.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llmessage/llurlrequest.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llmessage/llurlrequest.cpp')
-rw-r--r--linden/indra/llmessage/llurlrequest.cpp669
1 files changed, 669 insertions, 0 deletions
diff --git a/linden/indra/llmessage/llurlrequest.cpp b/linden/indra/llmessage/llurlrequest.cpp
new file mode 100644
index 0000000..c0d8d27
--- /dev/null
+++ b/linden/indra/llmessage/llurlrequest.cpp
@@ -0,0 +1,669 @@
1/**
2 * @file llurlrequest.cpp
3 * @author Phoenix
4 * @date 2005-04-28
5 * @brief Implementation of the URLRequest class and related classes.
6 *
7 * Copyright (c) 2005-2007, Linden Research, Inc.
8 *
9 * The source code in this file ("Source Code") is provided by Linden Lab
10 * to you under the terms of the GNU General Public License, version 2.0
11 * ("GPL"), unless you have obtained a separate licensing agreement
12 * ("Other License"), formally executed by you and Linden Lab. Terms of
13 * the GPL can be found in doc/GPL-license.txt in this distribution, or
14 * online at http://secondlife.com/developers/opensource/gplv2
15 *
16 * There are special exceptions to the terms and conditions of the GPL as
17 * it is applied to this Source Code. View the full text of the exception
18 * in the file doc/FLOSS-exception.txt in this software distribution, or
19 * online at http://secondlife.com/developers/opensource/flossexception
20 *
21 * By copying, modifying or distributing this software, you acknowledge
22 * that you have read and understood your obligations described above,
23 * and agree to abide by those obligations.
24 *
25 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
26 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
27 * COMPLETENESS OR PERFORMANCE.
28 */
29
30#include "linden_common.h"
31#include "llurlrequest.h"
32
33#include <curl/curl.h>
34#include <algorithm>
35
36#include "llioutil.h"
37#include "llmemtype.h"
38#include "llpumpio.h"
39#include "llsd.h"
40#include "llstring.h"
41
42static const U32 HTTP_STATUS_PIPE_ERROR = 499;
43
44/**
45 * String constants
46 */
47const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri");
48
49
50static
51size_t headerCallback(void* data, size_t size, size_t nmemb, void* user);
52
53/**
54 * class LLURLRequestDetail
55 */
56class LLURLRequestDetail
57{
58public:
59 LLURLRequestDetail();
60 ~LLURLRequestDetail();
61 CURLM* mCurlMulti;
62 CURL* mCurl;
63 struct curl_slist* mHeaders;
64 char* mURL;
65 char mCurlErrorBuf[CURL_ERROR_SIZE + 1]; /* Flawfinder: ignore */
66 bool mNeedToRemoveEasyHandle;
67 LLBufferArray* mResponseBuffer;
68 LLChannelDescriptors mChannels;
69 U8* mLastRead;
70 U32 mBodyLimit;
71 bool mIsBodyLimitSet;
72};
73
74LLURLRequestDetail::LLURLRequestDetail() :
75 mCurlMulti(NULL),
76 mCurl(NULL),
77 mHeaders(NULL),
78 mURL(NULL),
79 mNeedToRemoveEasyHandle(false),
80 mResponseBuffer(NULL),
81 mLastRead(NULL),
82 mBodyLimit(0),
83 mIsBodyLimitSet(false)
84
85{
86 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
87 mCurlErrorBuf[0] = '\0';
88}
89
90LLURLRequestDetail::~LLURLRequestDetail()
91{
92 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
93 if(mCurl)
94 {
95 if(mNeedToRemoveEasyHandle && mCurlMulti)
96 {
97 curl_multi_remove_handle(mCurlMulti, mCurl);
98 mNeedToRemoveEasyHandle = false;
99 }
100 curl_easy_cleanup(mCurl);
101 mCurl = NULL;
102 }
103 if(mCurlMulti)
104 {
105 curl_multi_cleanup(mCurlMulti);
106 mCurlMulti = NULL;
107 }
108 if(mHeaders)
109 {
110 curl_slist_free_all(mHeaders);
111 mHeaders = NULL;
112 }
113 delete[] mURL;
114 mURL = NULL;
115 mResponseBuffer = NULL;
116 mLastRead = NULL;
117}
118
119
120/**
121 * class LLURLRequest
122 */
123
124static std::string sCAFile("");
125static std::string sCAPath("");
126
127LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :
128 mAction(action)
129{
130 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
131 initialize();
132}
133
134LLURLRequest::LLURLRequest(
135 LLURLRequest::ERequestAction action,
136 const std::string& url) :
137 mAction(action)
138{
139 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
140 initialize();
141 setURL(url);
142}
143
144LLURLRequest::~LLURLRequest()
145{
146 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
147 delete mDetail;
148}
149
150void LLURLRequest::setURL(const std::string& url)
151{
152 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
153 if(mDetail->mURL)
154 {
155 // *NOTE: if any calls to set the url have been made to curl,
156 // this will probably lead to a crash.
157 delete[] mDetail->mURL;
158 mDetail->mURL = NULL;
159 }
160 if(!url.empty())
161 {
162 mDetail->mURL = new char[url.size() + 1];
163 url.copy(mDetail->mURL, url.size());
164 mDetail->mURL[url.size()] = '\0';
165 }
166}
167
168void LLURLRequest::addHeader(const char* header)
169{
170 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
171 mDetail->mHeaders = curl_slist_append(mDetail->mHeaders, header);
172}
173
174void LLURLRequest::requestEncoding(const char* encoding)
175{
176 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
177 curl_easy_setopt(mDetail->mCurl, CURLOPT_ENCODING, encoding);
178}
179
180void LLURLRequest::setBodyLimit(U32 size)
181{
182 mDetail->mBodyLimit = size;
183 mDetail->mIsBodyLimitSet = true;
184}
185
186void LLURLRequest::checkRootCertificate(bool check, const char* caBundle)
187{
188 curl_easy_setopt(mDetail->mCurl, CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE));
189 if (caBundle)
190 {
191 curl_easy_setopt(mDetail->mCurl, CURLOPT_CAINFO, caBundle);
192 }
193}
194
195void LLURLRequest::setCallback(LLURLRequestComplete* callback)
196{
197 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
198 mCompletionCallback = callback;
199
200 curl_easy_setopt(mDetail->mCurl, CURLOPT_HEADERFUNCTION, &headerCallback);
201 curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEHEADER, callback);
202}
203
204// virtual
205LLIOPipe::EStatus LLURLRequest::handleError(
206 LLIOPipe::EStatus status,
207 LLPumpIO* pump)
208{
209 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
210 if(mCompletionCallback && pump)
211 {
212 LLURLRequestComplete* complete = NULL;
213 complete = (LLURLRequestComplete*)mCompletionCallback.get();
214 complete->httpStatus(
215 HTTP_STATUS_PIPE_ERROR,
216 LLIOPipe::lookupStatusString(status));
217 complete->responseStatus(status);
218 pump->respond(complete);
219 mCompletionCallback = NULL;
220 }
221 return status;
222}
223
224// virtual
225LLIOPipe::EStatus LLURLRequest::process_impl(
226 const LLChannelDescriptors& channels,
227 buffer_ptr_t& buffer,
228 bool& eos,
229 LLSD& context,
230 LLPumpIO* pump)
231{
232 PUMP_DEBUG;
233 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
234 //llinfos << "LLURLRequest::process_impl()" << llendl;
235 if(!buffer) return STATUS_ERROR;
236 switch(mState)
237 {
238 case STATE_INITIALIZED:
239 {
240 PUMP_DEBUG;
241 // We only need to wait for input if we are uploading
242 // something.
243 if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos)
244 {
245 // we're waiting to get all of the information
246 return STATUS_BREAK;
247 }
248
249 // *FIX: bit of a hack, but it should work. The configure and
250 // callback method expect this information to be ready.
251 mDetail->mResponseBuffer = buffer.get();
252 mDetail->mChannels = channels;
253 if(!configure())
254 {
255 return STATUS_ERROR;
256 }
257 mState = STATE_WAITING_FOR_RESPONSE;
258
259 // *FIX: Maybe we should just go to the next state now...
260 return STATUS_BREAK;
261 }
262 case STATE_WAITING_FOR_RESPONSE:
263 case STATE_PROCESSING_RESPONSE:
264 {
265 PUMP_DEBUG;
266 const S32 MAX_CALLS = 5;
267 S32 count = MAX_CALLS;
268 CURLMcode code;
269 LLIOPipe::EStatus status = STATUS_BREAK;
270 S32 queue;
271 do
272 {
273 code = curl_multi_perform(mDetail->mCurlMulti, &queue);
274 }while((CURLM_CALL_MULTI_PERFORM == code) && (queue > 0) && count--);
275 CURLMsg* curl_msg;
276 do
277 {
278 curl_msg = curl_multi_info_read(mDetail->mCurlMulti, &queue);
279 if(curl_msg && (curl_msg->msg == CURLMSG_DONE))
280 {
281 mState = STATE_HAVE_RESPONSE;
282
283 CURLcode result = curl_msg->data.result;
284 switch(result)
285 {
286 case CURLE_OK:
287 case CURLE_WRITE_ERROR:
288 // NB: The error indication means that we stopped the
289 // writing due the body limit being reached
290 if(mCompletionCallback && pump)
291 {
292 LLURLRequestComplete* complete = NULL;
293 complete = (LLURLRequestComplete*)
294 mCompletionCallback.get();
295 complete->responseStatus(
296 result == CURLE_OK
297 ? STATUS_OK : STATUS_STOP);
298 LLPumpIO::links_t chain;
299 LLPumpIO::LLLinkInfo link;
300 link.mPipe = mCompletionCallback;
301 link.mChannels = LLBufferArray::makeChannelConsumer(
302 channels);
303 chain.push_back(link);
304 pump->respond(chain, buffer, context);
305 mCompletionCallback = NULL;
306 }
307 break;
308 case CURLE_COULDNT_CONNECT:
309 status = STATUS_NO_CONNECTION;
310 break;
311 default:
312 llwarns << "URLRequest Error: " << curl_msg->data.result
313 << ", "
314#if LL_DARWIN
315 // 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...
316 // There's a problem with the custom curl headers in our build that keeps me from #ifdefing this on the libcurl version number
317 // (the correct check would be #if LIBCURL_VERSION_NUM >= 0x070c00). We'll fix the header problem soon, but for now
318 // just punt and print the numeric error code on the Mac.
319 << curl_msg->data.result
320#else // LL_DARWIN
321 << curl_easy_strerror(curl_msg->data.result)
322#endif // LL_DARWIN
323 << ", "
324 << (mDetail->mURL ? mDetail->mURL : "<EMPTY URL>")
325 << llendl;
326 status = STATUS_ERROR;
327 break;
328 }
329 curl_multi_remove_handle(mDetail->mCurlMulti, mDetail->mCurl);
330 mDetail->mNeedToRemoveEasyHandle = false;
331 }
332 }while(curl_msg && (queue > 0));
333 return status;
334 }
335 case STATE_HAVE_RESPONSE:
336 PUMP_DEBUG;
337 // we already stuffed everything into channel in in the curl
338 // callback, so we are done.
339 eos = true;
340 return STATUS_DONE;
341
342 default:
343 PUMP_DEBUG;
344 return STATUS_ERROR;
345 }
346}
347
348void LLURLRequest::initialize()
349{
350 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
351 mState = STATE_INITIALIZED;
352 mDetail = new LLURLRequestDetail;
353 mDetail->mCurl = curl_easy_init();
354 mDetail->mCurlMulti = curl_multi_init();
355 curl_easy_setopt(mDetail->mCurl, CURLOPT_NOSIGNAL, 1);
356 curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEFUNCTION, &downCallback);
357 curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEDATA, this);
358 curl_easy_setopt(mDetail->mCurl, CURLOPT_READFUNCTION, &upCallback);
359 curl_easy_setopt(mDetail->mCurl, CURLOPT_READDATA, this);
360 curl_easy_setopt(
361 mDetail->mCurl,
362 CURLOPT_ERRORBUFFER,
363 mDetail->mCurlErrorBuf);
364
365 if(sCAPath != std::string(""))
366 {
367 curl_easy_setopt(mDetail->mCurl, CURLOPT_CAPATH, sCAPath.c_str());
368 }
369 if(sCAFile != std::string(""))
370 {
371 curl_easy_setopt(mDetail->mCurl, CURLOPT_CAINFO, sCAFile.c_str());
372 }
373}
374
375bool LLURLRequest::configure()
376{
377 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
378 bool rv = false;
379 S32 bytes = mDetail->mResponseBuffer->countAfter(
380 mDetail->mChannels.in(),
381 NULL);
382 switch(mAction)
383 {
384 case HTTP_GET:
385 curl_easy_setopt(mDetail->mCurl, CURLOPT_HTTPGET, 1);
386 curl_easy_setopt(mDetail->mCurl, CURLOPT_FOLLOWLOCATION, 1);
387 rv = true;
388 break;
389
390 case HTTP_PUT:
391 // Disable the expect http 1.1 extension. POST and PUT default
392 // to turning this on, and I am not too sure what it means.
393 addHeader("Expect:");
394
395 curl_easy_setopt(mDetail->mCurl, CURLOPT_UPLOAD, 1);
396 curl_easy_setopt(mDetail->mCurl, CURLOPT_INFILESIZE, bytes);
397 rv = true;
398 break;
399
400 case HTTP_POST:
401 // Disable the expect http 1.1 extension. POST and PUT default
402 // to turning this on, and I am not too sure what it means.
403 addHeader("Expect:");
404
405 // Disable the content type http header.
406 // *FIX: what should it be?
407 addHeader("Content-Type:");
408
409 // Set the handle for an http post
410 curl_easy_setopt(mDetail->mCurl, CURLOPT_POST, 1);
411 curl_easy_setopt(mDetail->mCurl, CURLOPT_POSTFIELDS, NULL);
412 curl_easy_setopt(mDetail->mCurl, CURLOPT_POSTFIELDSIZE, bytes);
413 rv = true;
414 break;
415
416 case HTTP_DELETE:
417 // Set the handle for an http post
418 curl_easy_setopt(mDetail->mCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
419 rv = true;
420 break;
421
422 default:
423 llwarns << "Unhandled URLRequest action: " << mAction << llendl;
424 break;
425 }
426 if(rv)
427 {
428 if(mDetail->mHeaders)
429 {
430 curl_easy_setopt(
431 mDetail->mCurl,
432 CURLOPT_HTTPHEADER,
433 mDetail->mHeaders);
434 }
435 curl_easy_setopt(mDetail->mCurl, CURLOPT_URL, mDetail->mURL);
436 curl_multi_add_handle(mDetail->mCurlMulti, mDetail->mCurl);
437 mDetail->mNeedToRemoveEasyHandle = true;
438 }
439 return rv;
440}
441
442// static
443size_t LLURLRequest::downCallback(
444 void* data,
445 size_t size,
446 size_t nmemb,
447 void* user)
448{
449 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
450 LLURLRequest* req = (LLURLRequest*)user;
451 if(STATE_WAITING_FOR_RESPONSE == req->mState)
452 {
453 req->mState = STATE_PROCESSING_RESPONSE;
454 }
455 U32 bytes = size * nmemb;
456 if (req->mDetail->mIsBodyLimitSet)
457 {
458 if (bytes > req->mDetail->mBodyLimit)
459 {
460 bytes = req->mDetail->mBodyLimit;
461 req->mDetail->mBodyLimit = 0;
462 }
463 else
464 {
465 req->mDetail->mBodyLimit -= bytes;
466 }
467 }
468
469 req->mDetail->mResponseBuffer->append(
470 req->mDetail->mChannels.out(),
471 (U8*)data,
472 bytes);
473 return bytes;
474}
475
476// static
477size_t LLURLRequest::upCallback(
478 void* data,
479 size_t size,
480 size_t nmemb,
481 void* user)
482{
483 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
484 LLURLRequest* req = (LLURLRequest*)user;
485 S32 bytes = llmin(
486 (S32)(size * nmemb),
487 req->mDetail->mResponseBuffer->countAfter(
488 req->mDetail->mChannels.in(),
489 req->mDetail->mLastRead));
490 req->mDetail->mLastRead = req->mDetail->mResponseBuffer->readAfter(
491 req->mDetail->mChannels.in(),
492 req->mDetail->mLastRead,
493 (U8*)data,
494 bytes);
495 return bytes;
496}
497
498static
499size_t headerCallback(void* data, size_t size, size_t nmemb, void* user)
500{
501 const char* headerLine = (const char*)data;
502 size_t headerLen = size * nmemb;
503 LLURLRequestComplete* complete = (LLURLRequestComplete*)user;
504
505 // *TODO: This should be a utility in llstring.h: isascii()
506 for (size_t i = 0; i < headerLen; ++i)
507 {
508 if (headerLine[i] < 0)
509 {
510 return headerLen;
511 }
512 }
513
514 size_t sep;
515 for (sep = 0; sep < headerLen && headerLine[sep] != ':'; ++sep) { }
516
517 if (sep < headerLen && complete)
518 {
519 std::string key(headerLine, sep);
520 std::string value(headerLine + sep + 1, headerLen - sep - 1);
521
522 key = utf8str_tolower(utf8str_trim(key));
523 value = utf8str_trim(value);
524
525 complete->header(key, value);
526 }
527 else
528 {
529 std::string s(headerLine, headerLen);
530
531 std::string::iterator end = s.end();
532 std::string::iterator pos1 = std::find(s.begin(), end, ' ');
533 if (pos1 != end) ++pos1;
534 std::string::iterator pos2 = std::find(pos1, end, ' ');
535 if (pos2 != end) ++pos2;
536 std::string::iterator pos3 = std::find(pos2, end, '\r');
537
538 std::string version(s.begin(), pos1);
539 std::string status(pos1, pos2);
540 std::string reason(pos2, pos3);
541
542 int statusCode = atoi(status.c_str());
543 if (statusCode > 0)
544 {
545 complete->httpStatus((U32)statusCode, reason);
546 }
547 }
548
549 return headerLen;
550}
551
552//static
553void LLURLRequest::setCertificateAuthorityFile(const std::string& file_name)
554{
555 sCAFile = file_name;
556}
557
558//static
559void LLURLRequest::setCertificateAuthorityPath(const std::string& path)
560{
561 sCAPath = path;
562}
563
564/**
565 * LLContextURLExtractor
566 */
567// virtual
568LLIOPipe::EStatus LLContextURLExtractor::process_impl(
569 const LLChannelDescriptors& channels,
570 buffer_ptr_t& buffer,
571 bool& eos,
572 LLSD& context,
573 LLPumpIO* pump)
574{
575 PUMP_DEBUG;
576 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
577 // The destination host is in the context.
578 if(context.isUndefined() || !mRequest)
579 {
580 return STATUS_PRECONDITION_NOT_MET;
581 }
582
583 // copy in to out, since this just extract the URL and does not
584 // actually change the data.
585 LLChangeChannel change(channels.in(), channels.out());
586 std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
587
588 // find the context url
589 if(context.has(CONTEXT_DEST_URI_SD_LABEL))
590 {
591 mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL]);
592 return STATUS_DONE;
593 }
594 return STATUS_ERROR;
595}
596
597
598/**
599 * LLURLRequestComplete
600 */
601LLURLRequestComplete::LLURLRequestComplete() :
602 mRequestStatus(LLIOPipe::STATUS_ERROR)
603{
604 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
605}
606
607// virtual
608LLURLRequestComplete::~LLURLRequestComplete()
609{
610 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
611}
612
613//virtual
614void LLURLRequestComplete::header(const std::string& header, const std::string& value)
615{
616}
617
618//virtual
619void LLURLRequestComplete::httpStatus(U32 status, const std::string& reason)
620{
621}
622
623//virtual
624void LLURLRequestComplete::complete(const LLChannelDescriptors& channels,
625 const buffer_ptr_t& buffer)
626{
627 if(STATUS_OK == mRequestStatus)
628 {
629 response(channels, buffer);
630 }
631 else
632 {
633 noResponse();
634 }
635}
636
637//virtual
638void LLURLRequestComplete::response(const LLChannelDescriptors& channels,
639 const buffer_ptr_t& buffer)
640{
641 llwarns << "LLURLRequestComplete::response default implementation called"
642 << llendl;
643}
644
645//virtual
646void LLURLRequestComplete::noResponse()
647{
648 llwarns << "LLURLRequestComplete::noResponse default implementation called"
649 << llendl;
650}
651
652void LLURLRequestComplete::responseStatus(LLIOPipe::EStatus status)
653{
654 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
655 mRequestStatus = status;
656}
657
658// virtual
659LLIOPipe::EStatus LLURLRequestComplete::process_impl(
660 const LLChannelDescriptors& channels,
661 buffer_ptr_t& buffer,
662 bool& eos,
663 LLSD& context,
664 LLPumpIO* pump)
665{
666 PUMP_DEBUG;
667 complete(channels, buffer);
668 return STATUS_OK;
669}