diff options
Diffstat (limited to 'indra/llmessage')
-rw-r--r-- | indra/llmessage/CMakeLists.txt | 8 | ||||
-rw-r--r-- | indra/llmessage/llavatarnamecache.cpp | 99 | ||||
-rw-r--r-- | indra/llmessage/llavatarnamecache.h | 7 | ||||
-rw-r--r-- | indra/llmessage/llcurl.cpp | 353 | ||||
-rw-r--r-- | indra/llmessage/llcurl.h | 85 | ||||
-rw-r--r-- | indra/llmessage/llhttpassetstorage.cpp | 2 | ||||
-rw-r--r-- | indra/llmessage/llsdmessagereader.cpp | 2 | ||||
-rw-r--r-- | indra/llmessage/llurlrequest.cpp | 5 | ||||
-rw-r--r-- | indra/llmessage/message_prehash.cpp | 3 | ||||
-rw-r--r-- | indra/llmessage/message_prehash.h | 3 | ||||
-rw-r--r-- | indra/llmessage/tests/llcurl_stub.cpp | 17 | ||||
-rw-r--r-- | indra/llmessage/tests/llhttpclient_test.cpp | 376 | ||||
-rw-r--r-- | indra/llmessage/tests/llsdmessage_test.cpp | 38 | ||||
-rw-r--r-- | indra/llmessage/tests/test_llsdmessage_peer.py | 19 |
14 files changed, 836 insertions, 181 deletions
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 0f40a670fa..d98781e9e6 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -254,6 +254,14 @@ if (LL_TESTS) "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py" ) + LL_ADD_INTEGRATION_TEST( + llhttpclient + "llhttpclient.cpp" + "${test_libs}" + ${PYTHON_EXECUTABLE} + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py" + ) + LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llpartdata "" "${test_libs}") diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 97f2792686..a6e2c89ba4 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -87,6 +87,9 @@ namespace LLAvatarNameCache /// Time when unrefreshed cached names were checked last static F64 sLastExpireCheck; + /// Time-to-live for a temp cache entry. + const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; + //----------------------------------------------------------------------- // Internal methods //----------------------------------------------------------------------- @@ -274,7 +277,7 @@ void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) { // there is no existing cache entry, so make a temporary name from legacy LL_WARNS("AvNameCache") << "LLAvatarNameCache get legacy for agent " - << agent_id << LL_ENDL; + << agent_id << LL_ENDL; gCacheName->get(agent_id, false, // legacy compatibility boost::bind(&LLAvatarNameCache::legacyNameCallback, _1, _2, _3)); @@ -287,13 +290,14 @@ void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) // Clear this agent from the pending list LLAvatarNameCache::sPendingQueue.erase(agent_id); - const LLAvatarName& av_name = existing->second; + LLAvatarName& av_name = existing->second; LL_DEBUGS("AvNameCache") << "LLAvatarNameCache use cache for agent " << agent_id << "user '" << av_name.mUsername << "' " << "display '" << av_name.mDisplayName << "' " << "expires in " << av_name.mExpires - LLFrameTimer::getTotalSeconds() << " seconds" << LL_ENDL; + av_name.mExpires = LLFrameTimer::getTotalSeconds() + TEMP_CACHE_ENTRY_LIFETIME; // reset expiry time so we don't constantly rerequest. } } @@ -330,8 +334,9 @@ void LLAvatarNameCache::requestNamesViaCapability() // http://pdp60.lindenlab.com:8000/agents/?ids=3941037e-78ab-45f0-b421-bd6e77c1804d&ids=0012809d-7d2d-4c24-9609-af1230a37715&ids=0019aaba-24af-4f0a-aa72-6457953cf7f0 // // Apache can handle URLs of 4096 chars, but let's be conservative - const U32 NAME_URL_MAX = 4096; - const U32 NAME_URL_SEND_THRESHOLD = 3000; + static const U32 NAME_URL_MAX = 4096; + static const U32 NAME_URL_SEND_THRESHOLD = 3500; + std::string url; url.reserve(NAME_URL_MAX); @@ -339,10 +344,12 @@ void LLAvatarNameCache::requestNamesViaCapability() agent_ids.reserve(128); U32 ids = 0; - ask_queue_t::const_iterator it = sAskQueue.begin(); - for ( ; it != sAskQueue.end(); ++it) + ask_queue_t::const_iterator it; + while(!sAskQueue.empty()) { - const LLUUID& agent_id = *it; + it = sAskQueue.begin(); + LLUUID agent_id = *it; + sAskQueue.erase(it); if (url.empty()) { @@ -365,27 +372,17 @@ void LLAvatarNameCache::requestNamesViaCapability() if (url.size() > NAME_URL_SEND_THRESHOLD) { - LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability first " - << ids << " ids" - << LL_ENDL; - LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); - url.clear(); - agent_ids.clear(); + break; } } if (!url.empty()) { - LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability all " + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaCapability requested " << ids << " ids" << LL_ENDL; LLHTTPClient::get(url, new LLAvatarNameResponder(agent_ids)); - url.clear(); - agent_ids.clear(); } - - // We've moved all asks to the pending request queue - sAskQueue.clear(); } void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id, @@ -402,20 +399,25 @@ void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id, << LL_ENDL; buildLegacyName(full_name, &av_name); - // Don't add to cache, the data already exists in the legacy name system - // cache and we don't want or need duplicate storage, because keeping the - // two copies in sync is complex. - processName(agent_id, av_name, false); + // Add to cache, because if we don't we'll keep rerequesting the + // same record forever. buildLegacyName should always guarantee + // that these records expire reasonably soon + // (in TEMP_CACHE_ENTRY_LIFETIME seconds), so if the failure was due + // to something temporary we will eventually request and get the right data. + processName(agent_id, av_name, true); } void LLAvatarNameCache::requestNamesViaLegacy() { + static const S32 MAX_REQUESTS = 100; F64 now = LLFrameTimer::getTotalSeconds(); std::string full_name; - ask_queue_t::const_iterator it = sAskQueue.begin(); - for (; it != sAskQueue.end(); ++it) + ask_queue_t::const_iterator it; + for (S32 requests = 0; !sAskQueue.empty() && requests < MAX_REQUESTS; ++requests) { - const LLUUID& agent_id = *it; + it = sAskQueue.begin(); + LLUUID agent_id = *it; + sAskQueue.erase(it); // Mark as pending first, just in case the callback is immediately // invoked below. This should never happen in practice. @@ -427,10 +429,6 @@ void LLAvatarNameCache::requestNamesViaLegacy() boost::bind(&LLAvatarNameCache::legacyNameCallback, _1, _2, _3)); } - - // We've either answered immediately or moved all asks to the - // pending queue - sAskQueue.clear(); } void LLAvatarNameCache::initClass(bool running) @@ -507,11 +505,11 @@ void LLAvatarNameCache::idle() // *TODO: Possibly re-enabled this based on People API load measurements // 100 ms is the threshold for "user speed" operations, so we can // stall for about that long to batch up requests. - //const F32 SECS_BETWEEN_REQUESTS = 0.1f; - //if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) - //{ - // return; - //} + const F32 SECS_BETWEEN_REQUESTS = 0.1f; + if (!sRequestTimer.hasExpired()) + { + return; + } if (!sAskQueue.empty()) { @@ -526,6 +524,12 @@ void LLAvatarNameCache::idle() } } + if (sAskQueue.empty()) + { + // cleared the list, reset the request timer. + sRequestTimer.resetWithExpiry(SECS_BETWEEN_REQUESTS); + } + // erase anything that has not been refreshed for more than MAX_UNREFRESHED_TIME eraseUnrefreshed(); } @@ -559,8 +563,7 @@ void LLAvatarNameCache::eraseUnrefreshed() const LLAvatarName& av_name = it->second; if (av_name.mExpires < max_unrefreshed) { - const LLUUID& agent_id = it->first; - LL_DEBUGS("AvNameCache") << agent_id + LL_DEBUGS("AvNameCache") << it->first << " user '" << av_name.mUsername << "' " << "expired " << now - av_name.mExpires << " secs ago" << LL_ENDL; @@ -583,7 +586,7 @@ void LLAvatarNameCache::buildLegacyName(const std::string& full_name, av_name->mDisplayName = full_name; av_name->mIsDisplayNameDefault = true; av_name->mIsTemporaryName = true; - av_name->mExpires = F64_MAX; // not used because these are not cached + av_name->mExpires = LLFrameTimer::getTotalSeconds() + TEMP_CACHE_ENTRY_LIFETIME; LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::buildLegacyName " << full_name << LL_ENDL; @@ -651,8 +654,10 @@ void LLAvatarNameCache::fireSignal(const LLUUID& agent_id, signal(agent_id, av_name); } -void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot) +LLAvatarNameCache::callback_connection_t LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot) { + callback_connection_t connection; + if (sRunning) { // ...only do immediate lookups when cache is running @@ -668,7 +673,7 @@ void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot) { // ...name already exists in cache, fire callback now fireSignal(agent_id, slot, av_name); - return; + return connection; } } } @@ -681,7 +686,7 @@ void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot) LLAvatarName av_name; buildLegacyName(full_name, &av_name); fireSignal(agent_id, slot, av_name); - return; + return connection; } } } @@ -698,15 +703,17 @@ void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot) { // ...new callback for this id callback_signal_t* signal = new callback_signal_t(); - signal->connect(slot); + connection = signal->connect(slot); sSignalMap[agent_id] = signal; } else { // ...existing callback, bind additional slot callback_signal_t* signal = sig_it->second; - signal->connect(slot); + connection = signal->connect(slot); } + + return connection; } @@ -733,12 +740,6 @@ void LLAvatarNameCache::erase(const LLUUID& agent_id) sCache.erase(agent_id); } -void LLAvatarNameCache::fetch(const LLUUID& agent_id) -{ - // re-request, even if request is already pending - sAskQueue.insert(agent_id); -} - void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_name) { // *TODO: update timestamp if zero? diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index 59c1329ffa..79f170f7c8 100644 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -71,10 +71,11 @@ namespace LLAvatarNameCache void (const LLUUID& agent_id, const LLAvatarName& av_name)> callback_signal_t; typedef callback_signal_t::slot_type callback_slot_t; + typedef boost::signals2::connection callback_connection_t; // Fetches name information and calls callback. // If name information is in cache, callback will be called immediately. - void get(const LLUUID& agent_id, callback_slot_t slot); + callback_connection_t get(const LLUUID& agent_id, callback_slot_t slot); // Allow display names to be explicitly disabled for testing. void setUseDisplayNames(bool use); @@ -85,10 +86,6 @@ namespace LLAvatarNameCache /// Provide some fallback for agents that return errors void handleAgentError(const LLUUID& agent_id); - // Force a re-fetch of the most recent data, but keep the current - // data in cache - void fetch(const LLUUID& agent_id); - void insert(const LLUUID& agent_id, const LLAvatarName& av_name); // Compute name expiration time from HTTP Cache-Control header, diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index b93d429feb..8ffa8e4271 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -133,12 +133,12 @@ std::string LLCurl::getVersionString() ////////////////////////////////////////////////////////////////////////////// LLCurl::Responder::Responder() - : mReferenceCount(0) { } LLCurl::Responder::~Responder() { + LL_CHECK_MEMORY } // virtual @@ -202,23 +202,6 @@ void LLCurl::Responder::completedHeader(U32 status, const std::string& reason, c } -namespace boost -{ - void intrusive_ptr_add_ref(LLCurl::Responder* p) - { - ++p->mReferenceCount; - } - - void intrusive_ptr_release(LLCurl::Responder* p) - { - if (p && 0 == --p->mReferenceCount) - { - delete p; - } - } -}; - - ////////////////////////////////////////////////////////////////////////////// std::set<CURL*> LLCurl::Easy::sFreeHandles; @@ -267,15 +250,18 @@ void LLCurl::Easy::releaseEasyHandle(CURL* handle) LLMutexLock lock(sHandleMutexp) ; if (sActiveHandles.find(handle) != sActiveHandles.end()) { + LL_CHECK_MEMORY sActiveHandles.erase(handle); - + LL_CHECK_MEMORY if(sFreeHandles.size() < MAX_NUM_FREE_HANDLES) { - sFreeHandles.insert(handle); - } - else - { + sFreeHandles.insert(handle); + LL_CHECK_MEMORY + } + else + { LLCurl::deleteEasyHandle(handle) ; + LL_CHECK_MEMORY } } else @@ -308,6 +294,8 @@ LLCurl::Easy* LLCurl::Easy::getEasy() // multi handles cache if they are added to one. CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); check_curl_code(result); + result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + check_curl_code(result); ++gCurlEasyCount; return easy; @@ -318,13 +306,15 @@ LLCurl::Easy::~Easy() releaseEasyHandle(mCurlEasyHandle); --gCurlEasyCount; curl_slist_free_all(mHeaders); + LL_CHECK_MEMORY for_each(mStrings.begin(), mStrings.end(), DeletePointerArray()); - + LL_CHECK_MEMORY if (mResponder && LLCurl::sNotQuitting) //aborted { std::string reason("Request timeout, aborted.") ; mResponder->completedRaw(408, //HTTP_REQUEST_TIME_OUT, timeout, abort reason, mChannels, mOutput); + LL_CHECK_MEMORY } mResponder = NULL; } @@ -494,7 +484,8 @@ void LLCurl::Easy::prepRequest(const std::string& url, //setopt(CURLOPT_VERBOSE, 1); // useful for debugging setopt(CURLOPT_NOSIGNAL, 1); - + setopt(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + // Set the CURL options for either Socks or HTTP proxy LLProxy::getInstance()->applyProxySettings(this); @@ -548,6 +539,7 @@ LLCurl::Multi::Multi(F32 idle_time_out) mErrorCount(0), mState(STATE_READY), mDead(FALSE), + mValid(TRUE), mMutexp(NULL), mDeletionMutexp(NULL), mEasyMutexp(NULL) @@ -583,41 +575,65 @@ LLCurl::Multi::Multi(F32 idle_time_out) LLCurl::Multi::~Multi() { - cleanup() ; + cleanup(true) ; + + delete mDeletionMutexp ; + mDeletionMutexp = NULL ; } -void LLCurl::Multi::cleanup() +void LLCurl::Multi::cleanup(bool deleted) { if(!mCurlMultiHandle) { return ; //nothing to clean. } + llassert_always(deleted || !mValid) ; + + LLMutexLock lock(mDeletionMutexp); + // Clean up active for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); iter != mEasyActiveList.end(); ++iter) { Easy* easy = *iter; + LL_CHECK_MEMORY check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); + LL_CHECK_MEMORY + if(deleted) + { + easy->mResponder = NULL ; //avoid triggering mResponder. + LL_CHECK_MEMORY + } delete easy; + LL_CHECK_MEMORY } mEasyActiveList.clear(); mEasyActiveMap.clear(); - // Clean up freed + LL_CHECK_MEMORY + + // Clean up freed for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer()); mEasyFreeList.clear(); - + + LL_CHECK_MEMORY + check_curl_multi_code(LLCurl::deleteMultiHandle(mCurlMultiHandle)); mCurlMultiHandle = NULL ; + LL_CHECK_MEMORY + delete mMutexp ; mMutexp = NULL ; - delete mDeletionMutexp ; - mDeletionMutexp = NULL ; + + LL_CHECK_MEMORY + delete mEasyMutexp ; mEasyMutexp = NULL ; + LL_CHECK_MEMORY + mQueued = 0 ; mState = STATE_COMPLETED; @@ -644,10 +660,20 @@ void LLCurl::Multi::unlock() void LLCurl::Multi::markDead() { - LLMutexLock lock(mDeletionMutexp) ; + { + LLMutexLock lock(mDeletionMutexp) ; - mDead = TRUE ; - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_URGENT) ; + if(mCurlMultiHandle != NULL) + { + mDead = TRUE ; + LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_URGENT) ; + + return; + } + } + + //not valid, delete it. + delete this; } void LLCurl::Multi::setState(LLCurl::Multi::ePerformState state) @@ -741,10 +767,14 @@ bool LLCurl::Multi::doPerform() setState(STATE_COMPLETED) ; mIdleTimer.reset() ; } - else if(mIdleTimer.getElapsedTimeF32() > mIdleTimeOut) //idle for too long, remove it. + else if(!mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut) //idle for too long, remove it. { dead = true ; } + else if(mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut - 1.f) //idle for too long, mark it invalid. + { + mValid = FALSE ; + } return dead ; } @@ -911,8 +941,8 @@ bool LLCurlThread::CurlRequest::processRequest() if(!completed) { - setPriority(LLQueuedThread::PRIORITY_LOW) ; - } + setPriority(LLQueuedThread::PRIORITY_LOW) ; + } } return completed ; @@ -922,7 +952,7 @@ void LLCurlThread::CurlRequest::finishRequest(bool completed) { if(mMulti->isDead()) { - mCurlThread->deleteMulti(mMulti) ; + mCurlThread->deleteMulti(mMulti) ; } else { @@ -966,15 +996,9 @@ void LLCurlThread::killMulti(LLCurl::Multi* multi) return ; } - if(multi->isValid()) - { + multi->markDead() ; } - else - { - deleteMulti(multi) ; - } -} //private bool LLCurlThread::doMultiPerform(LLCurl::Multi* multi) @@ -992,6 +1016,10 @@ void LLCurlThread::deleteMulti(LLCurl::Multi* multi) void LLCurlThread::cleanupMulti(LLCurl::Multi* multi) { multi->cleanup() ; + if(multi->isDead()) //check if marked dead during cleaning up. + { + deleteMulti(multi) ; + } } //------------------------------------------------------------ @@ -1074,12 +1102,15 @@ void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) { getByteRange(url, headers_t(), 0, -1, responder); } - + +// Note: (length==0) is interpreted as "the rest of the file", i.e. the whole file if (offset==0) or +// the remainder of the file if not. bool LLCurlRequest::getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder) { + llassert(LLCurl::sNotQuitting); LLCurl::Easy* easy = allocEasy(); if (!easy) { @@ -1092,6 +1123,11 @@ bool LLCurlRequest::getByteRange(const std::string& url, std::string range = llformat("Range: bytes=%d-%d", offset,offset+length-1); easy->slist_append(range.c_str()); } + else if (offset > 0) + { + std::string range = llformat("Range: bytes=%d-", offset); + easy->slist_append(range.c_str()); + } easy->setHeaders(); bool res = addEasy(easy); return res; @@ -1102,6 +1138,7 @@ bool LLCurlRequest::post(const std::string& url, const LLSD& data, LLCurl::ResponderPtr responder, S32 time_out) { + llassert(LLCurl::sNotQuitting); LLCurl::Easy* easy = allocEasy(); if (!easy) { @@ -1129,6 +1166,7 @@ bool LLCurlRequest::post(const std::string& url, const std::string& data, LLCurl::ResponderPtr responder, S32 time_out) { + llassert(LLCurl::sNotQuitting); LLCurl::Easy* easy = allocEasy(); if (!easy) { @@ -1217,6 +1255,208 @@ S32 LLCurlRequest::getQueued() return queued; } +LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : + LLCurlRequest(), + mConcurrency(concurrency), + mInQueue(0), + mMutex(NULL), + mHandleCounter(1), + mTotalIssuedRequests(0), + mTotalReceivedBits(0) +{ + mGlobalTimer.reset(); +} + +LLCurlTextureRequest::~LLCurlTextureRequest() +{ + mRequestMap.clear(); + + for(req_queue_t::iterator iter = mCachedRequests.begin(); iter != mCachedRequests.end(); ++iter) + { + delete *iter; + } + mCachedRequests.clear(); +} + +//return 0: success +// > 0: cached handle +U32 LLCurlTextureRequest::getByteRange(const std::string& url, + const headers_t& headers, + S32 offset, S32 length, U32 pri, + LLCurl::ResponderPtr responder, F32 delay_time) +{ + U32 ret_val = 0; + bool success = false; + + if(mInQueue < mConcurrency && delay_time < 0.f) + { + success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); + } + + LLMutexLock lock(&mMutex); + + if(success) + { + mInQueue++; + mTotalIssuedRequests++; + } + else + { + request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); + if(delay_time > 0.f) + { + request->mStartTime = mGlobalTimer.getElapsedTimeF32() + delay_time; + } + + mCachedRequests.insert(request); + mRequestMap[mHandleCounter] = request; + ret_val = mHandleCounter; + mHandleCounter++; + + if(!mHandleCounter) + { + mHandleCounter = 1; + } + } + + return ret_val; +} + +void LLCurlTextureRequest::completeRequest(S32 received_bytes) +{ + LLMutexLock lock(&mMutex); + + llassert_always(mInQueue > 0); + + mInQueue--; + mTotalReceivedBits += received_bytes * 8; +} + +void LLCurlTextureRequest::nextRequests() +{ + if(mCachedRequests.empty() || mInQueue >= mConcurrency) + { + return; + } + + F32 cur_time = mGlobalTimer.getElapsedTimeF32(); + + req_queue_t::iterator iter; + { + LLMutexLock lock(&mMutex); + iter = mCachedRequests.begin(); + } + while(1) + { + request_t* request = *iter; + if(request->mStartTime < cur_time) + { + if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) + { + break; + } + + LLMutexLock lock(&mMutex); + ++iter; + mInQueue++; + mTotalIssuedRequests++; + mCachedRequests.erase(request); + mRequestMap.erase(request->mHandle); + delete request; + + if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) + { + break; + } + } + else + { + LLMutexLock lock(&mMutex); + ++iter; + if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) + { + break; + } + } + } + + return; +} + +void LLCurlTextureRequest::updatePriority(U32 handle, U32 pri) +{ + if(!handle) + { + return; + } + + LLMutexLock lock(&mMutex); + + std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); + if(iter != mRequestMap.end()) + { + request_t* req = iter->second; + + if(req->mPriority != pri) + { + mCachedRequests.erase(req); + req->mPriority = pri; + mCachedRequests.insert(req); + } + } +} + +void LLCurlTextureRequest::removeRequest(U32 handle) +{ + if(!handle) + { + return; + } + + LLMutexLock lock(&mMutex); + + std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); + if(iter != mRequestMap.end()) + { + request_t* req = iter->second; + mRequestMap.erase(iter); + mCachedRequests.erase(req); + delete req; + } +} + +bool LLCurlTextureRequest::isWaiting(U32 handle) +{ + if(!handle) + { + return false; + } + + LLMutexLock lock(&mMutex); + return mRequestMap.find(handle) != mRequestMap.end(); +} + +U32 LLCurlTextureRequest::getTotalReceivedBits() +{ + LLMutexLock lock(&mMutex); + + U32 bits = mTotalReceivedBits; + mTotalReceivedBits = 0; + return bits; +} + +U32 LLCurlTextureRequest::getTotalIssuedRequests() +{ + LLMutexLock lock(&mMutex); + return mTotalIssuedRequests; +} + +S32 LLCurlTextureRequest::getNumRequests() +{ + LLMutexLock lock(&mMutex); + return mInQueue; +} + //////////////////////////////////////////////////////////////////////////// // For generating one easy request // associated with a single multi request @@ -1483,35 +1723,51 @@ void LLCurl::cleanupClass() break ; } } + LL_CHECK_MEMORY sCurlThread->shutdown() ; + LL_CHECK_MEMORY delete sCurlThread ; sCurlThread = NULL ; + LL_CHECK_MEMORY #if SAFE_SSL CRYPTO_set_locking_callback(NULL); for_each(sSSLMutex.begin(), sSSLMutex.end(), DeletePointer()); #endif + + LL_CHECK_MEMORY for (std::set<CURL*>::iterator iter = Easy::sFreeHandles.begin(); iter != Easy::sFreeHandles.end(); ++iter) { CURL* curl = *iter; LLCurl::deleteEasyHandle(curl); } + + LL_CHECK_MEMORY Easy::sFreeHandles.clear(); + LL_CHECK_MEMORY + delete Easy::sHandleMutexp ; Easy::sHandleMutexp = NULL ; + LL_CHECK_MEMORY + delete sHandleMutexp ; sHandleMutexp = NULL ; - llassert(Easy::sActiveHandles.empty()); + LL_CHECK_MEMORY + + // removed as per https://jira.secondlife.com/browse/SH-3115 + //llassert(Easy::sActiveHandles.empty()); } //static CURLM* LLCurl::newMultiHandle() { + llassert(sNotQuitting); + LLMutexLock lock(sHandleMutexp) ; if(sTotalHandles + 1 > sMaxHandles) @@ -1545,6 +1801,7 @@ CURLMcode LLCurl::deleteMultiHandle(CURLM* handle) //static CURL* LLCurl::newEasyHandle() { + llassert(sNotQuitting); LLMutexLock lock(sHandleMutexp) ; if(sTotalHandles + 1 > sMaxHandles) @@ -1569,7 +1826,9 @@ void LLCurl::deleteEasyHandle(CURL* handle) if(handle) { LLMutexLock lock(sHandleMutexp) ; + LL_CHECK_MEMORY curl_easy_cleanup(handle) ; + LL_CHECK_MEMORY sTotalHandles-- ; } } diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index fd664c0fa1..7bcf61e233 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -44,6 +44,8 @@ #include "llthread.h" #include "llqueuedthread.h" #include "llframetimer.h" +#include "llpointer.h" + class LLMutex; class LLCurlThread; @@ -67,7 +69,7 @@ public: F64 mSpeedDownload; }; - class Responder + class Responder : public LLThreadSafeRefCount { //LOG_CLASS(Responder); public: @@ -126,13 +128,10 @@ public: return false; } - public: /* but not really -- don't touch this */ - U32 mReferenceCount; - private: std::string mURL; }; - typedef boost::intrusive_ptr<Responder> ResponderPtr; + typedef LLPointer<Responder> ResponderPtr; /** @@ -304,7 +303,7 @@ public: ePerformState getState() ; bool isCompleted() ; - bool isValid() {return mCurlMultiHandle != NULL ;} + bool isValid() {return mCurlMultiHandle != NULL && mValid;} bool isDead() {return mDead;} bool waitToComplete() ; @@ -318,7 +317,7 @@ public: private: void easyFree(LLCurl::Easy*); - void cleanup() ; + void cleanup(bool deleted = false) ; CURLM* mCurlMultiHandle; @@ -333,6 +332,7 @@ private: ePerformState mState; BOOL mDead ; + BOOL mValid ; LLMutex* mMutexp ; LLMutex* mDeletionMutexp ; LLMutex* mEasyMutexp ; @@ -377,12 +377,6 @@ private: void cleanupMulti(LLCurl::Multi* multi) ; } ; -namespace boost -{ - void intrusive_ptr_add_ref(LLCurl::Responder* p); - void intrusive_ptr_release(LLCurl::Responder* p); -}; - class LLCurlRequest { @@ -413,6 +407,71 @@ private: BOOL mProcessing; }; +//for texture fetch only +class LLCurlTextureRequest : public LLCurlRequest +{ +public: + LLCurlTextureRequest(S32 concurrency); + ~LLCurlTextureRequest(); + + U32 getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder, F32 delay_time = -1.f); + void nextRequests(); + void completeRequest(S32 received_bytes); + + void updatePriority(U32 handle, U32 pri); + void removeRequest(U32 handle); + + U32 getTotalReceivedBits(); + U32 getTotalIssuedRequests(); + S32 getNumRequests(); + bool isWaiting(U32 handle); + +private: + LLMutex mMutex; + S32 mConcurrency; + S32 mInQueue; //request currently in queue. + U32 mHandleCounter; + U32 mTotalIssuedRequests; + U32 mTotalReceivedBits; + + typedef struct _request_t + { + _request_t(U32 handle, const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder) : + mHandle(handle), mUrl(url), mHeaders(headers), mOffset(offset), mLength(length), mPriority(pri), mResponder(responder), mStartTime(0.f) + {} + + U32 mHandle; + std::string mUrl; + LLCurlRequest::headers_t mHeaders; + S32 mOffset; + S32 mLength; + LLCurl::ResponderPtr mResponder; + U32 mPriority; + F32 mStartTime; //start time to issue this request + } request_t; + + struct request_compare + { + bool operator()(const request_t* lhs, const request_t* rhs) const + { + if(lhs->mPriority != rhs->mPriority) + { + return lhs->mPriority > rhs->mPriority; // higher priority in front of queue (set) + } + else + { + return (U32)lhs < (U32)rhs; + } + } + }; + + typedef std::set<request_t*, request_compare> req_queue_t; + req_queue_t mCachedRequests; + std::map<S32, request_t*> mRequestMap; + + LLFrameTimer mGlobalTimer; +}; + class LLCurlEasyRequest { public: diff --git a/indra/llmessage/llhttpassetstorage.cpp b/indra/llmessage/llhttpassetstorage.cpp index 612d765969..d6ed08055e 100644 --- a/indra/llmessage/llhttpassetstorage.cpp +++ b/indra/llmessage/llhttpassetstorage.cpp @@ -749,7 +749,7 @@ LLAssetRequest* LLHTTPAssetStorage::findNextRequest(LLAssetStorage::request_list request_list_t::iterator pending_iter = pending.begin(); request_list_t::iterator pending_end = pending.end(); // Loop over all pending requests until we miss finding it in the running list. - for (; pending_iter != pending.end(); ++pending_iter) + for (; pending_iter != pending_end; ++pending_iter) { LLAssetRequest* req = *pending_iter; // Look for this pending request in the running list. diff --git a/indra/llmessage/llsdmessagereader.cpp b/indra/llmessage/llsdmessagereader.cpp index 3d8ca2ad9f..a6fccd2a56 100644 --- a/indra/llmessage/llsdmessagereader.cpp +++ b/indra/llmessage/llsdmessagereader.cpp @@ -276,7 +276,7 @@ S32 getElementSize(const LLSD& llsd) case LLSD::TypeReal: return sizeof(F64); case LLSD::TypeString: - return llsd.asString().size(); + return llsd.size(); case LLSD::TypeUUID: return sizeof(LLUUID); case LLSD::TypeDate: diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index a16f5c7bf0..f3f0007205 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -289,6 +289,8 @@ LLIOPipe::EStatus LLURLRequest::handleError( } static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST("URL Request"); +static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result"); +static LLFastTimer::DeclareTimer FTM_URL_PERFORM("Perform"); // virtual LLIOPipe::EStatus LLURLRequest::process_impl( @@ -358,7 +360,6 @@ LLIOPipe::EStatus LLURLRequest::process_impl( { PUMP_DEBUG; LLIOPipe::EStatus status = STATUS_BREAK; - static LLFastTimer::DeclareTimer FTM_URL_PERFORM("Perform"); { LLFastTimer t(FTM_URL_PERFORM); if(!mDetail->mCurlRequest->wait()) @@ -371,8 +372,6 @@ LLIOPipe::EStatus LLURLRequest::process_impl( { CURLcode result; - static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result"); - bool newmsg = false; { LLFastTimer t(FTM_PROCESS_URL_REQUEST_GET_RESULT); diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index e71fb96540..d7658862da 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -943,7 +943,6 @@ char const* const _PREHASH_SysGPU = LLMessageStringTable::getInstance()->getStri char const* const _PREHASH_AvatarInterestsReply = LLMessageStringTable::getInstance()->getString("AvatarInterestsReply"); char const* const _PREHASH_StartLure = LLMessageStringTable::getInstance()->getString("StartLure"); char const* const _PREHASH_SysRAM = LLMessageStringTable::getInstance()->getString("SysRAM"); -char const* const _PREHASH_ObjectPosition = LLMessageStringTable::getInstance()->getString("ObjectPosition"); char const* const _PREHASH_SitPosition = LLMessageStringTable::getInstance()->getString("SitPosition"); char const* const _PREHASH_StartTime = LLMessageStringTable::getInstance()->getString("StartTime"); char const* const _PREHASH_BornOn = LLMessageStringTable::getInstance()->getString("BornOn"); @@ -999,7 +998,6 @@ char const* const _PREHASH_SnapshotID = LLMessageStringTable::getInstance()->get char const* const _PREHASH_Aspect = LLMessageStringTable::getInstance()->getString("Aspect"); char const* const _PREHASH_ParamSize = LLMessageStringTable::getInstance()->getString("ParamSize"); char const* const _PREHASH_VoteCast = LLMessageStringTable::getInstance()->getString("VoteCast"); -char const* const _PREHASH_CastsShadows = LLMessageStringTable::getInstance()->getString("CastsShadows"); char const* const _PREHASH_EveryoneMask = LLMessageStringTable::getInstance()->getString("EveryoneMask"); char const* const _PREHASH_ObjectSpinUpdate = LLMessageStringTable::getInstance()->getString("ObjectSpinUpdate"); char const* const _PREHASH_MaturePublish = LLMessageStringTable::getInstance()->getString("MaturePublish"); @@ -1048,7 +1046,6 @@ char const* const _PREHASH_SimIP = LLMessageStringTable::getInstance()->getStrin char const* const _PREHASH_GodID = LLMessageStringTable::getInstance()->getString("GodID"); char const* const _PREHASH_TeleportMinPrice = LLMessageStringTable::getInstance()->getString("TeleportMinPrice"); char const* const _PREHASH_VoteItem = LLMessageStringTable::getInstance()->getString("VoteItem"); -char const* const _PREHASH_ObjectRotation = LLMessageStringTable::getInstance()->getString("ObjectRotation"); char const* const _PREHASH_SitRotation = LLMessageStringTable::getInstance()->getString("SitRotation"); char const* const _PREHASH_SnapSelection = LLMessageStringTable::getInstance()->getString("SnapSelection"); char const* const _PREHASH_SoundTrigger = LLMessageStringTable::getInstance()->getString("SoundTrigger"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index dd2c2dbd64..da2b613f53 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -943,7 +943,6 @@ extern char const* const _PREHASH_SysGPU; extern char const* const _PREHASH_AvatarInterestsReply; extern char const* const _PREHASH_StartLure; extern char const* const _PREHASH_SysRAM; -extern char const* const _PREHASH_ObjectPosition; extern char const* const _PREHASH_SitPosition; extern char const* const _PREHASH_StartTime; extern char const* const _PREHASH_BornOn; @@ -999,7 +998,6 @@ extern char const* const _PREHASH_SnapshotID; extern char const* const _PREHASH_Aspect; extern char const* const _PREHASH_ParamSize; extern char const* const _PREHASH_VoteCast; -extern char const* const _PREHASH_CastsShadows; extern char const* const _PREHASH_EveryoneMask; extern char const* const _PREHASH_ObjectSpinUpdate; extern char const* const _PREHASH_MaturePublish; @@ -1048,7 +1046,6 @@ extern char const* const _PREHASH_SimIP; extern char const* const _PREHASH_GodID; extern char const* const _PREHASH_TeleportMinPrice; extern char const* const _PREHASH_VoteItem; -extern char const* const _PREHASH_ObjectRotation; extern char const* const _PREHASH_SitRotation; extern char const* const _PREHASH_SnapSelection; extern char const* const _PREHASH_SoundTrigger; diff --git a/indra/llmessage/tests/llcurl_stub.cpp b/indra/llmessage/tests/llcurl_stub.cpp index d84fe0a49f..9b298d0c04 100644 --- a/indra/llmessage/tests/llcurl_stub.cpp +++ b/indra/llmessage/tests/llcurl_stub.cpp @@ -28,7 +28,6 @@ #include "llcurl.h" LLCurl::Responder::Responder() - : mReferenceCount(0) { } @@ -77,19 +76,3 @@ void LLCurl::Responder::result(LLSD const&) { } -namespace boost -{ - void intrusive_ptr_add_ref(LLCurl::Responder* p) - { - ++p->mReferenceCount; - } - - void intrusive_ptr_release(LLCurl::Responder* p) - { - if(p && 0 == --p->mReferenceCount) - { - delete p; - } - } -}; - diff --git a/indra/llmessage/tests/llhttpclient_test.cpp b/indra/llmessage/tests/llhttpclient_test.cpp new file mode 100644 index 0000000000..a2be307cc8 --- /dev/null +++ b/indra/llmessage/tests/llhttpclient_test.cpp @@ -0,0 +1,376 @@ +/** + * @file llhttpclient_test.cpp + * @brief Testing the HTTP client classes. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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$ + */ + +/** + * + * These classes test the HTTP client framework. + * + */ + +#include <tut/tut.hpp> +#include "linden_common.h" + +#include "lltut.h" +#include "llhttpclient.h" +#include "llformat.h" +#include "llpipeutil.h" +#include "llproxy.h" +#include "llpumpio.h" + +#include "llsdhttpserver.h" +#include "lliohttpserver.h" +#include "lliosocket.h" +#include "stringize.h" + +namespace tut +{ + LLSD storage; + + class LLSDStorageNode : public LLHTTPNode + { + public: + LLSD simpleGet() const { return storage; } + LLSD simplePut(const LLSD& value) const { storage = value; return LLSD(); } + }; + + class ErrorNode : public LLHTTPNode + { + public: + void get(ResponsePtr r, const LLSD& context) const + { r->status(599, "Intentional error"); } + void post(ResponsePtr r, const LLSD& context, const LLSD& input) const + { r->status(input["status"], input["reason"]); } + }; + + class TimeOutNode : public LLHTTPNode + { + public: + void get(ResponsePtr r, const LLSD& context) const + { + /* do nothing, the request will eventually time out */ + } + }; + + LLHTTPRegistration<LLSDStorageNode> gStorageNode("/test/storage"); + LLHTTPRegistration<ErrorNode> gErrorNode("/test/error"); + LLHTTPRegistration<TimeOutNode> gTimeOutNode("/test/timeout"); + + struct HTTPClientTestData + { + public: + HTTPClientTestData(): + local_server(STRINGIZE("http://127.0.0.1:" << getenv("PORT") << "/")) + { + apr_pool_create(&mPool, NULL); + LLCurl::initClass(false); + mServerPump = new LLPumpIO(mPool); + mClientPump = new LLPumpIO(mPool); + + LLHTTPClient::setPump(*mClientPump); + } + + ~HTTPClientTestData() + { + delete mServerPump; + delete mClientPump; + LLProxy::cleanupClass(); + apr_pool_destroy(mPool); + } + + void setupTheServer() + { + LLHTTPNode& root = LLIOHTTPServer::create(mPool, *mServerPump, 8888); + + LLHTTPStandardServices::useServices(); + LLHTTPRegistrar::buildAllServices(root); + } + + void runThePump(float timeout = 100.0f) + { + LLTimer timer; + timer.setTimerExpirySec(timeout); + + while(!mSawCompleted && !mSawCompletedHeader && !timer.hasExpired()) + { + if (mServerPump) + { + mServerPump->pump(); + mServerPump->callback(); + } + if (mClientPump) + { + mClientPump->pump(); + mClientPump->callback(); + } + } + } + + void killServer() + { + delete mServerPump; + mServerPump = NULL; + } + + const std::string local_server; + + private: + apr_pool_t* mPool; + LLPumpIO* mServerPump; + LLPumpIO* mClientPump; + + protected: + void ensureStatusOK() + { + if (mSawError) + { + std::string msg = + llformat("error() called when not expected, status %d", + mStatus); + fail(msg); + } + } + + void ensureStatusError() + { + if (!mSawError) + { + fail("error() wasn't called"); + } + } + + LLSD getResult() + { + return mResult; + } + LLSD getHeader() + { + return mHeader; + } + + protected: + bool mSawError; + U32 mStatus; + std::string mReason; + bool mSawCompleted; + bool mSawCompletedHeader; + LLSD mResult; + LLSD mHeader; + bool mResultDeleted; + + class Result : public LLHTTPClient::Responder + { + protected: + Result(HTTPClientTestData& client) + : mClient(client) + { + } + + public: + static Result* build(HTTPClientTestData& client) + { + return new Result(client); + } + + ~Result() + { + mClient.mResultDeleted = true; + } + + virtual void error(U32 status, const std::string& reason) + { + mClient.mSawError = true; + mClient.mStatus = status; + mClient.mReason = reason; + } + + virtual void result(const LLSD& content) + { + mClient.mResult = content; + } + + virtual void completed( + U32 status, const std::string& reason, + const LLSD& content) + { + LLHTTPClient::Responder::completed(status, reason, content); + + mClient.mSawCompleted = true; + } + + virtual void completedHeader( + U32 status, const std::string& reason, + const LLSD& content) + { + mClient.mHeader = content; + mClient.mSawCompletedHeader = true; + } + + private: + HTTPClientTestData& mClient; + }; + + friend class Result; + + protected: + LLHTTPClient::ResponderPtr newResult() + { + mSawError = false; + mStatus = 0; + mSawCompleted = false; + mSawCompletedHeader = false; + mResult.clear(); + mHeader.clear(); + mResultDeleted = false; + + return Result::build(*this); + } + }; + + + typedef test_group<HTTPClientTestData> HTTPClientTestGroup; + typedef HTTPClientTestGroup::object HTTPClientTestObject; + HTTPClientTestGroup httpClientTestGroup("http_client"); + + template<> template<> + void HTTPClientTestObject::test<1>() + { + LLHTTPClient::get(local_server, newResult()); + runThePump(); + ensureStatusOK(); + ensure("result object wasn't destroyed", mResultDeleted); + } + + template<> template<> + void HTTPClientTestObject::test<2>() + { + // Please nobody listen on this particular port... + LLHTTPClient::get("http://127.0.0.1:7950", newResult()); + runThePump(); + ensureStatusError(); + } + + template<> template<> + void HTTPClientTestObject::test<3>() + { + LLSD sd; + + sd["list"][0]["one"] = 1; + sd["list"][0]["two"] = 2; + sd["list"][1]["three"] = 3; + sd["list"][1]["four"] = 4; + + setupTheServer(); + + LLHTTPClient::post("http://localhost:8888/web/echo", sd, newResult()); + runThePump(); + ensureStatusOK(); + ensure_equals("echoed result matches", getResult(), sd); + } + + template<> template<> + void HTTPClientTestObject::test<4>() + { + LLSD sd; + + sd["message"] = "This is my test message."; + + setupTheServer(); + LLHTTPClient::put("http://localhost:8888/test/storage", sd, newResult()); + runThePump(); + ensureStatusOK(); + + LLHTTPClient::get("http://localhost:8888/test/storage", newResult()); + runThePump(); + ensureStatusOK(); + ensure_equals("echoed result matches", getResult(), sd); + + } + + template<> template<> + void HTTPClientTestObject::test<5>() + { + LLSD sd; + sd["status"] = 543; + sd["reason"] = "error for testing"; + + setupTheServer(); + + LLHTTPClient::post("http://localhost:8888/test/error", sd, newResult()); + runThePump(); + ensureStatusError(); + ensure_contains("reason", mReason, sd["reason"]); + } + + template<> template<> + void HTTPClientTestObject::test<6>() + { + setupTheServer(); + + LLHTTPClient::get("http://localhost:8888/test/timeout", newResult()); + runThePump(1.0f); + killServer(); + runThePump(); + ensureStatusError(); + ensure_equals("reason", mReason, "STATUS_ERROR"); + } + + template<> template<> + void HTTPClientTestObject::test<7>() + { + // Can not use the little mini server. The blocking request + // won't ever let it run. Instead get from a known LLSD + // source and compare results with the non-blocking get which + // is tested against the mini server earlier. + LLHTTPClient::get(local_server, newResult()); + runThePump(); + ensureStatusOK(); + LLSD expected = getResult(); + + LLSD result; + result = LLHTTPClient::blockingGet(local_server); + LLSD body = result["body"]; + ensure_equals("echoed result matches", body.size(), expected.size()); + } + template<> template<> + void HTTPClientTestObject::test<8>() + { + // This is testing for the presence of the Header in the returned results + // from an HTTP::get call. + LLHTTPClient::get(local_server, newResult()); + runThePump(); + ensureStatusOK(); + LLSD header = getHeader(); + ensure("got a header", ! header.emptyMap().asBoolean()); + } + template<> template<> + void HTTPClientTestObject::test<9>() + { + LLHTTPClient::head(local_server, newResult()); + runThePump(); + ensureStatusOK(); + ensure("result object wasn't destroyed", mResultDeleted); + } +} diff --git a/indra/llmessage/tests/llsdmessage_test.cpp b/indra/llmessage/tests/llsdmessage_test.cpp index 0f2c069303..44b024a83f 100644 --- a/indra/llmessage/tests/llsdmessage_test.cpp +++ b/indra/llmessage/tests/llsdmessage_test.cpp @@ -42,6 +42,7 @@ // external library headers // other Linden headers #include "../test/lltut.h" +#include "../test/catch_and_store_what_in.h" #include "llsdserialize.h" #include "llevents.h" #include "stringize.h" @@ -72,43 +73,14 @@ namespace tut template<> template<> void llsdmessage_object::test<1>() { - bool threw = false; + std::string threw; // This should fail... try { LLSDMessage localListener; } - catch (const LLEventPump::DupPumpName&) - { - threw = true; - } - catch (const std::runtime_error& ex) - { - // This clause is because on Linux, on the viewer side, for this - // one test program (though not others!), the - // LLEventPump::DupPumpName exception isn't caught by the clause - // above. Warn the user... - std::cerr << "Failed to catch " << typeid(ex).name() << std::endl; - // But if the expected exception was thrown, allow the test to - // succeed anyway. Not sure how else to handle this odd case. - if (std::string(typeid(ex).name()) == typeid(LLEventPump::DupPumpName).name()) - { - threw = true; - } - else - { - // We don't even recognize this exception. Let it propagate - // out to TUT to fail the test. - throw; - } - } - catch (...) - { - std::cerr << "Utterly failed to catch expected exception!" << std::endl; - // This case is full of fail. We HAVE to address it. - throw; - } - ensure("second LLSDMessage should throw", threw); + CATCH_AND_STORE_WHAT_IN(threw, LLEventPump::DupPumpName) + ensure("second LLSDMessage should throw", ! threw.empty()); } template<> template<> @@ -143,7 +115,7 @@ namespace tut httpPump.post(request); ensure("got response", netio.pump()); ensure("success response", success); - ensure_equals(result.asString(), "success"); + ensure_equals(result["reply"].asString(), "success"); body["status"] = 499; body["reason"] = "custom error message"; diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index 22edd9dad8..fe4f3a8c01 100644 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -78,25 +78,32 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): ## debug("root node tag %s", tree.getroot().tag) ## return llsd.to_python(tree.getroot()) - def do_GET(self): + def do_HEAD(self): + self.do_GET(withdata=False) + + def do_GET(self, withdata=True): # Of course, don't attempt to read data. - self.answer(dict(reply="success", status=500, - reason="Your GET operation requested failure")) + data = dict(reply="success", body="avatar", random=17) + self.answer(data, withdata=withdata) def do_POST(self): # Read the provided POST data. self.answer(self.read_xml()) - def answer(self, data): + def answer(self, data, withdata=True): debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) if "fail" not in self.path: - response = llsd.format_xml(data.get("reply", llsd.LLSD("success"))) + data = data.copy() # we're going to modify + # Ensure there's a "reply" key in data, even if there wasn't before + data["reply"] = data.get("reply", llsd.LLSD("success")) + response = llsd.format_xml(data) debug("success: %s", response) self.send_response(200) self.send_header("Content-type", "application/llsd+xml") self.send_header("Content-Length", str(len(response))) self.end_headers() - self.wfile.write(response) + if withdata: + self.wfile.write(response) else: # fail requested status = data.get("status", 500) # self.responses maps an int status to a (short, long) pair of |