From cd17687f01420952712a500107e0f93e7ab8d5f8 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:45:34 -0500 Subject: Second Life viewer sources 1.19.1.0 --- linden/indra/llmessage/llassetstorage.cpp | 59 + linden/indra/llmessage/llassetstorage.h | 26 + linden/indra/llmessage/llbuffer.h | 12 + linden/indra/llmessage/llcachename.cpp | 194 ++- linden/indra/llmessage/llcachename.h | 11 +- linden/indra/llmessage/llcallbacklisth.h | 103 -- linden/indra/llmessage/llcircuit.h | 1 - linden/indra/llmessage/llcurl.cpp | 962 ++++++++++--- linden/indra/llmessage/llcurl.h | 205 ++- linden/indra/llmessage/llfiltersd2xmlrpc.cpp | 4 +- linden/indra/llmessage/llhost.cpp | 12 +- linden/indra/llmessage/llhost.h | 4 +- linden/indra/llmessage/llhttpassetstorage.cpp | 76 +- linden/indra/llmessage/llhttpclient.cpp | 157 +-- linden/indra/llmessage/llhttpclient.h | 71 +- linden/indra/llmessage/llhttpnode.h | 2 +- linden/indra/llmessage/lliohttpserver.cpp | 6 +- linden/indra/llmessage/llmessage.vcproj | 3 - linden/indra/llmessage/llmessage_vc8.vcproj | 1846 ++++++++++++------------ linden/indra/llmessage/llmessage_vc9.vcproj | 1848 ++++++++++++------------- linden/indra/llmessage/llnamevalue.h | 1 - linden/indra/llmessage/llpacketring.h | 1 - linden/indra/llmessage/llsdrpcclient.cpp | 2 +- linden/indra/llmessage/llsdrpcserver.cpp | 5 +- linden/indra/llmessage/llservicebuilder.cpp | 58 +- linden/indra/llmessage/llurlrequest.cpp | 209 +-- linden/indra/llmessage/llurlrequest.h | 26 +- linden/indra/llmessage/llxfermanager.cpp | 46 +- linden/indra/llmessage/llxfermanager.h | 4 +- linden/indra/llmessage/message.h | 2 - 30 files changed, 3273 insertions(+), 2683 deletions(-) delete mode 100644 linden/indra/llmessage/llcallbacklisth.h (limited to 'linden/indra/llmessage') diff --git a/linden/indra/llmessage/llassetstorage.cpp b/linden/indra/llmessage/llassetstorage.cpp index 74b55d9..13b1525 100644 --- a/linden/indra/llmessage/llassetstorage.cpp +++ b/linden/indra/llmessage/llassetstorage.cpp @@ -53,7 +53,10 @@ #include "lltransfersourceasset.h" #include "lltransfertargetvfile.h" // For debugging +#include "llmetrics.h" + LLAssetStorage *gAssetStorage = NULL; +LLMetrics *LLAssetStorage::metric_recipient = NULL; const LLUUID CATEGORIZE_LOST_AND_FOUND_ID("00000000-0000-0000-0000-000000000010"); @@ -1279,6 +1282,8 @@ void LLAssetStorage::storeAssetData( F64 timeout) { llwarns << "storeAssetData: wrong version called" << llendl; + // LLAssetStorage metric: Virtual base call + reportMetric( LLUUID::null, asset_type, NULL, LLUUID::null, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 1" ); } // virtual @@ -1296,6 +1301,8 @@ void LLAssetStorage::storeAssetData( F64 timeout) { llwarns << "storeAssetData: wrong version called" << llendl; + // LLAssetStorage metric: Virtual base call + reportMetric( asset_id, asset_type, NULL, requesting_agent_id, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 2" ); } // virtual @@ -1312,6 +1319,8 @@ void LLAssetStorage::storeAssetData( F64 timeout) { llwarns << "storeAssetData: wrong version called" << llendl; + // LLAssetStorage metric: Virtual base call + reportMetric( asset_id, asset_type, NULL, LLUUID::null, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 3" ); } // virtual @@ -1328,6 +1337,8 @@ void LLAssetStorage::storeAssetData( F64 timeout) { llwarns << "storeAssetData: wrong version called" << llendl; + // LLAssetStorage metric: Virtual base call + reportMetric( LLUUID::null, asset_type, NULL, LLUUID::null, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 4" ); } // static @@ -1372,3 +1383,51 @@ void LLAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const // virtual void LLAssetStorage::clearTempAssetData() { } + +// static +void LLAssetStorage::reportMetric( const LLUUID& asset_id, const LLAssetType::EType asset_type, const char *filename, + const LLUUID& agent_id, S32 asset_size, EMetricResult result, + const char *file, const S32 line, const char *message ) +{ + if( !metric_recipient ) + { + llinfos << "Couldn't store LLAssetStoreage::reportMetric - no metrics_recipient" << llendl; + return; + } + + filename = filename ? filename : ""; + file = file ? file : ""; + + // Create revised message - message = "message :: file:line" + std::string new_message; //( message ); + new_message = message; // << " " << file << " " << line; + new_message += " :: "; + new_message += filename; + char line_string[16]; + sprintf( line_string, ":%d", line ); + new_message += line_string; + message = new_message.c_str(); + + // Change always_report to true if debugging... do not check it in this way + static bool always_report = false; + const char *metric_name = "LLAssetStorage::Metrics"; + + bool success = result == MR_OKAY; + + if( (!success) || always_report ) + { + LLSD stats; + stats["asset_id"] = asset_id; + stats["asset_type"] = asset_type; + stats["filename"] = filename? filename : ""; + stats["agent_id"] = agent_id; + stats["asset_size"] = (S32)asset_size; + stats["result"] = (S32)result; + + metric_recipient->recordEventDetails( metric_name, message, success, stats); + } + else + { + metric_recipient->recordEvent(metric_name, message, success); + } +} diff --git a/linden/indra/llmessage/llassetstorage.h b/linden/indra/llmessage/llassetstorage.h index b7da197..739242a 100644 --- a/linden/indra/llmessage/llassetstorage.h +++ b/linden/indra/llmessage/llassetstorage.h @@ -420,6 +420,32 @@ private: LLXferManager *xfer, LLVFS *vfs, const LLHost &upstream_host); + +protected: + enum EMetricResult + { + // Static valued enums for #dw readability - please copy this + // declaration to them on updates -- source in llassetstorage.h + MR_INVALID = -1, // Makes no sense + MR_OKAY = 0, // Success - no metric normally + MR_ZERO_SIZE = 1, // Zero size asset + MR_BAD_FUNCTION = 2, // Tried to use a virtual base (PROGRAMMER ERROR) + MR_FILE_NONEXIST = 3, // Old format store call - source file does not exist + MR_NO_FILENAME = 4, // Old format store call - source filename is NULL/0-length + MR_NO_UPSTREAM = 5, // Upstream provider is missing + MR_VFS_CORRUPTION = 6 // VFS is corrupt - too-large or mismatched stated/returned sizes + }; + + static class LLMetrics *metric_recipient; + + static void reportMetric( const LLUUID& asset_id, const LLAssetType::EType asset_type, const char *filename, + const LLUUID& agent_id, S32 asset_size, EMetricResult result, + const char *file, const S32 line, const char *message ); +public: + static void setMetricRecipient( LLMetrics *recip ) + { + metric_recipient = recip; + } }; //////////////////////////////////////////////////////////////////////// diff --git a/linden/indra/llmessage/llbuffer.h b/linden/indra/llmessage/llbuffer.h index 4089a55..34d05ae 100644 --- a/linden/indra/llmessage/llbuffer.h +++ b/linden/indra/llmessage/llbuffer.h @@ -411,6 +411,18 @@ public: S32 countAfter(S32 channel, U8* start) const; /** + * @brief Count all bytes on channel. + * + * Helper method which just calls countAfter(). + * @param channel The channel to count. + * @return Returns the number of bytes in the channel. + */ + S32 count(S32 channel) const + { + return countAfter(channel, NULL); + } + + /** * @brief Read bytes in the buffer array on the specified channel * * You should prefer iterating over segments is possible since diff --git a/linden/indra/llmessage/llcachename.cpp b/linden/indra/llmessage/llcachename.cpp index fb6ca5b..6075958 100644 --- a/linden/indra/llmessage/llcachename.cpp +++ b/linden/indra/llmessage/llcachename.cpp @@ -34,19 +34,26 @@ #include "llcachename.h" // linden library includes -#include "message.h" -#include "llrand.h" #include "lldbstrings.h" #include "llframetimer.h" #include "llhost.h" +#include "llrand.h" +#include "llsdserialize.h" #include "lluuid.h" +#include "message.h" // Constants const char* CN_WAITING = "(waiting)"; const char* CN_NOBODY = "(nobody)"; const char* CN_NONE = "(none)"; -const char* CN_HIPPOS = "(hippos)"; -const F32 HIPPO_PROBABILITY = 0.01f; + +// llsd serialization constants +static const std::string AGENTS("agents"); +static const std::string GROUPS("groups"); +static const std::string CTIME("ctime"); +static const std::string FIRST("first"); +static const std::string LAST("last"); +static const std::string NAME("name"); // We track name requests in flight for up to this long. // We won't re-request a name during this time @@ -392,73 +399,123 @@ void LLCacheName::importFile(FILE* fp) llinfos << "LLCacheName loaded " << count << " names" << llendl; } - -void LLCacheName::exportFile(FILE* fp) +bool LLCacheName::importFile(std::istream& istr) { - fprintf(fp, "version\t%d\n", CN_FILE_VERSION); + LLSD data; + if(LLSDSerialize::fromXML(data, istr) < 1) + return false; - for (Cache::iterator iter = impl.mCache.begin(), - end = impl.mCache.end(); - iter != end; iter++) + // We'll expire entries more than a week old + U32 now = (U32)time(NULL); + const U32 SECS_PER_DAY = 60 * 60 * 24; + U32 delete_before_time = now - (7 * SECS_PER_DAY); + + // iterate over the agents + S32 count = 0; + LLSD agents = data[AGENTS]; + LLSD::map_iterator iter = agents.beginMap(); + LLSD::map_iterator end = agents.endMap(); + for( ; iter != end; ++iter) { - LLCacheNameEntry* entry = iter->second; - // Only write entries for which we have valid data. - // HACK: Only write agent names. This makes the reader easier. - if ( entry->mFirstName[0] - && entry->mLastName[0]) - { - LLUUID id = iter->first; + LLUUID id((*iter).first); + LLSD agent = (*iter).second; + U32 ctime = (U32)agent[CTIME].asInteger(); + if(ctime < delete_before_time) continue; - // Trivial XOR encoding - S32 i; - for (i = 0; i < UUID_BYTES; i++) - { - id.mData[i] ^= 0x33; - } + LLCacheNameEntry* entry = new LLCacheNameEntry(); + entry->mIsGroup = false; + entry->mCreateTime = ctime; + std::string first = agent[FIRST].asString(); + first.copy(entry->mFirstName, DB_FIRST_NAME_BUF_SIZE, 0); + entry->mFirstName[llmin(first.size(),(std::string::size_type)DB_FIRST_NAME_BUF_SIZE-1)] = '\0'; + std::string last = agent[LAST].asString(); + last.copy(entry->mLastName, DB_LAST_NAME_BUF_SIZE, 0); + entry->mLastName[llmin(last.size(),(std::string::size_type)DB_LAST_NAME_BUF_SIZE-1)] = '\0'; + impl.mCache[id] = entry; + ++count; + } + llinfos << "LLCacheName loaded " << count << " agent names" << llendl; - char id_string[UUID_STR_SIZE]; /*Flawfinder:ignore*/ - id.toString(id_string); + count = 0; + LLSD groups = data[GROUPS]; + iter = groups.beginMap(); + end = groups.endMap(); + for( ; iter != end; ++iter) + { + LLUUID id((*iter).first); + LLSD group = (*iter).second; + U32 ctime = (U32)group[CTIME].asInteger(); + if(ctime < delete_before_time) continue; - // ...not a group name - fprintf(fp, "%s\t%u\t%s\t%s\n", - id_string, - entry->mCreateTime, - entry->mFirstName, - entry->mLastName); + LLCacheNameEntry* entry = new LLCacheNameEntry(); + entry->mIsGroup = true; + entry->mCreateTime = ctime; + std::string name = group[NAME].asString(); + name.copy(entry->mGroupName, DB_GROUP_NAME_BUF_SIZE, 0); + entry->mGroupName[llmin(name.size(), (std::string::size_type)DB_GROUP_NAME_BUF_SIZE-1)] = '\0'; + impl.mCache[id] = entry; + ++count; + } + llinfos << "LLCacheName loaded " << count << " group names" << llendl; + return true; +} + +void LLCacheName::exportFile(std::ostream& ostr) +{ + LLSD data; + Cache::iterator iter = impl.mCache.begin(); + Cache::iterator end = impl.mCache.end(); + for( ; iter != end; ++iter) + { + // Only write entries for which we have valid data. + LLCacheNameEntry* entry = iter->second; + if(!entry + || (NULL != strchr(entry->mFirstName, '?')) + || (NULL != strchr(entry->mGroupName, '?'))) + { + continue; + } + + // store it + LLUUID id = iter->first; + std::string id_str = id.asString(); + if(entry->mFirstName[0] && entry->mLastName[0]) + { + data[AGENTS][id_str][FIRST] = entry->mFirstName; + data[AGENTS][id_str][LAST] = entry->mLastName; + data[AGENTS][id_str][CTIME] = (S32)entry->mCreateTime; + } + else if(entry->mIsGroup && entry->mGroupName[0]) + { + data[GROUPS][id_str][NAME] = entry->mGroupName; + data[GROUPS][id_str][CTIME] = (S32)entry->mCreateTime; } } + + LLSDSerialize::toPrettyXML(data, ostr); } -BOOL LLCacheName::getName(const LLUUID& id, char* first, char* last) +BOOL LLCacheName::getName(const LLUUID& id, std::string& first, std::string& last) { if(id.isNull()) { - // The function signature needs to change to pass in the - // length of first and last. - strcpy(first, CN_NOBODY); /*Flawfinder: ignore*/ - last[0] = '\0'; + first = CN_NOBODY; + last.clear(); return FALSE; } LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id ); if (entry) { - // The function signature needs to change to pass in the - // length of first and last. - strcpy(first, entry->mFirstName); /*Flawfinder: ignore*/ - strcpy(last, entry->mLastName); /*Flawfinder: ignore*/ + first = entry->mFirstName; + last = entry->mLastName; return TRUE; } else { - //The function signature needs to change to pass in the - //length of first and last. - strcpy(first,(ll_frand() < HIPPO_PROBABILITY) - ? CN_HIPPOS - : CN_WAITING); - strcpy(last, ""); /*Flawfinder: ignore*/ - + first = CN_WAITING; + last.clear(); if (!impl.isRequestPending(id)) { impl.mAskNameQueue.insert(id); @@ -468,15 +525,29 @@ BOOL LLCacheName::getName(const LLUUID& id, char* first, char* last) } +BOOL LLCacheName::getFullName(const LLUUID& id, std::string& fullname) +{ + std::string first_name, last_name; + BOOL res = getName(id, first_name, last_name); + fullname = first_name + " " + last_name; + return res; +} +// *TODO: Deprecate +BOOL LLCacheName::getName(const LLUUID& id, char* first, char* last) +{ + std::string first_name, last_name; + BOOL res = getName(id, first_name, last_name); + strcpy(first, first_name.c_str()); + strcpy(last, last_name.c_str()); + return res; +} -BOOL LLCacheName::getGroupName(const LLUUID& id, char* group) +BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group) { if(id.isNull()) { - // The function signature needs to change to pass in the - // length of first and last. - strcpy(group, CN_NONE); /*Flawfinder: ignore*/ + group = CN_NONE; return FALSE; } @@ -492,16 +563,12 @@ BOOL LLCacheName::getGroupName(const LLUUID& id, char* group) if (entry) { - // The function signature needs to change to pass in the length - // of group. - strcpy(group, entry->mGroupName); /*Flawfinder: ignore*/ + group = entry->mGroupName; return TRUE; } else { - // The function signature needs to change to pass in the length - // of first and last. - strcpy(group, CN_WAITING); /*Flawfinder: ignore*/ + group = CN_WAITING; if (!impl.isRequestPending(id)) { impl.mAskGroupQueue.insert(id); @@ -510,6 +577,16 @@ BOOL LLCacheName::getGroupName(const LLUUID& id, char* group) } } +// *TODO: Deprecate +BOOL LLCacheName::getGroupName(const LLUUID& id, char* group) +{ + std::string group_name; + BOOL res = getGroupName(id, group_name); + strcpy(group, group_name.c_str()); + return res; +} + + // TODO: Make the cache name callback take a SINGLE std::string, // not a separate first and last name. void LLCacheName::get(const LLUUID& id, BOOL is_group, LLCacheNameCallback callback, void* user_data) @@ -884,6 +961,3 @@ void LLCacheName::Impl::handleUUIDGroupNameReply(LLMessageSystem* msg, void** us ((LLCacheName::Impl*)userData)->processUUIDReply(msg, true); } - - - diff --git a/linden/indra/llmessage/llcachename.h b/linden/indra/llmessage/llcachename.h index 5a0a0d7..26a873f 100644 --- a/linden/indra/llmessage/llcachename.h +++ b/linden/indra/llmessage/llcachename.h @@ -63,9 +63,12 @@ public: void cancelCallback(const LLUUID& id, LLCacheNameCallback callback, void* user_data = NULL); - // storing cache on disk; for viewer, in name.cache + // janky old format. Remove after a while. Phoenix. 2008-01-30 void importFile(FILE* fp); - void exportFile(FILE* fp); + + // storing cache on disk; for viewer, in name.cache + bool importFile(std::istream& istr); + void exportFile(std::ostream& ostr); // If available, copies the first and last name into the strings provided. // first must be at least DB_FIRST_NAME_BUF_SIZE characters. @@ -73,12 +76,15 @@ public: // If not available, copies the string "waiting". // Returns TRUE iff available. BOOL getName(const LLUUID& id, char* first, char* last); + BOOL getName(const LLUUID& id, std::string& first, std::string& last); + BOOL getFullName(const LLUUID& id, std::string& fullname); // If available, this method copies the group name into the string // provided. The caller must allocate at least // DB_GROUP_NAME_BUF_SIZE characters. If not available, this // method copies the string "waiting". Returns TRUE iff available. BOOL getGroupName(const LLUUID& id, char* group); + BOOL getGroupName(const LLUUID& id, std::string& group); // Call the callback with the group or avatar name. // If the data is currently available, may call the callback immediatly @@ -104,6 +110,7 @@ public: static LLString getDefaultName(); private: + class Impl; Impl& impl; }; diff --git a/linden/indra/llmessage/llcallbacklisth.h b/linden/indra/llmessage/llcallbacklisth.h deleted file mode 100644 index 9b1e403..0000000 --- a/linden/indra/llmessage/llcallbacklisth.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * @file llcallbacklisth.h - * @brief Like LLCallbackList but uses handles instead of pointers for - * user data - * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2008, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LL_LLCALLBACKLISTH_H -#define LL_LLCALLBACKLISTH_H - -#include "doublelinkedlist.h" - -typedef void(*LLHandleCallback)(void** handle ); - -class LLCallbackNodeH; - -class LLCallbackListH -{ -protected: - class LLCallbackNodeH - { - public: - LLCallbackNodeH( LLHandleCallback cb, void** h ) : mCallback(cb), mHandle(h) {} - void call() { (*mCallback)(mHandle); } - - public: - LLHandleCallback mCallback; - void** mHandle; - }; - -public: - ~LLCallbackListH() - { mCallbackList.deleteAllData(); } - - void addFunction( LLHandleCallback func, void** handle = NULL ) // register a callback, which will be called as func(data) - { mCallbackList.addDataAtEnd( new LLCallbackNodeH( func, handle ) ); } - - BOOL containsFunction( LLHandleCallback func, void** handle = NULL ) // true if list already contains the function/data pair - { - for( LLCallbackNodeH *p = mCallbackList.getFirstData(); p; p = mCallbackList.getNextData() ) - { - if( p->mCallback == func && p->mHandle == handle) - { - return TRUE; - } - } - return FALSE; - } - - BOOL deleteFunction( LLHandleCallback func, void** handle = NULL ) // removes the first instance of this function/data pair from the list, false if not found - { - for( LLCallbackNodeH *p = mCallbackList.getFirstData(); p; p = mCallbackList.getNextData() ) - { - if( p->mCallback == func && p->mHandle == handle) - { - mCallbackList.deleteCurrentData(); - return TRUE; - } - } - return FALSE; - } - - void callFunctions() // calls all functions - { - for( LLCallbackNodeH *p = mCallbackList.getFirstData(); p; p = mCallbackList.getNextData() ) - { - p->call(); - } - } - - void deleteAllFunctions() - { mCallbackList.deleteAllData(); } - -protected: - LLDoubleLinkedList mCallbackList; -}; - -#endif // LL_LLCALLBACKLISTH_H diff --git a/linden/indra/llmessage/llcircuit.h b/linden/indra/llmessage/llcircuit.h index 3f1a46a..552b50f 100644 --- a/linden/indra/llmessage/llcircuit.h +++ b/linden/indra/llmessage/llcircuit.h @@ -37,7 +37,6 @@ #include #include "llerror.h" -#include "linked_lists.h" #include "lltimer.h" #include "timing.h" diff --git a/linden/indra/llmessage/llcurl.cpp b/linden/indra/llmessage/llcurl.cpp index 3969d18..badda64 100644 --- a/linden/indra/llmessage/llcurl.cpp +++ b/linden/indra/llmessage/llcurl.cpp @@ -2,7 +2,7 @@ * @file llcurl.h * @author Zero / Donovan * @date 2006-10-15 - * @brief Curl wrapper + * @brief Implementation of wrapper around libcurl. * * $LicenseInfo:firstyear=2006&license=viewergpl$ * @@ -31,13 +31,29 @@ * $/LicenseInfo$ */ +#if LL_WINDOWS +#define SAFE_SSL 1 +#elif LL_DARWIN +#define SAFE_SSL 1 +#else +#define SAFE_SSL 1 +#endif + #include "linden_common.h" #include "llcurl.h" +#include #include +#include +#if SAFE_SSL +#include +#endif +#include "llbufferstream.h" +#include "llstl.h" #include "llsdserialize.h" +#include "llthread.h" ////////////////////////////////////////////////////////////////////////////// /* @@ -55,40 +71,112 @@ do this. */ -using namespace std; - +////////////////////////////////////////////////////////////////////////////// + +static const S32 EASY_HANDLE_POOL_SIZE = 5; +static const S32 MULTI_PERFORM_CALL_REPEAT = 5; +static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds +static const S32 MAX_ACTIVE_REQUEST_COUNT = 100; + +// DEBUG // +S32 gCurlEasyCount = 0; +S32 gCurlMultiCount = 0; + +////////////////////////////////////////////////////////////////////////////// + +//static +std::vector LLCurl::sSSLMutex; +std::string LLCurl::sCAPath; +std::string LLCurl::sCAFile; + +//static +void LLCurl::setCAPath(const std::string& path) +{ + sCAPath = path; +} + +//static +void LLCurl::setCAFile(const std::string& file) +{ + sCAFile = file; +} + +////////////////////////////////////////////////////////////////////////////// + LLCurl::Responder::Responder() : mReferenceCount(0) { } + LLCurl::Responder::~Responder() { } // virtual -void LLCurl::Responder::error(U32 status, const std::stringstream& content) +void LLCurl::Responder::error(U32 status, const std::string& reason) { - llinfos << "LLCurl::Responder::error " << status << ": " << content.str() << llendl; + llinfos << status << ": " << reason << llendl; } // virtual -void LLCurl::Responder::result(const std::stringstream& content) +void LLCurl::Responder::result(const LLSD& content) { + llwarns << "Virtual Function not implemented" << llendl; } // virtual -void LLCurl::Responder::completed(U32 status, const std::stringstream& content) +void LLCurl::Responder::completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) { - if (200 <= status && status < 300) + if (isGoodStatus(status)) + { + LLSD content; + LLBufferStream istr(channels, buffer.get()); + LLSDSerialize::fromXML(content, istr); +/* + const S32 parseError = -1; + if(LLSDSerialize::fromXML(content, istr) == parseError) + { + mStatus = 498; + mReason = "Client Parse Error"; + } +*/ + completed(status, reason, content); + } + else if (status == 400) + { + // Get reason from buffer + char tbuf[4096]; + S32 len = 4096; + buffer->readAfter(channels.in(), NULL, (U8*)tbuf, len); + tbuf[len] = 0; + completed(status, std::string(tbuf), LLSD()); + } + else + { + completed(status, reason, LLSD()); + } +} + +// virtual +void LLCurl::Responder::completed(U32 status, const std::string& reason, const LLSD& content) +{ + if (isGoodStatus(status)) { result(content); } else { - error(status, content); + error(status, reason); } } +//virtual +void LLCurl::Responder::completedHeader(U32 status, const std::string& reason, const LLSD& content) +{ + +} namespace boost { @@ -106,226 +194,456 @@ namespace boost } }; + ////////////////////////////////////////////////////////////////////////////// -size_t -curlOutputCallback(void* data, size_t size, size_t nmemb, void* user_data) + +class LLCurl::Easy { - stringstream& output = *(stringstream*)user_data; + LOG_CLASS(Easy); + +private: + Easy(); - size_t n = size * nmemb; - output.write((const char*)data, n); - if (!((istream&)output).good()) { - std::cerr << "WHAT!?!?!? istream side bad" << std::endl; - } - if (!((ostream&)output).good()) { - std::cerr << "WHAT!?!?!? ostream side bad" << std::endl; - } +public: + static Easy* getEasy(); + ~Easy(); - return n; -} + CURL* getCurlHandle() const { return mCurlEasyHandle; } -// Only used if request contained a body (post or put), Not currently implemented. -// size_t -// curlRequestCallback(void* data, size_t size, size_t nmemb, void* user_data) -// { -// stringstream& request = *(stringstream*)user_data; + void setErrorBuffer(); + void setCA(); -// size_t n = size * nmemb; -// request.read((char*)data, n); -// return request.gcount(); -// } - + 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 gauranteed to stick around + void setoptString(CURLoption option, const std::string& value); + + void slist_append(const char* str); + void setHeaders(); + + U32 report(CURLcode); + void getTransferInfo(LLCurl::TransferInfo* info); + void prepRequest(const std::string& url, ResponderPtr, 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(); +private: + 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 mStrings; + + ResponderPtr mResponder; +}; LLCurl::Easy::Easy() + : mHeaders(NULL), + mCurlEasyHandle(NULL) { - mHeaders = 0; - mHeaders = curl_slist_append(mHeaders, "Connection: keep-alive"); - mHeaders = curl_slist_append(mHeaders, "Keep-alive: 300"); - mHeaders = curl_slist_append(mHeaders, "Content-Type: application/xml"); - // FIXME: shouldn't be there for GET/DELETE - // FIXME: should have ACCEPT headers - - mHandle = curl_easy_init(); + mErrorBuffer[0] = 0; +} + +LLCurl::Easy* LLCurl::Easy::getEasy() +{ + Easy* easy = new Easy(); + easy->mCurlEasyHandle = curl_easy_init(); + if (!easy->mCurlEasyHandle) + { + // this can happen if we have too many open files (fails in c-ares/ares_init.c) + llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl; + delete easy; + return NULL; + } + ++gCurlEasyCount; + return easy; } LLCurl::Easy::~Easy() { - curl_easy_cleanup(mHandle); + curl_easy_cleanup(mCurlEasyHandle); + --gCurlEasyCount; curl_slist_free_all(mHeaders); + for_each(mStrings.begin(), mStrings.end(), DeletePointer()); } -void -LLCurl::Easy::get(const string& url, ResponderPtr responder) +void LLCurl::Easy::resetState() { - prep(url, responder); - curl_easy_setopt(mHandle, CURLOPT_HTTPGET, 1); + 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::getByteRange(const string& url, S32 offset, S32 length, ResponderPtr responder) +void LLCurl::Easy::setErrorBuffer() { - mRange = llformat("Range: bytes=%d-%d", offset,offset+length-1); - mHeaders = curl_slist_append(mHeaders, mRange.c_str()); - prep(url, responder); - curl_easy_setopt(mHandle, CURLOPT_HTTPGET, 1); + setopt(CURLOPT_ERRORBUFFER, &mErrorBuffer); } -void -LLCurl::Easy::perform() +const char* LLCurl::Easy::getErrorBuffer() { - report(curl_easy_perform(mHandle)); + return mErrorBuffer; } -void -LLCurl::Easy::prep(const std::string& url, ResponderPtr responder) +void LLCurl::Easy::setCA() { -#if !LL_DARWIN - curl_easy_reset(mHandle); // SJB: doesn't exisit on OSX 10.3.9 -#else - // SJB: equivalent? fast? - curl_easy_cleanup(mHandle); - mHandle = curl_easy_init(); -#endif - - curl_easy_setopt(mHandle, CURLOPT_PRIVATE, this); - -// curl_easy_setopt(mHandle, CURLOPT_VERBOSE, 1); // usefull for debugging - curl_easy_setopt(mHandle, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(mHandle, CURLOPT_WRITEFUNCTION, &curlOutputCallback); - curl_easy_setopt(mHandle, CURLOPT_WRITEDATA, &mOutput); -#if 1 // For debug - curl_easy_setopt(mHandle, CURLOPT_HEADERFUNCTION, &curlOutputCallback); - curl_easy_setopt(mHandle, CURLOPT_HEADERDATA, &mHeaderOutput); -#endif - curl_easy_setopt(mHandle, CURLOPT_ERRORBUFFER, &mErrorBuffer); - curl_easy_setopt(mHandle, CURLOPT_ENCODING, ""); - curl_easy_setopt(mHandle, CURLOPT_SSL_VERIFYPEER, false); - curl_easy_setopt(mHandle, CURLOPT_HTTPHEADER, mHeaders); - - mOutput.str(""); - if (!((istream&)mOutput).good()) { - std::cerr << "WHAT!?!?!? istream side bad" << std::endl; + if(!sCAPath.empty()) + { + setoptString(CURLOPT_CAPATH, sCAPath); } - if (!((ostream&)mOutput).good()) { - std::cerr << "WHAT!?!?!? ostream side bad" << std::endl; + if(!sCAFile.empty()) + { + setoptString(CURLOPT_CAINFO, sCAFile); } +} - mURL = url; - curl_easy_setopt(mHandle, CURLOPT_URL, mURL.c_str()); +void LLCurl::Easy::setHeaders() +{ + setopt(CURLOPT_HTTPHEADER, mHeaders); +} - mResponder = responder; +void LLCurl::Easy::getTransferInfo(LLCurl::TransferInfo* info) +{ + curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SIZE_DOWNLOAD, &info->mSizeDownload); + curl_easy_getinfo(mCurlEasyHandle, CURLINFO_TOTAL_TIME, &info->mTotalTime); + curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SPEED_DOWNLOAD, &info->mSpeedDownload); } -void -LLCurl::Easy::report(CURLcode code) +U32 LLCurl::Easy::report(CURLcode code) { - if (!mResponder) return; - - long responseCode; + U32 responseCode = 0; + std::string responseReason; if (code == CURLE_OK) { - curl_easy_getinfo(mHandle, CURLINFO_RESPONSE_CODE, &responseCode); + curl_easy_getinfo(mCurlEasyHandle, CURLINFO_RESPONSE_CODE, &responseCode); + //*TODO: get reason from first line of mHeaderOutput } else { responseCode = 499; + responseReason = strerror(code) + " : " + mErrorBuffer; + } + + if (mResponder) + { + mResponder->completedRaw(responseCode, responseReason, mChannels, mOutput); + mResponder = NULL; } - mResponder->completed(responseCode, 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) +{ + curl_easy_setopt(mCurlEasyHandle, option, value); +} +void LLCurl::Easy::setopt(CURLoption option, void* value) +{ + curl_easy_setopt(mCurlEasyHandle, option, value); +} +void LLCurl::Easy::setopt(CURLoption option, char* value) +{ + curl_easy_setopt(mCurlEasyHandle, option, value); +} +// 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); + curl_easy_setopt(mCurlEasyHandle, option, tstring); +} +void LLCurl::Easy::slist_append(const char* str) +{ + mHeaders = curl_slist_append(mHeaders, str); +} -LLCurl::Multi::Multi() +size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data) { - mHandle = curl_multi_init(); + LLCurl::Easy* easy = (LLCurl::Easy*)user_data; + + S32 n = size * nmemb; + S32 startpos = easy->getInput().tellg(); + easy->getInput().seekg(0, std::ios::end); + S32 endpos = 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; } -LLCurl::Multi::~Multi() +size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data) { - // FIXME: should clean up excess handles in mFreeEasy - curl_multi_cleanup(mHandle); + 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); -void -LLCurl::Multi::get(const std::string& url, ResponderPtr responder) + return n; +} + +void LLCurl::Easy::prepRequest(const std::string& url, ResponderPtr responder, bool post) { - LLCurl::Easy* easy = easyAlloc(); - easy->get(url, responder); - curl_multi_add_handle(mHandle, easy->mHandle); + resetState(); + + if (post) setoptString(CURLOPT_ENCODING, ""); + +// setopt(CURLOPT_VERBOSE, 1); // usefull for debugging + setopt(CURLOPT_NOSIGNAL, 1); + + mOutput.reset(new LLBufferArray); + 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); + + setErrorBuffer(); + setCA(); + + setopt(CURLOPT_SSL_VERIFYPEER, true); + setopt(CURLOPT_TIMEOUT, CURL_REQUEST_TIMEOUT); + + setoptString(CURLOPT_URL, url); + + mResponder = responder; + + if (!post) + { + slist_append("Connection: keep-alive"); + slist_append("Keep-alive: 300"); + } + // *FIX: should have ACCEPT headers } + +//////////////////////////////////////////////////////////////////////////// + +class LLCurl::Multi +{ + LOG_CLASS(Multi); +public: + + Multi(); + ~Multi(); + + Easy* allocEasy(); + bool addEasy(Easy* easy); -void -LLCurl::Multi::getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder) + void removeEasy(Easy* easy); + + S32 process(); + S32 perform(); + + CURLMsg* info_read(S32* msgs_in_queue); + + S32 mQueued; + S32 mErrorCount; + +private: + void easyFree(Easy*); + + CURLM* mCurlMultiHandle; + + typedef std::set easy_active_list_t; + easy_active_list_t mEasyActiveList; + typedef std::map easy_active_map_t; + easy_active_map_t mEasyActiveMap; + typedef std::set easy_free_list_t; + easy_free_list_t mEasyFreeList; +}; + +LLCurl::Multi::Multi() + : mQueued(0), + mErrorCount(0) { - LLCurl::Easy* easy = easyAlloc(); - easy->getByteRange(url, offset, length, responder); - curl_multi_add_handle(mHandle, easy->mHandle); + mCurlMultiHandle = curl_multi_init(); + if (!mCurlMultiHandle) + { + llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl; + mCurlMultiHandle = curl_multi_init(); + } + llassert_always(mCurlMultiHandle); + ++gCurlMultiCount; } + +LLCurl::Multi::~Multi() +{ + // Clean up active + for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); + iter != mEasyActiveList.end(); ++iter) + { + Easy* easy = *iter; + curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle()); + delete easy; + } + mEasyActiveList.clear(); + mEasyActiveMap.clear(); -void -LLCurl::Multi::process() + // Clean up freed + for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer()); + mEasyFreeList.clear(); + + curl_multi_cleanup(mCurlMultiHandle); + --gCurlMultiCount; +} + +CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue) { - int count; - for (int call_count = 0; call_count < 5; call_count += 1) + CURLMsg* curlmsg = curl_multi_info_read(mCurlMultiHandle, msgs_in_queue); + return curlmsg; +} + + +S32 LLCurl::Multi::perform() +{ + S32 q = 0; + for (S32 call_count = 0; + call_count < MULTI_PERFORM_CALL_REPEAT; + call_count += 1) { - if (CURLM_CALL_MULTI_PERFORM != curl_multi_perform(mHandle, &count)) + CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); + if (CURLM_CALL_MULTI_PERFORM != code || q == 0) { break; } } - + mQueued = q; + return q; +} + +S32 LLCurl::Multi::process() +{ + perform(); + CURLMsg* msg; int msgs_in_queue; - while ((msg = curl_multi_info_read(mHandle, &msgs_in_queue))) + + S32 processed = 0; + while ((msg = info_read(&msgs_in_queue))) { - if (msg->msg != CURLMSG_DONE) continue; - Easy* easy = 0; - curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &easy); - if (!easy) continue; - easy->report(msg->data.result); - - curl_multi_remove_handle(mHandle, easy->mHandle); - easyFree(easy); + ++processed; + if (msg->msg == CURLMSG_DONE) + { + U32 response = 0; + easy_active_map_t::iterator iter = mEasyActiveMap.find(msg->easy_handle); + if (iter != mEasyActiveMap.end()) + { + Easy* easy = iter->second; + response = easy->report(msg->data.result); + removeEasy(easy); + } + else + { + response = 499; + //*TODO: change to llwarns + llerrs << "cleaned up curl request completed!" << llendl; + } + if (response >= 400) + { + // failure of some sort, inc mErrorCount for debugging and flagging multi for destruction + ++mErrorCount; + } + } } + return processed; } - - -LLCurl::Easy* -LLCurl::Multi::easyAlloc() +LLCurl::Easy* LLCurl::Multi::allocEasy() { Easy* easy = 0; - - if (mFreeEasy.empty()) + + if (mEasyFreeList.empty()) { - easy = new Easy(); + easy = Easy::getEasy(); } else { - easy = mFreeEasy.back(); - mFreeEasy.pop_back(); + easy = *(mEasyFreeList.begin()); + mEasyFreeList.erase(easy); + } + if (easy) + { + mEasyActiveList.insert(easy); + mEasyActiveMap[easy->getCurlHandle()] = easy; } - return easy; } -void -LLCurl::Multi::easyFree(Easy* easy) +bool LLCurl::Multi::addEasy(Easy* easy) { - if (mFreeEasy.size() < 5) + CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle()); + if (mcode != CURLM_OK) { - mFreeEasy.push_back(easy); + llwarns << "Curl Error: " << curl_multi_strerror(mcode) << llendl; + return false; + } + return true; +} + +void LLCurl::Multi::easyFree(Easy* easy) +{ + mEasyActiveList.erase(easy); + mEasyActiveMap.erase(easy->getCurlHandle()); + if (mEasyFreeList.size() < EASY_HANDLE_POOL_SIZE) + { + easy->resetState(); + mEasyFreeList.insert(easy); } else { @@ -333,53 +651,371 @@ LLCurl::Multi::easyFree(Easy* easy) } } +void LLCurl::Multi::removeEasy(Easy* easy) +{ + curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle()); + easyFree(easy); +} + +//static +std::string LLCurl::strerror(CURLcode errorcode) +{ +#if LL_DARWIN + // curl_easy_strerror was added in libcurl 7.12.0. Unfortunately, the version in the Mac OS X 10.3.9 SDK is 7.10.2... + // There's a problem with the custom curl headers in our build that keeps me from #ifdefing this on the libcurl version number + // (the correct check would be #if LIBCURL_VERSION_NUM >= 0x070c00). We'll fix the header problem soon, but for now + // just punt and print the numeric error code on the Mac. + return llformat("%d", errorcode); +#else // LL_DARWIN + return std::string(curl_easy_strerror(errorcode)); +#endif // LL_DARWIN +} + +//////////////////////////////////////////////////////////////////////////// +// For generating a simple request for data +// using one multi and one easy per request + +LLCurlRequest::LLCurlRequest() + : mActiveMulti(NULL) +{ +} + +LLCurlRequest::~LLCurlRequest() +{ + for_each(mMultiSet.begin(), mMultiSet.end(), DeletePointer()); +} + +void LLCurlRequest::addMulti() +{ + LLCurl::Multi* multi = new LLCurl::Multi(); + mMultiSet.insert(multi); + mActiveMulti = multi; + mActiveRequestCount = 0; +} + +LLCurl::Easy* LLCurlRequest::allocEasy() +{ + if (!mActiveMulti || + mActiveRequestCount >= MAX_ACTIVE_REQUEST_COUNT || + mActiveMulti->mErrorCount > 0) + { + addMulti(); + } + llassert_always(mActiveMulti); + ++mActiveRequestCount; + LLCurl::Easy* easy = mActiveMulti->allocEasy(); + return easy; +} + +bool LLCurlRequest::addEasy(LLCurl::Easy* easy) +{ + llassert_always(mActiveMulti); + bool res = mActiveMulti->addEasy(easy); + return res; +} +void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) +{ + getByteRange(url, 0, -1, responder); +} + +bool LLCurlRequest::getByteRange(const std::string& url, S32 offset, S32 length, LLCurl::ResponderPtr responder) +{ + LLCurl::Easy* easy = allocEasy(); + if (!easy) + { + return false; + } + easy->prepRequest(url, responder); + easy->setopt(CURLOPT_HTTPGET, 1); + if (length > 0) + { + std::string range = llformat("Range: bytes=%d-%d", offset,offset+length-1); + easy->slist_append(range.c_str()); + } + easy->setHeaders(); + bool res = addEasy(easy); + return res; +} -namespace +bool LLCurlRequest::post(const std::string& url, const LLSD& data, LLCurl::ResponderPtr responder) { - static LLCurl::Multi* sMainMulti = 0; + LLCurl::Easy* easy = allocEasy(); + if (!easy) + { + return false; + } + easy->prepRequest(url, responder); + + 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("Content-Type: application/xml"); + easy->setHeaders(); + + lldebugs << "POSTING: " << bytes << " bytes." << llendl; + bool res = addEasy(easy); + return res; +} - LLCurl::Multi* - mainMulti() +// Note: call once per frame +S32 LLCurlRequest::process() +{ + S32 res = 0; + for (curlmulti_set_t::iterator iter = mMultiSet.begin(); + iter != mMultiSet.end(); ) { - if (!sMainMulti) { - sMainMulti = new LLCurl::Multi(); + curlmulti_set_t::iterator curiter = iter++; + LLCurl::Multi* multi = *curiter; + S32 tres = multi->process(); + res += tres; + if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) + { + mMultiSet.erase(curiter); + delete multi; } - return sMainMulti; } + return res; +} - void freeMulti() +S32 LLCurlRequest::getQueued() +{ + S32 queued = 0; + for (curlmulti_set_t::iterator iter = mMultiSet.begin(); + iter != mMultiSet.end(); ) { - delete sMainMulti; - sMainMulti = NULL; + curlmulti_set_t::iterator curiter = iter++; + LLCurl::Multi* multi = *curiter; + queued += multi->mQueued; } + return queued; } -void -LLCurl::get(const std::string& url, ResponderPtr responder) +//////////////////////////////////////////////////////////////////////////// +// For generating one easy request +// associated with a single multi request + +LLCurlEasyRequest::LLCurlEasyRequest() + : mRequestSent(false), + mResultReturned(false) { - mainMulti()->get(url, responder); + mMulti = new LLCurl::Multi(); + mEasy = mMulti->allocEasy(); + if (mEasy) + { + mEasy->setErrorBuffer(); + mEasy->setCA(); + } } - -void -LLCurl::getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder) + +LLCurlEasyRequest::~LLCurlEasyRequest() { - mainMulti()->getByteRange(url, offset, length, responder); + delete mMulti; } -void LLCurl::initClass() +void LLCurlEasyRequest::setopt(CURLoption option, S32 value) { - curl_global_init(CURL_GLOBAL_ALL); + if (mEasy) + { + mEasy->setopt(option, value); + } } -void -LLCurl::process() +void LLCurlEasyRequest::setoptString(CURLoption option, const std::string& value) { - mainMulti()->process(); + if (mEasy) + { + mEasy->setoptString(option, value); + } } -void LLCurl::cleanup() +void LLCurlEasyRequest::setPost(char* postdata, S32 size) { - freeMulti(); + if (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 (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 (mEasy) + { + mEasy->setopt(CURLOPT_WRITEFUNCTION, (void*)callback); + mEasy->setopt(CURLOPT_WRITEDATA, userdata); + } +} + +void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata) +{ + if (mEasy) + { + mEasy->setopt(CURLOPT_READFUNCTION, (void*)callback); + mEasy->setopt(CURLOPT_READDATA, userdata); + } +} + +void LLCurlEasyRequest::slist_append(const char* str) +{ + if (mEasy) + { + mEasy->slist_append(str); + } +} + +void LLCurlEasyRequest::sendRequest(const std::string& url) +{ + llassert_always(!mRequestSent); + mRequestSent = true; + if (mEasy) + { + mEasy->setHeaders(); + mEasy->setoptString(CURLOPT_URL, url); + mMulti->addEasy(mEasy); + } +} + +void LLCurlEasyRequest::requestComplete() +{ + llassert_always(mRequestSent); + mRequestSent = false; + if (mEasy) + { + mMulti->removeEasy(mEasy); + } +} + +S32 LLCurlEasyRequest::perform() +{ + return mMulti->perform(); +} + +// Usage: Call getRestult until it returns false (no more messages) +bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info) +{ + 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 mEasy ? std::string(mEasy->getErrorBuffer()) : std::string(); +} + +//////////////////////////////////////////////////////////////////////////// + +#if SAFE_SSL +//static +void LLCurl::ssl_locking_callback(int mode, int type, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) + { + LLCurl::sSSLMutex[type]->lock(); + } + else + { + LLCurl::sSSLMutex[type]->unlock(); + } +} + +//static +unsigned long LLCurl::ssl_thread_id(void) +{ + return LLThread::currentID(); +} +#endif + +void LLCurl::initClass() +{ + // Do not change this "unless you are familiar with and mean to control + // internal operations of libcurl" + // - http://curl.haxx.se/libcurl/c/curl_global_init.html + curl_global_init(CURL_GLOBAL_ALL); + +#if SAFE_SSL + S32 mutex_count = CRYPTO_num_locks(); + for (S32 i=0; i #include -#include +#include // TODO: remove dependency -// #include "llhttpclient.h" +#include "llbuffer.h" +#include "lliopipe.h" +#include "llsd.h" + +class LLMutex; + +// 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); 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 { + //LOG_CLASS(Responder); public: + Responder(); virtual ~Responder(); - virtual void error(U32 status, const std::stringstream& content); // called with bad status codes + /** + * @brief return true if the status code indicates success. + */ + static bool isGoodStatus(U32 status) + { + return((200 <= status) && (status < 300)); + } + + virtual void error(U32 status, const std::string& reason); + // called with non-200 status codes - virtual void result(const std::stringstream& content); + virtual void result(const LLSD& content); - virtual void completed(U32 status, const std::stringstream& content); + // Override point for clients that may want to use this class when the response is some other format besides LLSD + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + + virtual void completed(U32 status, const std::string& reason, const LLSD& content); /**< The default implemetnation calls either: * result(), or * error() */ + // Override to handle parsing of the header only. Note: this is the only place where the contents + // of the header can be parsed. In the ::completed call above only the body is contained in the LLSD. + virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content); + public: /* but not really -- don't touch this */ U32 mReferenceCount; }; typedef boost::intrusive_ptr ResponderPtr; - - class Easy - { - public: - Easy(); - ~Easy(); - - void get(const std::string& url, ResponderPtr); - void getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr); - - void perform(); - private: - void prep(const std::string& url, ResponderPtr); - void report(CURLcode); - - CURL* mHandle; - struct curl_slist* mHeaders; - - std::string mURL; - std::string mRange; - std::stringstream mRequest; - std::stringstream mOutput; - char mErrorBuffer[CURL_ERROR_SIZE]; - - std::stringstream mHeaderOutput; // Debug - - ResponderPtr mResponder; - - friend class Multi; - }; + /** + * @ brief Set certificate authority file used to verify HTTPS certs. + */ + static void setCAFile(const std::string& file); + /** + * @ brief Set certificate authority path used to verify HTTPS certs. + */ + static void setCAPath(const std::string& path); + + /** + * @ brief Get certificate authority file used to verify HTTPS certs. + */ + static const std::string& getCAFile() { return sCAFile; } + + /** + * @ brief Get certificate authority path used to verify HTTPS certs. + */ + static const std::string& getCAPath() { return sCAPath; } + + /** + * @ brief Initialize LLCurl class + */ + static void initClass(); + + /** + * @ brief Cleanup LLCurl class + */ + static void cleanupClass(); + + /** + * @ brief curl error code -> string + */ + static std::string strerror(CURLcode errorcode); + + // For OpenSSL callbacks + static std::vector sSSLMutex; - class Multi - { - public: - Multi(); - ~Multi(); + // OpenSSL callbacks + static void LLCurl::ssl_locking_callback(int mode, int type, const char *file, int line); + static unsigned long LLCurl::ssl_thread_id(void); + + + +private: - void get(const std::string& url, ResponderPtr); - void getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr); + static std::string sCAPath; + static std::string sCAFile; +}; - void process(); - - private: - Easy* easyAlloc(); - void easyFree(Easy*); - - CURLM* mHandle; - - typedef std::vector EasyList; - EasyList mFreeEasy; - }; +namespace boost +{ + void intrusive_ptr_add_ref(LLCurl::Responder* p); + void intrusive_ptr_release(LLCurl::Responder* p); +}; - static void get(const std::string& url, ResponderPtr); - static void getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder); +class LLCurlRequest +{ +public: + LLCurlRequest(); + ~LLCurlRequest(); + + void get(const std::string& url, LLCurl::ResponderPtr responder); + bool getByteRange(const std::string& url, S32 offset, S32 length, LLCurl::ResponderPtr responder); + bool post(const std::string& url, const LLSD& data, LLCurl::ResponderPtr responder); + S32 process(); + S32 getQueued(); + +private: + void addMulti(); + LLCurl::Easy* allocEasy(); + bool addEasy(LLCurl::Easy* easy); - static void initClass(); // *NOTE:Mani - not thread safe! - static void process(); - static void cleanup(); // *NOTE:Mani - not thread safe! +private: + typedef std::set curlmulti_set_t; + curlmulti_set_t mMultiSet; + LLCurl::Multi* mActiveMulti; + S32 mActiveRequestCount; }; -namespace boost +class LLCurlEasyRequest { - void intrusive_ptr_add_ref(LLCurl::Responder* p); - void intrusive_ptr_release(LLCurl::Responder* p); +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 slist_append(const char* str); + void sendRequest(const std::string& url); + void requestComplete(); + S32 perform(); + bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL); + std::string getErrorString(); + +private: + CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info); + +private: + LLCurl::Multi* mMulti; + LLCurl::Easy* mEasy; + bool mRequestSent; + bool mResultReturned; }; #endif // LL_LLCURL_H diff --git a/linden/indra/llmessage/llfiltersd2xmlrpc.cpp b/linden/indra/llmessage/llfiltersd2xmlrpc.cpp index d9e0ad3..4e38924 100644 --- a/linden/indra/llmessage/llfiltersd2xmlrpc.cpp +++ b/linden/indra/llmessage/llfiltersd2xmlrpc.cpp @@ -336,7 +336,7 @@ LLIOPipe::EStatus LLFilterSD2XMLRPCResponse::process_impl( LLBufferStream stream(channels, buffer.get()); stream << XML_HEADER << XMLRPC_METHOD_RESPONSE_HEADER; LLSD sd; - LLSDSerialize::fromNotation(sd, stream); + LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in())); PUMP_DEBUG; LLIOPipe::EStatus rv = STATUS_ERROR; @@ -408,7 +408,7 @@ LLIOPipe::EStatus LLFilterSD2XMLRPCRequest::process_impl( // See if we can parse it LLBufferStream stream(channels, buffer.get()); LLSD sd; - LLSDSerialize::fromNotation(sd, stream); + LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in())); if(stream.fail()) { llinfos << "STREAM FAILURE reading structure data." << llendl; diff --git a/linden/indra/llmessage/llhost.cpp b/linden/indra/llmessage/llhost.cpp index 57677b0..e139f6d 100644 --- a/linden/indra/llmessage/llhost.cpp +++ b/linden/indra/llmessage/llhost.cpp @@ -132,14 +132,13 @@ void LLHost::getHostName(char *buf, S32 len) const } } -LLString LLHost::getHostName() const +std::string LLHost::getHostName() const { - hostent *he; - + hostent* he; if (INVALID_HOST_IP_ADDRESS == mIP) { llwarns << "LLHost::getHostName() : Invalid IP address" << llendl; - return ""; + return std::string(); } he = gethostbyaddr((char *)&mIP, sizeof(mIP), AF_INET); if (!he) @@ -151,12 +150,11 @@ LLString LLHost::getHostName() const llwarns << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: " << h_errno << llendl; #endif - return ""; + return std::string(); } else { - LLString hostname = he->h_name; - return hostname; + return ll_safe_string(he->h_name); } } diff --git a/linden/indra/llmessage/llhost.h b/linden/indra/llmessage/llhost.h index c393aa6..0b3afe8 100644 --- a/linden/indra/llmessage/llhost.h +++ b/linden/indra/llmessage/llhost.h @@ -38,8 +38,6 @@ #include "net.h" -#include "llstring.h" - const U32 INVALID_PORT = 0; const U32 INVALID_HOST_IP_ADDRESS = 0x0; @@ -102,7 +100,7 @@ public: void getIPString(char* buffer, S32 length) const; // writes IP into buffer std::string getIPString() const; void getHostName(char *buf, S32 len) const; - LLString getHostName() const; + std::string getHostName() const; std::string getIPandPort() const; friend std::ostream& operator<< (std::ostream& os, const LLHost &hh); diff --git a/linden/indra/llmessage/llhttpassetstorage.cpp b/linden/indra/llmessage/llhttpassetstorage.cpp index 74f5271..80598c6 100644 --- a/linden/indra/llmessage/llhttpassetstorage.cpp +++ b/linden/indra/llmessage/llhttpassetstorage.cpp @@ -422,11 +422,8 @@ void LLHTTPAssetStorage::_init(const char *web_host, const char *local_web_host, mLocalBaseURL = local_web_host; mHostName = host_name; - // Do not change this "unless you are familiar with and mean to control - // internal operations of libcurl" - // - http://curl.haxx.se/libcurl/c/curl_global_init.html - curl_global_init(CURL_GLOBAL_ALL); - + // curl_global_init moved to LLCurl::initClass() + mCurlMultiHandle = curl_multi_init(); } @@ -435,7 +432,7 @@ LLHTTPAssetStorage::~LLHTTPAssetStorage() curl_multi_cleanup(mCurlMultiHandle); mCurlMultiHandle = NULL; - curl_global_cleanup(); + // curl_global_cleanup moved to LLCurl::initClass() } // storing data is simpler than getting it, so we just overload the whole method @@ -451,7 +448,7 @@ void LLHTTPAssetStorage::storeAssetData( bool user_waiting, F64 timeout) { - if (mVFS->getExists(uuid, type)) + if (mVFS->getExists(uuid, type)) // VFS treats nonexistant and zero-length identically { LLAssetRequest *req = new LLAssetRequest(uuid, type); req->mUpCallback = callback; @@ -460,6 +457,19 @@ void LLHTTPAssetStorage::storeAssetData( req->mIsUserWaiting = user_waiting; req->mTimeout = timeout; + // LLAssetStorage metric: Successful Request + S32 size = mVFS->getSize(uuid, type); + const char *message; + if( store_local ) + { + message = "Added to local upload queue"; + } + else + { + message = "Added to upload queue"; + } + reportMetric( uuid, type, NULL, requesting_agent_id, size, MR_OKAY, __FILE__, __LINE__, message ); + // this will get picked up and transmitted in checkForTimeouts if(store_local) { @@ -479,6 +489,8 @@ void LLHTTPAssetStorage::storeAssetData( llwarns << "AssetStorage: attempt to upload non-existent vfile " << uuid << ":" << LLAssetType::lookup(type) << llendl; if (callback) { + // LLAssetStorage metric: Zero size VFS + reportMetric( uuid, type, NULL, requesting_agent_id, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); callback(uuid, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_NONEXISTENT_FILE); } } @@ -504,13 +516,17 @@ void LLHTTPAssetStorage::storeAssetData( legacy->mUserData = user_data; FILE *fp = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ + S32 size = 0; if (fp) { - LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE); - fseek(fp, 0, SEEK_END); - S32 size = ftell(fp); + size = ftell(fp); fseek(fp, 0, SEEK_SET); + } + + if( size ) + { + LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE); file.setMaxSize(size); @@ -528,6 +544,7 @@ void LLHTTPAssetStorage::storeAssetData( LLFile::remove(filename); } + // LLAssetStorage metric: Success not needed; handled in the overloaded method here: storeAssetData( asset_id, asset_type, @@ -540,8 +557,19 @@ void LLHTTPAssetStorage::storeAssetData( user_waiting, timeout); } - else + else // !size { + if( fp ) + { + // LLAssetStorage metric: Zero size + reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file was zero length" ); + fclose( fp ); + } + else + { + // LLAssetStorage metric: Missing File + reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_FILE_NONEXIST, __FILE__, __LINE__, "The file didn't exist" ); + } if (callback) { callback(LLUUID::null, user_data, LL_ERR_CANNOT_OPEN_FILE, LL_EXSTAT_BLOCKED_FILE); @@ -827,7 +855,16 @@ void LLHTTPAssetStorage::checkForTimeouts() } else { - llinfos << "Requesting PUT " << new_req->mURLBuffer << llendl; + // Get the uncompressed file size. + LLVFile file(mVFS,new_req->getUUID(),new_req->getType()); + S32 size = file.getSize(); + llinfos << "Requesting PUT " << new_req->mURLBuffer << ", asset size: " << size << " bytes" << llendl; + if (size == 0) + { + llwarns << "Rejecting zero size PUT request!" << llendl; + new_req->cleanupCurlHandle(); + deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID()); + } } // Pending upload will have been flagged by the request } @@ -867,8 +904,19 @@ void LLHTTPAssetStorage::checkForTimeouts() } else { + // Get the uncompressed file size. + S32 size = file.getSize(); + llinfos << "TAT: LLHTTPAssetStorage::checkForTimeouts() : pending local!" - << " Requesting PUT " << new_req->mURLBuffer << llendl; + << " Requesting PUT " << new_req->mURLBuffer << ", asset size: " << size << " bytes" << llendl; + if (size == 0) + { + + llwarns << "Rejecting zero size PUT request!" << llendl; + new_req->cleanupCurlHandle(); + deletePendingRequest(RT_UPLOAD, new_req->getType(), new_req->getUUID()); + } + } // Pending upload will have been flagged by the request } @@ -1403,5 +1451,3 @@ void LLHTTPAssetStorage::clearTempAssetData() llinfos << "TAT: Clearing temp asset data map" << llendl; mTempAssets.clear(); } - - diff --git a/linden/indra/llmessage/llhttpclient.cpp b/linden/indra/llmessage/llhttpclient.cpp index 3ede02a..22c0c68 100644 --- a/linden/indra/llmessage/llhttpclient.cpp +++ b/linden/indra/llmessage/llhttpclient.cpp @@ -1,4 +1,4 @@ -/** + /** * @file llhttpclient.cpp * @brief Implementation of classes for making HTTP requests. * @@ -46,75 +46,18 @@ #include const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; -static std::string gCABundle; - - -LLHTTPClient::Responder::Responder() - : mReferenceCount(0) -{ -} - -LLHTTPClient::Responder::~Responder() -{ -} - -// virtual -void LLHTTPClient::Responder::error(U32 status, const std::string& reason) -{ - llinfos << "LLHTTPClient::Responder::error " - << status << ": " << reason << llendl; -} - -// virtual -void LLHTTPClient::Responder::result(const LLSD& content) -{ -} - -// virtual -void LLHTTPClient::Responder::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - LLBufferStream istr(channels, buffer.get()); - LLSD content; - - if (isGoodStatus(status)) - { - LLSDSerialize::fromXML(content, istr); -/* - const S32 parseError = -1; - if(LLSDSerialize::fromXML(content, istr) == parseError) - { - mStatus = 498; - mReason = "Client Parse Error"; - } -*/ - } - - completed(status, reason, content); -} - -// virtual -void LLHTTPClient::Responder::completed(U32 status, const std::string& reason, const LLSD& content) -{ - if(isGoodStatus(status)) - { - result(content); - } - else - { - error(status, reason); - } -} +//////////////////////////////////////////////////////////////////////////// +// Responder class moved to LLCurl namespace { class LLHTTPClientURLAdaptor : public LLURLRequestComplete { public: - LLHTTPClientURLAdaptor(LLHTTPClient::ResponderPtr responder) - : mResponder(responder), - mStatus(499), mReason("LLURLRequest complete w/no status") + LLHTTPClientURLAdaptor(LLCurl::ResponderPtr responder) + : mResponder(responder), mStatus(499), + mReason("LLURLRequest complete w/no status") { } @@ -129,18 +72,24 @@ namespace } virtual void complete(const LLChannelDescriptors& channels, - const buffer_ptr_t& buffer) + const buffer_ptr_t& buffer) { if (mResponder.get()) { mResponder->completedRaw(mStatus, mReason, channels, buffer); + mResponder->completedHeader(mStatus, mReason, mHeaderOutput); } } + virtual void header(const std::string& header, const std::string& value) + { + mHeaderOutput[header] = value; + } private: - LLHTTPClient::ResponderPtr mResponder; + LLCurl::ResponderPtr mResponder; U32 mStatus; std::string mReason; + LLSD mHeaderOutput; }; class Injector : public LLIOPipe @@ -250,13 +199,14 @@ namespace LLPumpIO* theClientPump = NULL; } -static void request( - const std::string& url, - LLURLRequest::ERequestAction method, - Injector* body_injector, - LLHTTPClient::ResponderPtr responder, - const LLSD& headers, - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS) +static void request(const std::string& url, + LLURLRequest::ERequestAction method, + Injector* body_injector, + LLCurl::ResponderPtr responder, + const LLSD& headers = LLSD(), + const F32 timeout = HTTP_REQUEST_EXPIRY_SECS, + S32 offset = 0, + S32 bytes = 0) { if (!LLHTTPClient::hasPump()) { @@ -266,7 +216,7 @@ static void request( LLPumpIO::chain_t chain; LLURLRequest *req = new LLURLRequest(method, url); - req->requestEncoding(""); + req->checkRootCertificate(true); // Insert custom headers is the caller sent any if (headers.isMap()) @@ -291,10 +241,6 @@ static void request( req->addHeader(header.str().c_str()); } } - if (!gCABundle.empty()) - { - req->checkRootCertificate(true, gCABundle.c_str()); - } req->setCallback(new LLHTTPClientURLAdaptor(responder)); if (method == LLURLRequest::HTTP_POST && gMessageSystem) @@ -310,19 +256,26 @@ static void request( chain.push_back(LLIOPipe::ptr_t(body_injector)); } + + if (method == LLURLRequest::HTTP_GET && (offset > 0 || bytes > 0)) + { + std::string range = llformat("Range: bytes=%d-%d", offset,offset+bytes-1); + req->addHeader(range.c_str()); + } + chain.push_back(LLIOPipe::ptr_t(req)); theClientPump->addChain(chain, timeout); } -static void request( - const std::string& url, - LLURLRequest::ERequestAction method, - Injector* body_injector, - LLHTTPClient::ResponderPtr responder, - const F32 timeout=HTTP_REQUEST_EXPIRY_SECS) + +void LLHTTPClient::getByteRange(const std::string& url, + S32 offset, S32 bytes, + ResponderPtr responder, + const LLSD& headers, + const F32 timeout) { - request(url, method, body_injector, responder, LLSD(), timeout); + request(url, LLURLRequest::HTTP_GET, NULL, responder, LLSD(), timeout, offset, bytes); } void LLHTTPClient::head(const std::string& url, ResponderPtr responder, const F32 timeout) @@ -334,10 +287,13 @@ void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const LLS { request(url, LLURLRequest::HTTP_GET, NULL, responder, headers, timeout); } - -void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const F32 timeout) +void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout) { - get(url, responder, LLSD(), timeout); + request(url, LLURLRequest::HTTP_HEAD, NULL, responder, headers, timeout); +} +void LLHTTPClient::getHeaderOnly(const std::string& url, ResponderPtr responder, const F32 timeout) +{ + getHeaderOnly(url, responder, LLSD(), timeout); } void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const LLSD& headers, const F32 timeout) @@ -348,11 +304,6 @@ void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr r get(uri.asString(), responder, headers, timeout); } -void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const F32 timeout) -{ - get(url, query, responder, LLSD(), timeout); -} - // A simple class for managing data returned from a curl http request. class LLHTTPBuffer { @@ -388,6 +339,7 @@ private: std::string mBuffer; }; +// *TODO: Deprecate (only used by dataserver) // This call is blocking! This is probably usually bad. :( LLSD LLHTTPClient::blockingGet(const std::string& url) { @@ -481,24 +433,3 @@ bool LLHTTPClient::hasPump() { return theClientPump != NULL; } - -void LLHTTPClient::setCABundle(const std::string& caBundle) -{ - gCABundle = caBundle; -} - -namespace boost -{ - void intrusive_ptr_add_ref(LLHTTPClient::Responder* p) - { - ++p->mReferenceCount; - } - - void intrusive_ptr_release(LLHTTPClient::Responder* p) - { - if(p && 0 == --p->mReferenceCount) - { - delete p; - } - } -}; diff --git a/linden/indra/llmessage/llhttpclient.h b/linden/indra/llmessage/llhttpclient.h index 703ee61..1587268 100644 --- a/linden/indra/llmessage/llhttpclient.h +++ b/linden/indra/llmessage/llhttpclient.h @@ -41,7 +41,7 @@ #include #include "llassettype.h" -#include "llbuffer.h" +#include "llcurl.h" #include "lliopipe.h" extern const F32 HTTP_REQUEST_EXPIRY_SECS; @@ -54,48 +54,23 @@ class LLSD; class LLHTTPClient { public: - class Responder - { - public: - Responder(); - virtual ~Responder(); - - /** - * @brief return true if the status code indicates success. - */ - static bool isGoodStatus(U32 status) - { - return((200 <= status) && (status < 300)); - } - - virtual void error(U32 status, const std::string& reason); // called with bad status codes - - virtual void result(const LLSD& content); - - // Override point for clients that may want to use this class when the response is some other format besides LLSD - virtual void completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - - virtual void completed(U32 status, const std::string& reason, const LLSD& content); - /**< The default implemetnation calls - either: - * result(), or - * error() - */ - - public: /* but not really -- don't touch this */ - U32 mReferenceCount; - }; - - typedef boost::intrusive_ptr ResponderPtr; - + // class Responder moved to LLCurl + + // For convenience + typedef LLCurl::Responder Responder; + typedef LLCurl::ResponderPtr ResponderPtr; + + // non-blocking static void head(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void get(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void get(const std::string& url, ResponderPtr, const LLSD& headers, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void get(const std::string& url, const LLSD& query, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - static void get(const std::string& url, const LLSD& query, ResponderPtr, const LLSD& headers, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); + static void getByteRange(const std::string& url, S32 offset, S32 bytes, ResponderPtr, const LLSD& headers=LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); + static void get(const std::string& url, ResponderPtr, const LLSD& headers = LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); + static void get(const std::string& url, const LLSD& query, ResponderPtr, const LLSD& headers = LLSD(), const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); + static void put(const std::string& url, const LLSD& body, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); - ///< non-blocking + static void getHeaderOnly(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); + static void getHeaderOnly(const std::string& url, ResponderPtr, const LLSD& headers, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); + + ///< non-blocking static void post(const std::string& url, const LLSD& body, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); static void post(const std::string& url, const U8* data, S32 size, ResponderPtr responder, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); static void postFile(const std::string& url, const std::string& filename, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS); @@ -113,20 +88,6 @@ public: ///< must be called before any of the above calls are made static bool hasPump(); ///< for testing - - static void setCABundle(const std::string& caBundle); - ///< use this root CA bundle when checking SSL connections - ///< defaults to the standard system root CA bundle - ///< @see LLURLRequest::checkRootCertificate() }; - - -namespace boost -{ - void intrusive_ptr_add_ref(LLHTTPClient::Responder* p); - void intrusive_ptr_release(LLHTTPClient::Responder* p); -}; - - #endif // LL_LLHTTPCLIENT_H diff --git a/linden/indra/llmessage/llhttpnode.h b/linden/indra/llmessage/llhttpnode.h index 9317e9c..b4d4aed 100644 --- a/linden/indra/llmessage/llhttpnode.h +++ b/linden/indra/llmessage/llhttpnode.h @@ -176,7 +176,7 @@ public: /* @name Description system The Description object contains information about a service. - All subclasses of LLHTTPNode should override description() and use + All subclasses of LLHTTPNode should override describe() and use the methods of the Description class to set the various properties. */ //@{ diff --git a/linden/indra/llmessage/lliohttpserver.cpp b/linden/indra/llmessage/lliohttpserver.cpp index 40f70c3..6e94ec6 100644 --- a/linden/indra/llmessage/lliohttpserver.cpp +++ b/linden/indra/llmessage/lliohttpserver.cpp @@ -156,7 +156,9 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( // assume deferred unless mResponse does otherwise mResponse = Response::create(this); - // TODO: Babbage: Parameterize parser? + // *TODO: Babbage: Parameterize parser? + // *TODO: We should look at content-type and do the right + // thing. Phoenix 2007-12-31 LLBufferStream istr(channels, buffer.get()); static LLTimer timer; @@ -171,14 +173,12 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( { LLSD input; LLSDSerialize::fromXML(input, istr); - mNode.put(LLHTTPNode::ResponsePtr(mResponse), context, input); } else if(verb == HTTP_VERB_POST) { LLSD input; LLSDSerialize::fromXML(input, istr); - mNode.post(LLHTTPNode::ResponsePtr(mResponse), context, input); } else if(verb == HTTP_VERB_DELETE) diff --git a/linden/indra/llmessage/llmessage.vcproj b/linden/indra/llmessage/llmessage.vcproj index 56b7d30..b011c24 100644 --- a/linden/indra/llmessage/llmessage.vcproj +++ b/linden/indra/llmessage/llmessage.vcproj @@ -394,9 +394,6 @@ RelativePath=".\llcachename.h"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linden/indra/llmessage/llmessage_vc9.vcproj b/linden/indra/llmessage/llmessage_vc9.vcproj index f197feb..e9f4889 100644 --- a/linden/indra/llmessage/llmessage_vc9.vcproj +++ b/linden/indra/llmessage/llmessage_vc9.vcproj @@ -1,926 +1,922 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linden/indra/llmessage/llnamevalue.h b/linden/indra/llmessage/llnamevalue.h index 123836b..c58172a 100644 --- a/linden/indra/llmessage/llnamevalue.h +++ b/linden/indra/llmessage/llnamevalue.h @@ -33,7 +33,6 @@ #define LL_LLNAMEVALUE_H #include "string_table.h" -#include "llskipmap.h" #include "llmath.h" #include "v3math.h" #include "lldbstrings.h" diff --git a/linden/indra/llmessage/llpacketring.h b/linden/indra/llmessage/llpacketring.h index 2153e96..dac52e6 100644 --- a/linden/indra/llmessage/llpacketring.h +++ b/linden/indra/llmessage/llpacketring.h @@ -36,7 +36,6 @@ #include #include "llpacketbuffer.h" -#include "linked_lists.h" #include "llhost.h" #include "net.h" #include "llthrottle.h" diff --git a/linden/indra/llmessage/llsdrpcclient.cpp b/linden/indra/llmessage/llsdrpcclient.cpp index c67992d..7c4b8e3 100644 --- a/linden/indra/llmessage/llsdrpcclient.cpp +++ b/linden/indra/llmessage/llsdrpcclient.cpp @@ -221,7 +221,7 @@ LLIOPipe::EStatus LLSDRPCClient::process_impl( // << llendl; LLBufferStream resp(channels, buffer.get()); LLSD sd; - LLSDSerialize::fromNotation(sd, resp); + LLSDSerialize::fromNotation(sd, resp, buffer->count(channels.in())); LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get(); if (!response) { diff --git a/linden/indra/llmessage/llsdrpcserver.cpp b/linden/indra/llmessage/llsdrpcserver.cpp index 5406e02..c77ac5c 100644 --- a/linden/indra/llmessage/llsdrpcserver.cpp +++ b/linden/indra/llmessage/llsdrpcserver.cpp @@ -182,7 +182,10 @@ LLIOPipe::EStatus LLSDRPCServer::process_impl( PUMP_DEBUG; LLBufferStream istr(channels, buffer.get()); mRequest.clear(); - LLSDSerialize::fromNotation(mRequest, istr); + LLSDSerialize::fromNotation( + mRequest, + istr, + buffer->count(channels.in())); // { 'method':'...', 'parameter': ... } method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); diff --git a/linden/indra/llmessage/llservicebuilder.cpp b/linden/indra/llmessage/llservicebuilder.cpp index 6ffd480..195e24b 100644 --- a/linden/indra/llmessage/llservicebuilder.cpp +++ b/linden/indra/llmessage/llservicebuilder.cpp @@ -1,33 +1,33 @@ /** -* @file llservicebuilder.cpp -* @brief Implementation of the LLServiceBuilder class. -* -* $LicenseInfo:firstyear=2007&license=viewergpl$ -* -* Copyright (c) 2007-2008, Linden Research, Inc. -* -* Second Life Viewer Source Code -* The source code in this file ("Source Code") is provided by Linden Lab -* to you under the terms of the GNU General Public License, version 2.0 -* ("GPL"), unless you have obtained a separate licensing agreement -* ("Other License"), formally executed by you and Linden Lab. Terms of -* the GPL can be found in doc/GPL-license.txt in this distribution, or -* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 -* -* There are special exceptions to the terms and conditions of the GPL as -* it is applied to this Source Code. View the full text of the exception -* in the file doc/FLOSS-exception.txt in this software distribution, or -* online at http://secondlifegrid.net/programs/open_source/licensing/flossexception -* -* By copying, modifying or distributing this software, you acknowledge -* that you have read and understood your obligations described above, -* and agree to abide by those obligations. -* -* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -* COMPLETENESS OR PERFORMANCE. -* $/LicenseInfo$ -*/ + * @file llservicebuilder.cpp + * @brief Implementation of the LLServiceBuilder class. + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2008, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ #include "linden_common.h" #include "llapp.h" diff --git a/linden/indra/llmessage/llurlrequest.cpp b/linden/indra/llmessage/llurlrequest.cpp index 6c826cd..eabd951 100644 --- a/linden/indra/llmessage/llurlrequest.cpp +++ b/linden/indra/llmessage/llurlrequest.cpp @@ -37,6 +37,7 @@ #include #include +#include "llcurl.h" #include "llioutil.h" #include "llmemtype.h" #include "llpumpio.h" @@ -52,8 +53,7 @@ static const U32 HTTP_STATUS_PIPE_ERROR = 499; const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri"); -static -size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); +static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); /** * class LLURLRequestDetail @@ -63,12 +63,8 @@ class LLURLRequestDetail public: LLURLRequestDetail(); ~LLURLRequestDetail(); - CURLM* mCurlMulti; - CURL* mCurl; - struct curl_slist* mHeaders; - char* mURL; - char mCurlErrorBuf[CURL_ERROR_SIZE + 1]; /* Flawfinder: ignore */ - bool mNeedToRemoveEasyHandle; + std::string mURL; + LLCurlEasyRequest* mCurlRequest; LLBufferArray* mResponseBuffer; LLChannelDescriptors mChannels; U8* mLastRead; @@ -77,11 +73,7 @@ public: }; LLURLRequestDetail::LLURLRequestDetail() : - mCurlMulti(NULL), - mCurl(NULL), - mHeaders(NULL), - mURL(NULL), - mNeedToRemoveEasyHandle(false), + mCurlRequest(NULL), mResponseBuffer(NULL), mLastRead(NULL), mBodyLimit(0), @@ -89,34 +81,13 @@ LLURLRequestDetail::LLURLRequestDetail() : { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - mCurlErrorBuf[0] = '\0'; + mCurlRequest = new LLCurlEasyRequest(); } LLURLRequestDetail::~LLURLRequestDetail() { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - if(mCurl) - { - if(mNeedToRemoveEasyHandle && mCurlMulti) - { - curl_multi_remove_handle(mCurlMulti, mCurl); - mNeedToRemoveEasyHandle = false; - } - curl_easy_cleanup(mCurl); - mCurl = NULL; - } - if(mCurlMulti) - { - curl_multi_cleanup(mCurlMulti); - mCurlMulti = NULL; - } - if(mHeaders) - { - curl_slist_free_all(mHeaders); - mHeaders = NULL; - } - delete[] mURL; - mURL = NULL; + delete mCurlRequest; mResponseBuffer = NULL; mLastRead = NULL; } @@ -126,9 +97,6 @@ LLURLRequestDetail::~LLURLRequestDetail() * class LLURLRequest */ -static std::string sCAFile(""); -static std::string sCAPath(""); - LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) : mAction(action) { @@ -155,31 +123,13 @@ LLURLRequest::~LLURLRequest() void LLURLRequest::setURL(const std::string& url) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - if(mDetail->mURL) - { - // *NOTE: if any calls to set the url have been made to curl, - // this will probably lead to a crash. - delete[] mDetail->mURL; - mDetail->mURL = NULL; - } - if(!url.empty()) - { - mDetail->mURL = new char[url.size() + 1]; - url.copy(mDetail->mURL, url.size()); - mDetail->mURL[url.size()] = '\0'; - } + mDetail->mURL = url; } void LLURLRequest::addHeader(const char* header) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - mDetail->mHeaders = curl_slist_append(mDetail->mHeaders, header); -} - -void LLURLRequest::requestEncoding(const char* encoding) -{ - LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - curl_easy_setopt(mDetail->mCurl, CURLOPT_ENCODING, encoding); + mDetail->mCurlRequest->slist_append(header); } void LLURLRequest::setBodyLimit(U32 size) @@ -188,22 +138,17 @@ void LLURLRequest::setBodyLimit(U32 size) mDetail->mIsBodyLimitSet = true; } -void LLURLRequest::checkRootCertificate(bool check, const char* caBundle) +void LLURLRequest::checkRootCertificate(bool check) { - curl_easy_setopt(mDetail->mCurl, CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE)); - if (caBundle) - { - curl_easy_setopt(mDetail->mCurl, CURLOPT_CAINFO, caBundle); - } + mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE)); + mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); } void LLURLRequest::setCallback(LLURLRequestComplete* callback) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mCompletionCallback = callback; - - curl_easy_setopt(mDetail->mCurl, CURLOPT_HEADERFUNCTION, &headerCallback); - curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEHEADER, callback); + mDetail->mCurlRequest->setHeaderCallback(&headerCallback, (void*)callback); } // Added to mitigate the effect of libcurl looking @@ -235,15 +180,15 @@ void LLURLRequest::useProxy(bool use_proxy) } - lldebugs << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << env_proxy << llendl; + lldebugs << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (env_proxy ? env_proxy : "(null)") << llendl; if (env_proxy && use_proxy) { - curl_easy_setopt(mDetail->mCurl, CURLOPT_PROXY, env_proxy); + mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy); } else { - curl_easy_setopt(mDetail->mCurl, CURLOPT_PROXY, ""); + mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, ""); } } @@ -309,27 +254,20 @@ LLIOPipe::EStatus LLURLRequest::process_impl( case STATE_PROCESSING_RESPONSE: { PUMP_DEBUG; - const S32 MAX_CALLS = 5; - S32 count = MAX_CALLS; - CURLMcode code; LLIOPipe::EStatus status = STATUS_BREAK; - S32 queue; - do - { - LLFastTimer t2(LLFastTimer::FTM_CURL); - code = curl_multi_perform(mDetail->mCurlMulti, &queue); - }while((CURLM_CALL_MULTI_PERFORM == code) && (queue > 0) && count--); - CURLMsg* curl_msg; - do + mDetail->mCurlRequest->perform(); + while(1) { - curl_msg = curl_multi_info_read(mDetail->mCurlMulti, &queue); - if(curl_msg && (curl_msg->msg == CURLMSG_DONE)) + CURLcode result; + bool newmsg = mDetail->mCurlRequest->getResult(&result); + if (!newmsg) { - mState = STATE_HAVE_RESPONSE; + break; + } - CURLcode result = curl_msg->data.result; - switch(result) - { + mState = STATE_HAVE_RESPONSE; + switch(result) + { case CURLE_OK: case CURLE_WRITE_ERROR: // NB: The error indication means that we stopped the @@ -352,31 +290,21 @@ LLIOPipe::EStatus LLURLRequest::process_impl( mCompletionCallback = NULL; } break; + case CURLE_FAILED_INIT: case CURLE_COULDNT_CONNECT: status = STATUS_NO_CONNECTION; break; default: - llwarns << "URLRequest Error: " << curl_msg->data.result + llwarns << "URLRequest Error: " << result << ", " -#if LL_DARWIN - // curl_easy_strerror was added in libcurl 7.12.0. Unfortunately, the version in the Mac OS X 10.3.9 SDK is 7.10.2... - // There's a problem with the custom curl headers in our build that keeps me from #ifdefing this on the libcurl version number - // (the correct check would be #if LIBCURL_VERSION_NUM >= 0x070c00). We'll fix the header problem soon, but for now - // just punt and print the numeric error code on the Mac. - << curl_msg->data.result -#else // LL_DARWIN - << curl_easy_strerror(curl_msg->data.result) -#endif // LL_DARWIN + << LLCurl::strerror(result) << ", " - << (mDetail->mURL ? mDetail->mURL : "") + << (mDetail->mURL.empty() ? "" : mDetail->mURL) << llendl; status = STATUS_ERROR; break; - } - curl_multi_remove_handle(mDetail->mCurlMulti, mDetail->mCurl); - mDetail->mNeedToRemoveEasyHandle = false; } - }while(curl_msg && (queue > 0)); + } return status; } case STATE_HAVE_RESPONSE: @@ -397,26 +325,9 @@ void LLURLRequest::initialize() LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mState = STATE_INITIALIZED; mDetail = new LLURLRequestDetail; - mDetail->mCurl = curl_easy_init(); - mDetail->mCurlMulti = curl_multi_init(); - curl_easy_setopt(mDetail->mCurl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEFUNCTION, &downCallback); - curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEDATA, this); - curl_easy_setopt(mDetail->mCurl, CURLOPT_READFUNCTION, &upCallback); - curl_easy_setopt(mDetail->mCurl, CURLOPT_READDATA, this); - curl_easy_setopt( - mDetail->mCurl, - CURLOPT_ERRORBUFFER, - mDetail->mCurlErrorBuf); - - if(sCAPath != std::string("")) - { - curl_easy_setopt(mDetail->mCurl, CURLOPT_CAPATH, sCAPath.c_str()); - } - if(sCAFile != std::string("")) - { - curl_easy_setopt(mDetail->mCurl, CURLOPT_CAINFO, sCAFile.c_str()); - } + mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); + mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this); + mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this); } bool LLURLRequest::configure() @@ -429,15 +340,14 @@ bool LLURLRequest::configure() switch(mAction) { case HTTP_HEAD: - curl_easy_setopt(mDetail->mCurl, CURLOPT_HEADER, 1); - curl_easy_setopt(mDetail->mCurl, CURLOPT_NOBODY, 1); - curl_easy_setopt(mDetail->mCurl, CURLOPT_FOLLOWLOCATION, 1); + mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1); + mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1); + mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); rv = true; break; - case HTTP_GET: - curl_easy_setopt(mDetail->mCurl, CURLOPT_HTTPGET, 1); - curl_easy_setopt(mDetail->mCurl, CURLOPT_FOLLOWLOCATION, 1); + mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1); + mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); rv = true; break; @@ -446,8 +356,8 @@ bool LLURLRequest::configure() // to turning this on, and I am not too sure what it means. addHeader("Expect:"); - curl_easy_setopt(mDetail->mCurl, CURLOPT_UPLOAD, 1); - curl_easy_setopt(mDetail->mCurl, CURLOPT_INFILESIZE, bytes); + mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1); + mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes); rv = true; break; @@ -461,15 +371,13 @@ bool LLURLRequest::configure() addHeader("Content-Type:"); // Set the handle for an http post - curl_easy_setopt(mDetail->mCurl, CURLOPT_POST, 1); - curl_easy_setopt(mDetail->mCurl, CURLOPT_POSTFIELDS, NULL); - curl_easy_setopt(mDetail->mCurl, CURLOPT_POSTFIELDSIZE, bytes); + mDetail->mCurlRequest->setPost(NULL, bytes); rv = true; break; case HTTP_DELETE: // Set the handle for an http post - curl_easy_setopt(mDetail->mCurl, CURLOPT_CUSTOMREQUEST, "DELETE"); + mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE"); rv = true; break; @@ -479,24 +387,14 @@ bool LLURLRequest::configure() } if(rv) { - if(mDetail->mHeaders) - { - curl_easy_setopt( - mDetail->mCurl, - CURLOPT_HTTPHEADER, - mDetail->mHeaders); - } - curl_easy_setopt(mDetail->mCurl, CURLOPT_URL, mDetail->mURL); - lldebugs << "URL: " << mDetail->mURL << llendl; - curl_multi_add_handle(mDetail->mCurlMulti, mDetail->mCurl); - mDetail->mNeedToRemoveEasyHandle = true; + mDetail->mCurlRequest->sendRequest(mDetail->mURL); } return rv; } // static size_t LLURLRequest::downCallback( - void* data, + char* data, size_t size, size_t nmemb, void* user) @@ -530,7 +428,7 @@ size_t LLURLRequest::downCallback( // static size_t LLURLRequest::upCallback( - void* data, + char* data, size_t size, size_t nmemb, void* user) @@ -550,8 +448,7 @@ size_t LLURLRequest::upCallback( return bytes; } -static -size_t headerCallback(void* data, size_t size, size_t nmemb, void* user) +static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user) { const char* headerLine = (const char*)data; size_t headerLen = size * nmemb; @@ -607,18 +504,6 @@ size_t headerCallback(void* data, size_t size, size_t nmemb, void* user) return headerLen; } -//static -void LLURLRequest::setCertificateAuthorityFile(const std::string& file_name) -{ - sCAFile = file_name; -} - -//static -void LLURLRequest::setCertificateAuthorityPath(const std::string& path) -{ - sCAPath = path; -} - /** * LLContextURLExtractor */ diff --git a/linden/indra/llmessage/llurlrequest.h b/linden/indra/llmessage/llurlrequest.h index 3b91a1d..dd82a9f 100644 --- a/linden/indra/llmessage/llurlrequest.h +++ b/linden/indra/llmessage/llurlrequest.h @@ -129,18 +129,8 @@ public: * * Set whether request will check that remote server * certificates are signed by a known root CA when using HTTPS. - * Use the supplied root certificate bundle if supplied, else use - * the standard bundle as found by libcurl and openssl. */ - void checkRootCertificate(bool check, const char* caBundle = NULL); - - /** - * @brief Request a particular response encoding if available. - * - * This call is a shortcut for requesting a particular encoding - * from the server, eg, 'gzip'. - */ - void requestEncoding(const char* encoding); + void checkRootCertificate(bool check); /** * @brief Return at most size bytes of body. @@ -168,16 +158,6 @@ public: void setCallback(LLURLRequestComplete* callback); //@} - /** - * @ brief Set certificate authority file used to verify HTTPS certs. - */ - static void setCertificateAuthorityFile(const std::string& file_name); - - /** - * @ brief Set certificate authority path used to verify HTTPS certs. - */ - static void setCertificateAuthorityPath(const std::string& path); - /* @name LLIOPipe virtual implementations */ @@ -234,7 +214,7 @@ private: * @brief Download callback method. */ static size_t downCallback( - void* data, + char* data, size_t size, size_t nmemb, void* user); @@ -243,7 +223,7 @@ private: * @brief Upload callback method. */ static size_t upCallback( - void* data, + char* data, size_t size, size_t nmemb, void* user); diff --git a/linden/indra/llmessage/llxfermanager.cpp b/linden/indra/llmessage/llxfermanager.cpp index e08acd9..47c950b 100644 --- a/linden/indra/llmessage/llxfermanager.cpp +++ b/linden/indra/llmessage/llxfermanager.cpp @@ -90,8 +90,9 @@ void LLXferManager::free () { LLXfer *xferp; LLXfer *delp; - - mOutgoingHosts.deleteAllData(); + + for_each(mOutgoingHosts.begin(), mOutgoingHosts.end(), DeletePointer()); + mOutgoingHosts.clear(); delp = mSendList; while (delp) @@ -155,12 +156,15 @@ void LLXferManager::updateHostStatus() LLXfer *xferp; LLHostStatus *host_statusp = NULL; - mOutgoingHosts.deleteAllData(); + for_each(mOutgoingHosts.begin(), mOutgoingHosts.end(), DeletePointer()); + mOutgoingHosts.clear(); for (xferp = mSendList; xferp; xferp = xferp->mNext) { - for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData()) + for (status_list_t::iterator iter = mOutgoingHosts.begin(); + iter != mOutgoingHosts.end(); ++iter) { + host_statusp = *iter; if (host_statusp->mHost == xferp->mRemoteHost) { break; @@ -172,7 +176,7 @@ void LLXferManager::updateHostStatus() if (host_statusp) { host_statusp->mHost = xferp->mRemoteHost; - mOutgoingHosts.addData(host_statusp); + mOutgoingHosts.push_front(host_statusp); } } if (host_statusp) @@ -195,12 +199,14 @@ void LLXferManager::updateHostStatus() void LLXferManager::printHostStatus() { LLHostStatus *host_statusp = NULL; - if (mOutgoingHosts.getFirstData()) + if (!mOutgoingHosts.empty()) { llinfos << "Outgoing Xfers:" << llendl; - for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData()) + for (status_list_t::iterator iter = mOutgoingHosts.begin(); + iter != mOutgoingHosts.end(); ++iter) { + host_statusp = *iter; llinfos << " " << host_statusp->mHost << " active: " << host_statusp->mNumActive << " pending: " << host_statusp->mNumPending << llendl; } } @@ -275,8 +281,10 @@ S32 LLXferManager::numPendingXfers(const LLHost &host) { LLHostStatus *host_statusp = NULL; - for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData()) + for (status_list_t::iterator iter = mOutgoingHosts.begin(); + iter != mOutgoingHosts.end(); ++iter) { + host_statusp = *iter; if (host_statusp->mHost == host) { return (host_statusp->mNumPending); @@ -291,8 +299,10 @@ S32 LLXferManager::numActiveXfers(const LLHost &host) { LLHostStatus *host_statusp = NULL; - for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData()) + for (status_list_t::iterator iter = mOutgoingHosts.begin(); + iter != mOutgoingHosts.end(); ++iter) { + host_statusp = *iter; if (host_statusp->mHost == host) { return (host_statusp->mNumActive); @@ -307,8 +317,10 @@ void LLXferManager::changeNumActiveXfers(const LLHost &host, S32 delta) { LLHostStatus *host_statusp = NULL; - for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData()) + for (status_list_t::iterator iter = mOutgoingHosts.begin(); + iter != mOutgoingHosts.end(); ++iter) { + host_statusp = *iter; if (host_statusp->mHost == host) { host_statusp->mNumActive += delta; @@ -1010,15 +1022,15 @@ void LLXferManager::startPendingDownloads() // stateful iteration, it would be possible for old requests to // never start. LLXfer* xferp = mReceiveList; - LLLinkedList pending_downloads; + std::list pending_downloads; S32 download_count = 0; S32 pending_count = 0; while(xferp) { if(xferp->mStatus == e_LL_XFER_PENDING) { - ++pending_count; // getLength() is O(N), so track it here. - pending_downloads.addData(xferp); + ++pending_count; + pending_downloads.push_front(xferp); } else if(xferp->mStatus == e_LL_XFER_IN_PROGRESS) { @@ -1036,16 +1048,18 @@ void LLXferManager::startPendingDownloads() if((start_count > 0) && (pending_count > 0)) { S32 result; - xferp = pending_downloads.getFirstData(); - while(start_count-- && xferp) + for (std::list::iterator iter = pending_downloads.begin(); + iter != pending_downloads.end(); ++iter) { + xferp = *iter; + if (start_count-- <= 0) + break; result = xferp->startDownload(); if(result) { xferp->abort(result); ++start_count; } - xferp = pending_downloads.getNextData(); } } } diff --git a/linden/indra/llmessage/llxfermanager.h b/linden/indra/llmessage/llxfermanager.h index 7b3a888..82cd8e9 100644 --- a/linden/indra/llmessage/llxfermanager.h +++ b/linden/indra/llmessage/llxfermanager.h @@ -45,7 +45,6 @@ class LLVFS; #include "llxfer.h" #include "message.h" #include "llassetstorage.h" -#include "linked_lists.h" #include "lldir.h" #include "lllinkedqueue.h" #include "llthrottle.h" @@ -101,7 +100,8 @@ class LLXferManager LLXfer *mSendList; LLXfer *mReceiveList; - LLLinkedList mOutgoingHosts; + typedef std::list status_list_t; + status_list_t mOutgoingHosts; private: protected: diff --git a/linden/indra/llmessage/message.h b/linden/indra/llmessage/message.h index 424674d..05aa592 100644 --- a/linden/indra/llmessage/message.h +++ b/linden/indra/llmessage/message.h @@ -216,8 +216,6 @@ class LLMessageSystem LLPacketRing mPacketRing; LLReliablePacketParams mReliablePacketParams; - //LLLinkedList mAckList; - // Set this flag to TRUE when you want *very* verbose logs. BOOL mVerboseLog; -- cgit v1.1