llxmlrpctransaction.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 
00034 #include "llxmlrpctransaction.h"
00035 
00036 #include "llviewercontrol.h"
00037 
00038 // Have to include these last to avoid queue redefinition!
00039 #include <curl/curl.h>
00040 #include <xmlrpc-epi/xmlrpc.h>
00041 
00042 #include "viewer.h"
00043 
00044 LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const
00045 {
00046         return LLXMLRPCValue(XMLRPC_VectorGetValueWithID(mV, id));
00047 }
00048 
00049 std::string LLXMLRPCValue::asString() const
00050 {
00051         const char* s = XMLRPC_GetValueString(mV);
00052         return s ? s : "";
00053 }
00054 
00055 int             LLXMLRPCValue::asInt() const    { return XMLRPC_GetValueInt(mV); }
00056 bool    LLXMLRPCValue::asBool() const   { return XMLRPC_GetValueBoolean(mV) != 0; }
00057 double  LLXMLRPCValue::asDouble() const { return XMLRPC_GetValueDouble(mV); }
00058 
00059 LLXMLRPCValue LLXMLRPCValue::rewind()
00060 {
00061         return LLXMLRPCValue(XMLRPC_VectorRewind(mV));
00062 }
00063 
00064 LLXMLRPCValue LLXMLRPCValue::next()
00065 {
00066         return LLXMLRPCValue(XMLRPC_VectorNext(mV));
00067 }
00068 
00069 bool LLXMLRPCValue::isValid() const
00070 {
00071         return mV != NULL;
00072 }
00073 
00074 LLXMLRPCValue LLXMLRPCValue::createArray()
00075 {
00076         return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_array));
00077 }
00078 
00079 LLXMLRPCValue LLXMLRPCValue::createStruct()
00080 {
00081         return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_struct));
00082 }
00083 
00084 
00085 void LLXMLRPCValue::append(LLXMLRPCValue& v)
00086 {
00087         XMLRPC_AddValueToVector(mV, v.mV);
00088 }
00089 
00090 void LLXMLRPCValue::appendString(const std::string& v)
00091 {
00092         XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(NULL, v.c_str(), 0));
00093 }
00094 
00095 void LLXMLRPCValue::appendInt(int v)
00096 {
00097         XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(NULL, v));
00098 }
00099 
00100 void LLXMLRPCValue::appendBool(bool v)
00101 {
00102         XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(NULL, v));
00103 }
00104 
00105 void LLXMLRPCValue::appendDouble(double v)
00106 {
00107         XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(NULL, v));
00108 }
00109 
00110 
00111 void LLXMLRPCValue::append(const char* id, LLXMLRPCValue& v)
00112 {
00113         XMLRPC_SetValueID(v.mV, id, 0);
00114         XMLRPC_AddValueToVector(mV, v.mV);
00115 }
00116 
00117 void LLXMLRPCValue::appendString(const char* id, const std::string& v)
00118 {
00119         XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(id, v.c_str(), 0));
00120 }
00121 
00122 void LLXMLRPCValue::appendInt(const char* id, int v)
00123 {
00124         XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(id, v));
00125 }
00126 
00127 void LLXMLRPCValue::appendBool(const char* id, bool v)
00128 {
00129         XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(id, v));
00130 }
00131 
00132 void LLXMLRPCValue::appendDouble(const char* id, double v)
00133 {
00134         XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(id, v));
00135 }
00136 
00137 void LLXMLRPCValue::free()
00138 {
00139         XMLRPC_CleanupValue(mV);
00140         mV = NULL;
00141 }
00142 
00143 XMLRPC_VALUE LLXMLRPCValue::getValue() const
00144 {
00145         return mV;
00146 }
00147 
00148 
00149 class LLXMLRPCTransaction::Impl
00150 {
00151 public:
00152         typedef LLXMLRPCTransaction::Status     Status;
00153         
00154         CURL*   mCurl;
00155         CURLM*  mCurlMulti;
00156 
00157         Status          mStatus;
00158         CURLcode        mCurlCode;
00159         std::string     mStatusMessage;
00160         std::string     mStatusURI;
00161         
00162         char                            mCurlErrorBuffer[CURL_ERROR_SIZE];              /* Flawfinder: ignore */
00163 
00164         std::string                     mURI;
00165         char*                           mRequestText;
00166         int                                     mRequestTextSize;
00167         
00168         std::string                     mProxyAddress;
00169         struct curl_slist*      mHeaders;
00170 
00171         std::string                     mResponseText;
00172         XMLRPC_REQUEST          mResponse;
00173         
00174         Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip);
00175         Impl(const std::string& uri,
00176                 const std::string& method, LLXMLRPCValue params, bool useGzip);
00177         ~Impl();
00178         
00179         bool process();
00180         
00181         void setStatus(Status code,
00182                 const std::string& message = "", const std::string& uri = "");
00183         void setCurlStatus(CURLcode);
00184 
00185 private:
00186         void init(XMLRPC_REQUEST request, bool useGzip);
00187 
00188         static size_t curlDownloadCallback(
00189                 void* data, size_t size, size_t nmemb, void* user_data);
00190 };
00191 
00192 LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
00193                 XMLRPC_REQUEST request, bool useGzip)
00194         : mCurl(0), mCurlMulti(0),
00195           mStatus(LLXMLRPCTransaction::StatusNotStarted),
00196           mURI(uri),
00197           mRequestText(0), mHeaders(0),
00198           mResponse(0)
00199 {
00200         init(request, useGzip);
00201 }
00202 
00203 
00204 LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
00205                 const std::string& method, LLXMLRPCValue params, bool useGzip)
00206         : mCurl(0), mCurlMulti(0),
00207           mStatus(LLXMLRPCTransaction::StatusNotStarted),
00208           mURI(uri),
00209           mRequestText(0), mHeaders(0),
00210           mResponse(0)
00211 {
00212         XMLRPC_REQUEST request = XMLRPC_RequestNew();
00213         XMLRPC_RequestSetMethodName(request, method.c_str());
00214         XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
00215         XMLRPC_RequestSetData(request, params.getValue());
00216         
00217         init(request, useGzip);
00218 }
00219 
00220 
00221 
00222 
00223 void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)
00224 {
00225         mCurl = curl_easy_init();
00226 
00227         if (gSavedSettings.getBOOL("BrowserProxyEnabled"))
00228         {
00229                 mProxyAddress = gSavedSettings.getString("BrowserProxyAddress");
00230                 S32 port = gSavedSettings.getS32 ( "BrowserProxyPort" );
00231 
00232                 // tell curl about the settings
00233                 curl_easy_setopt(mCurl, CURLOPT_PROXY, mProxyAddress.c_str());
00234                 curl_easy_setopt(mCurl, CURLOPT_PROXYPORT, (long) port);
00235                 curl_easy_setopt(mCurl, CURLOPT_PROXYTYPE, (long) CURLPROXY_HTTP);
00236         };
00237 
00238 //      curl_easy_setopt(mCurl, CURLOPT_VERBOSE, 1L); // usefull for debugging
00239         curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, 1L);
00240         curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &curlDownloadCallback);
00241         curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, this);
00242         curl_easy_setopt(mCurl, CURLOPT_ERRORBUFFER, &mCurlErrorBuffer);
00243         curl_easy_setopt(mCurl, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str());
00244         curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, (long) gVerifySSLCert);
00245         curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYHOST, gVerifySSLCert? 2L : 0L);
00246         // Be a little impatient about establishing connections.
00247         curl_easy_setopt(mCurl, CURLOPT_CONNECTTIMEOUT, 40L);
00248 
00249         /* Setting the DNS cache timeout to -1 disables it completely.
00250            This might help with bug #503 */
00251         curl_easy_setopt(mCurl, CURLOPT_DNS_CACHE_TIMEOUT, -1L);
00252 
00253     mHeaders = curl_slist_append(mHeaders, "Content-Type: text/xml");
00254         curl_easy_setopt(mCurl, CURLOPT_URL, mURI.c_str());
00255         curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mHeaders);
00256         if (useGzip)
00257         {
00258                 curl_easy_setopt(mCurl, CURLOPT_ENCODING, "");
00259         }
00260         
00261         mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize);
00262         if (mRequestText)
00263         {
00264                 curl_easy_setopt(mCurl, CURLOPT_POSTFIELDS, mRequestText);
00265                 curl_easy_setopt(mCurl, CURLOPT_POSTFIELDSIZE, (long) mRequestTextSize);
00266         }
00267         else
00268         {
00269                 setStatus(StatusOtherError);
00270         }
00271         
00272         mCurlMulti = curl_multi_init();
00273         curl_multi_add_handle(mCurlMulti, mCurl);
00274 }
00275 
00276 
00277 LLXMLRPCTransaction::Impl::~Impl()
00278 {
00279         if (mResponse)
00280         {
00281                 XMLRPC_RequestFree(mResponse, 1);
00282         }
00283         
00284         if (mHeaders)
00285         {
00286                 curl_slist_free_all(mHeaders);
00287         }
00288         
00289         if (mRequestText)
00290         {
00291                 XMLRPC_Free(mRequestText);
00292         }
00293         
00294         if (mCurl)
00295         {
00296                 if (mCurlMulti)
00297                 {
00298                         curl_multi_remove_handle(mCurlMulti, mCurl);
00299                 }
00300                 curl_easy_cleanup(mCurl);
00301         }
00302         
00303         if (mCurlMulti)
00304         {
00305                 curl_multi_cleanup(mCurlMulti);
00306         }
00307         
00308 }
00309 
00310 bool LLXMLRPCTransaction::Impl::process()
00311 {
00312         switch(mStatus)
00313         {
00314                 case LLXMLRPCTransaction::StatusComplete:
00315                 case LLXMLRPCTransaction::StatusCURLError:
00316                 case LLXMLRPCTransaction::StatusXMLRPCError:
00317                 case LLXMLRPCTransaction::StatusOtherError:
00318                 {
00319                         return true;
00320                 }
00321                 
00322                 case LLXMLRPCTransaction::StatusNotStarted:
00323                 {
00324                         setStatus(LLXMLRPCTransaction::StatusStarted);
00325                         break;
00326                 }
00327                 
00328                 default:
00329                 {
00330                         // continue onward
00331                 }
00332         }
00333         
00334         const F32 MAX_PROCESSING_TIME = 0.05f;
00335         LLTimer timer;
00336         int count;
00337         
00338         while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(mCurlMulti, &count))
00339         {
00340                 if (timer.getElapsedTimeF32() >= MAX_PROCESSING_TIME)
00341                 {
00342                         return false;
00343                 }
00344         }
00345                          
00346         while(CURLMsg* curl_msg = curl_multi_info_read(mCurlMulti, &count))
00347         {
00348                 if (CURLMSG_DONE == curl_msg->msg)
00349                 {
00350                         if (curl_msg->data.result != CURLE_OK)
00351                         {
00352                                 setCurlStatus(curl_msg->data.result);
00353                                 llwarns << "LLXMLRPCTransaction CURL error "
00354                                         << mCurlCode << ": " << mCurlErrorBuffer << llendl;
00355                                 llwarns << "LLXMLRPCTransaction request URI: "
00356                                         << mURI << llendl;
00357                                         
00358                                 return true;
00359                         }
00360                         
00361                         setStatus(LLXMLRPCTransaction::StatusComplete);
00362 
00363                         mResponse = XMLRPC_REQUEST_FromXML(
00364                                 mResponseText.data(), mResponseText.size(), NULL);
00365 
00366                         bool            hasError = false;
00367                         bool            hasFault = false;
00368                         int                     faultCode = 0;
00369                         std::string     faultString;
00370 
00371                         LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse));
00372                         if (error.isValid())
00373                         {
00374                                 hasError = true;
00375                                 faultCode = error["faultCode"].asInt();
00376                                 faultString = error["faultString"].asString();
00377                         }
00378                         else if (XMLRPC_ResponseIsFault(mResponse))
00379                         {
00380                                 hasFault = true;
00381                                 faultCode = XMLRPC_GetResponseFaultCode(mResponse);
00382                                 faultString = XMLRPC_GetResponseFaultString(mResponse);
00383                         }
00384 
00385                         if (hasError || hasFault)
00386                         {
00387                                 setStatus(LLXMLRPCTransaction::StatusXMLRPCError);
00388                                 
00389                                 llwarns << "LLXMLRPCTransaction XMLRPC "
00390                                         << (hasError ? "error " : "fault ")
00391                                         << faultCode << ": "
00392                                         << faultString << llendl;
00393                                 llwarns << "LLXMLRPCTransaction request URI: "
00394                                         << mURI << llendl;
00395                         }
00396                         
00397                         return true;
00398                 }
00399         }
00400         
00401         return false;
00402 }
00403 
00404 void LLXMLRPCTransaction::Impl::setStatus(Status status,
00405         const std::string& message, const std::string& uri)
00406 {
00407         mStatus = status;
00408         mStatusMessage = message;
00409         mStatusURI = uri;
00410 
00411         if (mStatusMessage.empty())
00412         {
00413                 switch (mStatus)
00414                 {
00415                         case StatusNotStarted:
00416                                 mStatusMessage = "(not started)";
00417                                 break;
00418                                 
00419                         case StatusStarted:
00420                                 mStatusMessage = "(waiting for server response)";
00421                                 break;
00422                                 
00423                         case StatusDownloading:
00424                                 mStatusMessage = "(reading server response)";
00425                                 break;
00426                                 
00427                         case StatusComplete:
00428                                 mStatusMessage = "(done)";
00429                                 break;
00430                                 
00431                         default:
00432                                 // Usually this means that there's a problem with the login server,
00433                                 // not with the client.  Direct user to status page. JC
00434                                 mStatusMessage =
00435                                         "Despite our best efforts, something unexpected has gone wrong. \n"
00436                                         " \n"
00437                                         "Please check www.secondlife.com/status \n"
00438                                         "to see if there is a known problem with the service.";
00439 
00440                                 mStatusURI = "http://secondlife.com/status/";
00441                                 /*
00442                                 mStatusMessage =
00443                                         "Despite our best efforts, something unexpected has gone wrong.\n"
00444                                         "Please go to the Support section of the SecondLife.com web site\n"
00445                                         "and report the problem.  If possible, include your SecondLife.log\n"
00446                                         "file from:\n"
00447 #if LL_WINDOWS
00448                                         "C:\\Documents and Settings\<name>\\Application Data\\SecondLife\\logs\n"
00449 #elif LL_DARWIN
00450                                         "~/Library/Application Support/SecondLife/logs\n"
00451 #elif LL_LINUX
00452                                         "~/.secondlife/logs\n"
00453 #else
00454 #error "Need platform here."
00455 #endif
00456                                         "Thank you.";
00457                                 mStatusURI = "http://secondlife.com/community/support.php";
00458                                 */
00459                 }
00460         }
00461 }
00462 
00463 void LLXMLRPCTransaction::Impl::setCurlStatus(CURLcode code)
00464 {
00465         std::string message;
00466         std::string uri = "http://secondlife.com/community/support.php";
00467         
00468         switch (code)
00469         {
00470                 case CURLE_COULDNT_RESOLVE_HOST:
00471                         message =
00472                                 "DNS could not resolve the host name.\n"
00473                                 "Please verify that you can connect to the www.secondlife.com\n"
00474                                 "web site.  If you can, but continue to receive this error,\n"
00475                                 "please go to the support section and report this problem.";
00476                         break;
00477                         
00478                 case CURLE_SSL_PEER_CERTIFICATE:
00479                         message =
00480                                 "The login server couldn't verify itself via SSL.\n"
00481                                 "If you continue to receive this error, please go\n"
00482                                 "to the Support section of the SecondLife.com web site\n"
00483                                 "and report the problem.";
00484                         break;
00485                         
00486                 case CURLE_SSL_CACERT:
00487                 case CURLE_SSL_CONNECT_ERROR:
00488                         message =
00489                                 "Often this means that your computer\'s clock is set incorrectly.\n"
00490                                 "Please go to Control Panels and make sure the time and date\n"
00491                                 "are set correctly.\n"
00492                                 "\n"
00493                                 "If you continue to receive this error, please go\n"
00494                                 "to the Support section of the SecondLife.com web site\n"
00495                                 "and report the problem.";
00496                         break;
00497                         
00498                 default:
00499                                 break;
00500         }
00501         
00502         mCurlCode = code;
00503         setStatus(StatusCURLError, message, uri);
00504 }
00505 
00506 size_t LLXMLRPCTransaction::Impl::curlDownloadCallback(
00507                 void* data, size_t size, size_t nmemb, void* user_data)
00508 {
00509         Impl& impl(*(Impl*)user_data);
00510         
00511         size_t n = size * nmemb;
00512 
00513         impl.mResponseText.append((const char*)data, n);
00514         
00515         if (impl.mStatus == LLXMLRPCTransaction::StatusStarted)
00516         {
00517                 impl.setStatus(LLXMLRPCTransaction::StatusDownloading);
00518         }
00519         
00520         return n;
00521 }
00522 
00523 
00524 LLXMLRPCTransaction::LLXMLRPCTransaction(
00525         const std::string& uri, XMLRPC_REQUEST request, bool useGzip)
00526 : impl(* new Impl(uri, request, useGzip))
00527 { }
00528 
00529 
00530 LLXMLRPCTransaction::LLXMLRPCTransaction(
00531         const std::string& uri,
00532         const std::string& method, LLXMLRPCValue params, bool useGzip)
00533 : impl(* new Impl(uri, method, params, useGzip))
00534 { }
00535 
00536 LLXMLRPCTransaction::~LLXMLRPCTransaction()
00537 {
00538         delete &impl;
00539 }
00540 
00541 bool LLXMLRPCTransaction::process()
00542 {
00543         return impl.process();
00544 }
00545 
00546 LLXMLRPCTransaction::Status LLXMLRPCTransaction::status(int* curlCode)
00547 {
00548         if (curlCode)
00549         {
00550                 *curlCode = 
00551                         (impl.mStatus == StatusCURLError)
00552                                 ? impl.mCurlCode
00553                                 : CURLE_OK;
00554         }
00555                 
00556         return impl.mStatus;
00557 }
00558 
00559 std::string LLXMLRPCTransaction::statusMessage()
00560 {
00561         return impl.mStatusMessage;
00562 }
00563 
00564 std::string LLXMLRPCTransaction::statusURI()
00565 {
00566         return impl.mStatusURI;
00567 }
00568 
00569 XMLRPC_REQUEST LLXMLRPCTransaction::response()
00570 {
00571         return impl.mResponse;
00572 }
00573 
00574 LLXMLRPCValue LLXMLRPCTransaction::responseValue()
00575 {
00576         return LLXMLRPCValue(XMLRPC_RequestGetData(impl.mResponse));
00577 }
00578 
00579 
00580 F64 LLXMLRPCTransaction::transferRate()
00581 {
00582         if (!impl.mCurl  ||  impl.mStatus != StatusComplete)
00583         {
00584                 return 0.0L;
00585         }
00586         
00587         double size_bytes = 0.0;
00588         double time_seconds = 0.0;
00589         double rate_bytes_per_sec = 0.0;
00590 
00591         curl_easy_getinfo(impl.mCurl, CURLINFO_SIZE_DOWNLOAD, &size_bytes);
00592         curl_easy_getinfo(impl.mCurl, CURLINFO_TOTAL_TIME, &time_seconds);
00593         curl_easy_getinfo(impl.mCurl, CURLINFO_SPEED_DOWNLOAD, &rate_bytes_per_sec);
00594 
00595         double rate_bits_per_sec = rate_bytes_per_sec * 8.0;
00596         
00597         llinfos << "Buffer size:   " << impl.mResponseText.size() << " B" << llendl;
00598         llinfos << "Transfer size: " << size_bytes << " B" << llendl;
00599         llinfos << "Transfer time: " << time_seconds << " s" << llendl;
00600         llinfos << "Transfer rate: " << rate_bits_per_sec/1000.0 << " Kb/s" << llendl;
00601 
00602         return rate_bits_per_sec;
00603 }

Generated on Thu Jul 1 06:09:48 2010 for Second Life Viewer by  doxygen 1.4.7