diff options
author | Rider Linden <none@none> | 2015-03-16 17:14:34 -0700 |
---|---|---|
committer | Rider Linden <none@none> | 2015-03-16 17:14:34 -0700 |
commit | 6f4d36634e980bb989b9a8b762c3c622804c43dd (patch) | |
tree | efa71ac14bdef46b9796688d4a445d60fdd5b1c3 | |
parent | d4a2e9fd9a0e7001a6c824ddd6cf37039a632b9d (diff) |
Removal of RPCXML dep on LLCurl switching to LLCore::Html
30 files changed, 1280 insertions, 840 deletions
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index b9632a7921..48e22468cd 100755 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -115,8 +115,9 @@ namespace LLCore { -HttpOpRequest::HttpOpRequest() +HttpOpRequest::HttpOpRequest(HttpRequest const * const request) : HttpOperation(), + mRequest(request), mProcFlags(0U), mReqMethod(HOR_GET), mReqBody(NULL), @@ -139,7 +140,8 @@ HttpOpRequest::HttpOpRequest() mPolicyRetries(0), mPolicy503Retries(0), mPolicyRetryAt(HttpTime(0)), - mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT) + mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT), + mCallbackSSLVerify(NULL) { // *NOTE: As members are added, retry initialization/cleanup // may need to be extended in @see prepareRequest(). @@ -267,6 +269,14 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) response->setContentType(mReplyConType); response->setRetries(mPolicyRetries, mPolicy503Retries); + HttpResponse::TransferStats::ptr_t stats = HttpResponse::TransferStats::ptr_t(new HttpResponse::TransferStats); + + curl_easy_getinfo(mCurlHandle, CURLINFO_SIZE_DOWNLOAD, &stats->mSizeDownload); + curl_easy_getinfo(mCurlHandle, CURLINFO_TOTAL_TIME, &stats->mTotalTime); + curl_easy_getinfo(mCurlHandle, CURLINFO_SPEED_DOWNLOAD, &stats->mSpeedDownload); + + response->setTransferStats(stats); + mUserHandler->onCompleted(static_cast<HttpHandle>(this), response); response->release(); @@ -452,18 +462,8 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) code = curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); check_curl_easy_code(code, CURLOPT_ENCODING); - // The Linksys WRT54G V5 router has an issue with frequent - // DNS lookups from LAN machines. If they happen too often, - // like for every HTTP request, the router gets annoyed after - // about 700 or so requests and starts issuing TCP RSTs to - // new connections. Reuse the DNS lookups for even a few - // seconds and no RSTs. - code = curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); - check_curl_easy_code(code, CURLOPT_DNS_CACHE_TIMEOUT); code = curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); check_curl_easy_code(code, CURLOPT_AUTOREFERER); - code = curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); - check_curl_easy_code(code, CURLOPT_FOLLOWLOCATION); code = curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT); check_curl_easy_code(code, CURLOPT_MAXREDIRS); code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); @@ -474,11 +474,49 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) check_curl_easy_code(code, CURLOPT_READFUNCTION); code = curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this); check_curl_easy_code(code, CURLOPT_READDATA); - code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); + + code = curl_easy_setopt(mCurlHandle, CURLOPT_COOKIEFILE, ""); + check_curl_easy_code(code, CURLOPT_COOKIEFILE); + + if (gpolicy.mSslCtxCallback) + { + code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_CTX_FUNCTION, curlSslCtxCallback); + check_curl_easy_code(code, CURLOPT_SSL_CTX_FUNCTION); + code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_CTX_DATA, this); + check_curl_easy_code(code, CURLOPT_SSL_CTX_DATA); + mCallbackSSLVerify = gpolicy.mSslCtxCallback; + } + + long follow_redirect(1L); + long sslPeerV(0L); + long sslHostV(0L); + long dnsCacheTimeout(15L); + + if (mReqOptions) + { + follow_redirect = mReqOptions->getFollowRedirects() ? 1L : 0L; + sslPeerV = mReqOptions->getSSLVerifyHost() ? 0L : 1L; + sslHostV = mReqOptions->getSSLVerifyHost(); + dnsCacheTimeout = mReqOptions->getDNSCacheTimeout(); + } + code = curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, follow_redirect); + check_curl_easy_code(code, CURLOPT_FOLLOWLOCATION); + + code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, sslPeerV); check_curl_easy_code(code, CURLOPT_SSL_VERIFYPEER); - code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); + code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, sslHostV); check_curl_easy_code(code, CURLOPT_SSL_VERIFYHOST); + // The Linksys WRT54G V5 router has an issue with frequent + // DNS lookups from LAN machines. If they happen too often, + // like for every HTTP request, the router gets annoyed after + // about 700 or so requests and starts issuing TCP RSTs to + // new connections. Reuse the DNS lookups for even a few + // seconds and no RSTs. + code = curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, dnsCacheTimeout); + check_curl_easy_code(code, CURLOPT_DNS_CACHE_TIMEOUT); + + if (gpolicy.mUseLLProxy) { // Use the viewer-based thread-safe API which has a @@ -873,6 +911,35 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi } +CURLcode HttpOpRequest::curlSslCtxCallback(CURL *curl, void *sslctx, void *userdata) +{ + HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); + + if (op->mCallbackSSLVerify) + { + SSL_CTX * ctx = (SSL_CTX *)sslctx; + // disable any default verification for server certs + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + // set the verification callback. + SSL_CTX_set_cert_verify_callback(ctx, sslCertVerifyCallback, userdata); + // the calls are void + } + + return CURLE_OK; +} + +int HttpOpRequest::sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param) +{ + HttpOpRequest * op(static_cast<HttpOpRequest *>(param)); + + if (op->mCallbackSSLVerify) + { + op->mStatus = op->mCallbackSSLVerify(op->mReqURL, op->mUserHandler, ctx); + } + + return (op->mStatus) ? 1 : 0; +} + int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata) { HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 2f628b5aba..7a4b7c189e 100755 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -33,6 +33,9 @@ #include <string> #include <curl/curl.h> +#include <openssl/x509_vfy.h> +#include <openssl/ssl.h> + #include "httpcommon.h" #include "httprequest.h" #include "_httpoperation.h" @@ -63,7 +66,7 @@ class HttpOptions; class HttpOpRequest : public HttpOperation { public: - HttpOpRequest(); + HttpOpRequest(HttpRequest const * const request); protected: virtual ~HttpOpRequest(); // Use release() @@ -151,6 +154,9 @@ protected: static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata); static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); + static CURLcode curlSslCtxCallback(CURL *curl, void *ssl_ctx, void *userptr); + static int sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param); + static int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata); protected: @@ -159,8 +165,11 @@ protected: static const unsigned int PF_SAVE_HEADERS = 0x00000002U; static const unsigned int PF_USE_RETRY_AFTER = 0x00000004U; + HttpRequest::policyCallback mCallbackSSLVerify; + public: // Request data + HttpRequest const * const mRequest; EMethod mReqMethod; std::string mReqURL; BufferArray * mReqBody; diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index 1dc95f3dce..c4ef38a815 100755 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -106,6 +106,20 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, const std::stri return HttpStatus(); } +HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback value) +{ + switch (opt) + { + case HttpRequest::PO_SSL_VERIFY_CALLBACK: + mSslCtxCallback = value; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + return HttpStatus(); +} HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, long * value) const { @@ -154,4 +168,20 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, std::string * v return HttpStatus(); } + +HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback * value) const +{ + switch (opt) + { + case HttpRequest::PO_SSL_VERIFY_CALLBACK: + *value = mSslCtxCallback; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + return HttpStatus(); +} + } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h index 67c4ba9481..1696238814 100755 --- a/indra/llcorehttp/_httppolicyglobal.h +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -60,8 +60,10 @@ private: public: HttpStatus set(HttpRequest::EPolicyOption opt, long value); HttpStatus set(HttpRequest::EPolicyOption opt, const std::string & value); + HttpStatus set(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback value); HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const; HttpStatus get(HttpRequest::EPolicyOption opt, std::string * value) const; + HttpStatus get(HttpRequest::EPolicyOption opt, HttpRequest::policyCallback * value) const; public: long mConnectionLimit; @@ -70,6 +72,7 @@ public: std::string mHttpProxy; long mTrace; long mUseLLProxy; + HttpRequest::policyCallback mSslCtxCallback; }; // end class HttpPolicyGlobal } // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index c673e1be1d..7b8aac35a8 100755 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -53,15 +53,16 @@ namespace LLCore const HttpService::OptionDescriptor HttpService::sOptionDesc[] = { // isLong isDynamic isGlobal isClass - { true, true, true, true }, // PO_CONNECTION_LIMIT - { true, true, false, true }, // PO_PER_HOST_CONNECTION_LIMIT - { false, false, true, false }, // PO_CA_PATH - { false, false, true, false }, // PO_CA_FILE - { false, true, true, false }, // PO_HTTP_PROXY - { true, true, true, false }, // PO_LLPROXY - { true, true, true, false }, // PO_TRACE - { true, true, false, true }, // PO_ENABLE_PIPELINING - { true, true, false, true } // PO_THROTTLE_RATE + { true, true, true, true, false }, // PO_CONNECTION_LIMIT + { true, true, false, true, false }, // PO_PER_HOST_CONNECTION_LIMIT + { false, false, true, false, false }, // PO_CA_PATH + { false, false, true, false, false }, // PO_CA_FILE + { false, true, true, false, false }, // PO_HTTP_PROXY + { true, true, true, false, false }, // PO_LLPROXY + { true, true, true, false, false }, // PO_TRACE + { true, true, false, true, false }, // PO_ENABLE_PIPELINING + { true, true, false, true, false }, // PO_THROTTLE_RATE + { false, false, true, false, true } // PO_SSL_VERIFY_CALLBACK }; HttpService * HttpService::sInstance(NULL); volatile HttpService::EState HttpService::sState(NOT_INITIALIZED); @@ -413,6 +414,34 @@ HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ return status; } +HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, + HttpRequest::policyCallback * ret_value) +{ + HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG); + + if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range + || opt >= HttpRequest::PO_LAST // ditto + || (sOptionDesc[opt].mIsLong) // datatype is string + || (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range + || (pclass == HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsGlobal) // global setting permitted + || (pclass != HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsClass)) // class setting permitted + // can always get, no dynamic check + { + return status; + } + + // Only global has callback values + if (pclass == HttpRequest::GLOBAL_POLICY_ID) + { + HttpPolicyGlobal & opts(mPolicy->getGlobalOptions()); + + status = opts.get(opt, ret_value); + } + + return status; +} + + HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value, long * ret_value) @@ -489,6 +518,37 @@ HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ return status; } - + +HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, + HttpRequest::policyCallback value, HttpRequest::policyCallback * ret_value) +{ + HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG); + + if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range + || opt >= HttpRequest::PO_LAST // ditto + || (sOptionDesc[opt].mIsLong) // datatype is string + || (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy) // pclass in valid range + || (pclass == HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsGlobal) // global setting permitted + || (pclass != HttpRequest::GLOBAL_POLICY_ID && !sOptionDesc[opt].mIsClass) // class setting permitted + || (RUNNING == sState && !sOptionDesc[opt].mIsDynamic)) // dynamic setting permitted + { + return status; + } + + // Callbacks values are always global (at this time). + if (pclass == HttpRequest::GLOBAL_POLICY_ID) + { + HttpPolicyGlobal & opts(mPolicy->getGlobalOptions()); + + status = opts.set(opt, value); + if (status && ret_value) + { + status = opts.get(opt, ret_value); + } + } + + return status; +} + } // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index cf23f3ab61..699a8eaa4f 100755 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -201,17 +201,24 @@ protected: bool mIsDynamic; bool mIsGlobal; bool mIsClass; + bool mIsCallback; }; HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, long * ret_value); HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, std::string * ret_value); + HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, + HttpRequest::policyCallback * ret_value); + HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, long value, long * ret_value); HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, const std::string & value, std::string * ret_value); - + HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t, + HttpRequest::policyCallback value, + HttpRequest::policyCallback * ret_value); + protected: static const OptionDescriptor sOptionDesc[HttpRequest::PO_LAST]; static HttpService * sInstance; diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h index 402e725152..cd16e2e2b4 100755 --- a/indra/llcorehttp/_refcounted.h +++ b/indra/llcorehttp/_refcounted.h @@ -120,7 +120,18 @@ inline void RefCounted::destroySelf() delete this; } +inline void intrusive_ptr_add_ref(RefCounted* p) +{ + p->addRef(); +} + +inline void intrusive_ptr_release(RefCounted* p) +{ + p->release(); +} + } // end namespace LLCoreInt + #endif // LLCOREINT__REFCOUNTED_H_ diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index 1094a435b4..9c2b991de6 100755 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -73,6 +73,8 @@ public: BufferArray(); + typedef boost::intrusive_ptr<BufferArray> ptr_t; + protected: virtual ~BufferArray(); // Use release() diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index 7907e958a4..1aece696e3 100755 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -42,7 +42,7 @@ HttpStatus::operator unsigned long() const { static const int shift(sizeof(unsigned long) * 4); - unsigned long result(((unsigned long) mType) << shift | (unsigned long) (int) mStatus); + unsigned long result(((unsigned long)mDetails->mType) << shift | (unsigned long)(int)mDetails->mStatus); return result; } @@ -131,18 +131,18 @@ std::string HttpStatus::toString() const { return std::string(""); } - switch (mType) + switch (mDetails->mType) { case EXT_CURL_EASY: - return std::string(curl_easy_strerror(CURLcode(mStatus))); + return std::string(curl_easy_strerror(CURLcode(mDetails->mStatus))); case EXT_CURL_MULTI: - return std::string(curl_multi_strerror(CURLMcode(mStatus))); + return std::string(curl_multi_strerror(CURLMcode(mDetails->mStatus))); case LLCORE: - if (mStatus >= 0 && mStatus < llcore_errors_count) + if (mDetails->mStatus >= 0 && mDetails->mStatus < llcore_errors_count) { - return std::string(llcore_errors[mStatus]); + return std::string(llcore_errors[mDetails->mStatus]); } break; @@ -154,7 +154,7 @@ std::string HttpStatus::toString() const while (true) { int at((bottom + top) / 2); - if (mType == http_errors[at].mCode) + if (mDetails->mType == http_errors[at].mCode) { return std::string(http_errors[at].mText); } @@ -162,7 +162,7 @@ std::string HttpStatus::toString() const { break; } - else if (mType < http_errors[at].mCode) + else if (mDetails->mType < http_errors[at].mCode) { top = at; } @@ -182,9 +182,9 @@ std::string HttpStatus::toTerseString() const { std::ostringstream result; - unsigned int error_value((unsigned short) mStatus); + unsigned int error_value((unsigned short)mDetails->mStatus); - switch (mType) + switch (mDetails->mType) { case EXT_CURL_EASY: result << "Easy_"; @@ -202,7 +202,7 @@ std::string HttpStatus::toTerseString() const if (isHttpStatus()) { result << "Http_"; - error_value = mType; + error_value = mDetails->mType; } else { @@ -244,7 +244,7 @@ bool HttpStatus::isRetryable() const // Disable the '*this == inv_status' test and look for 'Core_9' // failures in log files. - return ((isHttpStatus() && mType >= 499 && mType <= 599) || // Include special 499 in retryables + return ((isHttpStatus() && mDetails->mType >= 499 && mDetails->mType <= 599) || // Include special 499 in retryables *this == cant_connect || // Connection reset/endpoint problems *this == cant_res_proxy || // DNS problems *this == cant_res_host || // DNS problems diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 9601f94125..0244755272 100755 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -191,7 +191,6 @@ #include <string> - namespace LLCore { @@ -292,35 +291,35 @@ struct HttpStatus typedef unsigned short type_enum_t; HttpStatus() - : mType(LLCORE), - mStatus(HE_SUCCESS) - {} + { + mDetails = std::unique_ptr<Details>(new Details(LLCORE, HE_SUCCESS)); + } HttpStatus(type_enum_t type, short status) - : mType(type), - mStatus(status) - {} + { + mDetails = std::unique_ptr<Details>(new Details(type, status)); + } HttpStatus(int http_status) - : mType(http_status), - mStatus(http_status >= 200 && http_status <= 299 - ? HE_SUCCESS - : HE_REPLY_ERROR) { + mDetails = std::unique_ptr<Details>(new Details(http_status, + (http_status >= 200 && http_status <= 299) ? HE_SUCCESS : HE_REPLY_ERROR)); llassert(http_status >= 100 && http_status <= 999); } HttpStatus(const HttpStatus & rhs) - : mType(rhs.mType), - mStatus(rhs.mStatus) - {} + { + mDetails = std::unique_ptr<Details>(new Details(*rhs.mDetails)); + } HttpStatus & operator=(const HttpStatus & rhs) { // Don't care if lhs & rhs are the same object + mDetails->mType = rhs.mDetails->mType; + mDetails->mStatus = rhs.mDetails->mStatus; + mDetails->mMessage = rhs.mDetails->mMessage; + mDetails->mErrorData = rhs.mDetails->mErrorData; - mType = rhs.mType; - mStatus = rhs.mStatus; return *this; } @@ -328,10 +327,6 @@ struct HttpStatus static const type_enum_t EXT_CURL_MULTI = 1; ///< mStatus is an error from a curl_multi_*() call static const type_enum_t LLCORE = 2; ///< mStatus is an HE_* error code ///< 100-999 directly represent HTTP status codes - - type_enum_t mType; - short mStatus; - /// Test for successful status in the code regardless /// of error source (internal, libcurl). /// @@ -339,7 +334,7 @@ struct HttpStatus /// operator bool() const { - return 0 == mStatus; + return 0 == mDetails->mStatus; } /// Inverse of previous operator. @@ -347,14 +342,15 @@ struct HttpStatus /// @return 'true' on any error condition bool operator !() const { - return 0 != mStatus; + return 0 != mDetails->mStatus; } /// Equality and inequality tests to bypass bool conversion /// which will do the wrong thing in conditional expressions. bool operator==(const HttpStatus & rhs) const { - return mType == rhs.mType && mStatus == rhs.mStatus; + return (mDetails->mType == rhs.mDetails->mType) && + (mDetails->mStatus == rhs.mDetails->mStatus); } bool operator!=(const HttpStatus & rhs) const @@ -395,7 +391,7 @@ struct HttpStatus /// HTTP response status (100 - 999). bool isHttpStatus() const { - return mType >= type_enum_t(100) && mType <= type_enum_t(999); + return mDetails->mType >= type_enum_t(100) && mDetails->mType <= type_enum_t(999); } /// Returns true if the status is one that will be retried @@ -403,7 +399,77 @@ struct HttpStatus /// where that logic needs to be replicated. Only applies /// to failed statuses, successful statuses will return false. bool isRetryable() const; - + + /// Returns the currently set status code as a raw number + /// + short getStatus() const + { + return mDetails->mStatus; + } + + /// Returns the currently set status type + /// + type_enum_t getType() const + { + return mDetails->mType; + } + + // TODO: There must be a better way to do this. Don't want to set these + // values here since they increase the size of a structure that is already + // being passed on the stack. Consider my options + /// Returns an optional error message if one has been set. + /// + std::string getMessage() const + { + return mDetails->mMessage; + } + + /// Sets an optional error message + /// + void setMessage(const std::string &message) + { + mDetails->mMessage = message; + } + + /// Retrieves an optionally recorded SSL certificate. + void * getErrorData() const + { + return mDetails->mErrorData; + } + + /// Optionally sets an SSL certificate on this status. + void setErrorData(void *data) + { + mDetails->mErrorData = data; + } + +private: + + struct Details + { + Details(type_enum_t type, short status): + mType(type), + mStatus(status), + mMessage(), + mErrorData(NULL) + {} + + Details(const Details &rhs) : + mType(rhs.mType), + mStatus(rhs.mStatus), + mMessage(rhs.mMessage), + mErrorData(rhs.mErrorData) + {} + + + type_enum_t mType; + short mStatus; + std::string mMessage; + void * mErrorData; + }; + + std::unique_ptr<Details> mDetails; + }; // end struct HttpStatus } // end namespace LLCore diff --git a/indra/llcorehttp/httphandler.h b/indra/llcorehttp/httphandler.h index 9171e4e7b9..740e986dec 100755 --- a/indra/llcorehttp/httphandler.h +++ b/indra/llcorehttp/httphandler.h @@ -45,7 +45,7 @@ class HttpResponse; /// be shared by any number of requests and across instances /// of HttpRequest running in the same thread. /// -/// Threading: HttpHandler itself is pure interface and is +/// Threading: HttpHandler itself is interface and is /// tread-compatible. Most derivations, however, will have /// different constraints. /// @@ -58,7 +58,7 @@ class HttpHandler { public: virtual ~HttpHandler() - {} + { } /// Method invoked during calls to @see update(). Each invocation /// represents the completion of some requested operation. Caller diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h index f70cd898f3..c89d6af222 100755 --- a/indra/llcorehttp/httpheaders.h +++ b/indra/llcorehttp/httpheaders.h @@ -92,6 +92,7 @@ public: /// the instance. HttpHeaders(); + typedef boost::intrusive_ptr<HttpHeaders> ptr_t; protected: virtual ~HttpHeaders(); // Use release() diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index 5bf1ecb4a5..28c2c25e92 100755 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -25,7 +25,7 @@ */ #include "httpoptions.h" - +#include "lldefs.h" #include "_httpinternal.h" @@ -33,14 +33,17 @@ namespace LLCore { -HttpOptions::HttpOptions() - : RefCounted(true), - mWantHeaders(false), - mTracing(HTTP_TRACE_OFF), - mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT), - mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT), - mRetries(HTTP_RETRY_COUNT_DEFAULT), - mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT) +HttpOptions::HttpOptions() : RefCounted(true), + mWantHeaders(false), + mTracing(HTTP_TRACE_OFF), + mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT), + mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT), + mRetries(HTTP_RETRY_COUNT_DEFAULT), + mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT), + mFollowRedirects(false), + mVerifyPeer(false), + mVerifyHost(0), + mDNSCacheTimeout(15) {} @@ -82,5 +85,23 @@ void HttpOptions::setUseRetryAfter(bool use_retry) mUseRetryAfter = use_retry; } +void HttpOptions::setFollowRedirects(bool follow_redirect) +{ + mFollowRedirects = follow_redirect; +} + +void HttpOptions::setSSLVerifyPeer(bool verify) +{ + mVerifyPeer = verify; +} +void HttpOptions::setSSLVerifyHost(unsigned int type) +{ + mVerifyHost = llclamp<unsigned int>(type, 0, 2); +} + +void HttpOptions::setDNSCacheTimeout(int timeout) +{ + mDNSCacheTimeout = timeout; +} } // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index 4ab5ff18c4..d6d892213d 100755 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -61,6 +61,8 @@ class HttpOptions : public LLCoreInt::RefCounted public: HttpOptions(); + typedef boost::intrusive_ptr<HttpOptions> ptr_t; + protected: virtual ~HttpOptions(); // Use release() @@ -109,6 +111,31 @@ public: { return mUseRetryAfter; } + + // Default: false + void setFollowRedirects(bool follow_redirect); + bool getFollowRedirects() const + { + return mFollowRedirects; + } + + void setSSLVerifyPeer(bool verify); + bool getSSLVerifyPeer() const + { + return mVerifyPeer; + } + + void setSSLVerifyHost(unsigned int type); + unsigned int getSSLVerifyHost() const + { + return mVerifyHost; + } + + void setDNSCacheTimeout(int timeout); + int getDNSCacheTimeout() const + { + return mDNSCacheTimeout; + } protected: bool mWantHeaders; @@ -117,6 +144,10 @@ protected: unsigned int mTransferTimeout; unsigned int mRetries; bool mUseRetryAfter; + bool mFollowRedirects; + bool mVerifyPeer; + unsigned int mVerifyHost; + int mDNSCacheTimeout; }; // end class HttpOptions diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 7b1888e3eb..5f1ed3d43b 100755 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -117,6 +117,15 @@ HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value); } +HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass, policyCallback value, policyCallback * ret_value) +{ + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + + return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value); +} HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass, long value, HttpHandler * handler) @@ -195,7 +204,7 @@ HttpHandle HttpRequest::requestGet(policy_t policy_id, HttpStatus status; HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpRequest * op = new HttpOpRequest(); + HttpOpRequest * op = new HttpOpRequest(this); if (! (status = op->setupGet(policy_id, priority, url, options, headers))) { op->release(); @@ -229,7 +238,7 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, HttpStatus status; HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpRequest * op = new HttpOpRequest(); + HttpOpRequest * op = new HttpOpRequest(this); if (! (status = op->setupGetByteRange(policy_id, priority, url, offset, len, options, headers))) { op->release(); @@ -262,7 +271,7 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, HttpStatus status; HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpRequest * op = new HttpOpRequest(); + HttpOpRequest * op = new HttpOpRequest(this); if (! (status = op->setupPost(policy_id, priority, url, body, options, headers))) { op->release(); @@ -295,7 +304,7 @@ HttpHandle HttpRequest::requestPut(policy_t policy_id, HttpStatus status; HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpRequest * op = new HttpOpRequest(); + HttpOpRequest * op = new HttpOpRequest(this); if (! (status = op->setupPut(policy_id, priority, url, body, options, headers))) { op->release(); diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 7f23723b0b..c90e056d62 100755 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -97,6 +97,7 @@ public: typedef unsigned int policy_t; typedef unsigned int priority_t; + typedef std::shared_ptr<HttpRequest> ptr_t; public: /// @name PolicyMethods /// @{ @@ -163,7 +164,7 @@ public: /// Long value that if non-zero enables the use of the /// traditional LLProxy code for http/socks5 support. If - // enabled, has priority over GP_HTTP_PROXY. + /// enabled, has priority over GP_HTTP_PROXY. /// /// Global only PO_LLPROXY, @@ -219,15 +220,25 @@ public: /// Controls whether client-side throttling should be /// performed on this policy class. Positive values /// enable throttling and specify the request rate - /// (requests per second) that should be targetted. + /// (requests per second) that should be targeted. /// A value of zero, the default, specifies no throttling. /// /// Per-class only PO_THROTTLE_RATE, + /// Controls the callback function used to control SSL CTX + /// certificate verification. + /// + /// Global only + PO_SSL_VERIFY_CALLBACK, + PO_LAST // Always at end }; + /// Prototype for policy based callbacks. The callback methods will be executed + /// on the worker thread so no modifications should be made to the HttpHandler object. + typedef HttpStatus(*policyCallback)(const std::string &, HttpHandler const * const, void *); + /// Set a policy option for a global or class parameter at /// startup time (prior to thread start). /// @@ -243,6 +254,8 @@ public: long value, long * ret_value); static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass, const std::string & value, std::string * ret_value); + static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass, + policyCallback value, policyCallback * ret_value);; /// Set a parameter on a class-based policy option. Calls /// made after the start of the servicing thread are diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index aee64e2878..01e9dd2bc6 100755 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -69,6 +69,18 @@ protected: void operator=(const HttpResponse &); // Not defined public: + /// Statistics for the HTTP + struct TransferStats + { + typedef std::shared_ptr<TransferStats> ptr_t; + + TransferStats() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {} + F64 mSizeDownload; + F64 mTotalTime; + F64 mSpeedDownload; + }; + + /// Returns the final status of the requested operation. /// HttpStatus getStatus() const @@ -168,6 +180,17 @@ public: m503Retries = retries_503; } + void setTransferStats(TransferStats::ptr_t &stats) + { + mStats = stats; + } + + TransferStats::ptr_t getTransferStats() + { + return mStats; + } + + protected: // Response data here HttpStatus mStatus; @@ -179,6 +202,8 @@ protected: std::string mContentType; unsigned int mRetries; unsigned int m503Retries; + + TransferStats::ptr_t mStats; }; diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 73df47b933..0080dd6138 100755 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -676,6 +676,7 @@ void LLCurl::Easy::prepRequest(const std::string& url, } //////////////////////////////////////////////////////////////////////////// +#if 1 LLCurl::Multi::Multi(F32 idle_time_out) : mQueued(0), mErrorCount(0), @@ -1056,6 +1057,7 @@ void LLCurl::Multi::removeEasy(Easy* easy) easyFree(easy); } +#endif //------------------------------------------------------------ //LLCurlThread LLCurlThread::CurlRequest::CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread) : @@ -1176,428 +1178,428 @@ std::string LLCurl::strerror(CURLcode errorcode) // For generating a simple request for data // using one multi and one easy per request -LLCurlRequest::LLCurlRequest() : - mActiveMulti(NULL), - mActiveRequestCount(0) -{ - mProcessing = FALSE; -} - -LLCurlRequest::~LLCurlRequest() -{ - //stop all Multi handle background threads - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) - { - LLCurl::getCurlThread()->killMulti(*iter) ; - } - mMultiSet.clear() ; -} - -void LLCurlRequest::addMulti() -{ - LLCurl::Multi* multi = new LLCurl::Multi(); - if(!multi->isValid()) - { - LLCurl::getCurlThread()->killMulti(multi) ; - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - return; - } - - mMultiSet.insert(multi); - mActiveMulti = multi; - mActiveRequestCount = 0; -} - -LLCurl::Easy* LLCurlRequest::allocEasy() -{ - if (!mActiveMulti || - mActiveRequestCount >= MAX_ACTIVE_REQUEST_COUNT || - mActiveMulti->mErrorCount > 0) - { - addMulti(); - } - if(!mActiveMulti) - { - return NULL ; - } - - //llassert_always(mActiveMulti); - ++mActiveRequestCount; - LLCurl::Easy* easy = mActiveMulti->allocEasy(); - return easy; -} - -bool LLCurlRequest::addEasy(LLCurl::Easy* easy) -{ - llassert_always(mActiveMulti); - - if (mProcessing) - { - LL_ERRS() << "Posting to a LLCurlRequest instance from within a responder is not allowed (causes DNS timeouts)." << LL_ENDL; - } - bool res = mActiveMulti->addEasy(easy); - return res; -} - -void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - getByteRange(url, headers_t(), 0, -1, responder); -} - -// Note: (length==0) is interpreted as "the rest of the file", i.e. the whole file if (offset==0) or -// the remainder of the file if not. -bool LLCurlRequest::getByteRange(const std::string& url, - const headers_t& headers, - S32 offset, S32 length, - LLCurl::ResponderPtr responder) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder); - easy->setopt(CURLOPT_HTTPGET, 1); - if (length > 0) - { - std::string range = llformat("bytes=%d-%d", offset,offset+length-1); - easy->slist_append(HTTP_OUT_HEADER_RANGE, range); - } - else if (offset > 0) - { - std::string range = llformat("bytes=%d-", offset); - easy->slist_append(HTTP_OUT_HEADER_RANGE, range); - } - easy->setHeaders(); - bool res = addEasy(easy); - return res; -} - -bool LLCurlRequest::post(const std::string& url, - const headers_t& headers, - const LLSD& data, - LLCurl::ResponderPtr responder, S32 time_out) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder, time_out); - - LLSDSerialize::toXML(data, easy->getInput()); - S32 bytes = easy->getInput().str().length(); - - easy->setopt(CURLOPT_POST, 1); - easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); - easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - - easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); - easy->setHeaders(); - - LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; - bool res = addEasy(easy); - return res; -} - -bool LLCurlRequest::post(const std::string& url, - const headers_t& headers, - const std::string& data, - LLCurl::ResponderPtr responder, S32 time_out) -{ - llassert(LLCurl::sNotQuitting); - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder, time_out); - - easy->getInput().write(data.data(), data.size()); - S32 bytes = easy->getInput().str().length(); - - easy->setopt(CURLOPT_POST, 1); - easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); - easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - - easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_OCTET_STREAM); - easy->setHeaders(); - - LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; - bool res = addEasy(easy); - return res; -} - -// Note: call once per frame -S32 LLCurlRequest::process() -{ - S32 res = 0; - - mProcessing = TRUE; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - - if(!multi->isValid()) - { - if(multi == mActiveMulti) - { - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - } - mMultiSet.erase(curiter) ; - LLCurl::getCurlThread()->killMulti(multi) ; - continue ; - } - - S32 tres = multi->process(); - res += tres; - if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) - { - mMultiSet.erase(curiter); - LLCurl::getCurlThread()->killMulti(multi); - } - } - mProcessing = FALSE; - return res; -} - -S32 LLCurlRequest::getQueued() -{ - S32 queued = 0; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - - if(!multi->isValid()) - { - if(multi == mActiveMulti) - { - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - } - LLCurl::getCurlThread()->killMulti(multi); - mMultiSet.erase(curiter) ; - continue ; - } - - queued += multi->mQueued; - if (multi->getState() != LLCurl::Multi::STATE_READY) - { - ++queued; - } - } - return queued; -} - -LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : - LLCurlRequest(), - mConcurrency(concurrency), - mInQueue(0), - mMutex(NULL), - mHandleCounter(1), - mTotalIssuedRequests(0), - mTotalReceivedBits(0) -{ - mGlobalTimer.reset(); -} - -LLCurlTextureRequest::~LLCurlTextureRequest() -{ - mRequestMap.clear(); - - for(req_queue_t::iterator iter = mCachedRequests.begin(); iter != mCachedRequests.end(); ++iter) - { - delete *iter; - } - mCachedRequests.clear(); -} - -//return 0: success -// > 0: cached handle -U32 LLCurlTextureRequest::getByteRange(const std::string& url, - const headers_t& headers, - S32 offset, S32 length, U32 pri, - LLCurl::ResponderPtr responder, F32 delay_time) -{ - U32 ret_val = 0; - bool success = false; - - if(mInQueue < mConcurrency && delay_time < 0.f) - { - success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); - } - - LLMutexLock lock(&mMutex); - - if(success) - { - mInQueue++; - mTotalIssuedRequests++; - } - else - { - request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); - if(delay_time > 0.f) - { - request->mStartTime = mGlobalTimer.getElapsedTimeF32() + delay_time; - } - - mCachedRequests.insert(request); - mRequestMap[mHandleCounter] = request; - ret_val = mHandleCounter; - mHandleCounter++; - - if(!mHandleCounter) - { - mHandleCounter = 1; - } - } - - return ret_val; -} - -void LLCurlTextureRequest::completeRequest(S32 received_bytes) -{ - LLMutexLock lock(&mMutex); - - llassert_always(mInQueue > 0); - - mInQueue--; - mTotalReceivedBits += received_bytes * 8; -} - -void LLCurlTextureRequest::nextRequests() -{ - if(mCachedRequests.empty() || mInQueue >= mConcurrency) - { - return; - } - - F32 cur_time = mGlobalTimer.getElapsedTimeF32(); - - req_queue_t::iterator iter; - { - LLMutexLock lock(&mMutex); - iter = mCachedRequests.begin(); - } - while(1) - { - request_t* request = *iter; - if(request->mStartTime < cur_time) - { - if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) - { - break; - } - - LLMutexLock lock(&mMutex); - ++iter; - mInQueue++; - mTotalIssuedRequests++; - mCachedRequests.erase(request); - mRequestMap.erase(request->mHandle); - delete request; - - if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) - { - break; - } - } - else - { - LLMutexLock lock(&mMutex); - ++iter; - if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) - { - break; - } - } - } - - return; -} - -void LLCurlTextureRequest::updatePriority(U32 handle, U32 pri) -{ - if(!handle) - { - return; - } - - LLMutexLock lock(&mMutex); - - std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); - if(iter != mRequestMap.end()) - { - request_t* req = iter->second; - - if(req->mPriority != pri) - { - mCachedRequests.erase(req); - req->mPriority = pri; - mCachedRequests.insert(req); - } - } -} - -void LLCurlTextureRequest::removeRequest(U32 handle) -{ - if(!handle) - { - return; - } - - LLMutexLock lock(&mMutex); - - std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); - if(iter != mRequestMap.end()) - { - request_t* req = iter->second; - mRequestMap.erase(iter); - mCachedRequests.erase(req); - delete req; - } -} - -bool LLCurlTextureRequest::isWaiting(U32 handle) -{ - if(!handle) - { - return false; - } - - LLMutexLock lock(&mMutex); - return mRequestMap.find(handle) != mRequestMap.end(); -} - -U32 LLCurlTextureRequest::getTotalReceivedBits() -{ - LLMutexLock lock(&mMutex); - - U32 bits = mTotalReceivedBits; - mTotalReceivedBits = 0; - return bits; -} - -U32 LLCurlTextureRequest::getTotalIssuedRequests() -{ - LLMutexLock lock(&mMutex); - return mTotalIssuedRequests; -} - -S32 LLCurlTextureRequest::getNumRequests() -{ - LLMutexLock lock(&mMutex); - return mInQueue; -} +// LLCurlRequest::LLCurlRequest() : +// mActiveMulti(NULL), +// mActiveRequestCount(0) +// { +// mProcessing = FALSE; +// } +// +// LLCurlRequest::~LLCurlRequest() +// { +// //stop all Multi handle background threads +// for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) +// { +// LLCurl::getCurlThread()->killMulti(*iter) ; +// } +// mMultiSet.clear() ; +// } +// +// void LLCurlRequest::addMulti() +// { +// LLCurl::Multi* multi = new LLCurl::Multi(); +// if(!multi->isValid()) +// { +// LLCurl::getCurlThread()->killMulti(multi) ; +// mActiveMulti = NULL ; +// mActiveRequestCount = 0 ; +// return; +// } +// +// mMultiSet.insert(multi); +// mActiveMulti = multi; +// mActiveRequestCount = 0; +// } +// +// LLCurl::Easy* LLCurlRequest::allocEasy() +// { +// if (!mActiveMulti || +// mActiveRequestCount >= MAX_ACTIVE_REQUEST_COUNT || +// mActiveMulti->mErrorCount > 0) +// { +// addMulti(); +// } +// if(!mActiveMulti) +// { +// return NULL ; +// } +// +// //llassert_always(mActiveMulti); +// ++mActiveRequestCount; +// LLCurl::Easy* easy = mActiveMulti->allocEasy(); +// return easy; +// } +// +// bool LLCurlRequest::addEasy(LLCurl::Easy* easy) +// { +// llassert_always(mActiveMulti); +// +// if (mProcessing) +// { +// LL_ERRS() << "Posting to a LLCurlRequest instance from within a responder is not allowed (causes DNS timeouts)." << LL_ENDL; +// } +// bool res = mActiveMulti->addEasy(easy); +// return res; +// } +// +// void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) +// { +// getByteRange(url, headers_t(), 0, -1, responder); +// } +// +// // Note: (length==0) is interpreted as "the rest of the file", i.e. the whole file if (offset==0) or +// // the remainder of the file if not. +// bool LLCurlRequest::getByteRange(const std::string& url, +// const headers_t& headers, +// S32 offset, S32 length, +// LLCurl::ResponderPtr responder) +// { +// llassert(LLCurl::sNotQuitting); +// LLCurl::Easy* easy = allocEasy(); +// if (!easy) +// { +// return false; +// } +// easy->prepRequest(url, headers, responder); +// easy->setopt(CURLOPT_HTTPGET, 1); +// if (length > 0) +// { +// std::string range = llformat("bytes=%d-%d", offset,offset+length-1); +// easy->slist_append(HTTP_OUT_HEADER_RANGE, range); +// } +// else if (offset > 0) +// { +// std::string range = llformat("bytes=%d-", offset); +// easy->slist_append(HTTP_OUT_HEADER_RANGE, range); +// } +// easy->setHeaders(); +// bool res = addEasy(easy); +// return res; +// } +// +// bool LLCurlRequest::post(const std::string& url, +// const headers_t& headers, +// const LLSD& data, +// LLCurl::ResponderPtr responder, S32 time_out) +// { +// llassert(LLCurl::sNotQuitting); +// LLCurl::Easy* easy = allocEasy(); +// if (!easy) +// { +// return false; +// } +// easy->prepRequest(url, headers, responder, time_out); +// +// LLSDSerialize::toXML(data, easy->getInput()); +// S32 bytes = easy->getInput().str().length(); +// +// easy->setopt(CURLOPT_POST, 1); +// easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); +// easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); +// +// easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); +// easy->setHeaders(); +// +// LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; +// bool res = addEasy(easy); +// return res; +// } +// +// bool LLCurlRequest::post(const std::string& url, +// const headers_t& headers, +// const std::string& data, +// LLCurl::ResponderPtr responder, S32 time_out) +// { +// llassert(LLCurl::sNotQuitting); +// LLCurl::Easy* easy = allocEasy(); +// if (!easy) +// { +// return false; +// } +// easy->prepRequest(url, headers, responder, time_out); +// +// easy->getInput().write(data.data(), data.size()); +// S32 bytes = easy->getInput().str().length(); +// +// easy->setopt(CURLOPT_POST, 1); +// easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); +// easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); +// +// easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_OCTET_STREAM); +// easy->setHeaders(); +// +// LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; +// bool res = addEasy(easy); +// return res; +// } +// +// // Note: call once per frame +// S32 LLCurlRequest::process() +// { +// S32 res = 0; +// +// mProcessing = TRUE; +// for (curlmulti_set_t::iterator iter = mMultiSet.begin(); +// iter != mMultiSet.end(); ) +// { +// curlmulti_set_t::iterator curiter = iter++; +// LLCurl::Multi* multi = *curiter; +// +// if(!multi->isValid()) +// { +// if(multi == mActiveMulti) +// { +// mActiveMulti = NULL ; +// mActiveRequestCount = 0 ; +// } +// mMultiSet.erase(curiter) ; +// LLCurl::getCurlThread()->killMulti(multi) ; +// continue ; +// } +// +// S32 tres = multi->process(); +// res += tres; +// if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) +// { +// mMultiSet.erase(curiter); +// LLCurl::getCurlThread()->killMulti(multi); +// } +// } +// mProcessing = FALSE; +// return res; +// } +// +// S32 LLCurlRequest::getQueued() +// { +// S32 queued = 0; +// for (curlmulti_set_t::iterator iter = mMultiSet.begin(); +// iter != mMultiSet.end(); ) +// { +// curlmulti_set_t::iterator curiter = iter++; +// LLCurl::Multi* multi = *curiter; +// +// if(!multi->isValid()) +// { +// if(multi == mActiveMulti) +// { +// mActiveMulti = NULL ; +// mActiveRequestCount = 0 ; +// } +// LLCurl::getCurlThread()->killMulti(multi); +// mMultiSet.erase(curiter) ; +// continue ; +// } +// +// queued += multi->mQueued; +// if (multi->getState() != LLCurl::Multi::STATE_READY) +// { +// ++queued; +// } +// } +// return queued; +// } + +// LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : +// LLCurlRequest(), +// mConcurrency(concurrency), +// mInQueue(0), +// mMutex(NULL), +// mHandleCounter(1), +// mTotalIssuedRequests(0), +// mTotalReceivedBits(0) +// { +// mGlobalTimer.reset(); +// } +// +// LLCurlTextureRequest::~LLCurlTextureRequest() +// { +// mRequestMap.clear(); +// +// for(req_queue_t::iterator iter = mCachedRequests.begin(); iter != mCachedRequests.end(); ++iter) +// { +// delete *iter; +// } +// mCachedRequests.clear(); +// } +// +// //return 0: success +// // > 0: cached handle +// U32 LLCurlTextureRequest::getByteRange(const std::string& url, +// const headers_t& headers, +// S32 offset, S32 length, U32 pri, +// LLCurl::ResponderPtr responder, F32 delay_time) +// { +// U32 ret_val = 0; +// bool success = false; +// +// if(mInQueue < mConcurrency && delay_time < 0.f) +// { +// success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); +// } +// +// LLMutexLock lock(&mMutex); +// +// if(success) +// { +// mInQueue++; +// mTotalIssuedRequests++; +// } +// else +// { +// request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); +// if(delay_time > 0.f) +// { +// request->mStartTime = mGlobalTimer.getElapsedTimeF32() + delay_time; +// } +// +// mCachedRequests.insert(request); +// mRequestMap[mHandleCounter] = request; +// ret_val = mHandleCounter; +// mHandleCounter++; +// +// if(!mHandleCounter) +// { +// mHandleCounter = 1; +// } +// } +// +// return ret_val; +// } +// +// void LLCurlTextureRequest::completeRequest(S32 received_bytes) +// { +// LLMutexLock lock(&mMutex); +// +// llassert_always(mInQueue > 0); +// +// mInQueue--; +// mTotalReceivedBits += received_bytes * 8; +// } +// +// void LLCurlTextureRequest::nextRequests() +// { +// if(mCachedRequests.empty() || mInQueue >= mConcurrency) +// { +// return; +// } +// +// F32 cur_time = mGlobalTimer.getElapsedTimeF32(); +// +// req_queue_t::iterator iter; +// { +// LLMutexLock lock(&mMutex); +// iter = mCachedRequests.begin(); +// } +// while(1) +// { +// request_t* request = *iter; +// if(request->mStartTime < cur_time) +// { +// if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) +// { +// break; +// } +// +// LLMutexLock lock(&mMutex); +// ++iter; +// mInQueue++; +// mTotalIssuedRequests++; +// mCachedRequests.erase(request); +// mRequestMap.erase(request->mHandle); +// delete request; +// +// if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) +// { +// break; +// } +// } +// else +// { +// LLMutexLock lock(&mMutex); +// ++iter; +// if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) +// { +// break; +// } +// } +// } +// +// return; +// } +// +// void LLCurlTextureRequest::updatePriority(U32 handle, U32 pri) +// { +// if(!handle) +// { +// return; +// } +// +// LLMutexLock lock(&mMutex); +// +// std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); +// if(iter != mRequestMap.end()) +// { +// request_t* req = iter->second; +// +// if(req->mPriority != pri) +// { +// mCachedRequests.erase(req); +// req->mPriority = pri; +// mCachedRequests.insert(req); +// } +// } +// } +// +// void LLCurlTextureRequest::removeRequest(U32 handle) +// { +// if(!handle) +// { +// return; +// } +// +// LLMutexLock lock(&mMutex); +// +// std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); +// if(iter != mRequestMap.end()) +// { +// request_t* req = iter->second; +// mRequestMap.erase(iter); +// mCachedRequests.erase(req); +// delete req; +// } +// } +// +// bool LLCurlTextureRequest::isWaiting(U32 handle) +// { +// if(!handle) +// { +// return false; +// } +// +// LLMutexLock lock(&mMutex); +// return mRequestMap.find(handle) != mRequestMap.end(); +// } +// +// U32 LLCurlTextureRequest::getTotalReceivedBits() +// { +// LLMutexLock lock(&mMutex); +// +// U32 bits = mTotalReceivedBits; +// mTotalReceivedBits = 0; +// return bits; +// } +// +// U32 LLCurlTextureRequest::getTotalIssuedRequests() +// { +// LLMutexLock lock(&mMutex); +// return mTotalIssuedRequests; +// } +// +// S32 LLCurlTextureRequest::getNumRequests() +// { +// LLMutexLock lock(&mMutex); +// return mInQueue; +// } //////////////////////////////////////////////////////////////////////////// // For generating one easy request @@ -1988,10 +1990,10 @@ void LLCurlFF::check_easy_code(CURLcode code) { check_curl_code(code); } -void LLCurlFF::check_multi_code(CURLMcode code) -{ - check_curl_multi_code(code); -} +// void LLCurlFF::check_multi_code(CURLMcode code) +// { +// check_curl_multi_code(code); +// } // Static diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 385d9fffa8..295e9c9fe5 100755 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -309,6 +309,7 @@ private: static void deleteAllFreeHandles(); }; +#if 1 class LLCurl::Multi { LOG_CLASS(Multi); @@ -379,6 +380,7 @@ private: LLFrameTimer mIdleTimer ; F32 mIdleTimeOut; }; +#endif class LLCurlThread : public LLQueuedThread { @@ -417,7 +419,7 @@ private: void cleanupMulti(LLCurl::Multi* multi) ; } ; - +#if 0 class LLCurlRequest { public: @@ -446,7 +448,9 @@ private: S32 mActiveRequestCount; BOOL mProcessing; }; +#endif +#if 0 //for texture fetch only class LLCurlTextureRequest : public LLCurlRequest { @@ -511,6 +515,7 @@ private: LLFrameTimer mGlobalTimer; }; +#endif class LLCurlEasyRequest { @@ -550,7 +555,7 @@ private: namespace LLCurlFF { void check_easy_code(CURLcode code); - void check_multi_code(CURLMcode code); + //void check_multi_code(CURLMcode code); } #endif // LL_LLCURL_H diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 200116337d..27c94b1182 100755 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -38,6 +38,10 @@ #include "lluri.h" #include "message.h" +#include "httpcommon.h" +#include "httprequest.h" +#include "httpoptions.h" + #include <curl/curl.h> @@ -214,6 +218,51 @@ void LLHTTPClient::setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback cal LLHTTPClient::mCertVerifyCallback = callback; } +#if 0 +typedef std::shared_ptr<LLCore::HttpRequest> HttpRequestPtr_t; +typedef std::unique_ptr<LLCore::HttpOptions> HttpOptionsPtr_t; +typedef std::unique_ptr<Injector> InjectorPtr_t; + +static void request_( + const std::string& url, + EHTTPMethod method, + Injector* body_injector, + LLCurl::ResponderPtr responder, + const F32 timeout = HTTP_REQUEST_EXPIRY_SECS, + const LLSD& headers = LLSD(), + bool follow_redirects = true + ) +{ + HttpRequestPtr_t httpReq = HttpRequestPtr_t(new LLCore::HttpRequest()); + + HttpOptionsPtr_t httpOpts = HttpOptionsPtr_t(new LLCore::HttpOptions()); + + httpOpts->setFollowRedirects(follow_redirects); + httpOpts->setRetries(12); + httpOpts->setUseRetryAfter(true); + // for the moment lets just truncate. 60 seconds vs 60.5 seconds + httpOpts->setTransferTimeout((unsigned int)timeout); + + switch (method) + { + case HTTP_GET: + httpReq->requestGet(0, 0, url, httpOpts.get(), headers, handler); + break; + case HTTP_HEAD: + httpReq->requestHead(0, 0, url, httpOpts.get(), headers, handler); + break; + case HTTP_PUT: + httpReq->requestPut(0, 0, url, ); + break; + case HTTP_POST: + httpReq->requestPost(0, 0, url, null, httpOpts.get(), headers, handler); + break; + } + + +} +#endif + static void request( const std::string& url, EHTTPMethod method, diff --git a/indra/llmessage/llhttpclientadapter.cpp b/indra/llmessage/llhttpclientadapter.cpp index b56a804f94..8c2a0ad9cf 100755 --- a/indra/llmessage/llhttpclientadapter.cpp +++ b/indra/llmessage/llhttpclientadapter.cpp @@ -26,48 +26,48 @@ #include "llhttpclientadapter.h" #include "llhttpclient.h" - -LLHTTPClientAdapter::~LLHTTPClientAdapter() -{ -} - -void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - LLSD empty_pragma_header; - // Pragma is required to stop curl adding "no-cache" - // Space is required to stop llurlrequest from turning off proxying - empty_pragma_header[HTTP_OUT_HEADER_PRAGMA] = " "; - LLHTTPClient::get(url, responder, empty_pragma_header); -} - -void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) -{ - LLSD empty_pragma_header = headers; - if (!empty_pragma_header.has(HTTP_OUT_HEADER_PRAGMA)) - { - // as above - empty_pragma_header[HTTP_OUT_HEADER_PRAGMA] = " "; - } - LLHTTPClient::get(url, responder, empty_pragma_header); -} - -void LLHTTPClientAdapter::put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) -{ - LLHTTPClient::put(url, body, responder); -} - -void LLHTTPClientAdapter::put( - const std::string& url, - const LLSD& body, - LLCurl::ResponderPtr responder, - const LLSD& headers) -{ - LLHTTPClient::put(url, body, responder, headers); -} - -void LLHTTPClientAdapter::del( - const std::string& url, - LLCurl::ResponderPtr responder) -{ - LLHTTPClient::del(url, responder); -} +// +// LLHTTPClientAdapter::~LLHTTPClientAdapter() +// { +// } +// +// void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder) +// { +// LLSD empty_pragma_header; +// // Pragma is required to stop curl adding "no-cache" +// // Space is required to stop llurlrequest from turning off proxying +// empty_pragma_header[HTTP_OUT_HEADER_PRAGMA] = " "; +// LLHTTPClient::get(url, responder, empty_pragma_header); +// } +// +// void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) +// { +// LLSD empty_pragma_header = headers; +// if (!empty_pragma_header.has(HTTP_OUT_HEADER_PRAGMA)) +// { +// // as above +// empty_pragma_header[HTTP_OUT_HEADER_PRAGMA] = " "; +// } +// LLHTTPClient::get(url, responder, empty_pragma_header); +// } +// +// void LLHTTPClientAdapter::put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) +// { +// LLHTTPClient::put(url, body, responder); +// } +// +// void LLHTTPClientAdapter::put( +// const std::string& url, +// const LLSD& body, +// LLCurl::ResponderPtr responder, +// const LLSD& headers) +// { +// LLHTTPClient::put(url, body, responder, headers); +// } +// +// void LLHTTPClientAdapter::del( +// const std::string& url, +// LLCurl::ResponderPtr responder) +// { +// LLHTTPClient::del(url, responder); +// } diff --git a/indra/llmessage/llhttpclientadapter.h b/indra/llmessage/llhttpclientadapter.h index 270282c66f..0067703895 100755 --- a/indra/llmessage/llhttpclientadapter.h +++ b/indra/llmessage/llhttpclientadapter.h @@ -30,6 +30,7 @@ #include "llhttpclientinterface.h" #include "llsingleton.h" // LLSingleton<> +/* class LLHTTPClientAdapter : public LLHTTPClientInterface, public LLSingleton<LLHTTPClientAdapter> { public: @@ -46,6 +47,7 @@ public: const std::string& url, LLCurl::ResponderPtr responder); }; +*/ #endif diff --git a/indra/llmessage/llhttpclientinterface.h b/indra/llmessage/llhttpclientinterface.h index 12a3857a61..9c1c8e7c11 100755 --- a/indra/llmessage/llhttpclientinterface.h +++ b/indra/llmessage/llhttpclientinterface.h @@ -32,14 +32,14 @@ #include <string> -class LLHTTPClientInterface -{ -public: - virtual ~LLHTTPClientInterface() {} - virtual void get(const std::string& url, LLCurl::ResponderPtr responder) = 0; - virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) = 0; - virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) = 0; -}; +// class LLHTTPClientInterface +// { +// public: +// virtual ~LLHTTPClientInterface() {} +// virtual void get(const std::string& url, LLCurl::ResponderPtr responder) = 0; +// virtual void get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) = 0; +// virtual void put(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder) = 0; +// }; #endif // LL_LLHTTPCLIENTINTERFACE_H diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h index c4e0333ca3..d097ecdff7 100755 --- a/indra/llmessage/llsdrpcclient.h +++ b/indra/llmessage/llsdrpcclient.h @@ -37,7 +37,9 @@ #include "llchainio.h" #include "llfiltersd2xmlrpc.h" #include "lliopipe.h" -#include "llurlrequest.h" +#if 0 +//#include "llurlrequest.h" +#endif /** * @class LLSDRPCClientResponse @@ -218,6 +220,7 @@ protected: LLIOPipe::ptr_t mResponse; }; +#if 0 /** * @class LLSDRPCClientFactory * @brief Basic implementation for making an SD RPC client factory @@ -267,7 +270,9 @@ public: protected: std::string mURL; }; +#endif +#if 0 /** * @class LLXMLSDRPCClientFactory * @brief Basic implementation for making an XMLRPC to SD RPC client factory @@ -319,5 +324,6 @@ public: protected: std::string mURL; }; +#endif #endif // LL_LLSDRPCCLIENT_H diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index f5f224b83e..dd39b9a959 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -31,6 +31,10 @@ #include "llappviewer.h" #include "llviewercontrol.h" +#include <openssl/x509_vfy.h> +#include <openssl/ssl.h> +#include "llsecapi.h" +#include <curl/curl.h> // Here is where we begin to get our connection usage under control. // This establishes llcorehttp policy classes that, among other @@ -151,6 +155,15 @@ void LLAppCoreHttp::init() << LL_ENDL; } + // Set up SSL Verification call back. + status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_SSL_VERIFY_CALLBACK, + LLCore::HttpRequest::GLOBAL_POLICY_ID, + sslVerify, NULL); + if (!status) + { + LL_WARNS("Init") << "Failed to set SSL Verification. Reason: " << status.toString() << LL_ENDL; + } + // Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): // 0 - None // 1 - Basic start, stop simple transitions @@ -457,6 +470,62 @@ void LLAppCoreHttp::refreshSettings(bool initial) } } +LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url, + LLCore::HttpHandler const * const handler, void *appdata) +{ + X509_STORE_CTX *ctx = static_cast<X509_STORE_CTX *>(appdata); + LLCore::HttpStatus result; + LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(""); + LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx); + LLSD validation_params = LLSD::emptyMap(); + LLURI uri(url); + + validation_params[CERT_HOSTNAME] = uri.hostName(); + + // *TODO*: In the case of an exception while validating the cert, we need a way + // to pass the offending(?) cert back out. *Rider* + + try + { + // don't validate hostname. Let libcurl do it instead. That way, it'll handle redirects + store->validate(VALIDATION_POLICY_SSL & (~VALIDATION_POLICY_HOSTNAME), chain, validation_params); + } + catch (LLCertValidationTrustException &cert_exception) + { + // this exception is is handled differently than the general cert + // exceptions, as we allow the user to actually add the certificate + // for trust. + // therefore we pass back a different error code + // NOTE: We're currently 'wired' to pass around CURL error codes. This is + // somewhat clumsy, as we may run into errors that do not map directly to curl + // error codes. Should be refactored with login refactoring, perhaps. + result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_CACERT); + result.setMessage(cert_exception.getMessage()); + LLPointer<LLCertificate> cert = cert_exception.getCert(); + cert->ref(); // adding an extra ref here + result.setErrorData(cert.get()); + // We should probably have a more generic way of passing information + // back to the error handlers. + } + catch (LLCertException &cert_exception) + { + result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_PEER_CERTIFICATE); + result.setMessage(cert_exception.getMessage()); + LLPointer<LLCertificate> cert = cert_exception.getCert(); + cert->ref(); // adding an extra ref here + result.setErrorData(cert.get()); + } + catch (...) + { + // any other odd error, we just handle as a connect error. + result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_CONNECT_ERROR); + } + + return result; +} + + + void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) { diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index 37d7a737e7..9616354093 100755 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -233,7 +233,9 @@ private: bool mStopped; HttpClass mHttpClasses[AP_COUNT]; bool mPipelined; // Global setting - boost::signals2::connection mPipelinedSignal; // Signal for 'HttpPipelining' setting + boost::signals2::connection mPipelinedSignal; // Signal for 'HttpPipelining' setting + + static LLCore::HttpStatus sslVerify(const std::string &uri, LLCore::HttpHandler const * const handler, void *appdata); }; diff --git a/indra/newview/llhttpretrypolicy.cpp b/indra/newview/llhttpretrypolicy.cpp index 2d4ce6c883..530eb685fa 100755 --- a/indra/newview/llhttpretrypolicy.cpp +++ b/indra/newview/llhttpretrypolicy.cpp @@ -87,7 +87,7 @@ void LLAdaptiveRetryPolicy::onFailure(const LLCore::HttpResponse *response) F32 retry_header_time; const LLCore::HttpHeaders *headers = response->getHeaders(); bool has_retry_header_time = getRetryAfter(headers,retry_header_time); - onFailureCommon(response->getStatus().mType, has_retry_header_time, retry_header_time); + onFailureCommon(response->getStatus().getType(), has_retry_header_time, retry_header_time); } void LLAdaptiveRetryPolicy::onFailureCommon(S32 status, bool has_retry_header_time, F32 retry_header_time) diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index aa440c06a6..4f08057477 100755 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -2294,7 +2294,6 @@ void LLVOAvatarSelf::sendViewerAppearanceChangeMetrics() if (!caps_url.empty()) { gPendingMetricsUploads++; - LLCurlRequest::headers_t headers; LLHTTPClient::post(caps_url, msg, new ViewerAppearanceChangeMetricsResponder(report_sequence, diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index c12c2cc24c..e4e63afa16 100755 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -35,6 +35,11 @@ #include "llxmlrpclistener.h" #include "llcurl.h" +#include "httpcommon.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" +#include "bufferarray.h" #include "llviewercontrol.h" // Have to include these last to avoid queue redefinition! @@ -155,55 +160,159 @@ XMLRPC_VALUE LLXMLRPCValue::getValue() const } +class LLXMLRPCTransaction::Handler : public LLCore::HttpHandler +{ +public: + Handler(LLCore::HttpRequest::ptr_t &request, LLXMLRPCTransaction::Impl *impl); + virtual ~Handler(); + + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + typedef std::unique_ptr<LLXMLRPCTransaction::Handler> ptr_t; + +private: + + LLXMLRPCTransaction::Impl *mImpl; + LLCore::HttpRequest::ptr_t mRequest; +}; + class LLXMLRPCTransaction::Impl { public: typedef LLXMLRPCTransaction::EStatus EStatus; - LLCurlEasyRequest* mCurlRequest; + LLCore::HttpRequest::ptr_t mHttpRequest; + + + EStatus mStatus; + CURLcode mCurlCode; + std::string mStatusMessage; + std::string mStatusURI; + LLCore::HttpResponse::TransferStats::ptr_t mTransferStats; + Handler::ptr_t mHandler; + LLCore::HttpHandle mPostH; - EStatus mStatus; - CURLcode mCurlCode; - std::string mStatusMessage; - std::string mStatusURI; - LLCurl::TransferInfo mTransferInfo; - std::string mURI; - char* mRequestText; - int mRequestTextSize; - + std::string mProxyAddress; std::string mResponseText; XMLRPC_REQUEST mResponse; std::string mCertStore; LLPointer<LLCertificate> mErrorCert; - + Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip); Impl(const std::string& uri, - const std::string& method, LLXMLRPCValue params, bool useGzip); + const std::string& method, LLXMLRPCValue params, bool useGzip); ~Impl(); - + bool process(); - - void setStatus(EStatus code, - const std::string& message = "", const std::string& uri = ""); - void setCurlStatus(CURLcode); + + void setStatus(EStatus code, const std::string& message = "", const std::string& uri = ""); + void setHttpStatus(const LLCore::HttpStatus &status); private: void init(XMLRPC_REQUEST request, bool useGzip); - static int _sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param); - static CURLcode _sslCtxFunction(CURL * curl, void *sslctx, void *param); - static size_t curlDownloadCallback( - char* data, size_t size, size_t nmemb, void* user_data); }; +LLXMLRPCTransaction::Handler::Handler(LLCore::HttpRequest::ptr_t &request, + LLXMLRPCTransaction::Impl *impl) : + mImpl(impl), + mRequest(request) +{ +} + +LLXMLRPCTransaction::Handler::~Handler() +{ +} + +void LLXMLRPCTransaction::Handler::onCompleted(LLCore::HttpHandle handle, + LLCore::HttpResponse * response) +{ + LLCore::HttpStatus status = response->getStatus(); + + if (!status) + { + if ((status.toULong() != CURLE_SSL_PEER_CERTIFICATE) && + (status.toULong() != CURLE_SSL_CACERT)) + { + // if we have a curl error that's not already been handled + // (a non cert error), then generate the error message as + // appropriate + mImpl->setHttpStatus(status); + LLCertificate *errordata = static_cast<LLCertificate *>(status.getErrorData()); + + if (errordata) + { + mImpl->mErrorCert = LLPointer<LLCertificate>(errordata); + status.setErrorData(NULL); + errordata->unref(); + } + + LL_WARNS() << "LLXMLRPCTransaction error " + << status.toHex() << ": " << status.toString() << LL_ENDL; + LL_WARNS() << "LLXMLRPCTransaction request URI: " + << mImpl->mURI << LL_ENDL; + } + + return; + } + + mImpl->setStatus(LLXMLRPCTransaction::StatusComplete); + mImpl->mTransferStats = response->getTransferStats(); + + // the contents of a buffer array are potentially noncontiguous, so we + // will need to copy them into an contiguous block of memory for XMLRPC. + LLCore::BufferArray *body = response->getBody(); + char * bodydata = new char[body->size()]; + + body->read(0, bodydata, body->size()); + + mImpl->mResponse = XMLRPC_REQUEST_FromXML(bodydata, body->size(), 0); + + delete[] bodydata; + + bool hasError = false; + bool hasFault = false; + int faultCode = 0; + std::string faultString; + + LLXMLRPCValue error(XMLRPC_RequestGetError(mImpl->mResponse)); + if (error.isValid()) + { + hasError = true; + faultCode = error["faultCode"].asInt(); + faultString = error["faultString"].asString(); + } + else if (XMLRPC_ResponseIsFault(mImpl->mResponse)) + { + hasFault = true; + faultCode = XMLRPC_GetResponseFaultCode(mImpl->mResponse); + faultString = XMLRPC_GetResponseFaultString(mImpl->mResponse); + } + + if (hasError || hasFault) + { + mImpl->setStatus(LLXMLRPCTransaction::StatusXMLRPCError); + + LL_WARNS() << "LLXMLRPCTransaction XMLRPC " + << (hasError ? "error " : "fault ") + << faultCode << ": " + << faultString << LL_ENDL; + LL_WARNS() << "LLXMLRPCTransaction request URI: " + << mImpl->mURI << LL_ENDL; + } + +} + +//========================================================================= + LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip) - : mCurlRequest(0), + : mHttpRequest(0), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), - mRequestText(0), +// mRequestText(0), mResponse(0) { init(request, useGzip); @@ -212,10 +321,10 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, LLXMLRPCTransaction::Impl::Impl(const std::string& uri, const std::string& method, LLXMLRPCValue params, bool useGzip) - : mCurlRequest(0), + : mHttpRequest(0), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), - mRequestText(0), +// mRequestText(0), mResponse(0) { XMLRPC_REQUEST request = XMLRPC_RequestNew(); @@ -231,127 +340,53 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_RequestFree(request, 1); } -// _sslCertVerifyCallback -// callback called when a cert verification is requested. -// calls SECAPI to validate the context -int LLXMLRPCTransaction::Impl::_sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param) +void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { - LLXMLRPCTransaction::Impl *transaction = (LLXMLRPCTransaction::Impl *)param; - LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(transaction->mCertStore); - LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx); - LLSD validation_params = LLSD::emptyMap(); - LLURI uri(transaction->mURI); - validation_params[CERT_HOSTNAME] = uri.hostName(); - try - { - // don't validate hostname. Let libcurl do it instead. That way, it'll handle redirects - store->validate(VALIDATION_POLICY_SSL & (~VALIDATION_POLICY_HOSTNAME), chain, validation_params); - } - catch (LLCertValidationTrustException& cert_exception) - { - // this exception is is handled differently than the general cert - // exceptions, as we allow the user to actually add the certificate - // for trust. - // therefore we pass back a different error code - // NOTE: We're currently 'wired' to pass around CURL error codes. This is - // somewhat clumsy, as we may run into errors that do not map directly to curl - // error codes. Should be refactored with login refactoring, perhaps. - transaction->mCurlCode = CURLE_SSL_CACERT; - // set the status directly. set curl status generates error messages and we want - // to use the fixed ones from the exceptions - transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); - // We should probably have a more generic way of passing information - // back to the error handlers. - transaction->mErrorCert = cert_exception.getCert(); - return 0; - } - catch (LLCertException& cert_exception) - { - transaction->mCurlCode = CURLE_SSL_PEER_CERTIFICATE; - // set the status directly. set curl status generates error messages and we want - // to use the fixed ones from the exceptions - transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); - transaction->mErrorCert = cert_exception.getCert(); - return 0; - } - catch (...) - { - // any other odd error, we just handle as a connect error. - transaction->mCurlCode = CURLE_SSL_CONNECT_ERROR; - transaction->setCurlStatus(CURLE_SSL_CONNECT_ERROR); - return 0; - } - return 1; -} + LLCore::HttpOptions::ptr_t httpOpts; + LLCore::HttpHeaders::ptr_t httpHeaders; -// _sslCtxFunction -// Callback function called when an SSL Context is created via CURL -// used to configure the context for custom cert validate(<, <#const & xs#>, <#T * #>, <#long #>)tion -// based on SECAPI - -CURLcode LLXMLRPCTransaction::Impl::_sslCtxFunction(CURL * curl, void *sslctx, void *param) -{ - SSL_CTX * ctx = (SSL_CTX *) sslctx; - // disable any default verification for server certs - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); - // set the verification callback. - SSL_CTX_set_cert_verify_callback(ctx, _sslCertVerifyCallback, param); - // the calls are void - return CURLE_OK; - -} -void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) -{ - if (!mCurlRequest) + if (!mHttpRequest) { - mCurlRequest = new LLCurlEasyRequest(); + mHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest); } - if(!mCurlRequest->isValid()) - { - LL_WARNS() << "mCurlRequest is invalid." << LL_ENDL ; - delete mCurlRequest ; - mCurlRequest = NULL ; - return ; - } + // LLRefCounted starts with a 1 ref, so don't add a ref in the smart pointer + httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions(), false); - mErrorCert = NULL; + httpOpts->setTimeout(40L); -// mCurlRequest->setopt(CURLOPT_VERBOSE, 1); // useful for debugging - mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); - mCurlRequest->setWriteCallback(&curlDownloadCallback, (void*)this); - BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); + bool vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); mCertStore = gSavedSettings.getString("CertStore"); - mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert); - mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0); - // Be a little impatient about establishing connections. - mCurlRequest->setopt(CURLOPT_CONNECTTIMEOUT, 40L); - mCurlRequest->setSSLCtxCallback(_sslCtxFunction, (void *)this); - /* Setting the DNS cache timeout to -1 disables it completely. - This might help with bug #503 */ - mCurlRequest->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1); + httpOpts->setSSLVerifyPeer( vefifySSLCert ); + httpOpts->setSSLVerifyHost( vefifySSLCert ? 2 : 0); - mCurlRequest->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); + // LLRefCounted starts with a 1 ref, so don't add a ref in the smart pointer + httpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders(), false); - if (useGzip) - { - mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - } + httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); + + ///* Setting the DNS cache timeout to -1 disables it completely. + //This might help with bug #503 */ + //httpOpts->setDNSCacheTimeout(-1); + + LLCore::BufferArray::ptr_t body = LLCore::BufferArray::ptr_t(new LLCore::BufferArray(), false); + + // TODO: See if there is a way to serialize to a preallocated buffer I'm + // not fond of the copy here. + int requestSize(0); + char * requestText = XMLRPC_REQUEST_ToXML(request, &requestSize); + + body->append(requestText, requestSize); - mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize); - if (mRequestText) - { - mCurlRequest->setoptString(CURLOPT_POSTFIELDS, mRequestText); - mCurlRequest->setopt(CURLOPT_POSTFIELDSIZE, mRequestTextSize); - } - else - { - setStatus(StatusOtherError); - } + XMLRPC_Free(requestText); + + mHandler = LLXMLRPCTransaction::Handler::ptr_t(new Handler( mHttpRequest, this )); + + mPostH = mHttpRequest->requestPost(LLCore::HttpRequest::DEFAULT_POLICY_ID, 0, + mURI, body.get(), httpOpts.get(), httpHeaders.get(), mHandler.get()); - mCurlRequest->sendRequest(mURI); } @@ -362,27 +397,24 @@ LLXMLRPCTransaction::Impl::~Impl() XMLRPC_RequestFree(mResponse, 1); } - if (mRequestText) - { - XMLRPC_Free(mRequestText); - } + //if (mRequestText) + //{ + // XMLRPC_Free(mRequestText); + //} - delete mCurlRequest; - mCurlRequest = NULL ; + //delete mCurlRequest; + //mCurlRequest = NULL ; } bool LLXMLRPCTransaction::Impl::process() { - if(!mCurlRequest || !mCurlRequest->isValid()) + if (!mPostH || !mHttpRequest) { - LL_WARNS() << "transaction failed." << LL_ENDL ; - - delete mCurlRequest ; - mCurlRequest = NULL ; - return true ; //failed, quit. + LL_WARNS() << "transaction failed." << LL_ENDL; + return true; //failed, quit. } - switch(mStatus) + switch (mStatus) { case LLXMLRPCTransaction::StatusComplete: case LLXMLRPCTransaction::StatusCURLError: @@ -391,93 +423,25 @@ bool LLXMLRPCTransaction::Impl::process() { return true; } - + case LLXMLRPCTransaction::StatusNotStarted: { setStatus(LLXMLRPCTransaction::StatusStarted); break; } - + default: - { - // continue onward - } - } - - if(!mCurlRequest->wait()) - { - return false ; + break; } - while(1) - { - CURLcode result; - bool newmsg = mCurlRequest->getResult(&result, &mTransferInfo); - if (newmsg) - { - if (result != CURLE_OK) - { - if ((result != CURLE_SSL_PEER_CERTIFICATE) && - (result != CURLE_SSL_CACERT)) - { - // if we have a curl error that's not already been handled - // (a non cert error), then generate the error message as - // appropriate - setCurlStatus(result); - - LL_WARNS() << "LLXMLRPCTransaction CURL error " - << mCurlCode << ": " << mCurlRequest->getErrorString() << LL_ENDL; - LL_WARNS() << "LLXMLRPCTransaction request URI: " - << mURI << LL_ENDL; - } - - return true; - } - - setStatus(LLXMLRPCTransaction::StatusComplete); - - mResponse = XMLRPC_REQUEST_FromXML( - mResponseText.data(), mResponseText.size(), NULL); - - bool hasError = false; - bool hasFault = false; - int faultCode = 0; - std::string faultString; + LLCore::HttpStatus status = mHttpRequest->update(0); - LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse)); - if (error.isValid()) - { - hasError = true; - faultCode = error["faultCode"].asInt(); - faultString = error["faultString"].asString(); - } - else if (XMLRPC_ResponseIsFault(mResponse)) - { - hasFault = true; - faultCode = XMLRPC_GetResponseFaultCode(mResponse); - faultString = XMLRPC_GetResponseFaultString(mResponse); - } - - if (hasError || hasFault) - { - setStatus(LLXMLRPCTransaction::StatusXMLRPCError); - - LL_WARNS() << "LLXMLRPCTransaction XMLRPC " - << (hasError ? "error " : "fault ") - << faultCode << ": " - << faultString << LL_ENDL; - LL_WARNS() << "LLXMLRPCTransaction request URI: " - << mURI << LL_ENDL; - } - - return true; - } - else - { - break; // done - } + status = mHttpRequest->getStatus(); + if (!status) + { + return false; } - + return false; } @@ -516,64 +480,49 @@ void LLXMLRPCTransaction::Impl::setStatus(EStatus status, } } -void LLXMLRPCTransaction::Impl::setCurlStatus(CURLcode code) +void LLXMLRPCTransaction::Impl::setHttpStatus(const LLCore::HttpStatus &status) { + CURLcode code = static_cast<CURLcode>(status.toULong()); std::string message; std::string uri = "http://secondlife.com/community/support.php"; - + switch (code) { - case CURLE_COULDNT_RESOLVE_HOST: - message = - "DNS could not resolve the host name.\n" - "Please verify that you can connect to the www.secondlife.com\n" - "web site. If you can, but continue to receive this error,\n" - "please go to the support section and report this problem."; - break; - - case CURLE_SSL_PEER_CERTIFICATE: - message = - "The login server couldn't verify itself via SSL.\n" - "If you continue to receive this error, please go\n" - "to the Support section of the SecondLife.com web site\n" - "and report the problem."; - break; - - case CURLE_SSL_CACERT: - case CURLE_SSL_CONNECT_ERROR: - message = - "Often this means that your computer\'s clock is set incorrectly.\n" - "Please go to Control Panels and make sure the time and date\n" - "are set correctly.\n" - "Also check that your network and firewall are set up correctly.\n" - "If you continue to receive this error, please go\n" - "to the Support section of the SecondLife.com web site\n" - "and report the problem."; - break; - - default: - break; + case CURLE_COULDNT_RESOLVE_HOST: + message = + "DNS could not resolve the host name.\n" + "Please verify that you can connect to the www.secondlife.com\n" + "web site. If you can, but continue to receive this error,\n" + "please go to the support section and report this problem."; + break; + + case CURLE_SSL_PEER_CERTIFICATE: + message = + "The login server couldn't verify itself via SSL.\n" + "If you continue to receive this error, please go\n" + "to the Support section of the SecondLife.com web site\n" + "and report the problem."; + break; + + case CURLE_SSL_CACERT: + case CURLE_SSL_CONNECT_ERROR: + message = + "Often this means that your computer\'s clock is set incorrectly.\n" + "Please go to Control Panels and make sure the time and date\n" + "are set correctly.\n" + "Also check that your network and firewall are set up correctly.\n" + "If you continue to receive this error, please go\n" + "to the Support section of the SecondLife.com web site\n" + "and report the problem."; + break; + + default: + break; } - + mCurlCode = code; setStatus(StatusCURLError, message, uri); -} - -size_t LLXMLRPCTransaction::Impl::curlDownloadCallback( - char* data, size_t size, size_t nmemb, void* user_data) -{ - Impl& impl(*(Impl*)user_data); - - size_t n = size * nmemb; - impl.mResponseText.append(data, n); - - if (impl.mStatus == LLXMLRPCTransaction::StatusStarted) - { - impl.setStatus(LLXMLRPCTransaction::StatusDownloading); - } - - return n; } @@ -645,11 +594,11 @@ F64 LLXMLRPCTransaction::transferRate() return 0.0L; } - double rate_bits_per_sec = impl.mTransferInfo.mSpeedDownload * 8.0; + double rate_bits_per_sec = impl.mTransferStats->mSpeedDownload * 8.0; LL_INFOS("AppInit") << "Buffer size: " << impl.mResponseText.size() << " B" << LL_ENDL; - LL_DEBUGS("AppInit") << "Transfer size: " << impl.mTransferInfo.mSizeDownload << " B" << LL_ENDL; - LL_DEBUGS("AppInit") << "Transfer time: " << impl.mTransferInfo.mTotalTime << " s" << LL_ENDL; + LL_DEBUGS("AppInit") << "Transfer size: " << impl.mTransferStats->mSizeDownload << " B" << LL_ENDL; + LL_DEBUGS("AppInit") << "Transfer time: " << impl.mTransferStats->mTotalTime << " s" << LL_ENDL; LL_INFOS("AppInit") << "Transfer rate: " << rate_bits_per_sec / 1000.0 << " Kb/s" << LL_ENDL; return rate_bits_per_sec; diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h index f2589c7f41..3a1c9c82b7 100755 --- a/indra/newview/llxmlrpctransaction.h +++ b/indra/newview/llxmlrpctransaction.h @@ -81,7 +81,7 @@ private: class LLXMLRPCTransaction - // an asynchronous request and respones via XML-RPC + // an asynchronous request and responses via XML-RPC { public: LLXMLRPCTransaction(const std::string& uri, @@ -127,7 +127,9 @@ public: // only valid if StsatusComplete, otherwise 0.0 private: + class Handler; class Impl; + Impl& impl; }; |