From 8fc350125c671baeae6b7f8b1814251009f4f50a Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 23 May 2012 19:12:09 -0400 Subject: Integrate llcorehttp library into lltexturefetch design. This is the first functional viewer pass with the HTTP work of the texture fetch code performed by the llcorehttp library. Not exactly a 'drop-in' replacement but a work-alike with some changes (e.g. handler notification in consumer thread versus responder notification in worker thread). This also includes some temporary changes in the priority scheme to prevent the kind of priority inversion found in VWR-28996. Scheme used here does provide liveness if not optimal responsiveness or order-of-operation. The llcorehttp library at this point is far from optimally performing. Its worker thread is making relatively poor use of cycles it gets and it doesn't idle or sleep intelligently yet. This early integration step helps shake out the interfaces, implementation niceties will be covered soon. --- indra/newview/llappviewer.cpp | 159 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 1174d108d2..8e6deb9cce 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -212,6 +212,11 @@ #include "llmachineid.h" #include "llmainlooprepeater.h" +// LLCore::HTTP +#include "httpcommon.h" +#include "httprequest.h" +#include "httphandler.h" + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -326,6 +331,53 @@ static std::string gLaunchFileOnQuit; // Used on Win32 for other apps to identify our window (eg, win_setup) const char* const VIEWER_WINDOW_CLASSNAME = "Second Life"; +namespace +{ + +// This class manages the lifecyle of the core http library. +// Slightly different style than traditional code but reflects +// the use of handler classes and light-weight interface +// object instances of the new libraries. To be used +// as a singleton and static construction is fine. +class CoreHttp : public LLCore::HttpHandler +{ +public: + CoreHttp(); + ~CoreHttp(); + + // Initialize the LLCore::HTTP library creating service classes + // and starting the servicing thread. Caller is expected to do + // other initializations (SSL mutex, thread hash function) appropriate + // for the application. + void init(); + + // Request that the servicing thread stop servicing requests, + // release resource references and stop. + void requestStop(); + + // Terminate LLCore::HTTP library services. Caller is expected + // to have made a best-effort to shutdown the servicing thread + // by issuing a requestThreadStop() and waiting for completion + // notification that the stop has completed. + void cleanup(); + + // Notification when the stop request is complete. + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + +private: + static const F64 MAX_THREAD_WAIT_TIME; + +private: + LLCore::HttpRequest * mRequest; + LLCore::HttpHandle mStopHandle; + F64 mStopRequested; + bool mStopped; +}; + +CoreHttp coreHttpLib; + +} // end anonymous namespace + //-- LLDeferredTaskList ------------------------------------------------------ /** @@ -720,6 +772,9 @@ bool LLAppViewer::init() LLViewerStatsRecorder::initClass(); #endif + // Initialize the non-LLCurl libcurl library + coreHttpLib.init(); + // *NOTE:Mani - LLCurl::initClass is not thread safe. // Called before threads are created. LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"), @@ -1807,6 +1862,7 @@ bool LLAppViewer::cleanup() // Delete workers first // shotdown all worker threads before deleting them in case of co-dependencies + coreHttpLib.requestStop(); sTextureFetch->shutdown(); sTextureCache->shutdown(); sImageDecodeThread->shutdown(); @@ -1890,6 +1946,9 @@ bool LLAppViewer::cleanup() // *NOTE:Mani - The following call is not thread safe. LLCurl::cleanupClass(); + // Non-LLCurl libcurl library + coreHttpLib.cleanup(); + // If we're exiting to launch an URL, do that here so the screen // is at the right resolution before we launch IE. if (!gLaunchFileOnQuit.empty()) @@ -5267,3 +5326,103 @@ void LLAppViewer::metricsSend(bool enable_reporting) gViewerAssetStatsMain->reset(); } +namespace +{ + +const F64 CoreHttp::MAX_THREAD_WAIT_TIME(10.0); + +CoreHttp::CoreHttp() + : mRequest(NULL), + mStopHandle(LLCORE_HTTP_HANDLE_INVALID), + mStopRequested(0.0), + mStopped(false) +{} + + +CoreHttp::~CoreHttp() +{ + delete mRequest; + mRequest = NULL; +} + + +void CoreHttp::init() +{ + LLCore::HttpStatus status = LLCore::HttpRequest::createService(); + if (! status) + { + LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + status = LLCore::HttpRequest::startThread(); + if (! status) + { + LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " + << status.toString() + << LL_ENDL; + } + + mRequest = new LLCore::HttpRequest; +} + + +void CoreHttp::requestStop() +{ + llassert_always(mRequest); + + mStopHandle = mRequest->requestStopThread(this); + if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle) + { + mStopRequested = LLTimer::getTotalSeconds(); + } +} + + +void CoreHttp::cleanup() +{ + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + // Should have been started already... + requestStop(); + } + + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services without thread shutdown" + << LL_ENDL; + } + else + { + while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME)) + { + mRequest->update(200); + ms_sleep(50); + } + if (! mStopped) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services with thread shutdown incomplete" + << LL_ENDL; + } + } + + delete mRequest; + mRequest = NULL; + + LLCore::HttpStatus status = LLCore::HttpRequest::destroyService(); + if (! status) + { + LL_WARNS("Cleanup") << "Failed to shutdown HTTP services, continuing. Reason: " + << status.toString() + << LL_ENDL; + } +} + + +void CoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) +{ + mStopped = true; +} + +} // end anonymous namespace -- cgit v1.3 From 05af16a23abe37210e0b880aa27387d8994419dd Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 6 Jun 2012 13:52:38 -0400 Subject: Policy + caching fixes + https support + POST working Implemented first global policy definitions to support SSL CA certificate configuration to support https: operations. Fixed HTTP 206 status handling to match what is currently being done by grid services and to lay a foundation for fixes that will be a response to ER-1824. More libcurl CURLOPT options set on easy handles to do peer verification in the traditional way. HTTP POST working and now reporting asset metrics back to grid for the viewer's asset system. This uses LLSD so that is also showing as compatible with the new library. --- indra/llcorehttp/CMakeLists.txt | 2 ++ indra/llcorehttp/_httplibcurl.cpp | 1 + indra/llcorehttp/_httpoprequest.cpp | 45 ++++++++++++++++++++++++++++++++----- indra/llcorehttp/_httppolicy.cpp | 4 ++-- indra/llcorehttp/_httppolicy.h | 10 +++++++++ indra/llcorehttp/_httpservice.h | 8 +++---- indra/llcorehttp/httpcommon.cpp | 5 ++++- indra/llcorehttp/httpcommon.h | 11 ++++++++- indra/llcorehttp/httprequest.cpp | 13 +++++++++-- indra/llcorehttp/httprequest.h | 16 +++++++------ indra/newview/llappviewer.cpp | 12 +++++++++- indra/newview/lltexturefetch.cpp | 37 +++++++++++++++++------------- 12 files changed, 126 insertions(+), 38 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 9a073eb850..3fda524ddf 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -33,6 +33,7 @@ set(llcorehttp_SOURCE_FILES _httpoprequest.cpp _httpopsetpriority.cpp _httppolicy.cpp + _httppolicyglobal.cpp _httpreplyqueue.cpp _httprequestqueue.cpp _httpservice.cpp @@ -55,6 +56,7 @@ set(llcorehttp_HEADER_FILES _httpoprequest.h _httpopsetpriority.h _httppolicy.h + _httppolicyglobal.h _httpreadyqueue.h _httpreplyqueue.h _httprequestqueue.h diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 704f9baac9..5272c391e8 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -27,6 +27,7 @@ #include "_httplibcurl.h" #include "httpheaders.h" +#include "bufferarray.h" #include "_httpoprequest.h" diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index f52ff5a44c..4bdc4a5257 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -40,8 +40,10 @@ #include "_httpreplyqueue.h" #include "_httpservice.h" #include "_httppolicy.h" +#include "_httppolicyglobal.h" #include "_httplibcurl.h" +#include "llhttpstatuscodes.h" namespace { @@ -153,14 +155,14 @@ HttpOpRequest::~HttpOpRequest() void HttpOpRequest::stageFromRequest(HttpService * service) { addRef(); - service->getPolicy()->addOp(this); // transfers refcount + service->getPolicy().addOp(this); // transfers refcount } void HttpOpRequest::stageFromReady(HttpService * service) { addRef(); - service->getTransport()->addOp(this); // transfers refcount + service->getTransport().addOp(this); // transfers refcount } @@ -195,6 +197,8 @@ void HttpOpRequest::stageFromActive(HttpService * service) void HttpOpRequest::visitNotifier(HttpRequest * request) { + static const HttpStatus partial_content(HTTP_PARTIAL_CONTENT, HE_SUCCESS); + if (mLibraryHandler) { HttpResponse * response = new HttpResponse(); @@ -208,9 +212,15 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) offset = mReplyOffset; length = mReplyLength; } - else if (mReplyBody) + else if (mReplyBody && partial_content == mStatus) { - // Provide implicit offset/length from request/response + // Legacy grid services did not provide a 'Content-Range' + // header in responses to full- or partly-satisfyiable + // 'Range' requests. For these, we have to hope that + // the data starts where requested and the length is simply + // whatever we received. A bit of sanity could be provided + // by overlapping ranged requests and verifying that the + // overlap matches. offset = mReqOffset; length = mReplyBody->size(); } @@ -306,6 +316,9 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) // *FIXME: better error handling later HttpStatus status; + // Get policy options + HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions()); + mCurlHandle = curl_easy_init(); // curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); @@ -322,21 +335,40 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, 10); + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, 10); // *FIXME: parameterize this later curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); + std::string opt_value; + if (policy.get(HttpRequest::GP_CA_PATH, opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value.c_str()); + } + if (policy.get(HttpRequest::GP_CA_FILE, opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value.c_str()); + } + if (policy.get(HttpRequest::GP_HTTP_PROXY, opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value.c_str()); + } + switch (mReqMethod) { case HOR_GET: curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); break; case HOR_POST: { curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); long data_size(0); if (mReqBody) { @@ -358,8 +390,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) data_size = mReqBody->size(); } curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); + curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); } break; diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 1d28f23d56..51f5e487dc 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -76,12 +76,12 @@ void HttpPolicy::addOp(HttpOpRequest * op) HttpService::ELoopSpeed HttpPolicy::processReadyQueue() { HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP); - HttpLibcurl * pTransport(mService->getTransport()); + HttpLibcurl & transport(mService->getTransport()); for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) { HttpReadyQueue & readyq(mReadyQueue[policy_class]); - int active(pTransport->getActiveCountInClass(policy_class)); + int active(transport.getActiveCountInClass(policy_class)); int needed(8 - active); if (needed > 0 && mReadyInClass[policy_class] > 0) diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 2bc03c531f..425079ec63 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -31,6 +31,7 @@ #include "httprequest.h" #include "_httpservice.h" #include "_httpreadyqueue.h" +#include "_httppolicyglobal.h" namespace LLCore @@ -68,11 +69,20 @@ public: // Shadows HttpService's method bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + + // Get pointer to global policy options. Caller is expected + // to do context checks like no setting once running. + HttpPolicyGlobal & getGlobalOptions() + { + return mGlobalOptions; + } + protected: int mReadyInClass[HttpRequest::POLICY_CLASS_LIMIT]; HttpReadyQueue mReadyQueue[HttpRequest::POLICY_CLASS_LIMIT]; HttpService * mService; // Naked pointer, not refcounted, not owner + HttpPolicyGlobal mGlobalOptions; }; // end class HttpPolicy diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 095316c8a7..748354a8e4 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -148,14 +148,14 @@ public: /// Threading: callable by worker thread. bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); - HttpPolicy * getPolicy() + HttpPolicy & getPolicy() { - return mPolicy; + return *mPolicy; } - HttpLibcurl * getTransport() + HttpLibcurl & getTransport() { - return mTransport; + return *mTransport; } protected: diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index a01182cf23..9f17b5c842 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -66,7 +66,10 @@ std::string HttpStatus::toString() const "Services shutting down", "Operation canceled", "Invalid Content-Range header encountered", - "Request handle not found" + "Request handle not found", + "Invalid datatype for argument or option", + "Option has not been explicitly set", + "Option is not dynamic and must be set early" }; static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index c01a5f85d3..fd2661b700 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -137,7 +137,16 @@ enum HttpError HE_INV_CONTENT_RANGE_HDR = 4, // Request handle not found - HE_HANDLE_NOT_FOUND = 5 + HE_HANDLE_NOT_FOUND = 5, + + // Invalid datatype for option/setting + HE_INVALID_ARG = 6, + + // Option hasn't been explicitly set + HE_OPT_NOT_SET = 7, + + // Option not dynamic, must be set during init phase + HE_OPT_NOT_DYNAMIC = 8 }; // end enum HttpError diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 0e512d97ed..baa0fe1a84 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -29,6 +29,7 @@ #include "_httprequestqueue.h" #include "_httpreplyqueue.h" #include "_httpservice.h" +#include "_httppolicy.h" #include "_httpoperation.h" #include "_httpoprequest.h" #include "_httpopsetpriority.h" @@ -127,9 +128,17 @@ HttpRequest::~HttpRequest() HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) { - HttpStatus status; + // *FIXME: Fail if thread is running. - return status; + return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value); +} + + +HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value) +{ + // *FIXME: Fail if thread is running. + + return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value); } diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 57d2da245b..3592d5c6a3 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -111,7 +111,10 @@ public: /// Maximum number of connections the library will use to /// perform operations. This is somewhat soft as the underlying /// transport will cache some connections (up to 5). - GLOBAL_CONNECTION_LIMIT + GP_CONNECTION_LIMIT, ///< Takes long giving number of connections + GP_CA_PATH, ///< System path/directory where SSL certs are stored. + GP_CA_FILE, ///< System path/file containing certs. + GP_HTTP_PROXY ///< String giving host/port to use for HTTP proxy }; /// Set a parameter on a global policy option. Calls @@ -122,6 +125,7 @@ public: /// @param value Desired value of option. /// @return Standard status code. HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); + HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); /// Create a new policy class into which requests can be made. /// @@ -134,15 +138,15 @@ public: enum EClassPolicy { /// Limits the number of connections used for the class. - CLASS_CONNECTION_LIMIT, + CP_CONNECTION_LIMIT, /// Limits the number of connections used for a single /// literal address/port pair within the class. - PER_HOST_CONNECTION_LIMIT, + CP_PER_HOST_CONNECTION_LIMIT, /// Suitable requests are allowed to pipeline on their /// connections when they ask for it. - ENABLE_PIPELINING + CP_ENABLE_PIPELINING }; /// Set a parameter on a class-based policy option. Calls @@ -153,9 +157,7 @@ public: /// @param opt Enum of option to be set. /// @param value Desired value of option. /// @return Standard status code. - HttpStatus setPolicyClassOption(policy_t policy_id, - EClassPolicy opt, - long value); + HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); /// @} diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 8e6deb9cce..7a44415fba 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5356,6 +5356,17 @@ void CoreHttp::init() << LL_ENDL; } + mRequest = new LLCore::HttpRequest; + + status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, + gDirUtilp->getCAFile()); + if (! status) + { + LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + status = LLCore::HttpRequest::startThread(); if (! status) { @@ -5364,7 +5375,6 @@ void CoreHttp::init() << LL_ENDL; } - mRequest = new LLCore::HttpRequest; } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 34fb21798f..f9294b4cd1 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -816,14 +816,6 @@ bool LLTextureFetchWorker::doWork(S32 param) mFetchTimer.reset(); } - static LLUUID last_id; - if (mID != last_id) - { - // LL_WARNS("Texture") << "DOWORK SWITCH: " << last_id << " to: " << mID - // << LL_ENDL; - last_id = mID; - } - if (mState == INIT) { mRawImage = NULL ; @@ -1109,10 +1101,6 @@ bool LLTextureFetchWorker::doWork(S32 param) << " Bytes: " << mRequestedSize << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth << LL_ENDL; -// LL_WARNS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset -// << " Bytes: " << mRequestedSize -// << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth -// << LL_ENDL; // Will call callbackHttpGet when curl request completes // *FIXME: enable redirection follow @@ -1241,7 +1229,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } } - if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded. + if (mHaveAllData /* && mRequestedDiscard == 0*/) //the image file is fully loaded. { mFileSize = total_size; } @@ -1692,13 +1680,32 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, body->addRef(); mHttpBufferArray = body; - if (data_size < mRequestedSize && mRequestedDiscard == 0) + if (! partial) + { + // Response indicates this is the entire asset regardless + // of our asking for a byte range. Mark it so and drop + // any partial data we might have so that the current + // response body becomes the entire dataset. + if (data_size <= mRequestedOffset) + { + LL_WARNS("Texture") << "Fetched entire texture " << mID + << " when it was expected to be marked complete. mImageSize: " + << mFileSize << " datasize: " << mFormattedImage->getDataSize() + << LL_ENDL; + } + mHaveAllData = TRUE; + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had + } + else if (data_size < mRequestedSize && mRequestedDiscard == 0) { + // *FIXME: I think we can treat this as complete regardless + // of requested discard level. Revisit this... mHaveAllData = TRUE; } else if (data_size > mRequestedSize) { - // *TODO: This shouldn't be happening any more + // *TODO: This shouldn't be happening any more (REALLY don't expect this anymore) llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl; mHaveAllData = TRUE; llassert_always(mDecodeHandle == 0); -- cgit v1.3 From 7adeb3923728ca84a309a6af141c148ce38066fc Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 12 Jun 2012 17:42:33 -0400 Subject: HTTP Proxy, PUT & POST, unit tests and refactoring. Implemented/modified PUT & POST to not used chunked encoding for the request. Made the unit test much happier and probably a better thing for the pipeline. Have a cheesy static & dynamic proxy capability using both local options and a way to wire into LLProxy in llmessages. Not a clean thing but it will get the proxy path working with both socks5 & http proxies. Refactoring to get rid of unneeded library handler and unified an HttpStatus return for all requests. Big batch of code removed as a result of that and more is possible as well as some syscall avoidance with a bit more work. Boosted the unit tests for simple PUT & POST test which revealed the test harness does *not* like chunked encoding so we'll avoid it for now (and don't really need it in any of our schemes). --- indra/llcorehttp/CMakeLists.txt | 5 + indra/llcorehttp/_httpopcancel.cpp | 11 -- indra/llcorehttp/_httpopcancel.h | 3 - indra/llcorehttp/_httpoperation.cpp | 18 +- indra/llcorehttp/_httpoperation.h | 13 +- indra/llcorehttp/_httpoprequest.cpp | 63 +++++-- indra/llcorehttp/_httpoprequest.h | 7 + indra/llcorehttp/_httpopsetget.cpp | 105 +++++++++++ indra/llcorehttp/_httpopsetget.h | 78 ++++++++ indra/llcorehttp/_httpopsetpriority.cpp | 14 -- indra/llcorehttp/_httpopsetpriority.h | 4 +- indra/llcorehttp/_httppolicy.cpp | 6 + indra/llcorehttp/_httppolicy.h | 4 +- indra/llcorehttp/_httppolicyglobal.cpp | 38 ++-- indra/llcorehttp/_httppolicyglobal.h | 7 +- indra/llcorehttp/_httpservice.cpp | 11 +- indra/llcorehttp/_httpservice.h | 10 +- indra/llcorehttp/httprequest.cpp | 151 ++++++++-------- indra/llcorehttp/httprequest.h | 44 ++++- indra/llcorehttp/tests/llcorehttp_test.cpp | 6 + indra/llcorehttp/tests/test_httpoperation.hpp | 5 +- indra/llcorehttp/tests/test_httprequest.hpp | 239 +++++++++++++++++++++++++ indra/llcorehttp/tests/test_llcorehttp_peer.py | 10 +- indra/newview/llappviewer.cpp | 13 ++ 24 files changed, 702 insertions(+), 163 deletions(-) create mode 100644 indra/llcorehttp/_httpopsetget.cpp create mode 100644 indra/llcorehttp/_httpopsetget.h (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index a0827286e3..4273b32fe3 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -10,12 +10,14 @@ include(OpenSSL) include(ZLIB) include(LLCoreHttp) include(LLAddBuildTest) +include(LLMessage) include(LLCommon) include(Tut) include_directories (${CMAKE_CURRENT_SOURCE_DIR}) include_directories( + ${LLMESSAGE_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLCOREHTTP_INCLUDE_DIRS} ) @@ -31,6 +33,7 @@ set(llcorehttp_SOURCE_FILES _httpopcancel.cpp _httpoperation.cpp _httpoprequest.cpp + _httpopsetget.cpp _httpopsetpriority.cpp _httppolicy.cpp _httppolicyglobal.cpp @@ -54,6 +57,7 @@ set(llcorehttp_HEADER_FILES _httpopcancel.h _httpoperation.h _httpoprequest.h + _httpopsetget.h _httpopsetpriority.h _httppolicy.h _httppolicyglobal.h @@ -113,6 +117,7 @@ if (LL_TESTS) set(test_libs ${LLCOREHTTP_LIBRARIES} ${WINDOWS_LIBRARIES} + ${LLMESSAGE_LIBRARIES} ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} ${CURL_LIBRARIES} diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp index 69dbff4bb4..ad624d2e57 100644 --- a/indra/llcorehttp/_httpopcancel.cpp +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -66,17 +66,6 @@ void HttpOpCancel::stageFromRequest(HttpService * service) } -void HttpOpCancel::visitNotifier(HttpRequest * request) -{ - if (mLibraryHandler) - { - HttpResponse * response = new HttpResponse(); - mLibraryHandler->onCompleted(static_cast(this), response); - response->release(); - } -} - - } // end namespace LLCore diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h index fab6f1f362..6d1e0f8774 100644 --- a/indra/llcorehttp/_httpopcancel.h +++ b/indra/llcorehttp/_httpopcancel.h @@ -59,13 +59,10 @@ private: public: virtual void stageFromRequest(HttpService *); - - virtual void visitNotifier(HttpRequest * request); public: // Request data HttpHandle mHandle; - }; // end class HttpOpCancel diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index d966efd12b..b5c58013d4 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -47,7 +47,6 @@ namespace LLCore HttpOperation::HttpOperation() : LLCoreInt::RefCounted(true), mReplyQueue(NULL), - mLibraryHandler(NULL), mUserHandler(NULL), mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), mReqPriority(0U) @@ -57,13 +56,12 @@ HttpOperation::HttpOperation() HttpOperation::~HttpOperation() { - setHandlers(NULL, NULL, NULL); + setReplyPath(NULL, NULL); } -void HttpOperation::setHandlers(HttpReplyQueue * reply_queue, - HttpHandler * lib_handler, - HttpHandler * user_handler) +void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue, + HttpHandler * user_handler) { if (reply_queue != mReplyQueue) { @@ -80,9 +78,6 @@ void HttpOperation::setHandlers(HttpReplyQueue * reply_queue, mReplyQueue = reply_queue; } - // Not refcounted - mLibraryHandler = lib_handler; - // Not refcounted mUserHandler = user_handler; } @@ -121,11 +116,12 @@ void HttpOperation::stageFromActive(HttpService *) void HttpOperation::visitNotifier(HttpRequest *) { - if (mLibraryHandler) + if (mUserHandler) { HttpResponse * response = new HttpResponse(); - mLibraryHandler->onCompleted(static_cast(this), response); + response->setStatus(mStatus); + mUserHandler->onCompleted(static_cast(this), response); response->release(); } @@ -142,7 +138,7 @@ HttpStatus HttpOperation::cancel() void HttpOperation::addAsReply() { - if (mReplyQueue && mLibraryHandler) + if (mReplyQueue) { addRef(); mReplyQueue->addOp(this); diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 01e26029d2..c93aa2def9 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -80,9 +80,8 @@ private: void operator=(const HttpOperation &); // Not defined public: - void setHandlers(HttpReplyQueue * reply_queue, - HttpHandler * lib_handler, - HttpHandler * user_handler); + void setReplyPath(HttpReplyQueue * reply_queue, + HttpHandler * handler); HttpHandler * getUserHandler() const { @@ -102,13 +101,15 @@ protected: protected: HttpReplyQueue * mReplyQueue; // Have refcount - HttpHandler * mLibraryHandler; // Have refcount - HttpHandler * mUserHandler; // Have refcount + HttpHandler * mUserHandler; public: + // Request Data HttpRequest::policy_t mReqPolicy; HttpRequest::priority_t mReqPriority; - + + // Reply Data + HttpStatus mStatus; }; // end class HttpOperation diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index ea0b99303e..e2550d057e 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -44,6 +44,7 @@ #include "_httplibcurl.h" #include "llhttpstatuscodes.h" +#include "llproxy.h" namespace { @@ -207,7 +208,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) { static const HttpStatus partial_content(HTTP_PARTIAL_CONTENT, HE_SUCCESS); - if (mLibraryHandler) + if (mUserHandler) { HttpResponse * response = new HttpResponse(); response->setStatus(mStatus); @@ -219,7 +220,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) response->setRange(mReplyOffset, mReplyLength); } - mLibraryHandler->onCompleted(static_cast(this), response); + mUserHandler->onCompleted(static_cast(this), response); response->release(); } @@ -304,6 +305,39 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, } +HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ + HttpStatus status; + + mProcFlags = 0; + mReqPolicy = policy_id; + mReqPriority = priority; + mReqMethod = HOR_PUT; + mReqURL = url; + if (body) + { + body->addRef(); + mReqBody = body; + } + if (headers && ! mReqHeaders) + { + headers->addRef(); + mReqHeaders = headers; + } + if (options && ! mReqOptions) + { + mReqOptions = new HttpOptions(*options); + } + + return status; +} + + HttpStatus HttpOpRequest::prepareRequest(HttpService * service) { // Scrub transport and result data for retried op case @@ -346,8 +380,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); - // *FIXME: Need to deal with proxy setup... - // curl_easy_setopt(handle, CURLOPT_PROXY, ""); // *FIXME: Revisit this old DNS timeout setting - may no longer be valid curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); @@ -361,18 +393,31 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); - std::string opt_value; + const std::string * opt_value(NULL); if (policy.get(HttpRequest::GP_CA_PATH, opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value.c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); } if (policy.get(HttpRequest::GP_CA_FILE, opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value.c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); } if (policy.get(HttpRequest::GP_HTTP_PROXY, opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value.c_str()); + if (*opt_value == "LLProxy") + { + // Use the viewer-based thread-safe API which has a + // fast/safe check for proxy enable. Would like to + // encapsulate this someway... + LLProxy::getInstance()->applyProxySettings(mCurlHandle); + } + else + { + // *TODO: This is fine for now but get fuller socks/ + // authentication thing going later.... + curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } } switch (mReqMethod) @@ -394,7 +439,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast(NULL)); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); } break; @@ -409,7 +453,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 6dcf30ca0c..80893beb40 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -91,6 +91,13 @@ public: HttpOptions * options, HttpHeaders * headers); + HttpStatus setupPut(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + HttpStatus prepareRequest(HttpService * service); virtual HttpStatus cancel(); diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp new file mode 100644 index 0000000000..21e058b2be --- /dev/null +++ b/indra/llcorehttp/_httpopsetget.cpp @@ -0,0 +1,105 @@ +/** + * @file _httpopsetget.cpp + * @brief Definitions for internal class HttpOpSetGet + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpopsetget.h" + +#include +#include + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOpSetget +// ================================== + + +HttpOpSetGet::HttpOpSetGet() + : HttpOperation(), + mIsGlobal(false), + mDoSet(false), + mSetting(-1), // Nothing requested + mLongValue(0L) +{} + + +HttpOpSetGet::~HttpOpSetGet() +{} + + +void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting) +{ + mIsGlobal = true; + mSetting = setting; +} + + +void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value) +{ + mIsGlobal = true; + mDoSet = true; + mSetting = setting; + mStrValue = value; +} + + +void HttpOpSetGet::stageFromRequest(HttpService * service) +{ + HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions()); + HttpRequest::EGlobalPolicy setting(static_cast(mSetting)); + + if (mDoSet) + { + mStatus = pol_opt.set(setting, mStrValue); + } + if (mStatus) + { + const std::string * value; + if ((mStatus = pol_opt.get(setting, value))) + { + mStrValue = *value; + } + } + + addAsReply(); +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h new file mode 100644 index 0000000000..e065eb4c30 --- /dev/null +++ b/indra/llcorehttp/_httpopsetget.h @@ -0,0 +1,78 @@ +/** + * @file _httpopsetget.h + * @brief Internal declarations for the HttpOpSetGet subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPSETGET_H_ +#define _LLCORE_HTTP_OPSETGET_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include "httpcommon.h" + +#include + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpSetGet requests dynamic changes to policy and +/// configuration settings. + +class HttpOpSetGet : public HttpOperation +{ +public: + HttpOpSetGet(); + virtual ~HttpOpSetGet(); + +private: + HttpOpSetGet(const HttpOpSetGet &); // Not defined + void operator=(const HttpOpSetGet &); // Not defined + +public: + void setupGet(HttpRequest::EGlobalPolicy setting); + void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value); + + virtual void stageFromRequest(HttpService *); + +public: + // Request data + bool mIsGlobal; + bool mDoSet; + int mSetting; + long mLongValue; + std::string mStrValue; + +}; // end class HttpOpSetGet + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPSETGET_H_ + diff --git a/indra/llcorehttp/_httpopsetpriority.cpp b/indra/llcorehttp/_httpopsetpriority.cpp index b0ee577087..d48c7a0b7d 100644 --- a/indra/llcorehttp/_httpopsetpriority.cpp +++ b/indra/llcorehttp/_httpopsetpriority.cpp @@ -60,18 +60,4 @@ void HttpOpSetPriority::stageFromRequest(HttpService * service) } -void HttpOpSetPriority::visitNotifier(HttpRequest * request) -{ - if (mLibraryHandler) - { - HttpResponse * response = new HttpResponse(); - - response->setStatus(mStatus); - mLibraryHandler->onCompleted(static_cast(this), response); - - response->release(); - } -} - - } // end namespace LLCore diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h index b972f50fff..f1e94b6e43 100644 --- a/indra/llcorehttp/_httpopsetpriority.h +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -56,10 +56,8 @@ private: public: virtual void stageFromRequest(HttpService *); - virtual void visitNotifier(HttpRequest * request); - protected: - HttpStatus mStatus; + // Request Data HttpHandle mHandle; HttpRequest::priority_t mPriority; }; // end class HttpOpSetPriority diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 72bb6f14e4..8ee3f88658 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -71,6 +71,12 @@ HttpPolicy::~HttpPolicy() } +void HttpPolicy::setPolicies(const HttpPolicyGlobal & global) +{ + mGlobalOptions = global; +} + + void HttpPolicy::addOp(HttpOpRequest * op) { const int policy_class(op->mReqPolicy); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 14f6a9a676..73c22bab78 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -95,7 +95,9 @@ public: { return mGlobalOptions; } - + + void setPolicies(const HttpPolicyGlobal & global); + protected: struct State { diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index 877b85896f..d95d73cfba 100644 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -32,7 +32,7 @@ namespace LLCore HttpPolicyGlobal::HttpPolicyGlobal() - : mValidMask(0UL), + : mSetMask(0UL), mConnectionLimit(32L) {} @@ -41,6 +41,20 @@ HttpPolicyGlobal::~HttpPolicyGlobal() {} +HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other) +{ + if (this != &other) + { + mSetMask = other.mSetMask; + mConnectionLimit = other.mConnectionLimit; + mCAPath = other.mCAPath; + mCAFile = other.mCAFile; + mHttpProxy = other.mHttpProxy; + } + return *this; +} + + HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) { switch (opt) @@ -53,7 +67,7 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } - mValidMask |= 1UL << int(opt); + mSetMask |= 1UL << int(opt); return HttpStatus(); } @@ -78,7 +92,7 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } - mValidMask |= 1UL << int(opt); + mSetMask |= 1UL << int(opt); return HttpStatus(); } @@ -90,7 +104,7 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value) switch (opt) { case HttpRequest::GP_CONNECTION_LIMIT: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; value = mConnectionLimit; break; @@ -103,28 +117,28 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value) } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, std::string & value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string *& value) { static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); - + switch (opt) { case HttpRequest::GP_CA_PATH: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; - value = mCAPath; + value = &mCAPath; break; case HttpRequest::GP_CA_FILE: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; - value = mCAFile; + value = &mCAFile; break; case HttpRequest::GP_HTTP_PROXY: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; - value = mHttpProxy; + value = &mHttpProxy; break; default: diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h index 39ffbcb9bb..f4bb4d4b25 100644 --- a/indra/llcorehttp/_httppolicyglobal.h +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -40,18 +40,19 @@ public: HttpPolicyGlobal(); ~HttpPolicyGlobal(); + HttpPolicyGlobal & operator=(const HttpPolicyGlobal &); + private: HttpPolicyGlobal(const HttpPolicyGlobal &); // Not defined - void operator=(const HttpPolicyGlobal &); // Not defined public: HttpStatus set(HttpRequest::EGlobalPolicy opt, long value); HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value); HttpStatus get(HttpRequest::EGlobalPolicy opt, long & value); - HttpStatus get(HttpRequest::EGlobalPolicy opt, std::string & value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string *& value); public: - unsigned long mValidMask; + unsigned long mSetMask; long mConnectionLimit; std::string mCAPath; std::string mCAFile; diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index b038bdb720..920a3f3b6d 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -79,11 +79,8 @@ HttpService::~HttpService() mTransport = NULL; } - if (mPolicy) - { - delete mPolicy; - mPolicy = NULL; - } + delete mPolicy; + mPolicy = NULL; if (mThread) { @@ -145,6 +142,10 @@ void HttpService::startThread() { mThread->release(); } + + // Push current policy definitions + mPolicy->setPolicies(mPolicyGlobal); + mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); mThread->addRef(); // Need an explicit reference, implicit one is used internally sState = RUNNING; diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 748354a8e4..3f953ec1a7 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -30,6 +30,7 @@ #include "httpcommon.h" #include "httprequest.h" +#include "_httppolicyglobal.h" namespace LLCoreInt @@ -157,6 +158,11 @@ public: { return *mTransport; } + + HttpPolicyGlobal & getGlobalOptions() + { + return mPolicyGlobal; + } protected: void threadRun(LLCoreInt::HttpThread * thread); @@ -173,11 +179,11 @@ protected: // === calling-thread-only data === LLCoreInt::HttpThread * mThread; - + HttpPolicyGlobal mPolicyGlobal; + // === working-thread-only data === HttpPolicy * mPolicy; // Simple pointer, has ownership HttpLibcurl * mTransport; // Simple pointer, has ownership - }; // end class HttpService } // end namespace LLCore diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 2f36168f8b..089eee76f3 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -34,6 +34,7 @@ #include "_httpoprequest.h" #include "_httpopsetpriority.h" #include "_httpopcancel.h" +#include "_httpopsetget.h" #include "lltimer.h" @@ -48,39 +49,6 @@ bool has_inited(false); namespace LLCore { -// ==================================== -// InternalHandler Implementation -// ==================================== - - -class HttpRequest::InternalHandler : public HttpHandler -{ -public: - InternalHandler(HttpRequest & request) - : mRequest(request) - {} - -protected: - InternalHandler(const InternalHandler &); // Not defined - void operator=(const InternalHandler &); // Not defined - -public: - void onCompleted(HttpHandle handle, HttpResponse * response) - { - HttpOperation * op(static_cast(handle)); - HttpHandler * user_handler(op->getUserHandler()); - if (user_handler) - { - user_handler->onCompleted(handle, response); - } - } - -protected: - HttpRequest & mRequest; - -}; // end class HttpRequest::InternalHandler - - // ==================================== // HttpRequest Implementation // ==================================== @@ -92,15 +60,12 @@ HttpRequest::policy_t HttpRequest::sNextPolicyID(1); HttpRequest::HttpRequest() : //HttpHandler(), mReplyQueue(NULL), - mRequestQueue(NULL), - mSelfHandler(NULL) + mRequestQueue(NULL) { mRequestQueue = HttpRequestQueue::instanceOf(); mRequestQueue->addRef(); mReplyQueue = new HttpReplyQueue(); - - mSelfHandler = new InternalHandler(*this); } @@ -117,9 +82,6 @@ HttpRequest::~HttpRequest() mReplyQueue->release(); mReplyQueue = NULL; } - - delete mSelfHandler; - mSelfHandler = NULL; } @@ -132,7 +94,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) { // *FIXME: Fail if thread is running. - return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value); + return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } @@ -140,7 +102,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::stri { // *FIXME: Fail if thread is running. - return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value); + return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } @@ -192,7 +154,7 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, mLastReqStatus = status; return handle; } - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); mRequestQueue->addOp(op); // transfers refcount mLastReqStatus = status; @@ -220,7 +182,7 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, mLastReqStatus = status; return handle; } - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); mRequestQueue->addOp(op); // transfers refcount mLastReqStatus = status; @@ -230,19 +192,31 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, } -HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +HttpHandle HttpRequest::requestPut(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) { HttpStatus status; - HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); - - HttpOpCancel * op = new HttpOpCancel(handle); - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); - mRequestQueue->addOp(op); // transfer refcount as well + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupPut(policy_id, priority, url, body, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfers refcount + mLastReqStatus = status; - ret_handle = static_cast(op); + handle = static_cast(op); - return ret_handle; + return handle; } @@ -252,7 +226,7 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); HttpOpNull * op = new HttpOpNull(); - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); mRequestQueue->addOp(op); // transfer refcount as well mLastReqStatus = status; @@ -262,23 +236,6 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) } -HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority, - HttpHandler * handler) -{ - HttpStatus status; - HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); - - HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); - op->setHandlers(mReplyQueue, mSelfHandler, handler); - mRequestQueue->addOp(op); // transfer refcount as well - - mLastReqStatus = status; - ret_handle = static_cast(op); - - return ret_handle; -} - - HttpStatus HttpRequest::update(long millis) { const HttpTime limit(totalTime() + (1000 * HttpTime(millis))); @@ -302,6 +259,38 @@ HttpStatus HttpRequest::update(long millis) // Request Management Methods // ==================================== +HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpCancel * op = new HttpOpCancel(handle); + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + + +HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority, + HttpHandler * handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); + op->setReplyPath(mReplyQueue, handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + // ==================================== // Utility Methods @@ -350,7 +339,27 @@ HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); HttpOpStop * op = new HttpOpStop(); - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + +// ==================================== +// Dynamic Policy Methods +// ==================================== + +HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandler * handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetGet * op = new HttpOpSetGet(); + op->setupSet(GP_HTTP_PROXY, proxy); + op->setReplyPath(mReplyQueue, handler); mRequestQueue->addOp(op); // transfer refcount as well mLastReqStatus = status; diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 01dbfba6dd..a953aa28d0 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -124,8 +124,8 @@ public: /// @param opt Enum of option to be set. /// @param value Desired value of option. /// @return Standard status code. - HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); - HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); + static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); + static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); /// Create a new policy class into which requests can be made. /// @@ -236,6 +236,32 @@ public: HttpHandler * handler); + /// + /// @param policy_id Default or user-defined policy class under + /// which this request is to be serviced. + /// @param priority Standard priority scheme inherited from + /// Indra code base. + /// @param url + /// @param body Byte stream to be sent as the body. No + /// further encoding or escaping will be done + /// to the content. + /// @param options (optional) + /// @param headers (optional) + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestPut(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + /// Queue a NoOp request. /// The request is queued and serviced by the working thread which /// immediately processes it and returns the request to the reply @@ -325,13 +351,20 @@ public: HttpHandle requestStopThread(HttpHandler * handler); /// @} + + /// @name DynamicPolicyMethods + /// + /// @{ + + /// Request that a running transport pick up a new proxy setting. + /// An empty string will indicate no proxy is to be used. + HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler); + + /// @} protected: void generateNotification(HttpOperation * op); - class InternalHandler; - friend class InternalHandler; - private: /// @name InstanceData /// @@ -339,7 +372,6 @@ private: HttpStatus mLastReqStatus; HttpReplyQueue * mReplyQueue; HttpRequestQueue * mRequestQueue; - InternalHandler * mSelfHandler; /// @} diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index f59361ab53..2b36d3a982 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -44,6 +44,8 @@ #include "test_bufferarray.hpp" #include "test_httprequestqueue.hpp" +#include "llproxy.h" + unsigned long ssl_thread_id_callback(void); void ssl_locking_callback(int mode, int type, const char * file, int line); @@ -91,11 +93,15 @@ void init_curl() CRYPTO_set_locking_callback(ssl_locking_callback); CRYPTO_set_id_callback(ssl_thread_id_callback); } + + LLProxy::getInstance(); } void term_curl() { + LLProxy::cleanupClass(); + CRYPTO_set_locking_callback(NULL); for (int i(0); i < ssl_mutex_count; ++i) { diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp index 6c3df1e9e3..17b1a96878 100644 --- a/indra/llcorehttp/tests/test_httpoperation.hpp +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -97,13 +97,12 @@ namespace tut // Get some handlers TestHandler * h1 = new TestHandler(); - TestHandler * h2 = new TestHandler(); // create a new ref counted object with an implicit reference HttpOpNull * op = new HttpOpNull(); // Add the handlers - op->setHandlers(NULL, h1, h2); + op->setReplyPath(NULL, h1); // Check ref count ensure(op->getRefCount() == 1); @@ -117,8 +116,6 @@ namespace tut // release the handlers delete h1; h1 = NULL; - delete h2; - h2 = NULL; ensure(mMemTotal == GetMemTotal()); } diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 81f8fe4a85..61698f34d8 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -27,6 +27,7 @@ #define TEST_LLCORE_HTTP_REQUEST_H_ #include "httprequest.h" +#include "bufferarray.h" #include "httphandler.h" #include "httpresponse.h" #include "_httpservice.h" @@ -604,6 +605,244 @@ void HttpRequestTestObjectType::test<6>() } } +template <> template <> +void HttpRequestTestObjectType::test<7>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest PUT to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + BufferArray * body = new BufferArray; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + static const char * body_text("Now is the time for all good men..."); + body->append(body_text, strlen(body_text)); + mStatus = HttpStatus(200); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + body, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // Lose the request body + body->release(); + body = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + if (body) + { + body->release(); + } + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<8>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest POST to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + BufferArray * body = new BufferArray; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + static const char * body_text("Now is the time for all good men..."); + body->append(body_text, strlen(body_text)); + mStatus = HttpStatus(200); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + body, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // Lose the request body + body->release(); + body = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + if (body) + { + body->release(); + } + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + } // end namespace tut namespace diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 3e200a5c19..8c3ad805b3 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -85,7 +85,15 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): def do_POST(self): # Read the provided POST data. - self.answer(self.read()) + # self.answer(self.read()) + self.answer(dict(reply="success", status=200, + reason=self.read())) + + def do_PUT(self): + # Read the provided PUT data. + # self.answer(self.read()) + self.answer(dict(reply="success", status=200, + reason=self.read())) def answer(self, data): debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 7a44415fba..e2c13e77e3 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5358,6 +5358,7 @@ void CoreHttp::init() mRequest = new LLCore::HttpRequest; + // Point to our certs or SSH/https: will fail on connect status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, gDirUtilp->getCAFile()); if (! status) @@ -5366,6 +5367,18 @@ void CoreHttp::init() << status.toString() << LL_ENDL; } + + // Establish HTTP Proxy. "LLProxy" is a special string which directs + // the code to use LLProxy::applyProxySettings() to establish any + // HTTP or SOCKS proxy for http operations. + status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_HTTP_PROXY, + std::string("LLProxy")); + if (! status) + { + LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } status = LLCore::HttpRequest::startThread(); if (! status) -- cgit v1.3 From b08125a5874a89ce5210f8fb2c961ae17fb80fde Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 14 Jun 2012 16:31:48 -0400 Subject: LLMutex recursive lock, global & per-request tracing, simple GET request, LLProxy support, HttpOptions starting to work, HTTP resource waiting fixed. Non-LLThread-based threads need to do some registration or LLMutex locks taken out in these threads will not work as expected (SH-3154). We'll get a better solution later, this fixes some things for now. Tracing of operations now supported. Global and per-request (via HttpOptions) tracing levels of [0..3]. The 2 and 3 levels use libcurl's VERBOSE mode combined with CURLOPT_DEBUGFUNCTION to stream high levels of detail into the log. *Very* laggy but useful. Simple GET request supported (no Range: header). Really just a degenrate case of a ranged get but supplied an API anyway. Global option to use the LLProxy interface to setup CURL handles for either socks5 or http proxy usage. This isn't really the most encapsulated way to do this but a better solution will have to come later. The wantHeaders and tracing options are now supported in HttpOptions giving per-request controls. Big refactoring of the HTTP resource waiter in lltexturefetch. What I was doing before wasn't correct. Instead, I'm implementing the resource wait after the Semaphore model (though not using system semaphores). So instead of having a sequence like: SEND_HTTP_REQ -> WAIT_HTTP_RESOURCE -> SEND_HTTP_REQ, we now do WAIT_HTTP_RESOURCE -> WAIT_HTTP_RESOURCE2 (actual wait) -> SEND_HTTP_REQ. Works well but the prioritized filling of the corehttp library needs some performance work later. --- indra/llcommon/llthread.cpp | 7 + indra/llcommon/llthread.h | 5 + indra/llcorehttp/_httplibcurl.cpp | 24 ++- indra/llcorehttp/_httpoperation.cpp | 15 +- indra/llcorehttp/_httpoperation.h | 4 + indra/llcorehttp/_httpoprequest.cpp | 271 +++++++++++++++++++++------- indra/llcorehttp/_httpoprequest.h | 16 +- indra/llcorehttp/_httpopsetget.cpp | 4 +- indra/llcorehttp/_httppolicy.cpp | 21 ++- indra/llcorehttp/_httppolicy.h | 5 +- indra/llcorehttp/_httppolicyglobal.cpp | 52 ++++-- indra/llcorehttp/_httppolicyglobal.h | 6 +- indra/llcorehttp/_httpservice.cpp | 18 +- indra/llcorehttp/httpoptions.cpp | 18 +- indra/llcorehttp/httpoptions.h | 14 +- indra/llcorehttp/httprequest.cpp | 27 +++ indra/llcorehttp/httprequest.h | 75 +++++++- indra/llcorehttp/tests/test_httprequest.hpp | 228 ++++++++++++++++++++++- indra/newview/llappviewer.cpp | 20 +- indra/newview/lltexturefetch.cpp | 120 +++++++----- indra/newview/lltexturefetch.h | 9 + indra/newview/lltextureview.cpp | 5 +- 22 files changed, 803 insertions(+), 161 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index a6ad6b125c..b27b64b26f 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -71,6 +71,13 @@ LL_COMMON_API void assert_main_thread() } } +void LLThread::registerThreadID() +{ +#if !LL_DARWIN + sThreadID = ++sIDIter; +#endif +} + // // Handed to the APR thread creation function // diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index b52e70ab2e..54af41ec59 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -88,6 +88,11 @@ public: U32 getID() const { return mID; } + // Called by threads *not* created via LLThread to register some + // internal state used by LLMutex. You must call this once early + // in the running thread to prevent collisions with the main thread. + static void registerThreadID(); + private: BOOL mPaused; diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index e134a28401..a176dd5b2a 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -159,6 +159,17 @@ void HttpLibcurl::addOp(HttpOpRequest * op) curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); op->mCurlActive = true; + if (op->mTracing > 0) + { + HttpPolicy & policy(mService->getPolicy()); + + LL_INFOS("CoreHttp") << "TRACE, ToActiveQueue, Handle: " + << static_cast(op) + << ", Actives: " << mActiveOps.size() + << ", Readies: " << policy.getReadyCount(op->mReqPolicy) + << LL_ENDL; + } + // On success, make operation active mActiveOps.insert(op); } @@ -190,10 +201,9 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode // Deactivate request op->mCurlActive = false; - // Set final status of request + // Set final status of request if it hasn't failed by other mechanisms yet if (op->mStatus) { - // Only set if it hasn't failed by other mechanisms yet op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status); } if (op->mStatus) @@ -209,6 +219,16 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode curl_easy_cleanup(handle); op->mCurlHandle = NULL; + // Tracing + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: " + << static_cast(op) + << ", Status: " << op->mStatus.toHex() + << LL_ENDL; + } + + // Dispatch to next stage HttpPolicy & policy(mService->getPolicy()); bool still_active(policy.stageAfterCompletion(op)); diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index b5c58013d4..5a31bf90e7 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -34,6 +34,8 @@ #include "_httpreplyqueue.h" #include "_httpservice.h" +#include "lltimer.h" + namespace LLCore { @@ -49,8 +51,10 @@ HttpOperation::HttpOperation() mReplyQueue(NULL), mUserHandler(NULL), mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), - mReqPriority(0U) + mReqPriority(0U), + mTracing(0) { + mMetricCreated = totalTime(); } @@ -113,7 +117,7 @@ void HttpOperation::stageFromActive(HttpService *) llassert_always(false); } - + void HttpOperation::visitNotifier(HttpRequest *) { if (mUserHandler) @@ -138,6 +142,13 @@ HttpStatus HttpOperation::cancel() void HttpOperation::addAsReply() { + if (mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: " + << static_cast(this) + << LL_ENDL; + } + if (mReplyQueue) { addRef(); diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index c93aa2def9..de4939a0ac 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -110,6 +110,10 @@ public: // Reply Data HttpStatus mStatus; + + // Tracing, debug and metrics + HttpTime mMetricCreated; + int mTracing; }; // end class HttpOperation diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index e2550d057e..f78971d8f2 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -63,6 +63,16 @@ int parse_content_range_header(char * buffer, unsigned int * last, unsigned int * length); + +// Take data from libcurl's CURLOPT_DEBUGFUNCTION callback and +// escape and format it for a tracing line in logging. Absolutely +// anything including NULs can be in the data. If @scrub is true, +// non-printing or non-ascii characters are replaced with spaces +// otherwise a %XX form of escaping is used. +void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, + std::string & safe_line); + + #if defined(WIN32) // Not available on windows where the legacy strtok interface @@ -78,11 +88,6 @@ namespace LLCore { -// ================================== -// HttpOpRequest -// ================================== - - HttpOpRequest::HttpOpRequest() : HttpOperation(), mProcFlags(0U), @@ -237,6 +242,19 @@ HttpStatus HttpOpRequest::cancel() } +HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, NULL, options, headers); + mReqMethod = HOR_GET; + + return HttpStatus(); +} + + HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, @@ -245,30 +263,16 @@ HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - HttpStatus status; - - mProcFlags = 0; - mReqPolicy = policy_id; - mReqPriority = priority; + setupCommon(policy_id, priority, url, NULL, options, headers); mReqMethod = HOR_GET; - mReqURL = url; mReqOffset = offset; mReqLength = len; if (offset || len) { mProcFlags |= PF_SCAN_RANGE_HEADER; } - if (headers && ! mReqHeaders) - { - headers->addRef(); - mReqHeaders = headers; - } - if (options && ! mReqOptions) - { - mReqOptions = new HttpOptions(*options); - } - return status; + return HttpStatus(); } @@ -279,29 +283,10 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - HttpStatus status; - - mProcFlags = 0; - mReqPolicy = policy_id; - mReqPriority = priority; + setupCommon(policy_id, priority, url, body, options, headers); mReqMethod = HOR_POST; - mReqURL = url; - if (body) - { - body->addRef(); - mReqBody = body; - } - if (headers && ! mReqHeaders) - { - headers->addRef(); - mReqHeaders = headers; - } - if (options && ! mReqOptions) - { - mReqOptions = new HttpOptions(*options); - } - return status; + return HttpStatus(); } @@ -312,12 +297,23 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - HttpStatus status; + setupCommon(policy_id, priority, url, body, options, headers); + mReqMethod = HOR_PUT; + + return HttpStatus(); +} + +void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ mProcFlags = 0; mReqPolicy = policy_id; mReqPriority = priority; - mReqMethod = HOR_PUT; mReqURL = url; if (body) { @@ -331,10 +327,14 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, } if (options && ! mReqOptions) { - mReqOptions = new HttpOptions(*options); + options->addRef(); + mReqOptions = options; + if (options->getWantHeaders()) + { + mProcFlags |= PF_SAVE_HEADERS; + } + mTracing = (std::max)(mTracing, llclamp(options->getTrace(), 0, 3)); } - - return status; } @@ -394,30 +394,29 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); const std::string * opt_value(NULL); - if (policy.get(HttpRequest::GP_CA_PATH, opt_value)) + long opt_long(0L); + policy.get(HttpRequest::GP_LLPROXY, &opt_long); + if (opt_long) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); + // Use the viewer-based thread-safe API which has a + // fast/safe check for proxy enable. Would like to + // encapsulate this someway... + LLProxy::getInstance()->applyProxySettings(mCurlHandle); } - if (policy.get(HttpRequest::GP_CA_FILE, opt_value)) + else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); + // *TODO: This is fine for now but get fuller socks/ + // authentication thing going later.... + curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); } - if (policy.get(HttpRequest::GP_HTTP_PROXY, opt_value)) + if (policy.get(HttpRequest::GP_CA_PATH, &opt_value)) { - if (*opt_value == "LLProxy") - { - // Use the viewer-based thread-safe API which has a - // fast/safe check for proxy enable. Would like to - // encapsulate this someway... - LLProxy::getInstance()->applyProxySettings(mCurlHandle); - } - else - { - // *TODO: This is fine for now but get fuller socks/ - // authentication thing going later.... - curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); - curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - } + curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); + } + if (policy.get(HttpRequest::GP_CA_FILE, &opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); } switch (mReqMethod) @@ -463,6 +462,14 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) // *FIXME: fail out here break; } + + // Tracing + if (mTracing > 1) + { + curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback); + } // There's a CURLOPT for this now... if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod) @@ -621,6 +628,101 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi return hdr_size; } + +int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata) +{ + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + // *FIXME: check the pointer + + std::string safe_line; + std::string tag; + bool logit(false); + len = (std::min)(len, size_t(256)); // Keep things reasonable in all cases + + switch (info) + { + case CURLINFO_TEXT: + if (op->mTracing > 1) + { + tag = "TEXT"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_HEADER_IN: + if (op->mTracing > 1) + { + tag = "HEADERIN"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_HEADER_OUT: + if (op->mTracing > 1) + { + tag = "HEADEROUT"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_DATA_IN: + if (op->mTracing > 1) + { + tag = "DATAIN"; + logit = true; + if (op->mTracing > 2) + { + escape_libcurl_debug_data(buffer, len, false, safe_line); + } + else + { + std::ostringstream out; + out << len << " Bytes"; + safe_line = out.str(); + } + } + break; + + case CURLINFO_DATA_OUT: + if (op->mTracing > 1) + { + tag = "DATAOUT"; + logit = true; + if (op->mTracing > 2) + { + escape_libcurl_debug_data(buffer, len, false, safe_line); + } + else + { + std::ostringstream out; + out << len << " Bytes"; + safe_line = out.str(); + } + } + break; + + default: + logit = false; + break; + } + + if (logit) + { + LL_INFOS("CoreHttp") << "TRACE, LibcurlDebug, Handle: " + << static_cast(op) + << ", Type: " << tag + << ", Data: " << safe_line + << LL_ENDL; + } + + return 0; +} + + } // end namespace LLCore @@ -694,6 +796,43 @@ char *strtok_r(char *str, const char *delim, char ** savestate) #endif + +void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line) +{ + std::string out; + len = (std::min)(len, size_t(200)); + out.reserve(3 * len); + for (int i(0); i < len; ++i) + { + unsigned char uc(static_cast(buffer[i])); + + if (uc < 32 || uc > 126) + { + if (scrub) + { + out.append(1, ' '); + } + else + { + static const char hex[] = "0123456789ABCDEF"; + char convert[4]; + + convert[0] = '%'; + convert[1] = hex[(uc >> 4) % 16]; + convert[2] = hex[uc % 16]; + convert[3] = '\0'; + out.append(convert); + } + } + else + { + out.append(1, buffer[i]); + } + } + safe_line.swap(out); +} + + } // end anonymous namespace diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 80893beb40..fc2301057c 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -76,6 +76,12 @@ public: public: // Setup Methods + HttpStatus setupGet(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers); + HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, @@ -103,15 +109,23 @@ public: virtual HttpStatus cancel(); protected: + void setupCommon(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + 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 int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata); protected: unsigned int mProcFlags; static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U; static const unsigned int PF_SAVE_HEADERS = 0x00000002U; - + public: // Request data EMethod mReqMethod; diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp index 21e058b2be..c1357f9ae5 100644 --- a/indra/llcorehttp/_httpopsetget.cpp +++ b/indra/llcorehttp/_httpopsetget.cpp @@ -89,8 +89,8 @@ void HttpOpSetGet::stageFromRequest(HttpService * service) } if (mStatus) { - const std::string * value; - if ((mStatus = pol_opt.get(setting, value))) + const std::string * value(NULL); + if ((mStatus = pol_opt.get(setting, &value))) { mStrValue = *value; } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 8ee3f88658..0e08d88276 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -107,6 +107,12 @@ void HttpPolicy::retryOp(HttpOpRequest * op) LL_WARNS("CoreHttp") << "URL op retry #" << op->mPolicyRetries << " being scheduled for " << delta << " uSecs from now." << LL_ENDL; + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle: " + << static_cast(op) + << LL_ENDL; + } mState[policy_class].mRetryQueue.push(op); } @@ -224,8 +230,8 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) } else if (op->mPolicyRetries) { - LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." - << LL_ENDL; + LL_DEBUGS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." + << LL_ENDL; } op->stageFromActive(mService); @@ -234,4 +240,15 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) } +int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) +{ + if (policy_class < HttpRequest::POLICY_CLASS_LIMIT) + { + return (mState[policy_class].mReadyQueue.size() + + mState[policy_class].mRetryQueue.size()); + } + return 0; +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 73c22bab78..4114f64848 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -97,7 +97,10 @@ public: } void setPolicies(const HttpPolicyGlobal & global); - + + // Get ready counts for a particular class + int getReadyCount(HttpRequest::policy_t policy_class); + protected: struct State { diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index d95d73cfba..6b1de38fd6 100644 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -33,7 +33,9 @@ namespace LLCore HttpPolicyGlobal::HttpPolicyGlobal() : mSetMask(0UL), - mConnectionLimit(32L) + mConnectionLimit(32L), + mTrace(0), + mUseLLProxy(0) {} @@ -50,6 +52,8 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other) mCAPath = other.mCAPath; mCAFile = other.mCAFile; mHttpProxy = other.mHttpProxy; + mTrace = other.mTrace; + mUseLLProxy = other.mUseLLProxy; } return *this; } @@ -63,6 +67,14 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) mConnectionLimit = value; break; + case HttpRequest::GP_TRACE: + mTrace = llclamp(value, 0L, 3L); + break; + + case HttpRequest::GP_LLPROXY: + mUseLLProxy = llclamp(value, 0L, 1L); + break; + default: return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } @@ -97,54 +109,64 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value) { static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + long * src(NULL); switch (opt) { case HttpRequest::GP_CONNECTION_LIMIT: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = mConnectionLimit; + src = &mConnectionLimit; + break; + + case HttpRequest::GP_TRACE: + src = &mTrace; + break; + + case HttpRequest::GP_LLPROXY: + src = &mUseLLProxy; break; default: return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = *src; return HttpStatus(); } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string *& value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string ** value) { static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + const std::string * src(NULL); switch (opt) { case HttpRequest::GP_CA_PATH: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = &mCAPath; + src = &mCAPath; break; case HttpRequest::GP_CA_FILE: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = &mCAFile; + src = &mCAFile; break; case HttpRequest::GP_HTTP_PROXY: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = &mHttpProxy; + src = &mHttpProxy; break; default: return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = src; return HttpStatus(); } diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h index f4bb4d4b25..a50d0e4188 100644 --- a/indra/llcorehttp/_httppolicyglobal.h +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -48,8 +48,8 @@ private: public: HttpStatus set(HttpRequest::EGlobalPolicy opt, long value); HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value); - HttpStatus get(HttpRequest::EGlobalPolicy opt, long & value); - HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string *& value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, long * value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string ** value); public: unsigned long mSetMask; @@ -57,6 +57,8 @@ public: std::string mCAPath; std::string mCAFile; std::string mHttpProxy; + long mTrace; + long mUseLLProxy; }; // end class HttpPolicyGlobal } // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 920a3f3b6d..beba8f08f4 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -36,6 +36,7 @@ #include "_thread.h" #include "lltimer.h" +#include "llthread.h" // Tuning parameters @@ -186,8 +187,10 @@ void HttpService::shutdown() void HttpService::threadRun(LLCoreInt::HttpThread * thread) { boost::this_thread::disable_interruption di; - ELoopSpeed loop(REQUEST_SLEEP); + + LLThread::registerThreadID(); + ELoopSpeed loop(REQUEST_SLEEP); while (! mExitRequested) { loop = processRequestQueue(loop); @@ -226,6 +229,19 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) // Process operation if (! mExitRequested) { + // Setup for subsequent tracing + long tracing(0); + mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing); + op->mTracing = (std::max)(op->mTracing, int(tracing)); + + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: " + << static_cast(op) + << LL_ENDL; + } + + // Stage op->stageFromRequest(this); } diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index 15b505f5bf..155fbda7f1 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -33,13 +33,15 @@ namespace LLCore HttpOptions::HttpOptions() : RefCounted(true), - mWantHeaders(false) + mWantHeaders(false), + mTracing(0) {} HttpOptions::HttpOptions(const HttpOptions & rhs) : RefCounted(true), - mWantHeaders(rhs.mWantHeaders) + mWantHeaders(rhs.mWantHeaders), + mTracing(rhs.mTracing) {} @@ -47,4 +49,16 @@ HttpOptions::~HttpOptions() {} +void HttpOptions::setWantHeaders() +{ + mWantHeaders = true; +} + + +void HttpOptions::setTrace(long level) +{ + mTracing = int(level); +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index 267a982dd5..0b9dfdc1de 100644 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -67,11 +67,21 @@ protected: void operator=(const HttpOptions &); // Not defined public: + void setWantHeaders(); + bool getWantHeaders() const + { + return mWantHeaders; + } + + void setTrace(long level); + int getTrace() const + { + return mTracing; + } protected: - // *TODO: add some options bool mWantHeaders; - + long int mTracing; }; // end class HttpOptions diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 089eee76f3..2036ecfd1c 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -135,6 +135,33 @@ HttpStatus HttpRequest::getStatus() const } +HttpHandle HttpRequest::requestGet(policy_t policy_id, + priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupGet(policy_id, priority, url, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfers refcount + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, priority_t priority, const std::string & url, diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index a953aa28d0..4e78ed3719 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -111,10 +111,43 @@ public: /// Maximum number of connections the library will use to /// perform operations. This is somewhat soft as the underlying /// transport will cache some connections (up to 5). - GP_CONNECTION_LIMIT, ///< Takes long giving number of connections - GP_CA_PATH, ///< System path/directory where SSL certs are stored. - GP_CA_FILE, ///< System path/file containing certs. - GP_HTTP_PROXY ///< String giving host/port to use for HTTP proxy + + /// A long value setting the maximum number of connections + /// allowed over all policy classes. Note that this will be + /// a somewhat soft value. There may be an additional five + /// connections per policy class depending upon runtime + /// behavior. + GP_CONNECTION_LIMIT, + + /// String containing a system-appropriate directory name + /// where SSL certs are stored. + GP_CA_PATH, + + /// String giving a full path to a file containing SSL certs. + GP_CA_FILE, + + /// String of host/port to use as simple HTTP proxy. This is + /// going to change in the future into something more elaborate + /// that may support richer schemes. + GP_HTTP_PROXY, + + /// 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. + GP_LLPROXY, + + /// Long value setting the logging trace level for the + /// library. Possible values are: + /// 0 - No tracing (default) + /// 1 - Basic tracing of request start, stop and major events. + /// 2 - Connection, header and payload size information from + /// HTTP transactions. + /// 3 - Partial logging of payload itself. + /// + /// These values are also used in the trace modes for + /// individual requests in HttpOptions. Also be aware that + /// tracing tends to impact performance of the viewer. + GP_TRACE }; /// Set a parameter on a global policy option. Calls @@ -133,7 +166,7 @@ public: /// the class in other methods. If -1, an error /// occurred and @see getStatus() may provide more /// detail on the reason. - policy_t createPolicyClass(); + static policy_t createPolicyClass(); enum EClassPolicy { @@ -157,7 +190,7 @@ public: /// @param opt Enum of option to be set. /// @param value Desired value of option. /// @return Standard status code. - HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); + static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); /// @} @@ -176,6 +209,36 @@ public: /// HttpStatus getStatus() const; + /// Queue a full HTTP GET request to be issued for entire entity. + /// The request is queued and serviced by the working thread and + /// notification of completion delivered to the optional HttpHandler + /// argument during @see update() calls. + /// + /// With a valid handle returned, it can be used to reference the + /// request in other requests (like cancellation) and will be an + /// argument when any HttpHandler object is invoked. + /// + /// @param policy_id Default or user-defined policy class under + /// which this request is to be serviced. + /// @param priority Standard priority scheme inherited from + /// Indra code base (U32-type scheme). + /// @param url + /// @param options (optional) + /// @param headers (optional) + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestGet(policy_t policy_id, + priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + /// Queue a full HTTP GET request to be issued with a 'Range' header. /// The request is queued and serviced by the working thread and /// notification of completion delivered to the optional HttpHandler diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 61698f34d8..5b04796c8a 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -395,7 +395,7 @@ void HttpRequestTestObjectType::test<5>() { ScopedCurlInit ready; - set_test_name("HttpRequest GET + Stop execution"); + set_test_name("HttpRequest GET to dead port + Stop execution"); // Handler can be stack-allocated *if* there are no dangling // references to it after completion of this method. @@ -496,6 +496,7 @@ void HttpRequestTestObjectType::test<5>() } } + template <> template <> void HttpRequestTestObjectType::test<6>() { @@ -517,6 +518,114 @@ void HttpRequestTestObjectType::test<6>() HttpRequest * req = NULL; + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<7>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with Range: header to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + try { // Get singletons created @@ -605,8 +714,9 @@ void HttpRequestTestObjectType::test<6>() } } + template <> template <> -void HttpRequestTestObjectType::test<7>() +void HttpRequestTestObjectType::test<8>() { ScopedCurlInit ready; @@ -725,7 +835,7 @@ void HttpRequestTestObjectType::test<7>() } template <> template <> -void HttpRequestTestObjectType::test<8>() +void HttpRequestTestObjectType::test<9>() { ScopedCurlInit ready; @@ -843,6 +953,118 @@ void HttpRequestTestObjectType::test<8>() } } +template <> template <> +void HttpRequestTestObjectType::test<10>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with some tracing"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Enable tracing + HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + } // end namespace tut namespace diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e2c13e77e3..430dd89c3e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5356,11 +5356,9 @@ void CoreHttp::init() << LL_ENDL; } - mRequest = new LLCore::HttpRequest; - // Point to our certs or SSH/https: will fail on connect - status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, - gDirUtilp->getCAFile()); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, + gDirUtilp->getCAFile()); if (! status) { LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " @@ -5371,15 +5369,22 @@ void CoreHttp::init() // Establish HTTP Proxy. "LLProxy" is a special string which directs // the code to use LLProxy::applyProxySettings() to establish any // HTTP or SOCKS proxy for http operations. - status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_HTTP_PROXY, - std::string("LLProxy")); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); if (! status) { LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. 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 + // 2 - libcurl CURLOPT_VERBOSE mode with brief lines + // 3 - with partial data content + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 0); + + // Kick the thread status = LLCore::HttpRequest::startThread(); if (! status) { @@ -5388,6 +5393,7 @@ void CoreHttp::init() << LL_ENDL; } + mRequest = new LLCore::HttpRequest; } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f5e7540e85..664af02f78 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -431,6 +431,22 @@ private: void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } + // Locks: Mw + void acquireHttpSemaphore() + { + llassert(! mHttpHasResource); + mHttpHasResource = true; + --mFetcher->mHttpSemaphore; + } + + // Locks: Mw + void releaseHttpSemaphore() + { + llassert(mHttpHasResource); + mHttpHasResource = false; + ++mFetcher->mHttpSemaphore; + } + private: enum e_state // mState { @@ -444,8 +460,9 @@ private: CACHE_POST, LOAD_FROM_NETWORK, LOAD_FROM_SIMULATOR, - SEND_HTTP_REQ, // Commit to sending as HTTP WAIT_HTTP_RESOURCE, // Waiting for HTTP resources + WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources + SEND_HTTP_REQ, // Commit to sending as HTTP WAIT_HTTP_REQ, // Request sent, wait for completion DECODE_IMAGE, DECODE_IMAGE_UPDATE, @@ -532,7 +549,7 @@ private: bool mHttpActive; // Active request to http library unsigned int mHttpReplySize; unsigned int mHttpReplyOffset; - bool mHttpReleased; // Has been released from resource wait once + bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore }; ////////////////////////////////////////////////////////////////////////////// @@ -768,8 +785,9 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "CACHE_POST", "LOAD_FROM_NETWORK", "LOAD_FROM_SIMULATOR", - "SEND_HTTP_REQ", "WAIT_HTTP_RESOURCE", + "WAIT_HTTP_RESOURCE2", + "SEND_HTTP_REQ", "WAIT_HTTP_REQ", "DECODE_IMAGE", "DECODE_IMAGE_UPDATE", @@ -836,7 +854,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHttpActive(false), mHttpReplySize(0U), mHttpReplyOffset(0U), - mHttpReleased(true) + mHttpHasResource(false) { mCanUseNET = mUrl.empty() ; @@ -860,6 +878,10 @@ LLTextureFetchWorker::~LLTextureFetchWorker() llassert_always(!haveWork()); lockWorkMutex(); // +Mw (should be useless) + if (mHttpHasResource) + { + releaseHttpSemaphore(); + } if (mHttpActive) { // Issue a cancel on a live request... @@ -1126,7 +1148,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llwarns << "Unknown URL Type: " << mUrl << llendl; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; } else { @@ -1223,7 +1245,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } if (mCanUseHTTP && !mUrl.empty()) { - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); if(mWriteToCacheState != NOT_WRITE) { @@ -1287,31 +1309,38 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; } - if (mState == SEND_HTTP_REQ) + if (mState == WAIT_HTTP_RESOURCE) { - if (! mCanUseHTTP) - { - return true; // abort - } - // NOTE: // control the number of the http requests issued for: // 1, not openning too many file descriptors at the same time; // 2, control the traffic of http so udp gets bandwidth. // - if (! mHttpReleased) + // If it looks like we're busy, keep this request here. + // Otherwise, advance into the HTTP states. + if (mFetcher->mHttpSemaphore <= 0 || mFetcher->getHttpWaitersCount()) { - // If this request hasn't been released before and it looks like - // we're busy, put this request into resource wait and allow something - // else to come to the front. - if (mFetcher->getNumHTTPRequests() >= HTTP_REQUESTS_IN_QUEUE_HIGH_WATER || - mFetcher->getHttpWaitersCount()) - { - mState = WAIT_HTTP_RESOURCE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mFetcher->addHttpWaiter(this->mID); - return false; - } + mState = WAIT_HTTP_RESOURCE2; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mFetcher->addHttpWaiter(this->mID); + return false; + } + mState = SEND_HTTP_REQ; + acquireHttpSemaphore(); + } + + if (mState == WAIT_HTTP_RESOURCE2) + { + // Just idle it if we make it to the head... + return false; + } + + if (mState == SEND_HTTP_REQ) + { + if (! mCanUseHTTP) + { + releaseHttpSemaphore(); + return true; // abort } mFetcher->removeFromNetworkQueue(this, false); @@ -1327,10 +1356,12 @@ bool LLTextureFetchWorker::doWork(S32 param) // We already have all the data, just decode it mLoadedDiscard = mFormattedImage->getDiscardLevel(); mState = DECODE_IMAGE; + releaseHttpSemaphore(); return false; } else { + releaseHttpSemaphore(); return true; // abort. } } @@ -1365,6 +1396,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { llwarns << "HTTP GET request failed for " << mID << llendl; resetFormattedData(); + releaseHttpSemaphore(); return true; // failed } @@ -1377,13 +1409,6 @@ bool LLTextureFetchWorker::doWork(S32 param) // fall through } - if (mState == WAIT_HTTP_RESOURCE) - { - // Nothing to do until releaseHttpWaiters() puts us back - // into the flow... - return false; - } - if (mState == WAIT_HTTP_REQ) { if (mLoaded) @@ -1401,6 +1426,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mState = INIT; mCanUseHTTP = false; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + releaseHttpSemaphore(); return false; } } @@ -1423,12 +1449,14 @@ bool LLTextureFetchWorker::doWork(S32 param) // Use available data mLoadedDiscard = mFormattedImage->getDiscardLevel(); mState = DECODE_IMAGE; + releaseHttpSemaphore(); return false; } // Fail harder resetFormattedData(); mState = DONE; + releaseHttpSemaphore(); return true; // failed } @@ -1443,6 +1471,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // abort. mState = DONE; + releaseHttpSemaphore(); return true; } @@ -1491,6 +1520,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mWriteToCacheState = SHOULD_WRITE ; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + releaseHttpSemaphore(); return false; } else @@ -2137,7 +2167,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mQAMode(qa_mode), mHttpRequest(NULL), mHttpOptions(NULL), - mHttpHeaders(NULL) + mHttpHeaders(NULL), + mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) { mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); @@ -3155,12 +3186,11 @@ void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) // Locks: -Mw (must not hold any worker when called) void LLTextureFetch::releaseHttpWaiters() { - if (HTTP_REQUESTS_IN_QUEUE_LOW_WATER < getNumHTTPRequests()) + if (mHttpSemaphore < HTTP_REQUESTS_IN_QUEUE_LOW_WATER) return; // Quickly make a copy of all the LLUIDs. Get off the // mutex as early as possible. - typedef std::vector uuid_vec_t; uuid_vec_t tids; @@ -3171,13 +3201,12 @@ void LLTextureFetch::releaseHttpWaiters() return; const size_t limit(mHttpWaitResource.size()); - tids.resize(limit); - wait_http_res_queue_t::iterator iter(mHttpWaitResource.begin()); - for (int i(0); - i < limit && mHttpWaitResource.end() != iter; - ++i, ++iter) + tids.reserve(limit); + for (wait_http_res_queue_t::iterator iter(mHttpWaitResource.begin()); + mHttpWaitResource.end() != iter; + ++iter) { - tids[i] = *iter; + tids.push_back(*iter); } } // -Mfnq @@ -3196,28 +3225,29 @@ void LLTextureFetch::releaseHttpWaiters() tids2.insert(worker); } } + tids.clear(); // Release workers up to the high water mark. Since we aren't // holding any locks at this point, we can be in competition // with other callers. Do defensive things like getting // refreshed counts of requests and checking if someone else // has moved any worker state around.... - tids.clear(); for (worker_set_t::iterator iter2(tids2.begin()); - tids2.end() != iter2 && 0 < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - getNumHTTPRequests()); + tids2.end() != iter2 && mHttpSemaphore > 0; ++iter2) { LLTextureFetchWorker * worker(* iter2); worker->lockWorkMutex(); // +Mw - if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE != worker->mState) + if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE2 != worker->mState) { worker->unlockWorkMutex(); // -Mw continue; } - worker->mHttpReleased = true; + worker->mState = LLTextureFetchWorker::SEND_HTTP_REQ; worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->acquireHttpSemaphore(); worker->unlockWorkMutex(); // -Mw removeHttpWaiter(worker->mID); @@ -3456,7 +3486,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) } // In QA mode, Metrics submode, log the result for ease of testing - if (fetcher->isQAMode() || true) + if (fetcher->isQAMode()) { LL_INFOS("Textures") << merged_llsd << LL_ENDL; } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 4ee13d171e..50e3181623 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -312,6 +312,15 @@ private: LLCore::HttpOptions * mHttpOptions; // Ttf LLCore::HttpHeaders * mHttpHeaders; // Ttf + // We use a resource semaphore to keep HTTP requests in + // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the + // transport. This keeps them near where they can be cheaply + // reprioritized rather than dumping them all across a thread + // where it's more expensive to get at them. Requests in either + // SEND_HTTP_REQ or WAIT_HTTP_REQ charge against the semaphore + // and tracking state transitions is critical to liveness. + int mHttpSemaphore; // Ttf + typedef std::set wait_http_res_queue_t; wait_http_res_queue_t mHttpWaitResource; // Mfnq diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 5f1d7829ed..bb1535d23d 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -234,15 +234,16 @@ void LLTextureBar::draw() { "DSK", LLColor4::blue }, // CACHE_POST { "NET", LLColor4::green }, // LOAD_FROM_NETWORK { "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE2 { "REQ", LLColor4::yellow },// SEND_HTTP_REQ - { "HTW", LLColor4::green }, // WAIT_HTTP_RES { "HTP", LLColor4::green }, // WAIT_HTTP_REQ { "DEC", LLColor4::yellow },// DECODE_IMAGE { "DEC", LLColor4::green }, // DECODE_IMAGE_UPDATE { "WRT", LLColor4::purple },// WRITE_TO_CACHE { "WRT", LLColor4::orange },// WAIT_ON_WRITE { "END", LLColor4::red }, // DONE -#define LAST_STATE 13 +#define LAST_STATE 14 { "CRE", LLColor4::magenta }, // LAST_STATE+1 { "FUL", LLColor4::green }, // LAST_STATE+2 { "BAD", LLColor4::red }, // LAST_STATE+3 -- cgit v1.3