00001
00034 #include "linden_common.h"
00035 #include "llurlrequest.h"
00036
00037 #include <curl/curl.h>
00038 #include <algorithm>
00039
00040 #include "llioutil.h"
00041 #include "llmemtype.h"
00042 #include "llpumpio.h"
00043 #include "llsd.h"
00044 #include "llstring.h"
00045 #include "apr-1/apr_env.h"
00046
00047 static const U32 HTTP_STATUS_PIPE_ERROR = 499;
00048
00052 const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri");
00053
00054
00055 static
00056 size_t headerCallback(void* data, size_t size, size_t nmemb, void* user);
00057
00061 class LLURLRequestDetail
00062 {
00063 public:
00064 LLURLRequestDetail();
00065 ~LLURLRequestDetail();
00066 CURLM* mCurlMulti;
00067 CURL* mCurl;
00068 struct curl_slist* mHeaders;
00069 char* mURL;
00070 char mCurlErrorBuf[CURL_ERROR_SIZE + 1];
00071 bool mNeedToRemoveEasyHandle;
00072 LLBufferArray* mResponseBuffer;
00073 LLChannelDescriptors mChannels;
00074 U8* mLastRead;
00075 U32 mBodyLimit;
00076 bool mIsBodyLimitSet;
00077 };
00078
00079 LLURLRequestDetail::LLURLRequestDetail() :
00080 mCurlMulti(NULL),
00081 mCurl(NULL),
00082 mHeaders(NULL),
00083 mURL(NULL),
00084 mNeedToRemoveEasyHandle(false),
00085 mResponseBuffer(NULL),
00086 mLastRead(NULL),
00087 mBodyLimit(0),
00088 mIsBodyLimitSet(false)
00089
00090 {
00091 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00092 mCurlErrorBuf[0] = '\0';
00093 }
00094
00095 LLURLRequestDetail::~LLURLRequestDetail()
00096 {
00097 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00098 if(mCurl)
00099 {
00100 if(mNeedToRemoveEasyHandle && mCurlMulti)
00101 {
00102 curl_multi_remove_handle(mCurlMulti, mCurl);
00103 mNeedToRemoveEasyHandle = false;
00104 }
00105 curl_easy_cleanup(mCurl);
00106 mCurl = NULL;
00107 }
00108 if(mCurlMulti)
00109 {
00110 curl_multi_cleanup(mCurlMulti);
00111 mCurlMulti = NULL;
00112 }
00113 if(mHeaders)
00114 {
00115 curl_slist_free_all(mHeaders);
00116 mHeaders = NULL;
00117 }
00118 delete[] mURL;
00119 mURL = NULL;
00120 mResponseBuffer = NULL;
00121 mLastRead = NULL;
00122 }
00123
00124
00129 static std::string sCAFile("");
00130 static std::string sCAPath("");
00131
00132 LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :
00133 mAction(action)
00134 {
00135 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00136 initialize();
00137 }
00138
00139 LLURLRequest::LLURLRequest(
00140 LLURLRequest::ERequestAction action,
00141 const std::string& url) :
00142 mAction(action)
00143 {
00144 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00145 initialize();
00146 setURL(url);
00147 }
00148
00149 LLURLRequest::~LLURLRequest()
00150 {
00151 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00152 delete mDetail;
00153 }
00154
00155 void LLURLRequest::setURL(const std::string& url)
00156 {
00157 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00158 if(mDetail->mURL)
00159 {
00160
00161
00162 delete[] mDetail->mURL;
00163 mDetail->mURL = NULL;
00164 }
00165 if(!url.empty())
00166 {
00167 mDetail->mURL = new char[url.size() + 1];
00168 url.copy(mDetail->mURL, url.size());
00169 mDetail->mURL[url.size()] = '\0';
00170 }
00171 }
00172
00173 void LLURLRequest::addHeader(const char* header)
00174 {
00175 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00176 mDetail->mHeaders = curl_slist_append(mDetail->mHeaders, header);
00177 }
00178
00179 void LLURLRequest::requestEncoding(const char* encoding)
00180 {
00181 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00182 curl_easy_setopt(mDetail->mCurl, CURLOPT_ENCODING, encoding);
00183 }
00184
00185 void LLURLRequest::setBodyLimit(U32 size)
00186 {
00187 mDetail->mBodyLimit = size;
00188 mDetail->mIsBodyLimitSet = true;
00189 }
00190
00191 void LLURLRequest::checkRootCertificate(bool check, const char* caBundle)
00192 {
00193 curl_easy_setopt(mDetail->mCurl, CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE));
00194 if (caBundle)
00195 {
00196 curl_easy_setopt(mDetail->mCurl, CURLOPT_CAINFO, caBundle);
00197 }
00198 }
00199
00200 void LLURLRequest::setCallback(LLURLRequestComplete* callback)
00201 {
00202 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00203 mCompletionCallback = callback;
00204
00205 curl_easy_setopt(mDetail->mCurl, CURLOPT_HEADERFUNCTION, &headerCallback);
00206 curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEHEADER, callback);
00207 }
00208
00209
00210
00211
00212
00213
00214
00215
00216 void LLURLRequest::useProxy(bool use_proxy)
00217 {
00218 static char *env_proxy;
00219
00220 if (use_proxy && (env_proxy == NULL))
00221 {
00222 apr_status_t status;
00223 apr_pool_t* pool;
00224 apr_pool_create(&pool, NULL);
00225 status = apr_env_get(&env_proxy, "ALL_PROXY", pool);
00226 if (status != APR_SUCCESS)
00227 {
00228 status = apr_env_get(&env_proxy, "http_proxy", pool);
00229 }
00230 if (status != APR_SUCCESS)
00231 {
00232 use_proxy = FALSE;
00233 }
00234 apr_pool_destroy(pool);
00235 }
00236
00237
00238 lldebugs << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << env_proxy << llendl;
00239
00240 if (env_proxy && use_proxy)
00241 {
00242 curl_easy_setopt(mDetail->mCurl, CURLOPT_PROXY, env_proxy);
00243 }
00244 else
00245 {
00246 curl_easy_setopt(mDetail->mCurl, CURLOPT_PROXY, "");
00247 }
00248 }
00249
00250
00251 LLIOPipe::EStatus LLURLRequest::handleError(
00252 LLIOPipe::EStatus status,
00253 LLPumpIO* pump)
00254 {
00255 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00256 if(mCompletionCallback && pump)
00257 {
00258 LLURLRequestComplete* complete = NULL;
00259 complete = (LLURLRequestComplete*)mCompletionCallback.get();
00260 complete->httpStatus(
00261 HTTP_STATUS_PIPE_ERROR,
00262 LLIOPipe::lookupStatusString(status));
00263 complete->responseStatus(status);
00264 pump->respond(complete);
00265 mCompletionCallback = NULL;
00266 }
00267 return status;
00268 }
00269
00270
00271 LLIOPipe::EStatus LLURLRequest::process_impl(
00272 const LLChannelDescriptors& channels,
00273 buffer_ptr_t& buffer,
00274 bool& eos,
00275 LLSD& context,
00276 LLPumpIO* pump)
00277 {
00278 PUMP_DEBUG;
00279 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00280
00281 if(!buffer) return STATUS_ERROR;
00282 switch(mState)
00283 {
00284 case STATE_INITIALIZED:
00285 {
00286 PUMP_DEBUG;
00287
00288
00289 if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos)
00290 {
00291
00292 return STATUS_BREAK;
00293 }
00294
00295
00296
00297 mDetail->mResponseBuffer = buffer.get();
00298 mDetail->mChannels = channels;
00299 if(!configure())
00300 {
00301 return STATUS_ERROR;
00302 }
00303 mState = STATE_WAITING_FOR_RESPONSE;
00304
00305
00306 return STATUS_BREAK;
00307 }
00308 case STATE_WAITING_FOR_RESPONSE:
00309 case STATE_PROCESSING_RESPONSE:
00310 {
00311 PUMP_DEBUG;
00312 const S32 MAX_CALLS = 5;
00313 S32 count = MAX_CALLS;
00314 CURLMcode code;
00315 LLIOPipe::EStatus status = STATUS_BREAK;
00316 S32 queue;
00317 do
00318 {
00319 LLFastTimer t2(LLFastTimer::FTM_CURL);
00320 code = curl_multi_perform(mDetail->mCurlMulti, &queue);
00321 }while((CURLM_CALL_MULTI_PERFORM == code) && (queue > 0) && count--);
00322 CURLMsg* curl_msg;
00323 do
00324 {
00325 curl_msg = curl_multi_info_read(mDetail->mCurlMulti, &queue);
00326 if(curl_msg && (curl_msg->msg == CURLMSG_DONE))
00327 {
00328 mState = STATE_HAVE_RESPONSE;
00329
00330 CURLcode result = curl_msg->data.result;
00331 switch(result)
00332 {
00333 case CURLE_OK:
00334 case CURLE_WRITE_ERROR:
00335
00336
00337 if(mCompletionCallback && pump)
00338 {
00339 LLURLRequestComplete* complete = NULL;
00340 complete = (LLURLRequestComplete*)
00341 mCompletionCallback.get();
00342 complete->responseStatus(
00343 result == CURLE_OK
00344 ? STATUS_OK : STATUS_STOP);
00345 LLPumpIO::links_t chain;
00346 LLPumpIO::LLLinkInfo link;
00347 link.mPipe = mCompletionCallback;
00348 link.mChannels = LLBufferArray::makeChannelConsumer(
00349 channels);
00350 chain.push_back(link);
00351 pump->respond(chain, buffer, context);
00352 mCompletionCallback = NULL;
00353 }
00354 break;
00355 case CURLE_COULDNT_CONNECT:
00356 status = STATUS_NO_CONNECTION;
00357 break;
00358 default:
00359 llwarns << "URLRequest Error: " << curl_msg->data.result
00360 << ", "
00361 #if LL_DARWIN
00362
00363
00364
00365
00366 << curl_msg->data.result
00367 #else // LL_DARWIN
00368 << curl_easy_strerror(curl_msg->data.result)
00369 #endif // LL_DARWIN
00370 << ", "
00371 << (mDetail->mURL ? mDetail->mURL : "<EMPTY URL>")
00372 << llendl;
00373 status = STATUS_ERROR;
00374 break;
00375 }
00376 curl_multi_remove_handle(mDetail->mCurlMulti, mDetail->mCurl);
00377 mDetail->mNeedToRemoveEasyHandle = false;
00378 }
00379 }while(curl_msg && (queue > 0));
00380 return status;
00381 }
00382 case STATE_HAVE_RESPONSE:
00383 PUMP_DEBUG;
00384
00385
00386 eos = true;
00387 return STATUS_DONE;
00388
00389 default:
00390 PUMP_DEBUG;
00391 return STATUS_ERROR;
00392 }
00393 }
00394
00395 void LLURLRequest::initialize()
00396 {
00397 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00398 mState = STATE_INITIALIZED;
00399 mDetail = new LLURLRequestDetail;
00400 mDetail->mCurl = curl_easy_init();
00401 mDetail->mCurlMulti = curl_multi_init();
00402 curl_easy_setopt(mDetail->mCurl, CURLOPT_NOSIGNAL, 1);
00403 curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEFUNCTION, &downCallback);
00404 curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEDATA, this);
00405 curl_easy_setopt(mDetail->mCurl, CURLOPT_READFUNCTION, &upCallback);
00406 curl_easy_setopt(mDetail->mCurl, CURLOPT_READDATA, this);
00407 curl_easy_setopt(
00408 mDetail->mCurl,
00409 CURLOPT_ERRORBUFFER,
00410 mDetail->mCurlErrorBuf);
00411
00412 if(sCAPath != std::string(""))
00413 {
00414 curl_easy_setopt(mDetail->mCurl, CURLOPT_CAPATH, sCAPath.c_str());
00415 }
00416 if(sCAFile != std::string(""))
00417 {
00418 curl_easy_setopt(mDetail->mCurl, CURLOPT_CAINFO, sCAFile.c_str());
00419 }
00420 }
00421
00422 bool LLURLRequest::configure()
00423 {
00424 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00425 bool rv = false;
00426 S32 bytes = mDetail->mResponseBuffer->countAfter(
00427 mDetail->mChannels.in(),
00428 NULL);
00429 switch(mAction)
00430 {
00431 case HTTP_GET:
00432 curl_easy_setopt(mDetail->mCurl, CURLOPT_HTTPGET, 1);
00433 curl_easy_setopt(mDetail->mCurl, CURLOPT_FOLLOWLOCATION, 1);
00434 rv = true;
00435 break;
00436
00437 case HTTP_PUT:
00438
00439
00440 addHeader("Expect:");
00441
00442 curl_easy_setopt(mDetail->mCurl, CURLOPT_UPLOAD, 1);
00443 curl_easy_setopt(mDetail->mCurl, CURLOPT_INFILESIZE, bytes);
00444 rv = true;
00445 break;
00446
00447 case HTTP_POST:
00448
00449
00450 addHeader("Expect:");
00451
00452
00453
00454 addHeader("Content-Type:");
00455
00456
00457 curl_easy_setopt(mDetail->mCurl, CURLOPT_POST, 1);
00458 curl_easy_setopt(mDetail->mCurl, CURLOPT_POSTFIELDS, NULL);
00459 curl_easy_setopt(mDetail->mCurl, CURLOPT_POSTFIELDSIZE, bytes);
00460 rv = true;
00461 break;
00462
00463 case HTTP_DELETE:
00464
00465 curl_easy_setopt(mDetail->mCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
00466 rv = true;
00467 break;
00468
00469 default:
00470 llwarns << "Unhandled URLRequest action: " << mAction << llendl;
00471 break;
00472 }
00473 if(rv)
00474 {
00475 if(mDetail->mHeaders)
00476 {
00477 curl_easy_setopt(
00478 mDetail->mCurl,
00479 CURLOPT_HTTPHEADER,
00480 mDetail->mHeaders);
00481 }
00482 curl_easy_setopt(mDetail->mCurl, CURLOPT_URL, mDetail->mURL);
00483 curl_multi_add_handle(mDetail->mCurlMulti, mDetail->mCurl);
00484 mDetail->mNeedToRemoveEasyHandle = true;
00485 }
00486 return rv;
00487 }
00488
00489
00490 size_t LLURLRequest::downCallback(
00491 void* data,
00492 size_t size,
00493 size_t nmemb,
00494 void* user)
00495 {
00496 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00497 LLURLRequest* req = (LLURLRequest*)user;
00498 if(STATE_WAITING_FOR_RESPONSE == req->mState)
00499 {
00500 req->mState = STATE_PROCESSING_RESPONSE;
00501 }
00502 U32 bytes = size * nmemb;
00503 if (req->mDetail->mIsBodyLimitSet)
00504 {
00505 if (bytes > req->mDetail->mBodyLimit)
00506 {
00507 bytes = req->mDetail->mBodyLimit;
00508 req->mDetail->mBodyLimit = 0;
00509 }
00510 else
00511 {
00512 req->mDetail->mBodyLimit -= bytes;
00513 }
00514 }
00515
00516 req->mDetail->mResponseBuffer->append(
00517 req->mDetail->mChannels.out(),
00518 (U8*)data,
00519 bytes);
00520 return bytes;
00521 }
00522
00523
00524 size_t LLURLRequest::upCallback(
00525 void* data,
00526 size_t size,
00527 size_t nmemb,
00528 void* user)
00529 {
00530 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00531 LLURLRequest* req = (LLURLRequest*)user;
00532 S32 bytes = llmin(
00533 (S32)(size * nmemb),
00534 req->mDetail->mResponseBuffer->countAfter(
00535 req->mDetail->mChannels.in(),
00536 req->mDetail->mLastRead));
00537 req->mDetail->mLastRead = req->mDetail->mResponseBuffer->readAfter(
00538 req->mDetail->mChannels.in(),
00539 req->mDetail->mLastRead,
00540 (U8*)data,
00541 bytes);
00542 return bytes;
00543 }
00544
00545 static
00546 size_t headerCallback(void* data, size_t size, size_t nmemb, void* user)
00547 {
00548 const char* headerLine = (const char*)data;
00549 size_t headerLen = size * nmemb;
00550 LLURLRequestComplete* complete = (LLURLRequestComplete*)user;
00551
00552
00553 for (size_t i = 0; i < headerLen; ++i)
00554 {
00555 if (headerLine[i] < 0)
00556 {
00557 return headerLen;
00558 }
00559 }
00560
00561 size_t sep;
00562 for (sep = 0; sep < headerLen && headerLine[sep] != ':'; ++sep) { }
00563
00564 if (sep < headerLen && complete)
00565 {
00566 std::string key(headerLine, sep);
00567 std::string value(headerLine + sep + 1, headerLen - sep - 1);
00568
00569 key = utf8str_tolower(utf8str_trim(key));
00570 value = utf8str_trim(value);
00571
00572 complete->header(key, value);
00573 }
00574 else
00575 {
00576 std::string s(headerLine, headerLen);
00577
00578 std::string::iterator end = s.end();
00579 std::string::iterator pos1 = std::find(s.begin(), end, ' ');
00580 if (pos1 != end) ++pos1;
00581 std::string::iterator pos2 = std::find(pos1, end, ' ');
00582 if (pos2 != end) ++pos2;
00583 std::string::iterator pos3 = std::find(pos2, end, '\r');
00584
00585 std::string version(s.begin(), pos1);
00586 std::string status(pos1, pos2);
00587 std::string reason(pos2, pos3);
00588
00589 int statusCode = atoi(status.c_str());
00590 if (statusCode > 0)
00591 {
00592 if (complete)
00593 {
00594 complete->httpStatus((U32)statusCode, reason);
00595 }
00596 }
00597 }
00598
00599 return headerLen;
00600 }
00601
00602
00603 void LLURLRequest::setCertificateAuthorityFile(const std::string& file_name)
00604 {
00605 sCAFile = file_name;
00606 }
00607
00608
00609 void LLURLRequest::setCertificateAuthorityPath(const std::string& path)
00610 {
00611 sCAPath = path;
00612 }
00613
00617
00618 LLIOPipe::EStatus LLContextURLExtractor::process_impl(
00619 const LLChannelDescriptors& channels,
00620 buffer_ptr_t& buffer,
00621 bool& eos,
00622 LLSD& context,
00623 LLPumpIO* pump)
00624 {
00625 PUMP_DEBUG;
00626 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00627
00628 if(context.isUndefined() || !mRequest)
00629 {
00630 return STATUS_PRECONDITION_NOT_MET;
00631 }
00632
00633
00634
00635 LLChangeChannel change(channels.in(), channels.out());
00636 std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
00637
00638
00639 if(context.has(CONTEXT_DEST_URI_SD_LABEL))
00640 {
00641 mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL]);
00642 return STATUS_DONE;
00643 }
00644 return STATUS_ERROR;
00645 }
00646
00647
00651 LLURLRequestComplete::LLURLRequestComplete() :
00652 mRequestStatus(LLIOPipe::STATUS_ERROR)
00653 {
00654 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00655 }
00656
00657
00658 LLURLRequestComplete::~LLURLRequestComplete()
00659 {
00660 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00661 }
00662
00663
00664 void LLURLRequestComplete::header(const std::string& header, const std::string& value)
00665 {
00666 }
00667
00668
00669 void LLURLRequestComplete::httpStatus(U32 status, const std::string& reason)
00670 {
00671 }
00672
00673
00674 void LLURLRequestComplete::complete(const LLChannelDescriptors& channels,
00675 const buffer_ptr_t& buffer)
00676 {
00677 if(STATUS_OK == mRequestStatus)
00678 {
00679 response(channels, buffer);
00680 }
00681 else
00682 {
00683 noResponse();
00684 }
00685 }
00686
00687
00688 void LLURLRequestComplete::response(const LLChannelDescriptors& channels,
00689 const buffer_ptr_t& buffer)
00690 {
00691 llwarns << "LLURLRequestComplete::response default implementation called"
00692 << llendl;
00693 }
00694
00695
00696 void LLURLRequestComplete::noResponse()
00697 {
00698 llwarns << "LLURLRequestComplete::noResponse default implementation called"
00699 << llendl;
00700 }
00701
00702 void LLURLRequestComplete::responseStatus(LLIOPipe::EStatus status)
00703 {
00704 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00705 mRequestStatus = status;
00706 }
00707
00708
00709 LLIOPipe::EStatus LLURLRequestComplete::process_impl(
00710 const LLChannelDescriptors& channels,
00711 buffer_ptr_t& buffer,
00712 bool& eos,
00713 LLSD& context,
00714 LLPumpIO* pump)
00715 {
00716 PUMP_DEBUG;
00717 complete(channels, buffer);
00718 return STATUS_OK;
00719 }