00001
00034 #include "linden_common.h"
00035 #include "llurlrequest.h"
00036
00037 #include <algorithm>
00038
00039 #include "llcurl.h"
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 size_t headerCallback(void* data, size_t size, size_t nmemb, void* user);
00056
00060 class LLURLRequestDetail
00061 {
00062 public:
00063 LLURLRequestDetail();
00064 ~LLURLRequestDetail();
00065 std::string mURL;
00066 LLCurlEasyRequest* mCurlRequest;
00067 LLBufferArray* mResponseBuffer;
00068 LLChannelDescriptors mChannels;
00069 U8* mLastRead;
00070 U32 mBodyLimit;
00071 bool mIsBodyLimitSet;
00072 };
00073
00074 LLURLRequestDetail::LLURLRequestDetail() :
00075 mCurlRequest(NULL),
00076 mResponseBuffer(NULL),
00077 mLastRead(NULL),
00078 mBodyLimit(0),
00079 mIsBodyLimitSet(false)
00080
00081 {
00082 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00083 mCurlRequest = new LLCurlEasyRequest();
00084 }
00085
00086 LLURLRequestDetail::~LLURLRequestDetail()
00087 {
00088 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00089 delete mCurlRequest;
00090 mResponseBuffer = NULL;
00091 mLastRead = NULL;
00092 }
00093
00094
00099 LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :
00100 mAction(action)
00101 {
00102 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00103 initialize();
00104 }
00105
00106 LLURLRequest::LLURLRequest(
00107 LLURLRequest::ERequestAction action,
00108 const std::string& url) :
00109 mAction(action)
00110 {
00111 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00112 initialize();
00113 setURL(url);
00114 }
00115
00116 LLURLRequest::~LLURLRequest()
00117 {
00118 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00119 delete mDetail;
00120 }
00121
00122 void LLURLRequest::setURL(const std::string& url)
00123 {
00124 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00125 mDetail->mURL = url;
00126 }
00127
00128 void LLURLRequest::addHeader(const char* header)
00129 {
00130 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00131 mDetail->mCurlRequest->slist_append(header);
00132 }
00133
00134 void LLURLRequest::setBodyLimit(U32 size)
00135 {
00136 mDetail->mBodyLimit = size;
00137 mDetail->mIsBodyLimitSet = true;
00138 }
00139
00140 void LLURLRequest::checkRootCertificate(bool check)
00141 {
00142 mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE));
00143 mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, "");
00144 }
00145
00146 void LLURLRequest::setCallback(LLURLRequestComplete* callback)
00147 {
00148 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00149 mCompletionCallback = callback;
00150 mDetail->mCurlRequest->setHeaderCallback(&headerCallback, (void*)callback);
00151 }
00152
00153
00154
00155
00156
00157
00158
00159
00160 void LLURLRequest::useProxy(bool use_proxy)
00161 {
00162 static char *env_proxy;
00163
00164 if (use_proxy && (env_proxy == NULL))
00165 {
00166 apr_status_t status;
00167 apr_pool_t* pool;
00168 apr_pool_create(&pool, NULL);
00169 status = apr_env_get(&env_proxy, "ALL_PROXY", pool);
00170 if (status != APR_SUCCESS)
00171 {
00172 status = apr_env_get(&env_proxy, "http_proxy", pool);
00173 }
00174 if (status != APR_SUCCESS)
00175 {
00176 use_proxy = FALSE;
00177 }
00178 apr_pool_destroy(pool);
00179 }
00180
00181
00182 lldebugs << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (env_proxy ? env_proxy : "(null)") << llendl;
00183
00184 if (env_proxy && use_proxy)
00185 {
00186 mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy);
00187 }
00188 else
00189 {
00190 mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, "");
00191 }
00192 }
00193
00194 void LLURLRequest::useProxy(const std::string &proxy)
00195 {
00196 mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy);
00197 }
00198
00199
00200 LLIOPipe::EStatus LLURLRequest::handleError(
00201 LLIOPipe::EStatus status,
00202 LLPumpIO* pump)
00203 {
00204 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00205 if(mCompletionCallback && pump)
00206 {
00207 LLURLRequestComplete* complete = NULL;
00208 complete = (LLURLRequestComplete*)mCompletionCallback.get();
00209 complete->httpStatus(
00210 HTTP_STATUS_PIPE_ERROR,
00211 LLIOPipe::lookupStatusString(status));
00212 complete->responseStatus(status);
00213 pump->respond(complete);
00214 mCompletionCallback = NULL;
00215 }
00216 return status;
00217 }
00218
00219
00220 LLIOPipe::EStatus LLURLRequest::process_impl(
00221 const LLChannelDescriptors& channels,
00222 buffer_ptr_t& buffer,
00223 bool& eos,
00224 LLSD& context,
00225 LLPumpIO* pump)
00226 {
00227 PUMP_DEBUG;
00228 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00229
00230 if(!buffer) return STATUS_ERROR;
00231 switch(mState)
00232 {
00233 case STATE_INITIALIZED:
00234 {
00235 PUMP_DEBUG;
00236
00237
00238 if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos)
00239 {
00240
00241 return STATUS_BREAK;
00242 }
00243
00244
00245
00246 mDetail->mResponseBuffer = buffer.get();
00247 mDetail->mChannels = channels;
00248 if(!configure())
00249 {
00250 return STATUS_ERROR;
00251 }
00252 mState = STATE_WAITING_FOR_RESPONSE;
00253
00254
00255 return STATUS_BREAK;
00256 }
00257 case STATE_WAITING_FOR_RESPONSE:
00258 case STATE_PROCESSING_RESPONSE:
00259 {
00260 PUMP_DEBUG;
00261 LLIOPipe::EStatus status = STATUS_BREAK;
00262 mDetail->mCurlRequest->perform();
00263 while(1)
00264 {
00265 CURLcode result;
00266 bool newmsg = mDetail->mCurlRequest->getResult(&result);
00267 if (!newmsg)
00268 {
00269 break;
00270 }
00271
00272 mState = STATE_HAVE_RESPONSE;
00273 switch(result)
00274 {
00275 case CURLE_OK:
00276 case CURLE_WRITE_ERROR:
00277
00278
00279 if(mCompletionCallback && pump)
00280 {
00281 LLURLRequestComplete* complete = NULL;
00282 complete = (LLURLRequestComplete*)
00283 mCompletionCallback.get();
00284 complete->responseStatus(
00285 result == CURLE_OK
00286 ? STATUS_OK : STATUS_STOP);
00287 LLPumpIO::links_t chain;
00288 LLPumpIO::LLLinkInfo link;
00289 link.mPipe = mCompletionCallback;
00290 link.mChannels = LLBufferArray::makeChannelConsumer(
00291 channels);
00292 chain.push_back(link);
00293 pump->respond(chain, buffer, context);
00294 mCompletionCallback = NULL;
00295 }
00296 break;
00297 case CURLE_FAILED_INIT:
00298 case CURLE_COULDNT_CONNECT:
00299 status = STATUS_NO_CONNECTION;
00300 break;
00301 default:
00302 llwarns << "URLRequest Error: " << result
00303 << ", "
00304 << LLCurl::strerror(result)
00305 << ", "
00306 << (mDetail->mURL.empty() ? "<EMPTY URL>" : mDetail->mURL)
00307 << llendl;
00308 status = STATUS_ERROR;
00309 break;
00310 }
00311 }
00312 return status;
00313 }
00314 case STATE_HAVE_RESPONSE:
00315 PUMP_DEBUG;
00316
00317
00318 eos = true;
00319 return STATUS_DONE;
00320
00321 default:
00322 PUMP_DEBUG;
00323 return STATUS_ERROR;
00324 }
00325 }
00326
00327 void LLURLRequest::initialize()
00328 {
00329 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00330 mState = STATE_INITIALIZED;
00331 mDetail = new LLURLRequestDetail;
00332 mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1);
00333 mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this);
00334 mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this);
00335 }
00336
00337 bool LLURLRequest::configure()
00338 {
00339 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00340 bool rv = false;
00341 S32 bytes = mDetail->mResponseBuffer->countAfter(
00342 mDetail->mChannels.in(),
00343 NULL);
00344 switch(mAction)
00345 {
00346 case HTTP_HEAD:
00347 mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1);
00348 mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1);
00349 mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
00350 rv = true;
00351 break;
00352 case HTTP_GET:
00353 mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1);
00354 mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1);
00355 rv = true;
00356 break;
00357
00358 case HTTP_PUT:
00359
00360
00361 addHeader("Expect:");
00362
00363 mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1);
00364 mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes);
00365 rv = true;
00366 break;
00367
00368 case HTTP_POST:
00369
00370
00371 addHeader("Expect:");
00372
00373
00374
00375 addHeader("Content-Type:");
00376
00377
00378 mDetail->mCurlRequest->setPost(NULL, bytes);
00379 rv = true;
00380 break;
00381
00382 case HTTP_DELETE:
00383
00384 mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE");
00385 rv = true;
00386 break;
00387
00388 case HTTP_MOVE:
00389
00390 mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE");
00391
00392 rv = true;
00393 break;
00394
00395 default:
00396 llwarns << "Unhandled URLRequest action: " << mAction << llendl;
00397 break;
00398 }
00399 if(rv)
00400 {
00401 mDetail->mCurlRequest->sendRequest(mDetail->mURL);
00402 }
00403 return rv;
00404 }
00405
00406
00407 size_t LLURLRequest::downCallback(
00408 char* data,
00409 size_t size,
00410 size_t nmemb,
00411 void* user)
00412 {
00413 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00414 LLURLRequest* req = (LLURLRequest*)user;
00415 if(STATE_WAITING_FOR_RESPONSE == req->mState)
00416 {
00417 req->mState = STATE_PROCESSING_RESPONSE;
00418 }
00419 U32 bytes = size * nmemb;
00420 if (req->mDetail->mIsBodyLimitSet)
00421 {
00422 if (bytes > req->mDetail->mBodyLimit)
00423 {
00424 bytes = req->mDetail->mBodyLimit;
00425 req->mDetail->mBodyLimit = 0;
00426 }
00427 else
00428 {
00429 req->mDetail->mBodyLimit -= bytes;
00430 }
00431 }
00432
00433 req->mDetail->mResponseBuffer->append(
00434 req->mDetail->mChannels.out(),
00435 (U8*)data,
00436 bytes);
00437 return bytes;
00438 }
00439
00440
00441 size_t LLURLRequest::upCallback(
00442 char* data,
00443 size_t size,
00444 size_t nmemb,
00445 void* user)
00446 {
00447 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00448 LLURLRequest* req = (LLURLRequest*)user;
00449 S32 bytes = llmin(
00450 (S32)(size * nmemb),
00451 req->mDetail->mResponseBuffer->countAfter(
00452 req->mDetail->mChannels.in(),
00453 req->mDetail->mLastRead));
00454 req->mDetail->mLastRead = req->mDetail->mResponseBuffer->readAfter(
00455 req->mDetail->mChannels.in(),
00456 req->mDetail->mLastRead,
00457 (U8*)data,
00458 bytes);
00459 return bytes;
00460 }
00461
00462 static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user)
00463 {
00464 const char* headerLine = (const char*)data;
00465 size_t headerLen = size * nmemb;
00466 LLURLRequestComplete* complete = (LLURLRequestComplete*)user;
00467
00468
00469 for (size_t i = 0; i < headerLen; ++i)
00470 {
00471 if (headerLine[i] < 0)
00472 {
00473 return headerLen;
00474 }
00475 }
00476
00477 size_t sep;
00478 for (sep = 0; sep < headerLen && headerLine[sep] != ':'; ++sep) { }
00479
00480 if (sep < headerLen && complete)
00481 {
00482 std::string key(headerLine, sep);
00483 std::string value(headerLine + sep + 1, headerLen - sep - 1);
00484
00485 key = utf8str_tolower(utf8str_trim(key));
00486 value = utf8str_trim(value);
00487
00488 complete->header(key, value);
00489 }
00490 else
00491 {
00492 std::string s(headerLine, headerLen);
00493
00494 std::string::iterator end = s.end();
00495 std::string::iterator pos1 = std::find(s.begin(), end, ' ');
00496 if (pos1 != end) ++pos1;
00497 std::string::iterator pos2 = std::find(pos1, end, ' ');
00498 if (pos2 != end) ++pos2;
00499 std::string::iterator pos3 = std::find(pos2, end, '\r');
00500
00501 std::string version(s.begin(), pos1);
00502 std::string status(pos1, pos2);
00503 std::string reason(pos2, pos3);
00504
00505 int statusCode = atoi(status.c_str());
00506 if (statusCode > 0)
00507 {
00508 if (complete)
00509 {
00510 complete->httpStatus((U32)statusCode, reason);
00511 }
00512 }
00513 }
00514
00515 return headerLen;
00516 }
00517
00521
00522 LLIOPipe::EStatus LLContextURLExtractor::process_impl(
00523 const LLChannelDescriptors& channels,
00524 buffer_ptr_t& buffer,
00525 bool& eos,
00526 LLSD& context,
00527 LLPumpIO* pump)
00528 {
00529 PUMP_DEBUG;
00530 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00531
00532 if(context.isUndefined() || !mRequest)
00533 {
00534 return STATUS_PRECONDITION_NOT_MET;
00535 }
00536
00537
00538
00539 LLChangeChannel change(channels.in(), channels.out());
00540 std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
00541
00542
00543 if(context.has(CONTEXT_DEST_URI_SD_LABEL))
00544 {
00545 mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL]);
00546 return STATUS_DONE;
00547 }
00548 return STATUS_ERROR;
00549 }
00550
00551
00555 LLURLRequestComplete::LLURLRequestComplete() :
00556 mRequestStatus(LLIOPipe::STATUS_ERROR)
00557 {
00558 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00559 }
00560
00561
00562 LLURLRequestComplete::~LLURLRequestComplete()
00563 {
00564 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00565 }
00566
00567
00568 void LLURLRequestComplete::header(const std::string& header, const std::string& value)
00569 {
00570 }
00571
00572
00573 void LLURLRequestComplete::httpStatus(U32 status, const std::string& reason)
00574 {
00575 }
00576
00577
00578 void LLURLRequestComplete::complete(const LLChannelDescriptors& channels,
00579 const buffer_ptr_t& buffer)
00580 {
00581 if(STATUS_OK == mRequestStatus)
00582 {
00583 response(channels, buffer);
00584 }
00585 else
00586 {
00587 noResponse();
00588 }
00589 }
00590
00591
00592 void LLURLRequestComplete::response(const LLChannelDescriptors& channels,
00593 const buffer_ptr_t& buffer)
00594 {
00595 llwarns << "LLURLRequestComplete::response default implementation called"
00596 << llendl;
00597 }
00598
00599
00600 void LLURLRequestComplete::noResponse()
00601 {
00602 llwarns << "LLURLRequestComplete::noResponse default implementation called"
00603 << llendl;
00604 }
00605
00606 void LLURLRequestComplete::responseStatus(LLIOPipe::EStatus status)
00607 {
00608 LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
00609 mRequestStatus = status;
00610 }
00611
00612
00613 LLIOPipe::EStatus LLURLRequestComplete::process_impl(
00614 const LLChannelDescriptors& channels,
00615 buffer_ptr_t& buffer,
00616 bool& eos,
00617 LLSD& context,
00618 LLPumpIO* pump)
00619 {
00620 PUMP_DEBUG;
00621 complete(channels, buffer);
00622 return STATUS_OK;
00623 }