diff options
-rwxr-xr-x | indra/llmessage/llcurl.cpp | 1707 | ||||
-rwxr-xr-x | indra/llmessage/llcurl.h | 341 | ||||
-rwxr-xr-x | indra/llmessage/llproxy.cpp | 10 | ||||
-rwxr-xr-x | indra/llmessage/llproxy.h | 14 | ||||
-rwxr-xr-x | indra/llmessage/llsdrpcclient.cpp | 249 | ||||
-rwxr-xr-x | indra/llmessage/llsdrpcclient.h | 226 | ||||
-rwxr-xr-x | indra/llmessage/llsdrpcserver.cpp | 341 | ||||
-rwxr-xr-x | indra/llmessage/llsdrpcserver.h | 361 | ||||
-rwxr-xr-x | indra/llmessage/llurlrequest.cpp | 785 | ||||
-rwxr-xr-x | indra/llmessage/llurlrequest.h | 357 | ||||
-rwxr-xr-x | indra/newview/llappviewer.cpp | 12 | ||||
-rwxr-xr-x | indra/test/io.cpp | 422 |
12 files changed, 22 insertions, 4803 deletions
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 630e5ebe71..61b7a52c76 100755 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -89,7 +89,7 @@ S32 gCurlMultiCount = 0; std::vector<LLMutex*> LLCurl::sSSLMutex; std::string LLCurl::sCAPath; std::string LLCurl::sCAFile; -LLCurlThread* LLCurl::sCurlThread = NULL ; +//LLCurlThread* LLCurl::sCurlThread = NULL ; LLMutex* LLCurl::sHandleMutexp = NULL ; S32 LLCurl::sTotalHandles = 0 ; bool LLCurl::sNotQuitting = true; @@ -135,1042 +135,6 @@ std::string LLCurl::getVersionString() return std::string(curl_version()); } -////////////////////////////////////////////////////////////////////////////// - -LLCurl::Responder::Responder() - : mHTTPMethod(HTTP_INVALID), mStatus(HTTP_INTERNAL_ERROR) -{ -} - -LLCurl::Responder::~Responder() -{ - LL_CHECK_MEMORY -} - -// virtual -void LLCurl::Responder::httpFailure() -{ - LL_WARNS("curl") << dumpResponse() << LL_ENDL; -} - -std::string LLCurl::Responder::dumpResponse() const -{ - std::ostringstream s; - s << "[" << httpMethodAsVerb(mHTTPMethod) << ":" << mURL << "] " - << "[status:" << mStatus << "] " - << "[reason:" << mReason << "] "; - - if (mResponseHeaders.has(HTTP_IN_HEADER_CONTENT_TYPE)) - { - s << "[content-type:" << mResponseHeaders[HTTP_IN_HEADER_CONTENT_TYPE] << "] "; - } - - s << "[content:" << mContent << "]"; - - return s.str(); -} - -// virtual -void LLCurl::Responder::httpSuccess() -{ -} - -void LLCurl::Responder::setURL(const std::string& url) -{ - mURL = url; -} - -void LLCurl::Responder::successResult(const LLSD& content) -{ - setResult(HTTP_OK, "", content); - httpSuccess(); -} - -void LLCurl::Responder::failureResult(S32 status, const std::string& reason, const LLSD& content /* = LLSD() */) -{ - setResult(status, reason, content); - httpFailure(); -} - -void LLCurl::Responder::completeResult(S32 status, const std::string& reason, const LLSD& content /* = LLSD() */) -{ - setResult(status, reason, content); - httpCompleted(); -} - -void LLCurl::Responder::setResult(S32 status, const std::string& reason, const LLSD& content /* = LLSD() */) -{ - mStatus = status; - mReason = reason; - mContent = content; -} - -void LLCurl::Responder::setHTTPMethod(EHTTPMethod method) -{ - mHTTPMethod = method; -} - -void LLCurl::Responder::setResponseHeader(const std::string& header, const std::string& value) -{ - mResponseHeaders[header] = value; -} - -const std::string& LLCurl::Responder::getResponseHeader(const std::string& header) const -{ - if (mResponseHeaders.has(header)) - { - return mResponseHeaders[header].asStringRef(); - } - static const std::string empty; - return empty; -} - -bool LLCurl::Responder::hasResponseHeader(const std::string& header) const -{ - if (mResponseHeaders.has(header)) return true; - return false; -} - -// virtual -void LLCurl::Responder::completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - LLBufferStream istr(channels, buffer.get()); - const bool emit_parse_errors = false; - - std::string debug_body("(empty)"); - bool parsed=true; - if (EOF == istr.peek()) - { - parsed=false; - } - // Try to parse body as llsd, no matter what 'content-type' says. - else if (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXML(mContent, istr, emit_parse_errors)) - { - parsed=false; - char body[1025]; - body[1024] = '\0'; - istr.seekg(0, std::ios::beg); - istr.get(body,1024); - if (strlen(body) > 0) - { - mContent = body; - debug_body = body; - } - } - - // Only emit a warning if we failed to parse when 'content-type' == 'application/llsd+xml' - if (!parsed && (HTTP_CONTENT_LLSD_XML == getResponseHeader(HTTP_IN_HEADER_CONTENT_TYPE))) - { - LL_WARNS() << "Failed to deserialize . " << mURL << " [status:" << mStatus << "] " - << "(" << mReason << ") body: " << debug_body << LL_ENDL; - } - - httpCompleted(); -} - -// virtual -void LLCurl::Responder::httpCompleted() -{ - if (isGoodStatus()) - { - httpSuccess(); - } - else - { - httpFailure(); - } -} - -////////////////////////////////////////////////////////////////////////////// - -std::set<CURL*> LLCurl::Easy::sFreeHandles; -std::set<CURL*> LLCurl::Easy::sActiveHandles; -LLMutex* LLCurl::Easy::sHandleMutexp = NULL ; - -//static -CURL* LLCurl::Easy::allocEasyHandle() -{ - llassert(LLCurl::getCurlThread()) ; - - CURL* ret = NULL; - - LLMutexLock lock(sHandleMutexp) ; - - if (sFreeHandles.empty()) - { - ret = LLCurl::newEasyHandle(); - } - else - { - ret = *(sFreeHandles.begin()); - sFreeHandles.erase(ret); - curl_easy_reset(ret); - } - - if (ret) - { - sActiveHandles.insert(ret); - } - - return ret; -} - -//static -void LLCurl::Easy::releaseEasyHandle(CURL* handle) -{ - static const S32 MAX_NUM_FREE_HANDLES = 32 ; - - if (!handle) - { - return ; //handle allocation failed. - //LL_ERRS() << "handle cannot be NULL!" << LL_ENDL; - } - - 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); - LL_CHECK_MEMORY - } - else - { - LLCurl::deleteEasyHandle(handle) ; - LL_CHECK_MEMORY - } - } - else - { - LL_ERRS() << "Invalid handle." << LL_ENDL; - } -} - -//static -void LLCurl::Easy::deleteAllActiveHandles() -{ - LLMutexLock lock(sHandleMutexp) ; - LL_CHECK_MEMORY - for (std::set<CURL*>::iterator activeHandle = sActiveHandles.begin(); activeHandle != sActiveHandles.end(); ++activeHandle) - { - CURL* curlHandle = *activeHandle; - LLCurl::deleteEasyHandle(curlHandle); - LL_CHECK_MEMORY - } - - sFreeHandles.clear(); -} - -//static -void LLCurl::Easy::deleteAllFreeHandles() -{ - LLMutexLock lock(sHandleMutexp) ; - LL_CHECK_MEMORY - for (std::set<CURL*>::iterator freeHandle = sFreeHandles.begin(); freeHandle != sFreeHandles.end(); ++freeHandle) - { - CURL* curlHandle = *freeHandle; - LLCurl::deleteEasyHandle(curlHandle); - LL_CHECK_MEMORY - } - - sFreeHandles.clear(); -} - -LLCurl::Easy::Easy() - : mHeaders(NULL), - mCurlEasyHandle(NULL) -{ - mErrorBuffer[0] = 0; -} - -LLCurl::Easy* LLCurl::Easy::getEasy() -{ - Easy* easy = new Easy(); - easy->mCurlEasyHandle = allocEasyHandle(); - - if (!easy->mCurlEasyHandle) - { - // this can happen if we have too many open files (fails in c-ares/ares_init.c) - LL_WARNS("curl") << "allocEasyHandle() returned NULL! Easy handles: " - << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << LL_ENDL; - delete easy; - return NULL; - } - - // Enable a brief cache period for now. This was zero for the longest time - // which caused some routers grief and generated unneeded traffic. For the - // threaded resolver, we're using system resolution libraries and non-zero values - // are preferred. The c-ares resolver is another matter and it might not - // track server changes as well. - CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); - check_curl_code(result); - result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - check_curl_code(result); - - ++gCurlEasyCount; - return easy; -} - -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 - { - // HTTP_REQUEST_TIME_OUT, timeout, abort - // *TODO: This looks like improper use of the 408 status code. - // See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.9 - // This status code should be returned by the *server* when: - // "The client did not produce a request within the time that the server was prepared to wait." - mResponder->setResult(HTTP_REQUEST_TIME_OUT, "Request timeout, aborted."); - mResponder->completedRaw(mChannels, mOutput); - LL_CHECK_MEMORY - } - mResponder = NULL; -} - -void LLCurl::Easy::resetState() -{ - curl_easy_reset(mCurlEasyHandle); - - if (mHeaders) - { - curl_slist_free_all(mHeaders); - mHeaders = NULL; - } - - mRequest.str(""); - mRequest.clear(); - - mOutput.reset(); - - mInput.str(""); - mInput.clear(); - - mErrorBuffer[0] = 0; - - mHeaderOutput.str(""); - mHeaderOutput.clear(); -} - -void LLCurl::Easy::setErrorBuffer() -{ - setopt(CURLOPT_ERRORBUFFER, &mErrorBuffer); -} - -const char* LLCurl::Easy::getErrorBuffer() -{ - return mErrorBuffer; -} - -void LLCurl::Easy::setCA() -{ - if (!sCAPath.empty()) - { - setoptString(CURLOPT_CAPATH, sCAPath); - } - if (!sCAFile.empty()) - { - setoptString(CURLOPT_CAINFO, sCAFile); - } -} - -void LLCurl::Easy::setHeaders() -{ - setopt(CURLOPT_HTTPHEADER, mHeaders); -} - -void LLCurl::Easy::getTransferInfo(LLCurl::TransferInfo* info) -{ - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SIZE_DOWNLOAD, &info->mSizeDownload)); - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_TOTAL_TIME, &info->mTotalTime)); - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SPEED_DOWNLOAD, &info->mSpeedDownload)); -} - -S32 LLCurl::Easy::report(CURLcode code) -{ - S32 responseCode = 0; - std::string responseReason; - - if (code == CURLE_OK) - { - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_RESPONSE_CODE, &responseCode)); - //*TODO: get reason from first line of mHeaderOutput - } - else - { - responseCode = HTTP_INTERNAL_ERROR; - responseReason = strerror(code) + " : " + mErrorBuffer; - setopt(CURLOPT_FRESH_CONNECT, TRUE); - } - - if (mResponder) - { - mResponder->setResult(responseCode, responseReason); - mResponder->completedRaw(mChannels, mOutput); - mResponder = NULL; - } - - resetState(); - return responseCode; -} - -// Note: these all assume the caller tracks the value (i.e. keeps it persistant) -void LLCurl::Easy::setopt(CURLoption option, S32 value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -void LLCurl::Easy::setopt(CURLoption option, void* value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -void LLCurl::Easy::setopt(CURLoption option, char* value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -// Note: this copies the string so that the caller does not have to keep it around -void LLCurl::Easy::setoptString(CURLoption option, const std::string& value) -{ - char* tstring = new char[value.length()+1]; - strcpy(tstring, value.c_str()); - mStrings.push_back(tstring); - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, tstring); - check_curl_code(result); -} - -void LLCurl::Easy::slist_append(const std::string& header, const std::string& value) -{ - std::string pair(header); - if (value.empty()) - { - pair += ":"; - } - else - { - pair += ": "; - pair += value; - } - slist_append(pair.c_str()); -} - -void LLCurl::Easy::slist_append(const char* str) -{ - if (str) - { - mHeaders = curl_slist_append(mHeaders, str); - if (!mHeaders) - { - LL_WARNS() << "curl_slist_append() call returned NULL appending " << str << LL_ENDL; - } - } -} - -size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - S32 n = size * nmemb; - S32 startpos = (S32)easy->getInput().tellg(); - easy->getInput().seekg(0, std::ios::end); - S32 endpos = (S32)easy->getInput().tellg(); - easy->getInput().seekg(startpos, std::ios::beg); - S32 maxn = endpos - startpos; - n = llmin(n, maxn); - easy->getInput().read((char*)data, n); - - return n; -} - -size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - S32 n = size * nmemb; - easy->getOutput()->append(easy->getChannels().in(), (const U8*)data, n); - - return n; -} - -size_t curlHeaderCallback(void* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - size_t n = size * nmemb; - easy->getHeaderOutput().write((const char*)data, n); - - return n; -} - -void LLCurl::Easy::prepRequest(const std::string& url, - const std::vector<std::string>& headers, - ResponderPtr responder, S32 time_out, bool post) -{ - resetState(); - - if (post) setoptString(CURLOPT_ENCODING, ""); - - //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); - - mOutput.reset(new LLBufferArray); - mOutput->setThreaded(true); - setopt(CURLOPT_WRITEFUNCTION, (void*)&curlWriteCallback); - setopt(CURLOPT_WRITEDATA, (void*)this); - - setopt(CURLOPT_READFUNCTION, (void*)&curlReadCallback); - setopt(CURLOPT_READDATA, (void*)this); - - setopt(CURLOPT_HEADERFUNCTION, (void*)&curlHeaderCallback); - setopt(CURLOPT_HEADERDATA, (void*)this); - - // Allow up to five redirects - if (responder && responder->followRedir()) - { - setopt(CURLOPT_FOLLOWLOCATION, 1); - setopt(CURLOPT_MAXREDIRS, MAX_REDIRECTS); - } - - setErrorBuffer(); - setCA(); - - setopt(CURLOPT_SSL_VERIFYPEER, true); - - //don't verify host name so urls with scrubbed host names will work (improves DNS performance) - setopt(CURLOPT_SSL_VERIFYHOST, 0); - setopt(CURLOPT_TIMEOUT, llmax(time_out, CURL_REQUEST_TIMEOUT)); - setopt(CURLOPT_CONNECTTIMEOUT, CURL_CONNECT_TIMEOUT); - - setoptString(CURLOPT_URL, url); - - mResponder = responder; - - if (!post) - { - // *TODO: Should this be set to 'Keep-Alive' ? - slist_append(HTTP_OUT_HEADER_CONNECTION, "keep-alive"); - slist_append(HTTP_OUT_HEADER_KEEP_ALIVE, "300"); - // Accept and other headers - for (std::vector<std::string>::const_iterator iter = headers.begin(); - iter != headers.end(); ++iter) - { - slist_append((*iter).c_str()); - } - } -} - -//////////////////////////////////////////////////////////////////////////// -#if 1 -LLCurl::Multi::Multi(F32 idle_time_out) - : mQueued(0), - mErrorCount(0), - mState(STATE_READY), - mDead(FALSE), - mValid(TRUE), - mMutexp(NULL), - mDeletionMutexp(NULL), - mEasyMutexp(NULL) -{ - mCurlMultiHandle = LLCurl::newMultiHandle(); - if (!mCurlMultiHandle) - { - LL_WARNS() << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << LL_ENDL; - mCurlMultiHandle = LLCurl::newMultiHandle(); - } - - //llassert_always(mCurlMultiHandle); - - if(mCurlMultiHandle) - { - if(LLCurl::getCurlThread()->getThreaded()) - { - mMutexp = new LLMutex(NULL) ; - mDeletionMutexp = new LLMutex(NULL) ; - mEasyMutexp = new LLMutex(NULL) ; - } - LLCurl::getCurlThread()->addMulti(this) ; - - mIdleTimeOut = idle_time_out ; - if(mIdleTimeOut < LLCurl::sCurlRequestTimeOut) - { - mIdleTimeOut = LLCurl::sCurlRequestTimeOut ; - } - - ++gCurlMultiCount; -} -} - -LLCurl::Multi::~Multi() -{ - cleanup(true) ; - - delete mDeletionMutexp ; - mDeletionMutexp = NULL ; -} - -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(); - - 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 ; - - LL_CHECK_MEMORY - - delete mEasyMutexp ; - mEasyMutexp = NULL ; - - LL_CHECK_MEMORY - - mQueued = 0 ; - mState = STATE_COMPLETED; - - --gCurlMultiCount; - - return ; -} - -void LLCurl::Multi::lock() -{ - if(mMutexp) - { - mMutexp->lock() ; - } -} - -void LLCurl::Multi::unlock() -{ - if(mMutexp) - { - mMutexp->unlock() ; - } -} - -void LLCurl::Multi::markDead() -{ - { - LLMutexLock lock(mDeletionMutexp) ; - - 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) -{ - lock() ; - mState = state ; - unlock() ; - - if(mState == STATE_READY) - { - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_NORMAL) ; - } -} - -LLCurl::Multi::ePerformState LLCurl::Multi::getState() -{ - return mState; -} - -bool LLCurl::Multi::isCompleted() -{ - return STATE_COMPLETED == getState() ; -} - -bool LLCurl::Multi::waitToComplete() -{ - if(!isValid()) - { - return true ; - } - - if(!mMutexp) //not threaded - { - doPerform() ; - return true ; - } - - bool completed = (STATE_COMPLETED == mState) ; - if(!completed) - { - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_HIGH) ; - } - - return completed; -} - -CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue) -{ - LLMutexLock lock(mMutexp) ; - - CURLMsg* curlmsg = curl_multi_info_read(mCurlMultiHandle, msgs_in_queue); - return curlmsg; -} - -//return true if dead -bool LLCurl::Multi::doPerform() -{ - LLMutexLock lock(mDeletionMutexp) ; - - bool dead = mDead ; - - if(mDead) - { - setState(STATE_COMPLETED); - mQueued = 0 ; - } - else if(getState() != STATE_COMPLETED) - { - setState(STATE_PERFORMING); - - S32 q = 0; - for (S32 call_count = 0; - call_count < MULTI_PERFORM_CALL_REPEAT; - call_count++) - { - LLMutexLock lock(mMutexp) ; - - //WARNING: curl_multi_perform will block for many hundreds of milliseconds - // NEVER call this from the main thread, and NEVER allow the main thread to - // wait on a mutex held by this thread while curl_multi_perform is executing - CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); - if (CURLM_CALL_MULTI_PERFORM != code || q == 0) - { - check_curl_multi_code(code); - - break; - } - } - - mQueued = q; - setState(STATE_COMPLETED) ; - mIdleTimer.reset() ; - } - 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 ; -} - -S32 LLCurl::Multi::process() -{ - if(!isValid()) - { - return 0 ; - } - - waitToComplete() ; - - if (getState() != STATE_COMPLETED) - { - return 0; - } - - CURLMsg* msg; - int msgs_in_queue; - - S32 processed = 0; - while ((msg = info_read(&msgs_in_queue))) - { - ++processed; - if (msg->msg == CURLMSG_DONE) - { - S32 response = 0; - Easy* easy = NULL ; - - { - LLMutexLock lock(mEasyMutexp) ; - easy_active_map_t::iterator iter = mEasyActiveMap.find(msg->easy_handle); - if (iter != mEasyActiveMap.end()) - { - easy = iter->second; - } - } - - if(easy) - { - response = easy->report(msg->data.result); - removeEasy(easy); - } - else - { - response = HTTP_INTERNAL_ERROR; - //*TODO: change to LL_WARNS() - LL_ERRS() << "cleaned up curl request completed!" << LL_ENDL; - } - if (response >= 400) - { - // failure of some sort, inc mErrorCount for debugging and flagging multi for destruction - ++mErrorCount; - } - } - } - - setState(STATE_READY); - - return processed; -} - -LLCurl::Easy* LLCurl::Multi::allocEasy() -{ - Easy* easy = 0; - - if (mEasyFreeList.empty()) - { - easy = Easy::getEasy(); - } - else - { - LLMutexLock lock(mEasyMutexp) ; - easy = *(mEasyFreeList.begin()); - mEasyFreeList.erase(easy); - } - if (easy) - { - LLMutexLock lock(mEasyMutexp) ; - mEasyActiveList.insert(easy); - mEasyActiveMap[easy->getCurlHandle()] = easy; - } - return easy; -} - -bool LLCurl::Multi::addEasy(Easy* easy) -{ - LLMutexLock lock(mMutexp) ; - CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle()); - check_curl_multi_code(mcode); - //if (mcode != CURLM_OK) - //{ - // LL_WARNS() << "Curl Error: " << curl_multi_strerror(mcode) << LL_ENDL; - // return false; - //} - return true; -} - -void LLCurl::Multi::easyFree(Easy* easy) -{ - if(mEasyMutexp) - { - mEasyMutexp->lock() ; - } - - mEasyActiveList.erase(easy); - mEasyActiveMap.erase(easy->getCurlHandle()); - - if (mEasyFreeList.size() < EASY_HANDLE_POOL_SIZE) - { - mEasyFreeList.insert(easy); - - if(mEasyMutexp) - { - mEasyMutexp->unlock() ; - } - - easy->resetState(); - } - else - { - if(mEasyMutexp) - { - mEasyMutexp->unlock() ; - } - delete easy; - } -} - -void LLCurl::Multi::removeEasy(Easy* easy) -{ - { - LLMutexLock lock(mMutexp) ; - check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); - } - easyFree(easy); -} - -#endif - -#if 1 -//------------------------------------------------------------ -//LLCurlThread -LLCurlThread::CurlRequest::CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread) : - LLQueuedThread::QueuedRequest(handle, LLQueuedThread::PRIORITY_NORMAL, FLAG_AUTO_COMPLETE), - mMulti(multi), - mCurlThread(curl_thread) -{ -} - -LLCurlThread::CurlRequest::~CurlRequest() -{ - if(mMulti) - { - mCurlThread->deleteMulti(mMulti) ; - mMulti = NULL ; - } -} - -bool LLCurlThread::CurlRequest::processRequest() -{ - bool completed = true ; - if(mMulti) - { - completed = mCurlThread->doMultiPerform(mMulti) ; - - if(!completed) - { - setPriority(LLQueuedThread::PRIORITY_LOW) ; - } - } - - return completed ; -} - -void LLCurlThread::CurlRequest::finishRequest(bool completed) -{ - if(mMulti->isDead()) - { - mCurlThread->deleteMulti(mMulti) ; - } - else - { - mCurlThread->cleanupMulti(mMulti) ; //being idle too long, remove the request. - } - - mMulti = NULL ; -} - -LLCurlThread::LLCurlThread(bool threaded) : - LLQueuedThread("curlthread", threaded) -{ -} - -//virtual -LLCurlThread::~LLCurlThread() -{ -} - -S32 LLCurlThread::update(F32 max_time_ms) -{ - return LLQueuedThread::update(max_time_ms); -} - -void LLCurlThread::addMulti(LLCurl::Multi* multi) -{ - multi->mHandle = generateHandle() ; - - CurlRequest* req = new CurlRequest(multi->mHandle, multi, this) ; - - if (!addRequest(req)) - { - LL_WARNS() << "curl request added when the thread is quitted" << LL_ENDL; - } -} - -void LLCurlThread::killMulti(LLCurl::Multi* multi) -{ - if(!multi) - { - return ; - } - - - multi->markDead() ; -} - -//private -bool LLCurlThread::doMultiPerform(LLCurl::Multi* multi) -{ - return multi->doPerform() ; -} - -//private -void LLCurlThread::deleteMulti(LLCurl::Multi* multi) -{ - delete multi ; -} - -//private -void LLCurlThread::cleanupMulti(LLCurl::Multi* multi) -{ - multi->cleanup() ; - if(multi->isDead()) //check if marked dead during cleaning up. - { - deleteMulti(multi) ; - } -} -#endif -//------------------------------------------------------------ //static std::string LLCurl::strerror(CURLcode errorcode) @@ -1178,641 +142,6 @@ std::string LLCurl::strerror(CURLcode errorcode) return std::string(curl_easy_strerror(errorcode)); } -//////////////////////////////////////////////////////////////////////////// -// For generating a simple request for data -// using one multi and one easy per request - -// LLCurlRequest::LLCurlRequest() : -// mActiveMulti(NULL), -// mActiveRequestCount(0) -// { -// mProcessing = FALSE; -// } -// -// LLCurlRequest::~LLCurlRequest() -// { -// //stop all Multi handle background threads -// for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) -// { -// LLCurl::getCurlThread()->killMulti(*iter) ; -// } -// mMultiSet.clear() ; -// } -// -// void LLCurlRequest::addMulti() -// { -// LLCurl::Multi* multi = new LLCurl::Multi(); -// if(!multi->isValid()) -// { -// LLCurl::getCurlThread()->killMulti(multi) ; -// mActiveMulti = NULL ; -// mActiveRequestCount = 0 ; -// return; -// } -// -// mMultiSet.insert(multi); -// mActiveMulti = multi; -// mActiveRequestCount = 0; -// } -// -// LLCurl::Easy* LLCurlRequest::allocEasy() -// { -// if (!mActiveMulti || -// mActiveRequestCount >= MAX_ACTIVE_REQUEST_COUNT || -// mActiveMulti->mErrorCount > 0) -// { -// addMulti(); -// } -// if(!mActiveMulti) -// { -// return NULL ; -// } -// -// //llassert_always(mActiveMulti); -// ++mActiveRequestCount; -// LLCurl::Easy* easy = mActiveMulti->allocEasy(); -// return easy; -// } -// -// bool LLCurlRequest::addEasy(LLCurl::Easy* easy) -// { -// llassert_always(mActiveMulti); -// -// if (mProcessing) -// { -// LL_ERRS() << "Posting to a LLCurlRequest instance from within a responder is not allowed (causes DNS timeouts)." << LL_ENDL; -// } -// bool res = mActiveMulti->addEasy(easy); -// return res; -// } -// -// void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) -// { -// getByteRange(url, headers_t(), 0, -1, responder); -// } -// -// // Note: (length==0) is interpreted as "the rest of the file", i.e. the whole file if (offset==0) or -// // the remainder of the file if not. -// bool LLCurlRequest::getByteRange(const std::string& url, -// const headers_t& headers, -// S32 offset, S32 length, -// LLCurl::ResponderPtr responder) -// { -// llassert(LLCurl::sNotQuitting); -// LLCurl::Easy* easy = allocEasy(); -// if (!easy) -// { -// return false; -// } -// easy->prepRequest(url, headers, responder); -// easy->setopt(CURLOPT_HTTPGET, 1); -// if (length > 0) -// { -// std::string range = llformat("bytes=%d-%d", offset,offset+length-1); -// easy->slist_append(HTTP_OUT_HEADER_RANGE, range); -// } -// else if (offset > 0) -// { -// std::string range = llformat("bytes=%d-", offset); -// easy->slist_append(HTTP_OUT_HEADER_RANGE, range); -// } -// easy->setHeaders(); -// bool res = addEasy(easy); -// return res; -// } -// -// bool LLCurlRequest::post(const std::string& url, -// const headers_t& headers, -// const LLSD& data, -// LLCurl::ResponderPtr responder, S32 time_out) -// { -// llassert(LLCurl::sNotQuitting); -// LLCurl::Easy* easy = allocEasy(); -// if (!easy) -// { -// return false; -// } -// easy->prepRequest(url, headers, responder, time_out); -// -// LLSDSerialize::toXML(data, easy->getInput()); -// S32 bytes = easy->getInput().str().length(); -// -// easy->setopt(CURLOPT_POST, 1); -// easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); -// easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); -// -// easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); -// easy->setHeaders(); -// -// LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; -// bool res = addEasy(easy); -// return res; -// } -// -// bool LLCurlRequest::post(const std::string& url, -// const headers_t& headers, -// const std::string& data, -// LLCurl::ResponderPtr responder, S32 time_out) -// { -// llassert(LLCurl::sNotQuitting); -// LLCurl::Easy* easy = allocEasy(); -// if (!easy) -// { -// return false; -// } -// easy->prepRequest(url, headers, responder, time_out); -// -// easy->getInput().write(data.data(), data.size()); -// S32 bytes = easy->getInput().str().length(); -// -// easy->setopt(CURLOPT_POST, 1); -// easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); -// easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); -// -// easy->slist_append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_OCTET_STREAM); -// easy->setHeaders(); -// -// LL_DEBUGS() << "POSTING: " << bytes << " bytes." << LL_ENDL; -// bool res = addEasy(easy); -// return res; -// } -// -// // Note: call once per frame -// S32 LLCurlRequest::process() -// { -// S32 res = 0; -// -// mProcessing = TRUE; -// for (curlmulti_set_t::iterator iter = mMultiSet.begin(); -// iter != mMultiSet.end(); ) -// { -// curlmulti_set_t::iterator curiter = iter++; -// LLCurl::Multi* multi = *curiter; -// -// if(!multi->isValid()) -// { -// if(multi == mActiveMulti) -// { -// mActiveMulti = NULL ; -// mActiveRequestCount = 0 ; -// } -// mMultiSet.erase(curiter) ; -// LLCurl::getCurlThread()->killMulti(multi) ; -// continue ; -// } -// -// S32 tres = multi->process(); -// res += tres; -// if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) -// { -// mMultiSet.erase(curiter); -// LLCurl::getCurlThread()->killMulti(multi); -// } -// } -// mProcessing = FALSE; -// return res; -// } -// -// S32 LLCurlRequest::getQueued() -// { -// S32 queued = 0; -// for (curlmulti_set_t::iterator iter = mMultiSet.begin(); -// iter != mMultiSet.end(); ) -// { -// curlmulti_set_t::iterator curiter = iter++; -// LLCurl::Multi* multi = *curiter; -// -// if(!multi->isValid()) -// { -// if(multi == mActiveMulti) -// { -// mActiveMulti = NULL ; -// mActiveRequestCount = 0 ; -// } -// LLCurl::getCurlThread()->killMulti(multi); -// mMultiSet.erase(curiter) ; -// continue ; -// } -// -// queued += multi->mQueued; -// if (multi->getState() != LLCurl::Multi::STATE_READY) -// { -// ++queued; -// } -// } -// return queued; -// } - -// LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : -// LLCurlRequest(), -// mConcurrency(concurrency), -// mInQueue(0), -// mMutex(NULL), -// mHandleCounter(1), -// mTotalIssuedRequests(0), -// mTotalReceivedBits(0) -// { -// mGlobalTimer.reset(); -// } -// -// LLCurlTextureRequest::~LLCurlTextureRequest() -// { -// mRequestMap.clear(); -// -// for(req_queue_t::iterator iter = mCachedRequests.begin(); iter != mCachedRequests.end(); ++iter) -// { -// delete *iter; -// } -// mCachedRequests.clear(); -// } -// -// //return 0: success -// // > 0: cached handle -// U32 LLCurlTextureRequest::getByteRange(const std::string& url, -// const headers_t& headers, -// S32 offset, S32 length, U32 pri, -// LLCurl::ResponderPtr responder, F32 delay_time) -// { -// U32 ret_val = 0; -// bool success = false; -// -// if(mInQueue < mConcurrency && delay_time < 0.f) -// { -// success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); -// } -// -// LLMutexLock lock(&mMutex); -// -// if(success) -// { -// mInQueue++; -// mTotalIssuedRequests++; -// } -// else -// { -// request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); -// if(delay_time > 0.f) -// { -// request->mStartTime = mGlobalTimer.getElapsedTimeF32() + delay_time; -// } -// -// mCachedRequests.insert(request); -// mRequestMap[mHandleCounter] = request; -// ret_val = mHandleCounter; -// mHandleCounter++; -// -// if(!mHandleCounter) -// { -// mHandleCounter = 1; -// } -// } -// -// return ret_val; -// } -// -// void LLCurlTextureRequest::completeRequest(S32 received_bytes) -// { -// LLMutexLock lock(&mMutex); -// -// llassert_always(mInQueue > 0); -// -// mInQueue--; -// mTotalReceivedBits += received_bytes * 8; -// } -// -// void LLCurlTextureRequest::nextRequests() -// { -// if(mCachedRequests.empty() || mInQueue >= mConcurrency) -// { -// return; -// } -// -// F32 cur_time = mGlobalTimer.getElapsedTimeF32(); -// -// req_queue_t::iterator iter; -// { -// LLMutexLock lock(&mMutex); -// iter = mCachedRequests.begin(); -// } -// while(1) -// { -// request_t* request = *iter; -// if(request->mStartTime < cur_time) -// { -// if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) -// { -// break; -// } -// -// LLMutexLock lock(&mMutex); -// ++iter; -// mInQueue++; -// mTotalIssuedRequests++; -// mCachedRequests.erase(request); -// mRequestMap.erase(request->mHandle); -// delete request; -// -// if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) -// { -// break; -// } -// } -// else -// { -// LLMutexLock lock(&mMutex); -// ++iter; -// if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) -// { -// break; -// } -// } -// } -// -// return; -// } -// -// void LLCurlTextureRequest::updatePriority(U32 handle, U32 pri) -// { -// if(!handle) -// { -// return; -// } -// -// LLMutexLock lock(&mMutex); -// -// std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); -// if(iter != mRequestMap.end()) -// { -// request_t* req = iter->second; -// -// if(req->mPriority != pri) -// { -// mCachedRequests.erase(req); -// req->mPriority = pri; -// mCachedRequests.insert(req); -// } -// } -// } -// -// void LLCurlTextureRequest::removeRequest(U32 handle) -// { -// if(!handle) -// { -// return; -// } -// -// LLMutexLock lock(&mMutex); -// -// std::map<S32, request_t*>::iterator iter = mRequestMap.find(handle); -// if(iter != mRequestMap.end()) -// { -// request_t* req = iter->second; -// mRequestMap.erase(iter); -// mCachedRequests.erase(req); -// delete req; -// } -// } -// -// bool LLCurlTextureRequest::isWaiting(U32 handle) -// { -// if(!handle) -// { -// return false; -// } -// -// LLMutexLock lock(&mMutex); -// return mRequestMap.find(handle) != mRequestMap.end(); -// } -// -// U32 LLCurlTextureRequest::getTotalReceivedBits() -// { -// LLMutexLock lock(&mMutex); -// -// U32 bits = mTotalReceivedBits; -// mTotalReceivedBits = 0; -// return bits; -// } -// -// U32 LLCurlTextureRequest::getTotalIssuedRequests() -// { -// LLMutexLock lock(&mMutex); -// return mTotalIssuedRequests; -// } -// -// S32 LLCurlTextureRequest::getNumRequests() -// { -// LLMutexLock lock(&mMutex); -// return mInQueue; -// } - -//////////////////////////////////////////////////////////////////////////// -// For generating one easy request -// associated with a single multi request - -LLCurlEasyRequest::LLCurlEasyRequest() - : mRequestSent(false), - mResultReturned(false) -{ - mMulti = new LLCurl::Multi(); - - if(mMulti->isValid()) - { - mEasy = mMulti->allocEasy(); - if (mEasy) - { - mEasy->setErrorBuffer(); - mEasy->setCA(); - // Set proxy settings if configured to do so. - LLProxy::getInstance()->applyProxySettings(mEasy); - } -} - else - { - LLCurl::getCurlThread()->killMulti(mMulti) ; - mEasy = NULL ; - mMulti = NULL ; - } -} - -LLCurlEasyRequest::~LLCurlEasyRequest() -{ - LLCurl::getCurlThread()->killMulti(mMulti) ; -} - -void LLCurlEasyRequest::setopt(CURLoption option, S32 value) -{ - if (isValid() && mEasy) - { - mEasy->setopt(option, value); - } -} - -void LLCurlEasyRequest::setoptString(CURLoption option, const std::string& value) -{ - if (isValid() && mEasy) - { - mEasy->setoptString(option, value); - } -} - -void LLCurlEasyRequest::setPost(char* postdata, S32 size) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_POST, 1); - mEasy->setopt(CURLOPT_POSTFIELDS, postdata); - mEasy->setopt(CURLOPT_POSTFIELDSIZE, size); - } -} - -void LLCurlEasyRequest::setHeaderCallback(curl_header_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_HEADERFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_HEADERDATA, userdata); // aka CURLOPT_WRITEHEADER - } -} - -void LLCurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_WRITEFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_WRITEDATA, userdata); - } -} - -void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_READFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_READDATA, userdata); - } -} - -void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata); - } -} - -void LLCurlEasyRequest::slist_append(const std::string& header, const std::string& value) -{ - if (isValid() && mEasy) - { - mEasy->slist_append(header, value); - } -} - -void LLCurlEasyRequest::slist_append(const char* str) -{ - if (isValid() && mEasy) - { - mEasy->slist_append(str); - } -} - -void LLCurlEasyRequest::sendRequest(const std::string& url) -{ - llassert_always(!mRequestSent); - mRequestSent = true; - LL_DEBUGS() << url << LL_ENDL; - if (isValid() && mEasy) - { - mEasy->setHeaders(); - mEasy->setoptString(CURLOPT_URL, url); - mMulti->addEasy(mEasy); - } -} - -void LLCurlEasyRequest::requestComplete() -{ - llassert_always(mRequestSent); - mRequestSent = false; - if (isValid() && mEasy) - { - mMulti->removeEasy(mEasy); - } -} - -// Usage: Call getRestult until it returns false (no more messages) -bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info) -{ - if(!isValid()) - { - return false ; - } - if (!mMulti->isCompleted()) - { //we're busy, try again later - return false; - } - mMulti->setState(LLCurl::Multi::STATE_READY) ; - - if (!mEasy) - { - // Special case - we failed to initialize a curl_easy (can happen if too many open files) - // Act as though the request failed to connect - if (mResultReturned) - { - return false; - } - else - { - *result = CURLE_FAILED_INIT; - mResultReturned = true; - return true; - } - } - // In theory, info_read might return a message with a status other than CURLMSG_DONE - // In practice for all messages returned, msg == CURLMSG_DONE - // Ignore other messages just in case - while(1) - { - S32 q; - CURLMsg* curlmsg = info_read(&q, info); - if (curlmsg) - { - if (curlmsg->msg == CURLMSG_DONE) - { - *result = curlmsg->data.result; - return true; - } - // else continue - } - else - { - return false; - } - } -} - -// private -CURLMsg* LLCurlEasyRequest::info_read(S32* q, LLCurl::TransferInfo* info) -{ - if (mEasy) - { - CURLMsg* curlmsg = mMulti->info_read(q); - if (curlmsg && curlmsg->msg == CURLMSG_DONE) - { - if (info) - { - mEasy->getTransferInfo(info); - } - } - return curlmsg; - } - return NULL; -} - -std::string LLCurlEasyRequest::getErrorString() -{ - return isValid() && mEasy ? std::string(mEasy->getErrorBuffer()) : std::string(); -} //////////////////////////////////////////////////////////////////////////// @@ -1859,11 +188,11 @@ void LLCurl::initClass(F32 curl_reuest_timeout, S32 max_number_handles, bool mul CRYPTO_set_locking_callback(&LLCurl::ssl_locking_callback); #endif - sCurlThread = new LLCurlThread(multi_threaded) ; +// sCurlThread = new LLCurlThread(multi_threaded) ; if(multi_threaded) { sHandleMutexp = new LLMutex(NULL) ; - Easy::sHandleMutexp = new LLMutex(NULL) ; +// Easy::sHandleMutexp = new LLMutex(NULL) ; } } @@ -1872,18 +201,18 @@ void LLCurl::cleanupClass() sNotQuitting = false; //set quitting //shut down curl thread - while(1) - { - if(!sCurlThread->update(1)) //finish all tasks - { - break ; - } - } +// while(1) +// { +// if(!sCurlThread->update(1)) //finish all tasks +// { +// break ; +// } +// } LL_CHECK_MEMORY - sCurlThread->shutdown() ; +// sCurlThread->shutdown() ; LL_CHECK_MEMORY - delete sCurlThread ; - sCurlThread = NULL ; +// delete sCurlThread ; +// sCurlThread = NULL ; LL_CHECK_MEMORY #if SAFE_SSL @@ -1892,21 +221,13 @@ void LLCurl::cleanupClass() sSSLMutex.clear(); #endif - LL_CHECK_MEMORY - Easy::deleteAllFreeHandles(); - LL_CHECK_MEMORY - Easy::deleteAllActiveHandles(); - LL_CHECK_MEMORY + LL_CHECK_MEMORY // Free the template easy handle curl_easy_cleanup(sCurlTemplateStandardHandle); sCurlTemplateStandardHandle = NULL; LL_CHECK_MEMORY - delete Easy::sHandleMutexp ; - Easy::sHandleMutexp = NULL ; - - LL_CHECK_MEMORY delete sHandleMutexp ; sHandleMutexp = NULL ; diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index ca70462d0e..289e4bfd22 100755 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -48,7 +48,7 @@ #include "llsingleton.h" class LLMutex; -class LLCurlThread; +//class LLCurlThread; // For whatever reason, this is not typedef'd in curl.h typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream); @@ -58,114 +58,6 @@ class LLCurl LOG_CLASS(LLCurl); public: - class Easy; - class Multi; - - struct TransferInfo - { - TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {} - F64 mSizeDownload; - F64 mTotalTime; - F64 mSpeedDownload; - }; - - class Responder : public LLThreadSafeRefCount - { - //LOG_CLASS(Responder); - public: - - Responder(); - virtual ~Responder(); - - virtual bool followRedir() - { - return false; - } - - /** - * @brief return true if the status code indicates success. - */ - bool isGoodStatus() const { return isHttpGoodStatus(mStatus); } - - S32 getStatus() const { return mStatus; } - const std::string& getReason() const { return mReason; } - const LLSD& getContent() const { return mContent; } - bool hasResponseHeader(const std::string& header) const; - const std::string& getResponseHeader(const std::string& header) const; - const LLSD& getResponseHeaders() const { return mResponseHeaders; } - const std::string& getURL() const { return mURL; } - EHTTPMethod getHTTPMethod() const { return mHTTPMethod; } - - // This formats response information for use in log spam. Includes content spam. - std::string dumpResponse() const; - - // Allows direct triggering of success/error with different results. - void completeResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); - void successResult(const LLSD& content); - void failureResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); - - // The default implementation will try to parse body content as an LLSD, however - // it should not spam about parsing failures unless the server sent a - // Content-Type: application/llsd+xml header. - virtual void completedRaw( - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - /**< Override point for clients that may want to use this - class when the response is some other format besides LLSD - */ - - - // The http* methods are not public since these should be triggered internally - // after status, reason, content, etc have been set. - // If you need to trigger a completion method, use the *Result methods, above. - protected: - // These methods are the preferred way to process final results. - // By default, when one of these is called the following information will be resolved: - // * HTTP status code - getStatus() - // * Reason string - getReason() - // * Content - getContent() - // * Response Headers - getResponseHeaders() - - // By default, httpSuccess is triggered whenever httpCompleted is called with a 2xx status code. - virtual void httpSuccess(); - //< called by completed for good status codes. - - // By default, httpFailure is triggered whenever httpCompleted is called with a non-2xx status code. - virtual void httpFailure(); - //< called by httpCompleted() on bad status - - // httpCompleted does not generally need to be overridden, unless - // you don't care about the status code (which determine httpFailure or httpSuccess) - // or if you want to re-interpret what a 'good' vs' bad' status code is. - virtual void httpCompleted(); - /**< The default implementation calls - either: - * httpSuccess(), or - * httpFailure() - */ - - public: - void setHTTPMethod(EHTTPMethod method); - void setURL(const std::string& url); - void setResult(S32 status, const std::string& reason, const LLSD& content = LLSD()); - void setResponseHeader(const std::string& header, const std::string& value); - - private: - // These can be accessed by the get* methods. Treated as 'read-only' during completion handlers. - EHTTPMethod mHTTPMethod; - std::string mURL; - LLSD mResponseHeaders; - - protected: - // These should also generally be treated as 'read-only' during completion handlers - // and should be accessed by the get* methods. The exception to this rule would - // be when overriding the completedRaw method in preparation for calling httpCompleted(). - S32 mStatus; - std::string mReason; - LLSD mContent; - }; - typedef LLPointer<Responder> ResponderPtr; - /** * @ brief Set certificate authority file used to verify HTTPS certs. @@ -214,7 +106,7 @@ public: static void ssl_locking_callback(int mode, int type, const char *file, int line); static unsigned long ssl_thread_id(void); - static LLCurlThread* getCurlThread() { return sCurlThread ;} +// static LLCurlThread* getCurlThread() { return sCurlThread ;} static CURLM* newMultiHandle() ; static CURLMcode deleteMultiHandle(CURLM* handle) ; @@ -227,7 +119,8 @@ private: static std::string sCAPath; static std::string sCAFile; static const unsigned int MAX_REDIRECTS; - static LLCurlThread* sCurlThread; + // static LLCurlThread* sCurlThread; +// static LLCurlThread* sCurlThread; static LLMutex* sHandleMutexp ; static S32 sTotalHandles ; @@ -238,232 +131,6 @@ public: static F32 sCurlRequestTimeOut; }; -class LLCurl::Easy -{ - LOG_CLASS(Easy); - -private: - Easy(); - -public: - static Easy* getEasy(); - ~Easy(); - - CURL* getCurlHandle() const { return mCurlEasyHandle; } - - void setErrorBuffer(); - void setCA(); - - void setopt(CURLoption option, S32 value); - // These assume the setter does not free value! - void setopt(CURLoption option, void* value); - void setopt(CURLoption option, char* value); - // Copies the string so that it is guaranteed to stick around - void setoptString(CURLoption option, const std::string& value); - - void slist_append(const std::string& header, const std::string& value); - void slist_append(const char* str); - void setHeaders(); - - S32 report(CURLcode); - void getTransferInfo(LLCurl::TransferInfo* info); - - void prepRequest(const std::string& url, const std::vector<std::string>& headers, LLCurl::ResponderPtr, S32 time_out = 0, bool post = false); - - const char* getErrorBuffer(); - - std::stringstream& getInput() { return mInput; } - std::stringstream& getHeaderOutput() { return mHeaderOutput; } - LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; } - const LLChannelDescriptors& getChannels() { return mChannels; } - - void resetState(); - - static CURL* allocEasyHandle(); - static void releaseEasyHandle(CURL* handle); - -private: - friend class LLCurl; -#if 1 - friend class LLCurl::Multi; -#endif - - CURL* mCurlEasyHandle; - struct curl_slist* mHeaders; - - std::stringstream mRequest; - LLChannelDescriptors mChannels; - LLIOPipe::buffer_ptr_t mOutput; - std::stringstream mInput; - std::stringstream mHeaderOutput; - char mErrorBuffer[CURL_ERROR_SIZE]; - - // Note: char*'s not strings since we pass pointers to curl - std::vector<char*> mStrings; - - LLCurl::ResponderPtr mResponder; - - static std::set<CURL*> sFreeHandles; - static std::set<CURL*> sActiveHandles; - static LLMutex* sHandleMutexp ; - - static void deleteAllActiveHandles(); - static void deleteAllFreeHandles(); -}; - -#if 1 -class LLCurl::Multi -{ - LOG_CLASS(Multi); - - friend class LLCurlThread ; - -private: - ~Multi(); - - void markDead() ; - bool doPerform(); - -public: - - typedef enum - { - STATE_READY=0, - STATE_PERFORMING=1, - STATE_COMPLETED=2 - } ePerformState; - - Multi(F32 idle_time_out = 0.f); - - LLCurl::Easy* allocEasy(); - bool addEasy(LLCurl::Easy* easy); - void removeEasy(LLCurl::Easy* easy); - - void lock() ; - void unlock() ; - - void setState(ePerformState state) ; - ePerformState getState() ; - - bool isCompleted() ; - bool isValid() {return mCurlMultiHandle != NULL && mValid;} - bool isDead() {return mDead;} - - bool waitToComplete() ; - - S32 process(); - - CURLMsg* info_read(S32* msgs_in_queue); - - S32 mQueued; - S32 mErrorCount; - -private: - void easyFree(LLCurl::Easy*); - void cleanup(bool deleted = false) ; - - CURLM* mCurlMultiHandle; - - typedef std::set<LLCurl::Easy*> easy_active_list_t; - easy_active_list_t mEasyActiveList; - typedef std::map<CURL*, LLCurl::Easy*> easy_active_map_t; - easy_active_map_t mEasyActiveMap; - typedef std::set<LLCurl::Easy*> easy_free_list_t; - easy_free_list_t mEasyFreeList; - - LLQueuedThread::handle_t mHandle ; - ePerformState mState; - - BOOL mDead ; - BOOL mValid ; - LLMutex* mMutexp ; - LLMutex* mDeletionMutexp ; - LLMutex* mEasyMutexp ; - LLFrameTimer mIdleTimer ; - F32 mIdleTimeOut; -}; -#endif - -#if 1 -class LLCurlThread : public LLQueuedThread -{ -public: - - class CurlRequest : public LLQueuedThread::QueuedRequest - { - protected: - virtual ~CurlRequest(); // use deleteRequest() - - public: - CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread); - - /*virtual*/ bool processRequest(); - /*virtual*/ void finishRequest(bool completed); - - private: - // input - LLCurl::Multi* mMulti; - LLCurlThread* mCurlThread; - }; - friend class CurlRequest; - -public: - LLCurlThread(bool threaded = true) ; - virtual ~LLCurlThread() ; - - S32 update(F32 max_time_ms); - - void addMulti(LLCurl::Multi* multi) ; - void killMulti(LLCurl::Multi* multi) ; - -private: - bool doMultiPerform(LLCurl::Multi* multi) ; - void deleteMulti(LLCurl::Multi* multi) ; - void cleanupMulti(LLCurl::Multi* multi) ; -} ; -#endif - - -class LLCurlEasyRequest -{ -public: - LLCurlEasyRequest(); - ~LLCurlEasyRequest(); - void setopt(CURLoption option, S32 value); - void setoptString(CURLoption option, const std::string& value); - void setPost(char* postdata, S32 size); - void setHeaderCallback(curl_header_callback callback, void* userdata); - void setWriteCallback(curl_write_callback callback, void* userdata); - void setReadCallback(curl_read_callback callback, void* userdata); - void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata); - void slist_append(const std::string& header, const std::string& value); - void slist_append(const char* str); - void sendRequest(const std::string& url); - void requestComplete(); - bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL); - std::string getErrorString(); -#if 0 - bool isCompleted() { return false; } - bool wait() { return false; } - bool isValid() { return false; } -#else - bool isCompleted() {return mMulti->isCompleted() ;} - bool wait() { return mMulti->waitToComplete(); } - bool isValid() {return mMulti && mMulti->isValid(); } -#endif - LLCurl::Easy* getEasy() const { return mEasy; } - -private: - CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info); - -private: -#if 1 - LLCurl::Multi* mMulti; -#endif - LLCurl::Easy* mEasy; - bool mRequestSent; - bool mResultReturned; -}; // Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace. namespace LLCurlFF diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 9b8d19cc3e..c25f1ec5e5 100755 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -408,16 +408,6 @@ void LLProxy::cleanupClass() deleteSingleton(); } -void LLProxy::applyProxySettings(LLCurlEasyRequest* handle) -{ - applyProxySettings(handle->getEasy()); -} - -void LLProxy::applyProxySettings(LLCurl::Easy* handle) -{ - applyProxySettings(handle->getCurlHandle()); -} - /** * @brief Apply proxy settings to a CuRL request if an HTTP proxy is enabled. * diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index a919370540..da965219fa 100755 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -208,16 +208,13 @@ enum LLSocks5AuthType * thread-safe method to apply those options to a curl request * (LLProxy::applyProxySettings()). This method is overloaded * to accommodate the various abstraction libcurl layers that exist - * throughout the viewer (LLCurlEasyRequest, LLCurl::Easy, and CURL). - * - * If you are working with LLCurl or LLCurlEasyRequest objects, - * the configured proxy settings will be applied in the constructors - * of those request handles. If you are working with CURL objects - * directly, you will need to pass the handle of the request to - * applyProxySettings() before issuing the request. + * throughout the viewer (CURL). * * To ensure thread safety, all LLProxy members that relate to the HTTP * proxy require the LLProxyMutex to be locked before accessing. + * + * *TODO$: This should be moved into the LLCore::Http space. + * */ class LLProxy: public LLSingleton<LLProxy> { @@ -252,9 +249,6 @@ public: // Apply the current proxy settings to a curl request. Doesn't do anything if mHTTPProxyEnabled is false. // Safe to call from any thread. void applyProxySettings(CURL* handle); - void applyProxySettings(LLCurl::Easy* handle); - void applyProxySettings(LLCurlEasyRequest* handle); - // Start a connection to the SOCKS 5 proxy. Call from main thread only. S32 startSOCKSProxy(LLHost host); diff --git a/indra/llmessage/llsdrpcclient.cpp b/indra/llmessage/llsdrpcclient.cpp deleted file mode 100755 index bb4739f733..0000000000 --- a/indra/llmessage/llsdrpcclient.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/** - * @file llsdrpcclient.cpp - * @author Phoenix - * @date 2005-11-05 - * @brief Implementation of the llsd client classes. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#include "linden_common.h" -#include "llsdrpcclient.h" - -#include "llbufferstream.h" -#include "llfasttimer.h" -#include "llfiltersd2xmlrpc.h" -#include "llpumpio.h" -#include "llsd.h" -#include "llsdserialize.h" -#include "llurlrequest.h" - -/** - * String constants - */ -static std::string LLSDRPC_RESPONSE_NAME("response"); -static std::string LLSDRPC_FAULT_NAME("fault"); - -#if 0 -/** - * LLSDRPCResponse - */ -LLSDRPCResponse::LLSDRPCResponse() : - mIsError(false), - mIsFault(false) -{ -} - -// virtual -LLSDRPCResponse::~LLSDRPCResponse() -{ -} - -bool LLSDRPCResponse::extractResponse(const LLSD& sd) -{ - bool rv = true; - if(sd.has(LLSDRPC_RESPONSE_NAME)) - { - mReturnValue = sd[LLSDRPC_RESPONSE_NAME]; - mIsFault = false; - } - else if(sd.has(LLSDRPC_FAULT_NAME)) - { - mReturnValue = sd[LLSDRPC_FAULT_NAME]; - mIsFault = true; - } - else - { - mReturnValue.clear(); - mIsError = true; - rv = false; - } - return rv; -} - -static LLTrace::BlockTimerStatHandle FTM_SDRPC_RESPONSE("SDRPC Response"); - -// virtual -LLIOPipe::EStatus LLSDRPCResponse::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_SDRPC_RESPONSE); - PUMP_DEBUG; - if(mIsError) - { - error(pump); - } - else if(mIsFault) - { - fault(pump); - } - else - { - response(pump); - } - PUMP_DEBUG; - return STATUS_DONE; -} -#endif - -#if 0 -/** - * LLSDRPCClient - */ - -LLSDRPCClient::LLSDRPCClient() : - mState(STATE_NONE), - mQueue(EPBQ_PROCESS) -{ -} - -// virtual -LLSDRPCClient::~LLSDRPCClient() -{ -} - -bool LLSDRPCClient::call( - const std::string& uri, - const std::string& method, - const LLSD& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue) -{ - //LL_INFOS() << "RPC: " << uri << "." << method << "(" << *parameter << ")" - // << LL_ENDL; - if(method.empty() || !response) - { - return false; - } - mState = STATE_READY; - mURI.assign(uri); - std::stringstream req; - req << LLSDRPC_REQUEST_HEADER_1 << method - << LLSDRPC_REQUEST_HEADER_2; - LLSDSerialize::toNotation(parameter, req); - req << LLSDRPC_REQUEST_FOOTER; - mRequest = req.str(); - mQueue = queue; - mResponse = response; - return true; -} - -bool LLSDRPCClient::call( - const std::string& uri, - const std::string& method, - const std::string& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue) -{ - //LL_INFOS() << "RPC: " << uri << "." << method << "(" << parameter << ")" - // << LL_ENDL; - if(method.empty() || parameter.empty() || !response) - { - return false; - } - mState = STATE_READY; - mURI.assign(uri); - std::stringstream req; - req << LLSDRPC_REQUEST_HEADER_1 << method - << LLSDRPC_REQUEST_HEADER_2 << parameter - << LLSDRPC_REQUEST_FOOTER; - mRequest = req.str(); - mQueue = queue; - mResponse = response; - return true; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_SDRPC_CLIENT("SDRPC Client"); - -// virtual -LLIOPipe::EStatus LLSDRPCClient::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_SDRPC_CLIENT); - PUMP_DEBUG; - if((STATE_NONE == mState) || (!pump)) - { - // You should have called the call() method already. - return STATUS_PRECONDITION_NOT_MET; - } - EStatus rv = STATUS_DONE; - switch(mState) - { - case STATE_READY: - { - PUMP_DEBUG; -// LL_DEBUGS() << "LLSDRPCClient::process_impl STATE_READY" << LL_ENDL; - buffer->append( - channels.out(), - (U8*)mRequest.c_str(), - mRequest.length()); - context[CONTEXT_DEST_URI_SD_LABEL] = mURI; - mState = STATE_WAITING_FOR_RESPONSE; - break; - } - case STATE_WAITING_FOR_RESPONSE: - { - PUMP_DEBUG; - // The input channel has the sd response in it. - //LL_DEBUGS() << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE" - // << LL_ENDL; - LLBufferStream resp(channels, buffer.get()); - LLSD sd; - LLSDSerialize::fromNotation(sd, resp, buffer->count(channels.in())); - LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get(); - if (!response) - { - mState = STATE_DONE; - break; - } - response->extractResponse(sd); - if(EPBQ_PROCESS == mQueue) - { - LLPumpIO::chain_t chain; - chain.push_back(mResponse); - pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS); - } - else - { - pump->respond(mResponse.get()); - } - mState = STATE_DONE; - break; - } - case STATE_DONE: - default: - PUMP_DEBUG; - LL_INFOS() << "invalid state to process" << LL_ENDL; - rv = STATUS_ERROR; - break; - } - return rv; -} -#endif diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h deleted file mode 100755 index c22051094d..0000000000 --- a/indra/llmessage/llsdrpcclient.h +++ /dev/null @@ -1,226 +0,0 @@ -/** - * @file llsdrpcclient.h - * @author Phoenix - * @date 2005-11-05 - * @brief Implementation and helpers for structure data RPC clients. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#ifndef LL_LLSDRPCCLIENT_H -#define LL_LLSDRPCCLIENT_H - -/** - * This file declares classes to encapsulate a basic structured data - * remote procedure client. - */ - -#include "llchainio.h" -#include "llfiltersd2xmlrpc.h" -#include "lliopipe.h" -#if 0 -//#include "llurlrequest.h" -#endif - -#if 0 -/** - * @class LLSDRPCClientResponse - * @brief Abstract base class to represent a response from an SD server. - * - * This is used as a base class for callbacks generated from an - * structured data remote procedure call. The - * <code>extractResponse</code> method will deal with the llsdrpc method - * call overhead, and keep track of what to call during the next call - * into <code>process</code>. If you use this as a base class, you - * need to implement <code>response</code>, <code>fault</code>, and - * <code>error</code> to do something useful. When in those methods, - * you can parse and utilize the mReturnValue member data. - */ -class LLSDRPCResponse : public LLIOPipe -{ -public: - LLSDRPCResponse(); - virtual ~LLSDRPCResponse(); - - /** - * @brief This method extracts the response out of the sd passed in - * - * Any appropriate data found in the sd passed in will be - * extracted and managed by this object - not copied or cloned. It - * will still be up to the caller to delete the pointer passed in. - * @param sd The raw structured data response from the remote server. - * @return Returns true if this was able to parse the structured data. - */ - bool extractResponse(const LLSD& sd); - -protected: - /** - * @brief Method called when the response is ready. - */ - virtual bool response(LLPumpIO* pump) = 0; - - /** - * @brief Method called when a fault is generated by the remote server. - */ - virtual bool fault(LLPumpIO* pump) = 0; - - /** - * @brief Method called when there was an error - */ - virtual bool error(LLPumpIO* pump) = 0; - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - LLSD mReturnValue; - bool mIsError; - bool mIsFault; -}; -#endif - -#if 0 -/** - * @class LLSDRPCClient - * @brief Client class for a structured data remote procedure call. - * - * This class helps deal with making structured data calls to a remote - * server. You can visualize the calls as: - * <code> - * response = uri.method(parameter) - * </code> - * where you pass in everything to <code>call</code> and this class - * takes care of the rest of the details. - * In typical usage, you will derive a class from this class and - * provide an API more useful for the specific application at - * hand. For example, if you were writing a service to send an instant - * message, you could create an API for it to send the messsage, and - * that class would do the work of translating it into the method and - * parameter, find the destination, and invoke <code>call</call> with - * a useful implementation of LLSDRPCResponse passed in to handle the - * response from the network. - */ -class LLSDRPCClient : public LLIOPipe -{ -public: - LLSDRPCClient(); - virtual ~LLSDRPCClient(); - - /** - * @brief Enumeration for tracking which queue to process the - * response. - */ - enum EPassBackQueue - { - EPBQ_PROCESS, - EPBQ_CALLBACK, - }; - - /** - * @brief Call a method on a remote LLSDRPCServer - * - * @param uri The remote object to call, eg, - * http://localhost/usher. If you are using a factory with a fixed - * url, the uri passed in will probably be ignored. - * @param method The method to call on the remote object - * @param parameter The parameter to pass into the remote - * object. It is up to the caller to delete the value passed in. - * @param response The object which gets the response. - * @param queue Specifies to call the response on the process or - * callback queue. - * @return Returns true if this object will be able to make the RPC call. - */ - bool call( - const std::string& uri, - const std::string& method, - const LLSD& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue); - - /** - * @brief Call a method on a remote LLSDRPCServer - * - * @param uri The remote object to call, eg, - * http://localhost/usher. If you are using a factory with a fixed - * url, the uri passed in will probably be ignored. - * @param method The method to call on the remote object - * @param parameter The seriailized parameter to pass into the - * remote object. - * @param response The object which gets the response. - * @param queue Specifies to call the response on the process or - * callback queue. - * @return Returns true if this object will be able to make the RPC call. - */ - bool call( - const std::string& uri, - const std::string& method, - const std::string& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue); - -protected: - /** - * @brief Enumeration for tracking client state. - */ - enum EState - { - STATE_NONE, - STATE_READY, - STATE_WAITING_FOR_RESPONSE, - STATE_DONE - }; - - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - EState mState; - std::string mURI; - std::string mRequest; - EPassBackQueue mQueue; - LLIOPipe::ptr_t mResponse; -}; -#endif -#endif // LL_LLSDRPCCLIENT_H diff --git a/indra/llmessage/llsdrpcserver.cpp b/indra/llmessage/llsdrpcserver.cpp deleted file mode 100755 index ac3f341d0f..0000000000 --- a/indra/llmessage/llsdrpcserver.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/** - * @file llsdrpcserver.cpp - * @author Phoenix - * @date 2005-10-11 - * @brief Implementation of the LLSDRPCServer and related classes. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#include "linden_common.h" -#include "llsdrpcserver.h" - -#include "llbuffer.h" -#include "llbufferstream.h" -#include "llfasttimer.h" -#include "llpumpio.h" -#include "llsdserialize.h" -#include "llstl.h" - -#if 0 -static const char FAULT_PART_1[] = "{'fault':{'code':i"; -static const char FAULT_PART_2[] = ", 'description':'"; -static const char FAULT_PART_3[] = "'}}"; - -static const char RESPONSE_PART_1[] = "{'response':"; -static const char RESPONSE_PART_2[] = "}"; - -static const S32 FAULT_GENERIC = 1000; -static const S32 FAULT_METHOD_NOT_FOUND = 1001; - -static const std::string LLSDRPC_METHOD_SD_NAME("method"); -static const std::string LLSDRPC_PARAMETER_SD_NAME("parameter"); - - -/** - * LLSDRPCServer - */ -LLSDRPCServer::LLSDRPCServer() : - mState(LLSDRPCServer::STATE_NONE), - mPump(NULL), - mLock(0) -{ -} - -LLSDRPCServer::~LLSDRPCServer() -{ - std::for_each( - mMethods.begin(), - mMethods.end(), - llcompose1( - DeletePointerFunctor<LLSDRPCMethodCallBase>(), - llselect2nd<method_map_t::value_type>())); - std::for_each( - mCallbackMethods.begin(), - mCallbackMethods.end(), - llcompose1( - DeletePointerFunctor<LLSDRPCMethodCallBase>(), - llselect2nd<method_map_t::value_type>())); -} - - -// virtual -ESDRPCSStatus LLSDRPCServer::deferredResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data) { - // subclass should provide a sane implementation - return ESDRPCS_DONE; -} - -void LLSDRPCServer::clearLock() -{ - if(mLock && mPump) - { - mPump->clearLock(mLock); - mPump = NULL; - mLock = 0; - } -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_SDRPC_SERVER("SDRPC Server"); - -// virtual -LLIOPipe::EStatus LLSDRPCServer::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_SDRPC_SERVER); - PUMP_DEBUG; -// LL_DEBUGS() << "LLSDRPCServer::process_impl" << LL_ENDL; - // Once we have all the data, We need to read the sd on - // the the in channel, and respond on the out channel - if(!eos) return STATUS_BREAK; - if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET; - - std::string method_name; - LLIOPipe::EStatus status = STATUS_DONE; - - switch(mState) - { - case STATE_DEFERRED: - PUMP_DEBUG; - if(ESDRPCS_DONE != deferredResponse(channels, buffer.get())) - { - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "deferred response failed."); - } - mState = STATE_DONE; - return STATUS_DONE; - - case STATE_DONE: -// LL_DEBUGS() << "STATE_DONE" << LL_ENDL; - break; - case STATE_CALLBACK: -// LL_DEBUGS() << "STATE_CALLBACK" << LL_ENDL; - PUMP_DEBUG; - method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); - if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME)) - { - if(ESDRPCS_DONE != callbackMethod( - method_name, - mRequest[LLSDRPC_PARAMETER_SD_NAME], - channels, - buffer.get())) - { - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Callback method call failed."); - } - } - else - { - // this should never happen, since we should not be in - // this state unless we originally found a method and - // params during the first call to process. - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Invalid LLSDRPC sever state - callback without method."); - } - pump->clearLock(mLock); - mLock = 0; - mState = STATE_DONE; - break; - case STATE_NONE: -// LL_DEBUGS() << "STATE_NONE" << LL_ENDL; - default: - { - // First time we got here - process the SD request, and call - // the method. - PUMP_DEBUG; - LLBufferStream istr(channels, buffer.get()); - mRequest.clear(); - LLSDSerialize::fromNotation( - mRequest, - istr, - buffer->count(channels.in())); - - // { 'method':'...', 'parameter': ... } - method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); - if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME)) - { - ESDRPCSStatus rv = callMethod( - method_name, - mRequest[LLSDRPC_PARAMETER_SD_NAME], - channels, - buffer.get()); - switch(rv) - { - case ESDRPCS_DEFERRED: - mPump = pump; - mLock = pump->setLock(); - mState = STATE_DEFERRED; - status = STATUS_BREAK; - break; - - case ESDRPCS_CALLBACK: - { - mState = STATE_CALLBACK; - LLPumpIO::LLLinkInfo link; - link.mPipe = LLIOPipe::ptr_t(this); - link.mChannels = channels; - LLPumpIO::links_t links; - links.push_back(link); - pump->respond(links, buffer, context); - mLock = pump->setLock(); - status = STATUS_BREAK; - break; - } - case ESDRPCS_DONE: - mState = STATE_DONE; - break; - case ESDRPCS_ERROR: - default: - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Method call failed."); - break; - } - } - else - { - // send a fault - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Unable to find method and parameter in request."); - } - break; - } - } - - PUMP_DEBUG; - return status; -} - -// virtual -ESDRPCSStatus LLSDRPCServer::callMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) -{ - // Try to find the method in the method table. - ESDRPCSStatus rv = ESDRPCS_DONE; - method_map_t::iterator it = mMethods.find(method); - if(it != mMethods.end()) - { - rv = (*it).second->call(params, channels, response); - } - else - { - it = mCallbackMethods.find(method); - if(it == mCallbackMethods.end()) - { - // method not found. - std::ostringstream message; - message << "rpc server unable to find method: " << method; - buildFault( - channels, - response, - FAULT_METHOD_NOT_FOUND, - message.str()); - } - else - { - // we found it in the callback methods - tell the process - // to coordinate calling on the pump callback. - return ESDRPCS_CALLBACK; - } - } - return rv; -} - -// virtual -ESDRPCSStatus LLSDRPCServer::callbackMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) -{ - // Try to find the method in the callback method table. - ESDRPCSStatus rv = ESDRPCS_DONE; - method_map_t::iterator it = mCallbackMethods.find(method); - if(it != mCallbackMethods.end()) - { - rv = (*it).second->call(params, channels, response); - } - else - { - std::ostringstream message; - message << "pcserver unable to find callback method: " << method; - buildFault( - channels, - response, - FAULT_METHOD_NOT_FOUND, - message.str()); - } - return rv; -} - -// static -void LLSDRPCServer::buildFault( - const LLChannelDescriptors& channels, - LLBufferArray* data, - S32 code, - const std::string& msg) -{ - LLBufferStream ostr(channels, data); - ostr << FAULT_PART_1 << code << FAULT_PART_2 << msg << FAULT_PART_3; - LL_INFOS() << "LLSDRPCServer::buildFault: " << code << ", " << msg << LL_ENDL; -} - -// static -void LLSDRPCServer::buildResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data, - const LLSD& response) -{ - LLBufferStream ostr(channels, data); - ostr << RESPONSE_PART_1; - LLSDSerialize::toNotation(response, ostr); - ostr << RESPONSE_PART_2; -#if LL_DEBUG - std::ostringstream debug_ostr; - debug_ostr << "LLSDRPCServer::buildResponse: "; - LLSDSerialize::toNotation(response, debug_ostr); - LL_INFOS() << debug_ostr.str() << LL_ENDL; -#endif -} -#endif diff --git a/indra/llmessage/llsdrpcserver.h b/indra/llmessage/llsdrpcserver.h deleted file mode 100755 index 07d7b58b31..0000000000 --- a/indra/llmessage/llsdrpcserver.h +++ /dev/null @@ -1,361 +0,0 @@ -/** - * @file llsdrpcserver.h - * @author Phoenix - * @date 2005-10-11 - * @brief Declaration of the structured data remote procedure call server. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#ifndef LL_LLSDRPCSERVER_H -#define LL_LLSDRPCSERVER_H - -#if 0 -/** - * I've set this up to be pretty easy to use when you want to make a - * structured data rpc server which responds to methods by - * name. Derive a class from the LLSDRPCServer, and during - * construction (or initialization if you have the luxury) map method - * names to pointers to member functions. This will look a lot like: - * - * <code> - * class LLMessageAgents : public LLSDRPCServer {<br> - * public:<br> - * typedef LLSDRPCServer<LLUsher> mem_fn_t;<br> - * LLMessageAgents() {<br> - * mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);<br> - * mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);<br> - * }<br> - * protected:<br> - * rpc_IM(const LLSD& params, - * const LLChannelDescriptors& channels, - * LLBufferArray* data) - * {...}<br> - * rpc_Alert(const LLSD& params, - * const LLChannelDescriptors& channels, - * LLBufferArray* data) - * {...}<br> - * };<br> - * </code> - * - * The params are an array where each element in the array is a single - * parameter in the call. - * - * It is up to you to pack a valid serialized llsd response into the - * data object passed into the method, but you can use the helper - * methods below to help. - */ - -#include <map> -#include "lliopipe.h" -#include "lliohttpserver.h" -#include "llfiltersd2xmlrpc.h" - -class LLSD; - -/** - * @brief Enumeration for specifying server method call status. This - * enumeration controls how the server class will manage the pump - * process/callback mechanism. - */ -enum ESDRPCSStatus -{ - // The call went ok, but the response is not yet ready. The - // method will arrange for the clearLock() call to be made at - // a later date, after which, once the chain is being pumped - // again, deferredResponse() will be called to gather the result - ESDRPCS_DEFERRED, - - // The LLSDRPCServer would like to handle the method on the - // callback queue of the pump. - ESDRPCS_CALLBACK, - - // The method call finished and generated output. - ESDRPCS_DONE, - - // Method failed for some unspecified reason - you should avoid - // this. A generic fault will be sent to the output. - ESDRPCS_ERROR, - - ESDRPCS_COUNT, -}; - -/** - * @class LLSDRPCMethodCallBase - * @brief Base class for calling a member function in an sd rpcserver - * implementation. - */ -class LLSDRPCMethodCallBase -{ -public: - LLSDRPCMethodCallBase() {} - virtual ~LLSDRPCMethodCallBase() {} - - virtual ESDRPCSStatus call( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) = 0; -protected: -}; - -/** - * @class LLSDRPCMethodCall - * @brief Class which implements member function calls. - */ -template<class Server> -class LLSDRPCMethodCall : public LLSDRPCMethodCallBase -{ -public: - typedef ESDRPCSStatus (Server::*mem_fn)( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - LLSDRPCMethodCall(Server* s, mem_fn fn) : - mServer(s), - mMemFn(fn) - { - } - virtual ~LLSDRPCMethodCall() {} - virtual ESDRPCSStatus call( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data) - { - return (*mServer.*mMemFn)(params, channels, data); - } - -protected: - Server* mServer; - mem_fn mMemFn; - //bool (Server::*mMemFn)(const LLSD& params, LLBufferArray& data); -}; - - -/** - * @class LLSDRPCServer - * @brief Basic implementation of a structure data rpc server - * - * The rpc server is also designed to appropriately straddle the pump - * <code>process()</code> and <code>callback()</code> to specify which - * thread you want to work on when handling a method call. The - * <code>mMethods</code> methods are called from - * <code>process()</code>, while the <code>mCallbackMethods</code> are - * called when a pump is in a <code>callback()</code> cycle. - */ -class LLSDRPCServer : public LLIOPipe -{ -public: - LLSDRPCServer(); - virtual ~LLSDRPCServer(); - - /** - * enumeration for generic fault codes - */ - enum - { - FAULT_BAD_REQUEST = 2000, - FAULT_NO_RESPONSE = 2001, - }; - - /** - * @brief Call this method to return an rpc fault. - * - * @param channel The channel for output on the data buffer - * @param data buffer which will recieve the final output - * @param code The fault code - * @param msg The fault message - */ - static void buildFault( - const LLChannelDescriptors& channels, - LLBufferArray* data, - S32 code, - const std::string& msg); - - /** - * @brief Call this method to build an rpc response. - * - * @param channel The channel for output on the data buffer - * @param data buffer which will recieve the final output - * @param response The return value from the method call - */ - static void buildResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data, - const LLSD& response); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - - /** - * @brief Enumeration to track the state of the rpc server instance - */ - enum EState - { - STATE_NONE, - STATE_CALLBACK, - STATE_DEFERRED, - STATE_DONE - }; - - /** - * @brief This method is called when an http post comes in. - * - * The default behavior is to look at the method name, look up the - * method in the method table, and call it. If the method is not - * found, this function will build a fault response. You can - * implement your own version of this function if you want to hard - * wire some behavior or optimize things a bit. - * @param method The method name being called - * @param params The parameters - * @param channel The channel for output on the data buffer - * @param data The http data - * @return Returns the status of the method call, done/deferred/etc - */ - virtual ESDRPCSStatus callMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - - /** - * @brief This method is called when a pump callback is processed. - * - * The default behavior is to look at the method name, look up the - * method in the callback method table, and call it. If the method - * is not found, this function will build a fault response. You - * can implement your own version of this function if you want to - * hard wire some behavior or optimize things a bit. - * @param method The method name being called - * @param params The parameters - * @param channel The channel for output on the data buffer - * @param data The http data - * @return Returns the status of the method call, done/deferred/etc - */ - virtual ESDRPCSStatus callbackMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - - /** - * @brief Called after a deferred service is unlocked - * - * If a method returns ESDRPCS_DEFERRED, then the service chain - * will be locked and not processed until some other system calls - * clearLock() on the service instance again. At that point, - * once the pump starts processing the chain again, this method - * will be called so the service can output the final result - * into the buffers. - */ - virtual ESDRPCSStatus deferredResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data); - - // donovan put this public here 7/27/06 -public: - /** - * @brief unlock a service that as ESDRPCS_DEFERRED - */ - void clearLock(); - -protected: - EState mState; - LLSD mRequest; - LLPumpIO* mPump; - S32 mLock; - typedef std::map<std::string, LLSDRPCMethodCallBase*> method_map_t; - method_map_t mMethods; - method_map_t mCallbackMethods; -}; - -/** - * @name Helper Templates for making LLHTTPNodes - * - * These templates help in creating nodes for handing a service from - * either SDRPC or XMLRPC, given a single implementation of LLSDRPCServer. - * - * To use it: - * \code - * class LLUsefulServer : public LLSDRPCServer { ... } - * - * LLHTTPNode& root = LLCreateHTTPWireServer(...); - * root.addNode("llsdrpc/useful", new LLSDRPCNode<LLUsefulServer>); - * root.addNode("xmlrpc/useful", new LLXMLRPCNode<LLUsefulServer>); - * \endcode - */ -//@{ - -template<class Server> -class LLSDRPCServerFactory : public LLChainIOFactory -{ -public: - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - LL_DEBUGS() << "LLXMLSDRPCServerFactory::build" << LL_ENDL; - chain.push_back(LLIOPipe::ptr_t(new Server)); - return true; - } -}; - -template<class Server> -class LLSDRPCNode : public LLHTTPNodeForFactory< - LLSDRPCServerFactory<Server> > -{ -}; - -template<class Server> -class LLXMLRPCServerFactory : public LLChainIOFactory -{ -public: - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - LL_DEBUGS() << "LLXMLSDRPCServerFactory::build" << LL_ENDL; - chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD)); - chain.push_back(LLIOPipe::ptr_t(new Server)); - chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse)); - return true; - } -}; - -template<class Server> -class LLXMLRPCNode : public LLHTTPNodeForFactory< - LLXMLRPCServerFactory<Server> > -{ -}; - -//@} -#endif -#endif // LL_LLSDRPCSERVER_H diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp deleted file mode 100755 index 1bd9c8940e..0000000000 --- a/indra/llmessage/llurlrequest.cpp +++ /dev/null @@ -1,785 +0,0 @@ -/** - * @file llurlrequest.cpp - * @author Phoenix - * @date 2005-04-28 - * @brief Implementation of the URLRequest class and related classes. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#include "linden_common.h" -#include "llurlrequest.h" - -#include <algorithm> -#include <openssl/x509_vfy.h> -#include <openssl/ssl.h> -#include "llcurl.h" -#include "llfasttimer.h" -#include "llioutil.h" -#include "llproxy.h" -#include "llpumpio.h" -#include "llsd.h" -#include "llstring.h" -#include "apr_env.h" -#include "llapr.h" - -#if 0 -/** - * String constants - */ -const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri"); -const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes"); - -// These are defined in llhttpnode.h/llhttpnode.cpp -extern const std::string CONTEXT_REQUEST; -extern const std::string CONTEXT_RESPONSE; - -static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); - -/** - * class LLURLRequestDetail - */ -class LLURLRequestDetail -{ -public: - LLURLRequestDetail(); - ~LLURLRequestDetail(); - std::string mURL; - LLCurlEasyRequest* mCurlRequest; - LLIOPipe::buffer_ptr_t mResponseBuffer; - LLChannelDescriptors mChannels; - U8* mLastRead; - U32 mBodyLimit; - S32 mByteAccumulator; - bool mIsBodyLimitSet; - LLURLRequest::SSLCertVerifyCallback mSSLVerifyCallback; -}; - -LLURLRequestDetail::LLURLRequestDetail() : - mCurlRequest(NULL), - mLastRead(NULL), - mBodyLimit(0), - mByteAccumulator(0), - mIsBodyLimitSet(false), - mSSLVerifyCallback(NULL) -{ - mCurlRequest = new LLCurlEasyRequest(); - - if(!mCurlRequest->isValid()) //failed. - { - delete mCurlRequest ; - mCurlRequest = NULL ; - } -} - -LLURLRequestDetail::~LLURLRequestDetail() -{ - delete mCurlRequest; - mLastRead = NULL; -} - -void LLURLRequest::setSSLVerifyCallback(SSLCertVerifyCallback callback, void *param) -{ - mDetail->mSSLVerifyCallback = callback; - mDetail->mCurlRequest->setSSLCtxCallback(LLURLRequest::_sslCtxCallback, (void *)this); - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, true); - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, 2); -} - - -// _sslCtxFunction -// Callback function called when an SSL Context is created via CURL -// used to configure the context for custom cert validation - -CURLcode LLURLRequest::_sslCtxCallback(CURL * curl, void *sslctx, void *param) -{ - LLURLRequest *req = (LLURLRequest *)param; - if(req == NULL || req->mDetail->mSSLVerifyCallback == NULL) - { - SSL_CTX_set_cert_verify_callback((SSL_CTX *)sslctx, NULL, NULL); - return CURLE_OK; - } - SSL_CTX * ctx = (SSL_CTX *) sslctx; - // disable any default verification for server certs - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); - // set the verification callback. - SSL_CTX_set_cert_verify_callback(ctx, req->mDetail->mSSLVerifyCallback, (void *)req); - // the calls are void - return CURLE_OK; - -} - -/** - * class LLURLRequest - */ - - -LLURLRequest::LLURLRequest(EHTTPMethod action, bool follow_redirects /* = true */) : - mAction(action), - mFollowRedirects(follow_redirects) -{ - initialize(); -} - -LLURLRequest::LLURLRequest( - EHTTPMethod action, - const std::string& url, - bool follow_redirects /* = true */) : - mAction(action), - mFollowRedirects(follow_redirects) -{ - initialize(); - setURL(url); -} - -LLURLRequest::~LLURLRequest() -{ - delete mDetail; - mDetail = NULL ; -} - -void LLURLRequest::setURL(const std::string& url) -{ - mDetail->mURL = url; - if (url.empty()) - { - LL_WARNS() << "empty URL specified" << LL_ENDL; - } -} - -const std::string& LLURLRequest::getURL() const -{ - return mDetail->mURL; -} - -void LLURLRequest::addHeader(const std::string& header, const std::string& value /* = "" */) -{ - mDetail->mCurlRequest->slist_append(header, value); -} - -void LLURLRequest::addHeaderRaw(const char* header) -{ - mDetail->mCurlRequest->slist_append(header); -} - -void LLURLRequest::setBodyLimit(U32 size) -{ - mDetail->mBodyLimit = size; - mDetail->mIsBodyLimitSet = true; -} - -void LLURLRequest::setCallback(LLURLRequestComplete* callback) -{ - mCompletionCallback = callback; - mDetail->mCurlRequest->setHeaderCallback(&headerCallback, (void*)callback); -} - -// Added to mitigate the effect of libcurl looking -// for the ALL_PROXY and http_proxy env variables -// and deciding to insert a Pragma: no-cache -// header! The only usage of this method at the -// time of this writing is in llhttpclient.cpp -// in the request() method, where this method -// is called with use_proxy = FALSE -void LLURLRequest::useProxy(bool use_proxy) -{ - static char *env_proxy; - - if (use_proxy && (env_proxy == NULL)) - { - apr_status_t status; - LLAPRPool pool; - status = apr_env_get(&env_proxy, "ALL_PROXY", pool.getAPRPool()); - if (status != APR_SUCCESS) - { - status = apr_env_get(&env_proxy, "http_proxy", pool.getAPRPool()); - } - if (status != APR_SUCCESS) - { - use_proxy = FALSE; - } - } - - - LL_DEBUGS() << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (env_proxy ? env_proxy : "(null)") << LL_ENDL; - - if (env_proxy && use_proxy) - { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy); - } - else - { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, ""); - } -} - -void LLURLRequest::useProxy(const std::string &proxy) -{ - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy); -} - -void LLURLRequest::allowCookies() -{ - mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, ""); -} - -//virtual -bool LLURLRequest::isValid() -{ - return mDetail->mCurlRequest && mDetail->mCurlRequest->isValid(); -} - -// virtual -LLIOPipe::EStatus LLURLRequest::handleError( - LLIOPipe::EStatus status, - LLPumpIO* pump) -{ - if(!isValid()) - { - return STATUS_EXPIRED ; - } - - if(mCompletionCallback && pump) - { - LLURLRequestComplete* complete = NULL; - complete = (LLURLRequestComplete*)mCompletionCallback.get(); - complete->httpStatus( - HTTP_INTERNAL_ERROR, - LLIOPipe::lookupStatusString(status)); - complete->responseStatus(status); - pump->respond(complete); - mCompletionCallback = NULL; - } - return status; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_REQUEST("URL Request"); -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result"); -static LLTrace::BlockTimerStatHandle FTM_URL_PERFORM("Perform"); -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_PUMP_RESPOND("Pump Respond"); -static LLTrace::BlockTimerStatHandle FTM_URL_ADJUST_TIMEOUT("Adjust Timeout"); - -// virtual -LLIOPipe::EStatus LLURLRequest::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_REQUEST); - PUMP_DEBUG; - //LL_INFOS() << "LLURLRequest::process_impl()" << LL_ENDL; - if (!buffer) return STATUS_ERROR; - - // we're still waiting or prcessing, check how many - // bytes we have accumulated. - const S32 MIN_ACCUMULATION = 100000; - if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) - { - LL_RECORD_BLOCK_TIME(FTM_URL_ADJUST_TIMEOUT); - // This is a pretty sloppy calculation, but this - // tries to make the gross assumption that if data - // is coming in at 56kb/s, then this transfer will - // probably succeed. So, if we're accumlated - // 100,000 bytes (MIN_ACCUMULATION) then let's - // give this client another 2s to complete. - const F32 TIMEOUT_ADJUSTMENT = 2.0f; - mDetail->mByteAccumulator = 0; - pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT); - LL_DEBUGS() << "LLURLRequest adjustTimeoutSeconds for request: " << mDetail->mURL << LL_ENDL; - if (mState == STATE_INITIALIZED) - { - LL_INFOS() << "LLURLRequest adjustTimeoutSeconds called during upload" << LL_ENDL; - } - } - - switch(mState) - { - case STATE_INITIALIZED: - { - PUMP_DEBUG; - // We only need to wait for input if we are uploading - // something. - if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos) - { - // we're waiting to get all of the information - return STATUS_BREAK; - } - - // *FIX: bit of a hack, but it should work. The configure and - // callback method expect this information to be ready. - mDetail->mResponseBuffer = buffer; - mDetail->mChannels = channels; - if(!configure()) - { - return STATUS_ERROR; - } - mState = STATE_WAITING_FOR_RESPONSE; - - // *FIX: Maybe we should just go to the next state now... - return STATUS_BREAK; - } - case STATE_WAITING_FOR_RESPONSE: - case STATE_PROCESSING_RESPONSE: - { - PUMP_DEBUG; - LLIOPipe::EStatus status = STATUS_BREAK; - { - LL_RECORD_BLOCK_TIME(FTM_URL_PERFORM); - if(!mDetail->mCurlRequest->wait()) - { - return status ; - } - } - - bool keep_looping = true; - while(keep_looping) - { - CURLcode result; - - bool newmsg = false; - { - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_REQUEST_GET_RESULT); - newmsg = mDetail->mCurlRequest->getResult(&result); - } - - if(!newmsg) - { - // keep processing - break; - } - - - mState = STATE_HAVE_RESPONSE; - context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; - context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; - LL_DEBUGS() << this << "Setting context to " << context << LL_ENDL; - switch(result) - { - case CURLE_OK: - case CURLE_WRITE_ERROR: - // NB: The error indication means that we stopped the - // writing due the body limit being reached - if(mCompletionCallback && pump) - { - LLURLRequestComplete* complete = NULL; - complete = (LLURLRequestComplete*) - mCompletionCallback.get(); - complete->responseStatus( - result == CURLE_OK - ? STATUS_OK : STATUS_STOP); - LLPumpIO::links_t chain; - LLPumpIO::LLLinkInfo link; - link.mPipe = mCompletionCallback; - link.mChannels = LLBufferArray::makeChannelConsumer( - channels); - chain.push_back(link); - { - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_PUMP_RESPOND); - pump->respond(chain, buffer, context); - } - mCompletionCallback = NULL; - } - break; - case CURLE_FAILED_INIT: - case CURLE_COULDNT_CONNECT: - status = STATUS_NO_CONNECTION; - keep_looping = false; - break; - default: // CURLE_URL_MALFORMAT - LL_WARNS() << "URLRequest Error: " << result - << ", " - << LLCurl::strerror(result) - << ", " - << (mDetail->mURL.empty() ? "<EMPTY URL>" : mDetail->mURL) - << LL_ENDL; - status = STATUS_ERROR; - keep_looping = false; - break; - } - } - return status; - } - case STATE_HAVE_RESPONSE: - PUMP_DEBUG; - // we already stuffed everything into channel in in the curl - // callback, so we are done. - eos = true; - context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; - context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; - LL_DEBUGS() << this << "Setting context to " << context << LL_ENDL; - return STATUS_DONE; - - default: - PUMP_DEBUG; - context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; - context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; - LL_DEBUGS() << this << "Setting context to " << context << LL_ENDL; - return STATUS_ERROR; - } -} - -void LLURLRequest::initialize() -{ - mState = STATE_INITIALIZED; - mDetail = new LLURLRequestDetail; - - if(!isValid()) - { - return ; - } - - mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); - mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this); - mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this); - mRequestTransferedBytes = 0; - mResponseTransferedBytes = 0; -} - -static LLTrace::BlockTimerStatHandle FTM_URL_REQUEST_CONFIGURE("URL Configure"); -bool LLURLRequest::configure() -{ - LL_RECORD_BLOCK_TIME(FTM_URL_REQUEST_CONFIGURE); - - bool rv = false; - S32 bytes = mDetail->mResponseBuffer->countAfter( - mDetail->mChannels.in(), - NULL); - switch(mAction) - { - case HTTP_HEAD: - mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1); - mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1); - if (mFollowRedirects) - { - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); - } - rv = true; - break; - case HTTP_GET: - mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1); - if (mFollowRedirects) - { - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); - } - - // Set Accept-Encoding to allow response compression - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - rv = true; - break; - - case HTTP_PUT: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader(HTTP_OUT_HEADER_EXPECT); - - mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1); - mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes); - rv = true; - break; - - case HTTP_PATCH: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader(HTTP_OUT_HEADER_EXPECT); - - mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1); - mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes); - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "PATCH"); - rv = true; - break; - - case HTTP_POST: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader(HTTP_OUT_HEADER_EXPECT); - - // Disable the content type http header. - // *FIX: what should it be? - addHeader(HTTP_OUT_HEADER_CONTENT_TYPE); - - // Set the handle for an http post - mDetail->mCurlRequest->setPost(NULL, bytes); - - // Set Accept-Encoding to allow response compression - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - rv = true; - break; - - case HTTP_DELETE: - // Set the handle for an http delete - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE"); - rv = true; - break; - - case HTTP_COPY: - // Set the handle for an http copy - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "COPY"); - rv = true; - break; - - case HTTP_MOVE: - // Set the handle for an http move - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE"); - // *NOTE: should we check for the Destination header? - rv = true; - break; - - default: - LL_WARNS() << "Unhandled URLRequest action: " << mAction << LL_ENDL; - break; - } - if(rv) - { - mDetail->mCurlRequest->sendRequest(mDetail->mURL); - } - return rv; -} - -// static -size_t LLURLRequest::downCallback( - char* data, - size_t size, - size_t nmemb, - void* user) -{ - LLURLRequest* req = (LLURLRequest*)user; - if(STATE_WAITING_FOR_RESPONSE == req->mState) - { - req->mState = STATE_PROCESSING_RESPONSE; - } - U32 bytes = size * nmemb; - if (req->mDetail->mIsBodyLimitSet) - { - if (bytes > req->mDetail->mBodyLimit) - { - bytes = req->mDetail->mBodyLimit; - req->mDetail->mBodyLimit = 0; - } - else - { - req->mDetail->mBodyLimit -= bytes; - } - } - - req->mDetail->mResponseBuffer->append( - req->mDetail->mChannels.out(), - (U8*)data, - bytes); - req->mResponseTransferedBytes += bytes; - req->mDetail->mByteAccumulator += bytes; - return bytes; -} - -// static -size_t LLURLRequest::upCallback( - char* data, - size_t size, - size_t nmemb, - void* user) -{ - LLURLRequest* req = (LLURLRequest*)user; - S32 bytes = llmin( - (S32)(size * nmemb), - req->mDetail->mResponseBuffer->countAfter( - req->mDetail->mChannels.in(), - req->mDetail->mLastRead)); - req->mDetail->mLastRead = req->mDetail->mResponseBuffer->readAfter( - req->mDetail->mChannels.in(), - req->mDetail->mLastRead, - (U8*)data, - bytes); - req->mRequestTransferedBytes += bytes; - return bytes; -} - -static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user) -{ - const char* header_line = (const char*)data; - size_t header_len = size * nmemb; - LLURLRequestComplete* complete = (LLURLRequestComplete*)user; - - if (!complete || !header_line) - { - return header_len; - } - - // *TODO: This should be a utility in llstring.h: isascii() - for (size_t i = 0; i < header_len; ++i) - { - if (header_line[i] < 0) - { - return header_len; - } - } - - std::string header(header_line, header_len); - - // Per HTTP spec the first header line must be the status line. - if (header.substr(0,5) == "HTTP/") - { - std::string::iterator end = header.end(); - std::string::iterator pos1 = std::find(header.begin(), end, ' '); - if (pos1 != end) ++pos1; - std::string::iterator pos2 = std::find(pos1, end, ' '); - if (pos2 != end) ++pos2; - std::string::iterator pos3 = std::find(pos2, end, '\r'); - - std::string version(header.begin(), pos1); - std::string status(pos1, pos2); - std::string reason(pos2, pos3); - - S32 status_code = atoi(status.c_str()); - if (status_code > 0) - { - complete->httpStatus(status_code, reason); - return header_len; - } - } - - std::string::iterator sep = std::find(header.begin(),header.end(),':'); - - if (sep != header.end()) - { - std::string key(header.begin(), sep); - std::string value(sep + 1, header.end()); - - key = utf8str_tolower(utf8str_trim(key)); - value = utf8str_trim(value); - - complete->header(key, value); - } - else - { - LLStringUtil::trim(header); - if (!header.empty()) - { - LL_WARNS() << "Unable to parse header: " << header << LL_ENDL; - } - } - - return header_len; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_EXTRACTOR("URL Extractor"); -/** - * LLContextURLExtractor - */ -// virtual -LLIOPipe::EStatus LLContextURLExtractor::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_EXTRACTOR); - PUMP_DEBUG; - // The destination host is in the context. - if(context.isUndefined() || !mRequest) - { - return STATUS_PRECONDITION_NOT_MET; - } - - // copy in to out, since this just extract the URL and does not - // actually change the data. - LLChangeChannel change(channels.in(), channels.out()); - std::for_each(buffer->beginSegment(), buffer->endSegment(), change); - - // find the context url - if(context.has(CONTEXT_DEST_URI_SD_LABEL)) - { - mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL].asString()); - return STATUS_DONE; - } - return STATUS_ERROR; -} - - -/** - * LLURLRequestComplete - */ -LLURLRequestComplete::LLURLRequestComplete() : - mRequestStatus(LLIOPipe::STATUS_ERROR) -{ -} - -// virtual -LLURLRequestComplete::~LLURLRequestComplete() -{ -} - -//virtual -void LLURLRequestComplete::header(const std::string& header, const std::string& value) -{ -} - -//virtual -void LLURLRequestComplete::complete(const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer) -{ - if(STATUS_OK == mRequestStatus) - { - response(channels, buffer); - } - else - { - noResponse(); - } -} - -//virtual -void LLURLRequestComplete::response(const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer) -{ - LL_WARNS() << "LLURLRequestComplete::response default implementation called" - << LL_ENDL; -} - -//virtual -void LLURLRequestComplete::noResponse() -{ - LL_WARNS() << "LLURLRequestComplete::noResponse default implementation called" - << LL_ENDL; -} - -void LLURLRequestComplete::responseStatus(LLIOPipe::EStatus status) -{ - mRequestStatus = status; -} - -static LLTrace::BlockTimerStatHandle FTM_PROCESS_URL_COMPLETE("URL Complete"); -// virtual -LLIOPipe::EStatus LLURLRequestComplete::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LL_RECORD_BLOCK_TIME(FTM_PROCESS_URL_COMPLETE); - PUMP_DEBUG; - complete(channels, buffer); - return STATUS_OK; -} -#endif diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h deleted file mode 100755 index e71a4c1d01..0000000000 --- a/indra/llmessage/llurlrequest.h +++ /dev/null @@ -1,357 +0,0 @@ -/** - * @file llurlrequest.h - * @author Phoenix - * @date 2005-04-21 - * @brief Declaration of url based requests on pipes. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#ifndef LL_LLURLREQUEST_H -#define LL_LLURLREQUEST_H - -/** - * This file holds the declaration of useful classes for dealing with - * url based client requests. - */ - -#include <string> -#include "lliopipe.h" -#include "llchainio.h" -#include "llerror.h" -#include <openssl/x509_vfy.h> -#include "llcurl.h" - -#if 0 -/** - * External constants - */ -extern const std::string CONTEXT_DEST_URI_SD_LABEL; -extern const std::string CONTEXT_TRANSFERED_BYTES; - -class LLURLRequestDetail; - -class LLURLRequestComplete; - -/** - * @class LLURLRequest - * @brief Class to handle url based requests. - * @see LLIOPipe - * - * Currently, this class is implemented on top of curl. From the - * vantage of a programmer using this class, you do not care so much, - * but it's useful to know since in order to accomplish 'non-blocking' - * behavior, we have to use a more expensive curl interface which can - * still block if the server enters a half-accepted state. It would be - * worth the time and effort to eventually port this to a raw client - * socket. - */ -class LLURLRequest : public LLIOPipe -{ - LOG_CLASS(LLURLRequest); -public: - typedef int (* SSLCertVerifyCallback)(X509_STORE_CTX *ctx, void *param); - - /** - * @brief Constructor. - * - * @param action One of the EHTTPMethod enumerations. - */ - LLURLRequest(EHTTPMethod action, bool follow_redirects = true); - - /** - * @brief Constructor. - * - * @param action One of the EHTTPMethod enumerations. - * @param url The url of the request. It should already be encoded. - */ - LLURLRequest(EHTTPMethod action, const std::string& url, bool follow_redirects = true); - - /** - * @brief Destructor. - */ - virtual ~LLURLRequest(); - - /* @name Instance methods - */ - //@{ - /** - * @brief Set the url for the request - * - * This method assumes the url is encoded appropriately for the - * request. - * The url must be set somehow before the first call to process(), - * or the url will not be set correctly. - * - */ - void setURL(const std::string& url); - const std::string& getURL() const; - /** - * @brief Add a header to the http post. - * - * This provides a raw interface if you know what kind of request you - * will be making during construction of this instance. All - * required headers will be automatically constructed, so this is - * usually useful for encoding parameters. - */ - void addHeader(const std::string& header, const std::string& value = ""); - void addHeaderRaw(const char* header); - - /** - * @brief Check remote server certificate signed by a known root CA. - * - * Set whether request will check that remote server - * certificates are signed by a known root CA when using HTTPS. - */ - void setSSLVerifyCallback(SSLCertVerifyCallback callback, void * param); - - - /** - * @brief Return at most size bytes of body. - * - * If the body had more bytes than this limit, they will not be - * returned and the connection closed. In this case, STATUS_STOP - * will be passed to responseStatus(); - */ - void setBodyLimit(U32 size); - - /** - * @brief Set a completion callback for this URLRequest. - * - * The callback is added to this URLRequet's pump when either the - * entire buffer is known or an error like timeout or connection - * refused has happened. In the case of a complete transfer, this - * object builds a response chain such that the callback and the - * next process consumer get to read the output. - * - * This setup is a little fragile since the url request consumer - * might not just read the data - it may do a channel change, - * which invalidates the input to the callback, but it works well - * in practice. - */ - void setCallback(LLURLRequestComplete* callback); - //@} - - /* @name LLIOPipe virtual implementations - */ - - /** - * @ brief Turn off (or on) the CURLOPT_PROXY header. - */ - void useProxy(bool use_proxy); - - /** - * @ brief Set the CURLOPT_PROXY header to the given value. - */ - void useProxy(const std::string& proxy); - - /** - * @brief Turn on cookie handling for this request with CURLOPT_COOKIEFILE. - */ - void allowCookies(); - - /*virtual*/ bool isValid() ; - -public: - /** - * @brief Give this pipe a chance to handle a generated error - */ - virtual EStatus handleError(EStatus status, LLPumpIO* pump); - - -protected: - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - enum EState - { - STATE_INITIALIZED, - STATE_WAITING_FOR_RESPONSE, - STATE_PROCESSING_RESPONSE, - STATE_HAVE_RESPONSE, - }; - EState mState; - EHTTPMethod mAction; - bool mFollowRedirects; - LLURLRequestDetail* mDetail; - LLIOPipe::ptr_t mCompletionCallback; - S32 mRequestTransferedBytes; - S32 mResponseTransferedBytes; - - static CURLcode _sslCtxCallback(CURL * curl, void *sslctx, void *param); - -private: - /** - * @brief Initialize the object. Called during construction. - */ - void initialize(); - - /** - * @brief Handle action specific url request configuration. - * - * @return Returns true if this is configured. - */ - bool configure(); - - /** - * @brief Download callback method. - */ - static size_t downCallback( - char* data, - size_t size, - size_t nmemb, - void* user); - - /** - * @brief Upload callback method. - */ - static size_t upCallback( - char* data, - size_t size, - size_t nmemb, - void* user); - - /** - * @brief Declaration of unimplemented method to prevent copy - * construction. - */ - LLURLRequest(const LLURLRequest&); -}; - - -/** - * @class LLContextURLExtractor - * @brief This class unpacks the url out of a agent usher service so - * it can be packed into a LLURLRequest object. - * @see LLIOPipe - * - * This class assumes that the context is a map that contains an entry - * named CONTEXT_DEST_URI_SD_LABEL. - */ -class LLContextURLExtractor : public LLIOPipe -{ -public: - LLContextURLExtractor(LLURLRequest* req) : mRequest(req) {} - ~LLContextURLExtractor() {} - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - LLURLRequest* mRequest; -}; - - -/** - * @class LLURLRequestComplete - * @brief Class which can optionally be used with an LLURLRequest to - * get notification when the url request is complete. - */ -class LLURLRequestComplete : public LLIOPipe -{ -public: - - // Called once for each header received, except status lines - virtual void header(const std::string& header, const std::string& value); - - // May be called more than once, particularly for redirects and proxy madness. - // Ex. a 200 for a connection to https through a proxy, followed by the "real" status - // a 3xx for a redirect followed by a "real" status, or more redirects. - virtual void httpStatus(S32 status, const std::string& reason) { } - - virtual void complete( - const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer); - - /** - * @brief This method is called when we got a valid response. - * - * It is up to class implementers to do something useful here. - */ - virtual void response( - const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer); - - /** - * @brief This method is called if there was no response. - * - * It is up to class implementers to do something useful here. - */ - virtual void noResponse(); - - /** - * @brief This method will be called by the LLURLRequest object. - * - * If this is set to STATUS_OK or STATUS_STOP, then the transfer - * is asssumed to have worked. This will lead to calling response() - * on the next call to process(). Otherwise, this object will call - * noResponse() on the next call to process. - * @param status The status of the URLRequest. - */ - void responseStatus(EStatus status); - - // constructor & destructor. - LLURLRequestComplete(); - virtual ~LLURLRequestComplete(); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - - // value to note if we actually got the response. This value - // depends on correct usage from the LLURLRequest instance. - EStatus mRequestStatus; -}; -#endif -#endif // LL_LLURLREQUEST_H diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 16dac4a9e5..45e21d9129 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1556,11 +1556,6 @@ bool LLAppViewer::mainLoop() } gMeshRepo.update() ; - if(!LLCurl::getCurlThread()->update(1)) - { - LLCurl::getCurlThread()->pause() ; //nothing in the curl thread. - } - if(!total_work_pending) //pause texture fetching threads if nothing to process. { LLAppViewer::getTextureCache()->pause(); @@ -1998,7 +1993,6 @@ bool LLAppViewer::cleanup() pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread pending += LLVFSThread::updateClass(0); pending += LLLFSThread::updateClass(0); - pending += LLCurl::getCurlThread()->update(1) ; F64 idle_time = idleTimer.getElapsedTimeF64(); if(!pending) { @@ -2010,7 +2004,6 @@ bool LLAppViewer::cleanup() break; } } - LLCurl::getCurlThread()->pause() ; // Delete workers first // shotdown all worker threads before deleting them in case of co-dependencies @@ -2025,11 +2018,6 @@ bool LLAppViewer::cleanup() LL_INFOS() << "Shutting down message system" << LL_ENDL; end_messaging_system(); - // *NOTE:Mani - The following call is not thread safe. - LL_CHECK_MEMORY - LLCurl::cleanupClass(); - LL_CHECK_MEMORY - // Non-LLCurl libcurl library mAppCoreHttp.cleanup(); diff --git a/indra/test/io.cpp b/indra/test/io.cpp index 099331b727..ff900ab96b 100755 --- a/indra/test/io.cpp +++ b/indra/test/io.cpp @@ -41,8 +41,6 @@ #include "llpipeutil.h" #include "llpumpio.h" #include "llsd.h" -#include "llsdrpcclient.h" -#include "llsdrpcserver.h" #include "llsdserialize.h" #include "llcommon.h" #include "lluuid.h" @@ -1177,426 +1175,6 @@ namespace tut } } -#if 0 -namespace tut -{ - struct rpc_server_data - { - class LLSimpleRPCResponse : public LLSDRPCResponse - { - public: - LLSimpleRPCResponse(LLSD* response) : - mResponsePtr(response) - { - } - ~LLSimpleRPCResponse() - { - } - virtual bool response(LLPumpIO* pump) - { - *mResponsePtr = mReturnValue; - return true; - } - virtual bool fault(LLPumpIO* pump) - { - *mResponsePtr = mReturnValue; - return false; - } - virtual bool error(LLPumpIO* pump) - { - ensure("LLSimpleRPCResponse::error()", false); - return false; - } - public: - LLSD* mResponsePtr; - }; - - class LLSimpleRPCClient : public LLSDRPCClient - { - public: - LLSimpleRPCClient(LLSD* response) : - mResponsePtr(response) - { - } - ~LLSimpleRPCClient() {} - void echo(const LLSD& parameter) - { - LLSimpleRPCResponse* resp; - resp = new LLSimpleRPCResponse(mResponsePtr); - static const std::string URI_NONE; - static const std::string METHOD_ECHO("echo"); - call(URI_NONE, METHOD_ECHO, parameter, resp, EPBQ_CALLBACK); - } - public: - LLSD* mResponsePtr; - }; - - class LLSimpleRPCServer : public LLSDRPCServer - { - public: - LLSimpleRPCServer() - { - mMethods["echo"] = new mem_fn_t( - this, - &LLSimpleRPCServer::rpc_Echo); - } - ~LLSimpleRPCServer() {} - protected: - typedef LLSDRPCMethodCall<LLSimpleRPCServer> mem_fn_t; - ESDRPCSStatus rpc_Echo( - const LLSD& parameter, - const LLChannelDescriptors& channels, - LLBufferArray* data) - { - buildResponse(channels, data, parameter); - return ESDRPCS_DONE; - } - }; - - apr_pool_t* mPool; - LLPumpIO* mPump; - LLPumpIO::chain_t mChain; - LLSimpleRPCClient* mClient; - LLSD mResponse; - - rpc_server_data() : - mPool(NULL), - mPump(NULL), - mClient(NULL) - { - apr_pool_create(&mPool, NULL); - mPump = new LLPumpIO(mPool); - mClient = new LLSimpleRPCClient(&mResponse); - mChain.push_back(LLIOPipe::ptr_t(mClient)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD)); - mChain.push_back(LLIOPipe::ptr_t(new LLSimpleRPCServer)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD)); - mChain.push_back(LLIOPipe::ptr_t(mClient)); - } - ~rpc_server_data() - { - mChain.clear(); - delete mPump; - mPump = NULL; - apr_pool_destroy(mPool); - mPool = NULL; - } - void pump_loop(const LLSD& request) - { - LLTimer timer; - timer.setTimerExpirySec(1.0f); - mClient->echo(request); - mPump->addChain(mChain, DEFAULT_CHAIN_EXPIRY_SECS); - while(mResponse.isUndefined() && !timer.hasExpired()) - { - mPump->pump(); - mPump->callback(); - } - } - }; - typedef test_group<rpc_server_data> rpc_server_test; - typedef rpc_server_test::object rpc_server_object; - tut::rpc_server_test rpc("rpc_server"); - - template<> template<> - void rpc_server_object::test<1>() - { - LLSD request; - request = 1; - pump_loop(request); - //LL_INFOS() << "request: " << *request << LL_ENDL; - //LL_INFOS() << "response: " << *mResponse << LL_ENDL; - ensure_equals("integer request response", mResponse.asInteger(), 1); - } - - template<> template<> - void rpc_server_object::test<2>() - { -//#if LL_WINDOWS && _MSC_VER >= 1400 -// skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser."); -//#endif - std::string uri("sl-am:66.150.244.180:12035/location?start=region&px=70.9247&py=254.378&pz=38.7304&lx=-0.043753&ly=-0.999042&lz=0"); - std::stringstream stream; - stream << "{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n}"; - std::vector<U8> expected_binary; - expected_binary.resize(stream.str().size()); - memcpy(&expected_binary[0], stream.str().c_str(), stream.str().size()); /* Flawfinder: ignore */ - stream.str(""); - stream << "[{'uri':'" << uri << "'}, {'version':i1}, " - << "{'agent_id':'3c115e51-04f4-523c-9fa6-98aff1034730', 'session_id':'2c585cec-038c-40b0-b42e-a25ebab4d132', 'circuit_code':i1075, 'start':'region', 'limited_to_estate':i1 'first_name':'Phoenix', 'last_name':'Linden', 'group_title':'', 'group_id':u00000000-0000-0000-0000-000000000000, 'position':[r70.9247,r254.378,r38.7304], 'look_at':[r-0.043753,r-0.999042,r0], 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], 'texture_data':['5e481e8a-58a6-fc34-6e61-c7a36095c07f', 'c39675f5-ca90-a304-bb31-42cdb803a132', '5c989edf-88d1-b2ac-b00b-5ed4bab8e368', '6522e74d-1660-4e7f-b601-6f48c1659a77', '7ca39b4c-bd19-4699-aff7-f93fd03d3e7b', '41c58177-5eb6-5aeb-029d-bc4093f3c130', '97b75473-8b93-9b25-2a11-035b9ae93195', '1c2d8d9b-90eb-89d4-dea8-c1ed83990614', '69ec543f-e27b-c07c-9094-a8be6300f274', 'c9f8b80f-c629-4633-04ee-c566ce9fea4b', '989cddba-7ab6-01ed-67aa-74accd2a2a65', '45e319b2-6a8c-fa5c-895b-1a7149b88aef', '5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', '406f98fd-9c89-1d52-5f39-e67d508c5ee5', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'], " - << "'attachment_data':[" - << "{'attachment_point':i2, 'item_id':'d6852c11-a74e-309a-0462-50533f1ef9b3', 'asset_id':'c69b29b1-8944-58ae-a7c5-2ca7b23e22fb'}," - << "{'attachment_point':i10, 'item_id':'ff852c22-a74e-309a-0462-50533f1ef900', 'asset_data':b(" << expected_binary.size() << ")\""; - stream.write((const char*)&expected_binary[0], expected_binary.size()); - stream << "\"}" - << "]" - << "}]"; - - LLSD request; - S32 count = LLSDSerialize::fromNotation( - request, - stream, - stream.str().size()); - ensure("parsed something", (count > 0)); - - pump_loop(request); - ensure_equals("return type", mResponse.type(), LLSD::TypeArray); - ensure_equals("return size", mResponse.size(), 3); - - ensure_equals( - "uri parameter type", - mResponse[0].type(), - LLSD::TypeMap); - ensure_equals( - "uri type", - mResponse[0]["uri"].type(), - LLSD::TypeString); - ensure_equals("uri value", mResponse[0]["uri"].asString(), uri); - - ensure_equals( - "version parameter type", - mResponse[1].type(), - LLSD::TypeMap); - ensure_equals( - "version type", - mResponse[1]["version"].type(), - LLSD::TypeInteger); - ensure_equals( - "version value", - mResponse[1]["version"].asInteger(), - 1); - - ensure_equals("agent params type", mResponse[2].type(), LLSD::TypeMap); - LLSD attachment_data = mResponse[2]["attachment_data"]; - ensure("attachment data exists", attachment_data.isDefined()); - ensure_equals( - "attachment type", - attachment_data.type(), - LLSD::TypeArray); - ensure_equals( - "attachment type 0", - attachment_data[0].type(), - LLSD::TypeMap); - ensure_equals( - "attachment type 1", - attachment_data[1].type(), - LLSD::TypeMap); - ensure_equals("attachment size 1", attachment_data[1].size(), 3); - ensure_equals( - "asset data type", - attachment_data[1]["asset_data"].type(), - LLSD::TypeBinary); - std::vector<U8> actual_binary; - actual_binary = attachment_data[1]["asset_data"].asBinary(); - ensure_equals( - "binary data size", - actual_binary.size(), - expected_binary.size()); - ensure( - "binary data", - (0 == memcmp( - &actual_binary[0], - &expected_binary[0], - expected_binary.size()))); - } - - template<> template<> - void rpc_server_object::test<3>() - { -//#if LL_WINDOWS && _MSC_VER >= 1400 -// skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser."); -//#endif - std::string uri("sl-am:66.150.244.180:12035/location?start=region&px=70.9247&py=254.378&pz=38.7304&lx=-0.043753&ly=-0.999042&lz=0"); - - LLBufferArray buffer; - LLChannelDescriptors buffer_channels = buffer.nextChannel(); - LLBufferStream stream(buffer_channels, &buffer); - stream << "[{'uri':'" << uri << "'}, {'version':i1}, " - << "{'agent_id':'3c115e51-04f4-523c-9fa6-98aff1034730', 'session_id':'2c585cec-038c-40b0-b42e-a25ebab4d132', 'circuit_code':i1075, 'start':'region', 'limited_to_estate':i1 'first_name':'Phoenix', 'last_name':'Linden', 'group_title':'', 'group_id':u00000000-0000-0000-0000-000000000000, 'position':[r70.9247,r254.378,r38.7304], 'look_at':[r-0.043753,r-0.999042,r0], 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], 'texture_data':['5e481e8a-58a6-fc34-6e61-c7a36095c07f', 'c39675f5-ca90-a304-bb31-42cdb803a132', '5c989edf-88d1-b2ac-b00b-5ed4bab8e368', '6522e74d-1660-4e7f-b601-6f48c1659a77', '7ca39b4c-bd19-4699-aff7-f93fd03d3e7b', '41c58177-5eb6-5aeb-029d-bc4093f3c130', '97b75473-8b93-9b25-2a11-035b9ae93195', '1c2d8d9b-90eb-89d4-dea8-c1ed83990614', '69ec543f-e27b-c07c-9094-a8be6300f274', 'c9f8b80f-c629-4633-04ee-c566ce9fea4b', '989cddba-7ab6-01ed-67aa-74accd2a2a65', '45e319b2-6a8c-fa5c-895b-1a7149b88aef', '5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', '406f98fd-9c89-1d52-5f39-e67d508c5ee5', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'], " - << "'attachment_data':[" - << "{'attachment_point':i2, 'item_id':'d6852c11-a74e-309a-0462-50533f1ef9b3', 'asset_id':'c69b29b1-8944-58ae-a7c5-2ca7b23e22fb'},"; - - std::stringstream tmp_str; - tmp_str << "{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n}"; - std::vector<U8> expected_binary; - expected_binary.resize(tmp_str.str().size()); - memcpy( /* Flawfinder: ignore */ - &expected_binary[0], - tmp_str.str().c_str(), - tmp_str.str().size()); - - LLBufferArray attachment_buffer; - LLChannelDescriptors attach_channels = attachment_buffer.nextChannel(); - LLBufferStream attach_stream(attach_channels, &attachment_buffer); - attach_stream.write((const char*)&expected_binary[0], expected_binary.size()); - attach_stream.flush(); - S32 len = attachment_buffer.countAfter(attach_channels.out(), NULL); - stream << "{'attachment_point':i10, 'item_id':'ff852c22-a74e-309a-0462-50533f1ef900', 'asset_data':b(" << len << ")\""; - stream.flush(); - buffer.takeContents(attachment_buffer); - stream << "\"}]}]"; - stream.flush(); - - LLChannelDescriptors read_channel = buffer.nextChannel(); - LLBufferStream read_stream(read_channel, &buffer); - LLSD request; - S32 count = LLSDSerialize::fromNotation( - request, - read_stream, - buffer.count(read_channel.in())); - ensure("parsed something", (count > 0)); - ensure("deserialized", request.isDefined()); - - // do the rpc round trip - pump_loop(request); - - ensure_equals("return type", mResponse.type(), LLSD::TypeArray); - ensure_equals("return size", mResponse.size(), 3); - - LLSD child = mResponse[0]; - ensure("uri map exists", child.isDefined()); - ensure_equals("uri parameter type", child.type(), LLSD::TypeMap); - ensure("uri string exists", child.has("uri")); - ensure_equals("uri type", child["uri"].type(), LLSD::TypeString); - ensure_equals("uri value", child["uri"].asString(), uri); - - child = mResponse[1]; - ensure("version map exists", child.isDefined()); - ensure_equals("version param type", child.type(), LLSD::TypeMap); - ensure_equals( - "version type", - child["version"].type(), - LLSD::TypeInteger); - ensure_equals("version value", child["version"].asInteger(), 1); - - child = mResponse[2]; - ensure("agent params map exists", child.isDefined()); - ensure_equals("agent params type", child.type(), LLSD::TypeMap); - child = child["attachment_data"]; - ensure("attachment data exists", child.isDefined()); - ensure_equals("attachment type", child.type(), LLSD::TypeArray); - LLSD attachment = child[0]; - ensure_equals("attachment type 0", attachment.type(), LLSD::TypeMap); - attachment = child[1]; - ensure_equals("attachment type 1", attachment.type(), LLSD::TypeMap); - ensure_equals("attachment size 1", attachment.size(), 3); - ensure_equals( - "asset data type", - attachment["asset_data"].type(), - LLSD::TypeBinary); - std::vector<U8> actual_binary = attachment["asset_data"].asBinary(); - ensure_equals( - "binary data size", - actual_binary.size(), - expected_binary.size()); - ensure( - "binary data", - (0 == memcmp( - &actual_binary[0], - &expected_binary[0], - expected_binary.size()))); - } - - template<> template<> - void rpc_server_object::test<4>() - { - std::string message("parcel '' is naughty."); - std::stringstream str; - str << "{'message':'" << LLSDNotationFormatter::escapeString(message) - << "'}"; - LLSD request; - S32 count = LLSDSerialize::fromNotation( - request, - str, - str.str().size()); - ensure_equals("parse count", count, 2); - ensure_equals("request type", request.type(), LLSD::TypeMap); - pump_loop(request); - ensure("valid response", mResponse.isDefined()); - ensure_equals("response type", mResponse.type(), LLSD::TypeMap); - std::string actual = mResponse["message"].asString(); - ensure_equals("message contents", actual, message); - } - - template<> template<> - void rpc_server_object::test<5>() - { - // test some of the problem cases with llsdrpc over xmlrpc - - // for example: - // * arrays are auto-converted to parameter lists, thus, this - // becomes one parameter. - // * undef goes over the wire as false (this might not be a good idea) - // * uuids are converted to string. - std::string val = "[{'failures':!,'successfuls':[u3c115e51-04f4-523c-9fa6-98aff1034730]}]"; - std::istringstream istr; - istr.str(val); - LLSD sd; - LLSDSerialize::fromNotation(sd, istr, val.size()); - pump_loop(sd); - ensure("valid response", mResponse.isDefined()); - ensure_equals("parsed type", mResponse.type(), LLSD::TypeMap); - ensure_equals("parsed size", mResponse.size(), 2); - LLSD failures = mResponse["failures"]; - ensure_equals("no failures.", failures.asBoolean(), false); - LLSD success = mResponse["successfuls"]; - ensure_equals("success type", success.type(), LLSD::TypeArray); - ensure_equals("success size", success.size(), 1); - ensure_equals( - "success instance type", - success[0].type(), - LLSD::TypeString); - } - -/* - template<> template<> - void rpc_server_object::test<5>() - { - std::string expected("\xf3");//\xffsomething"); - LLSD* request = LLSD::createString(expected); - pump_loop(request); - std::string actual; - mResponse->getString(actual); - if(actual != expected) - { - //LL_WARNS() << "iteration " << i << LL_ENDL; - std::ostringstream e_str; - std::string::iterator iter = expected.begin(); - std::string::iterator end = expected.end(); - for(; iter != end; ++iter) - { - e_str << (S32)((U8)(*iter)) << " "; - } - e_str << std::endl; - llsd_serialize_string(e_str, expected); - LL_WARNS() << "expected size: " << expected.size() << LL_ENDL; - LL_WARNS() << "expected: " << e_str.str() << LL_ENDL; - - std::ostringstream a_str; - iter = actual.begin(); - end = actual.end(); - for(; iter != end; ++iter) - { - a_str << (S32)((U8)(*iter)) << " "; - } - a_str << std::endl; - llsd_serialize_string(a_str, actual); - LL_WARNS() << "actual size: " << actual.size() << LL_ENDL; - LL_WARNS() << "actual: " << a_str.str() << LL_ENDL; - } - ensure_equals("binary string request response", actual, expected); - delete request; - } - - template<> template<> - void rpc_server_object::test<5>() - { - } -*/ -} -#endif - /* 'asset_data':b(12100)"{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061088050622956\n\treztime\t1094866329019785\n\tparceltime\t1133568981980596\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':u61fa7364-e151-0597-774c-523312dae31b}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444922\n\ttotal_crc\t324\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.367110789\t0.00780026987\t-0.566269755\n\toldpos\t150.115005\t25.8479004\t8.18669987\n\trotation\t0.47332942485809326171875\t-0.380102097988128662109375\t-0.5734078884124755859375\t0.550168216228485107421875\n\tchildpos\t-0.00499999989\t-0.0370000005\t0.305000007\n\tchildrot\t-0.736649334430694580078125\t-0.03042060509324073791503906\t-0.02784589119255542755126953\t0.67501628398895263671875\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087839248891\n\treztime\t1094866329020800\n\tparceltime\t1133568981981983\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ub8d68643-7dd8-57af-0d24-8790032aed0c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444923\n\ttotal_crc\t235\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.120029509\t-0.00284469454\t-0.0302077383\n\toldpos\t150.710999\t25.8584995\t8.19172001\n\trotation\t0.145459949970245361328125\t-0.1646589934825897216796875\t0.659558117389678955078125\t-0.718826770782470703125\n\tchildpos\t0\t-0.182999998\t-0.26699999\n\tchildrot\t0.991444766521453857421875\t3.271923924330621957778931e-05\t-0.0002416197530692443251609802\t0.1305266767740249633789062\n\tscale\t0.0382982\t0.205957\t0.368276\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087534454174\n\treztime\t1094866329021741\n\tparceltime\t1133568981982889\n\ttax_rate\t1.00326\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ue4b19200-9d33-962f-c8c5-6f25be3a3fd0}\n{\n\tname\tApotheosis_Immolaine_tail|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444924\n\ttotal_crc\t675\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.34780401\t-0.00968400016\t-0.260098994\n\toldpos\t0\t0\t0\n\trotation\t0.73164522647857666015625\t-0.67541944980621337890625\t-0.07733880728483200073242188\t0.05022468417882919311523438\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.0382982\t0.32228\t0.383834\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087463950186\n\treztime\t1094866329022555\n\tparceltime\t1133568981984359\n\tdescription\t(No Description)|\n\ttax_rate\t1.01736\n\tnamevalue\tAttachPt U32 RW S 10\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.110088, -0.182018, 1.493795\n\tnamevalue\tAttachmentOffset VEC3 RW DS -0.347804, -0.009684, -0.260099\n\tnamevalue\tAttachItemID STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n" */ |