diff options
Diffstat (limited to 'indra/llmessage')
37 files changed, 820 insertions, 121 deletions
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 47fbc3ab69..a611de0cda 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -3,6 +3,7 @@ project(llmessage) include(00-Common) +include(GoogleMock) include(LLAddBuildTest) include(LLCommon) include(LLMath) @@ -22,6 +23,7 @@ include_directories( set(llmessage_SOURCE_FILES llares.cpp + llareslistener.cpp llassetstorage.cpp llblowfishcipher.cpp llbuffer.cpp @@ -104,6 +106,7 @@ set(llmessage_HEADER_FILES CMakeLists.txt llares.h + llareslistener.h llassetstorage.h llblowfishcipher.h llbuffer.h @@ -221,6 +224,7 @@ SET(llmessage_TEST_SOURCE_FILES llnamevalue.cpp lltrustedmessageservice.cpp lltemplatemessagedispatcher.cpp + llregionpresenceverifier.cpp ) LL_ADD_PROJECT_UNIT_TESTS(llmessage "${llmessage_TEST_SOURCE_FILES}") @@ -231,6 +235,7 @@ set(test_libs ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} ) LL_ADD_INTEGRATION_TEST( diff --git a/indra/llmessage/llares.cpp b/indra/llmessage/llares.cpp index fe37fe8142..acbf51d75c 100644 --- a/indra/llmessage/llares.cpp +++ b/indra/llmessage/llares.cpp @@ -33,6 +33,7 @@ */ #include "linden_common.h" +#include "llares.h" #include <ares_dns.h> #include <ares_version.h> @@ -42,9 +43,10 @@ #include "apr_poll.h" #include "llapr.h" -#include "llares.h" +#include "llareslistener.h" #if defined(LL_WINDOWS) +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally # define ns_c_in 1 # define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */ # define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */ @@ -102,7 +104,9 @@ void LLAres::QueryResponder::queryError(int code) } LLAres::LLAres() : -chan_(NULL), mInitSuccess(false) + chan_(NULL), + mInitSuccess(false), + mListener(new LLAresListener("LLAres", this)) { if (ares_init(&chan_) != ARES_SUCCESS) { diff --git a/indra/llmessage/llares.h b/indra/llmessage/llares.h index c709a08499..78febcd560 100644 --- a/indra/llmessage/llares.h +++ b/indra/llmessage/llares.h @@ -36,7 +36,13 @@ #define LL_LLARES_H #ifdef LL_WINDOWS +// ares.h is broken on windows in that it depends on types defined in ws2tcpip.h +// we need to include them first to work around it, but the headers issue warnings +# pragma warning(push) +# pragma warning(disable:4996) +# include <winsock2.h> # include <ws2tcpip.h> +# pragma warning(pop) #endif #ifdef LL_STANDALONE @@ -49,7 +55,10 @@ #include "llrefcount.h" #include "lluri.h" +#include <boost/shared_ptr.hpp> + class LLQueryResponder; +class LLAresListener; /** * @brief Supported DNS RR types. @@ -444,6 +453,9 @@ public: protected: ares_channel chan_; bool mInitSuccess; + // boost::scoped_ptr would actually fit the requirement better, but it + // can't handle incomplete types as boost::shared_ptr can. + boost::shared_ptr<LLAresListener> mListener; }; /** diff --git a/indra/llmessage/llareslistener.cpp b/indra/llmessage/llareslistener.cpp new file mode 100644 index 0000000000..a8beb8cbde --- /dev/null +++ b/indra/llmessage/llareslistener.cpp @@ -0,0 +1,75 @@ +/** + * @file llareslistener.cpp + * @author Nat Goodspeed + * @date 2009-03-18 + * @brief Implementation for llareslistener. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llareslistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llares.h" +#include "llerror.h" +#include "llevents.h" +#include "llsdutil.h" + +LLAresListener::LLAresListener(const std::string& pumpname, LLAres* llares): + LLDispatchListener(pumpname, "op"), + mAres(llares) +{ + // add() every method we want to be able to invoke via this event API. + // Optional third parameter validates expected LLSD request structure. + add("rewriteURI", &LLAresListener::rewriteURI, + LLSD().insert("uri", LLSD()).insert("reply", LLSD())); +} + +/// This UriRewriteResponder subclass packages returned URIs as an LLSD +/// array to send back to the requester. +class UriRewriteResponder: public LLAres::UriRewriteResponder +{ +public: + /** + * Specify the request, containing the event pump name on which to send + * the reply. + */ + UriRewriteResponder(const LLSD& request): + mReqID(request), + mPumpName(request["reply"]) + {} + + /// Called by base class with results. This is called in both the + /// success and error cases. On error, the calling logic passes the + /// original URI. + virtual void rewriteResult(const std::vector<std::string>& uris) + { + LLSD result; + for (std::vector<std::string>::const_iterator ui(uris.begin()), uend(uris.end()); + ui != uend; ++ui) + { + result.append(*ui); + } + // This call knows enough to avoid trying to insert a map key into an + // LLSD array. It's there so that if, for any reason, we ever decide + // to change the response from array to map, it will Just Start Working. + mReqID.stamp(result); + LLEventPumps::instance().obtain(mPumpName).post(result); + } + +private: + LLReqID mReqID; + const std::string mPumpName; +}; + +void LLAresListener::rewriteURI(const LLSD& data) +{ + mAres->rewriteURI(data["uri"], new UriRewriteResponder(data)); +} diff --git a/indra/llmessage/llareslistener.h b/indra/llmessage/llareslistener.h new file mode 100644 index 0000000000..bf093b3d3d --- /dev/null +++ b/indra/llmessage/llareslistener.h @@ -0,0 +1,37 @@ +/** + * @file llareslistener.h + * @author Nat Goodspeed + * @date 2009-03-18 + * @brief LLEventPump API for LLAres. This header doesn't actually define the + * API; the API is defined by the pump name on which this class + * listens, and by the expected content of LLSD it receives. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLARESLISTENER_H) +#define LL_LLARESLISTENER_H + +#include "lleventdispatcher.h" + +class LLAres; +class LLSD; + +/// Listen on an LLEventPump with specified name for LLAres request events. +class LLAresListener: public LLDispatchListener +{ +public: + /// Specify the pump name on which to listen, and bind the LLAres instance + /// to use (e.g. gAres) + LLAresListener(const std::string& pumpname, LLAres* llares); + +private: + /// command["op"] == "rewriteURI" + void rewriteURI(const LLSD& data); + + LLAres* mAres; +}; + +#endif /* ! defined(LL_LLARESLISTENER_H) */ diff --git a/indra/llmessage/llcachename.h b/indra/llmessage/llcachename.h index 47d49076f4..8641437d86 100644 --- a/indra/llmessage/llcachename.h +++ b/indra/llmessage/llcachename.h @@ -42,9 +42,9 @@ class LLUUID; typedef boost::signals2::signal<void (const LLUUID& id, - const std::string& first_name, - const std::string& last_name, - BOOL is_group)> LLCacheNameSignal; + const std::string& first_name, + const std::string& last_name, + BOOL is_group)> LLCacheNameSignal; typedef LLCacheNameSignal::slot_type LLCacheNameCallback; // Old callback with user data for compatability @@ -100,7 +100,6 @@ public: // LEGACY boost::signals2::connection get(const LLUUID& id, BOOL is_group, old_callback_t callback, void* user_data); - // This method needs to be called from time to time to send out // requests. void processPending(); diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index a4af8e989b..5ff41322b7 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -131,7 +131,7 @@ void LLCurl::Responder::errorWithContent( // virtual void LLCurl::Responder::error(U32 status, const std::string& reason) { - llinfos << status << ": " << reason << llendl; + llinfos << mURL << " [" << status << "]: " << reason << llendl; } // virtual @@ -139,6 +139,11 @@ void LLCurl::Responder::result(const LLSD& content) { } +void LLCurl::Responder::setURL(const std::string& url) +{ + mURL = url; +} + // virtual void LLCurl::Responder::completedRaw( U32 status, @@ -148,7 +153,11 @@ void LLCurl::Responder::completedRaw( { LLSD content; LLBufferStream istr(channels, buffer.get()); - LLSDSerialize::fromXML(content, istr); + if (!LLSDSerialize::fromXML(content, istr)) + { + llinfos << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl; + } + completed(status, reason, content); } diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index fbd3077cbf..0b58e7c4a5 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -120,8 +120,14 @@ public: // 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); + // Used internally to set the url for debugging later. + void setURL(const std::string& url); + public: /* but not really -- don't touch this */ U32 mReferenceCount; + + private: + std::string mURL; }; typedef boost::intrusive_ptr<Responder> ResponderPtr; diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 8b90a4c5ca..12ecbb36eb 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -265,6 +265,11 @@ static void request( } } + if (responder) + { + responder->setURL(url); + } + req->setCallback(new LLHTTPClientURLAdaptor(responder)); if (method == LLURLRequest::HTTP_POST && gMessageSystem) diff --git a/indra/llmessage/llhttpclientadapter.cpp b/indra/llmessage/llhttpclientadapter.cpp index b6988224ce..9d3c83f828 100644 --- a/indra/llmessage/llhttpclientadapter.cpp +++ b/indra/llmessage/llhttpclientadapter.cpp @@ -1,5 +1,5 @@ /** - * @file + * @file llhttpclientadapter.cpp * @brief * * $LicenseInfo:firstyear=2009&license=viewergpl$ diff --git a/indra/llmessage/llhttpclientadapter.h b/indra/llmessage/llhttpclientadapter.h index 7f76390d0c..a205a2f260 100644 --- a/indra/llmessage/llhttpclientadapter.h +++ b/indra/llmessage/llhttpclientadapter.h @@ -1,5 +1,5 @@ /** - * @file + * @file llhttpclientadepter.h * @brief * * $LicenseInfo:firstyear=2008&license=viewergpl$ diff --git a/indra/llmessage/llhttpclientinterface.h b/indra/llmessage/llhttpclientinterface.h index 42a8e5cd0a..085a59cf27 100644 --- a/indra/llmessage/llhttpclientinterface.h +++ b/indra/llmessage/llhttpclientinterface.h @@ -1,5 +1,5 @@ /** - * @file + * @file llhttpclientinterface.h * @brief * * $LicenseInfo:firstyear=2008&license=viewergpl$ diff --git a/indra/llmessage/llinstantmessage.cpp b/indra/llmessage/llinstantmessage.cpp index 7c63625004..3da41939fa 100644 --- a/indra/llmessage/llinstantmessage.cpp +++ b/indra/llmessage/llinstantmessage.cpp @@ -40,7 +40,7 @@ #include "lluuid.h" #include "llsd.h" #include "llsdserialize.h" -#include "llsdutil.h" +#include "llsdutil_math.h" #include "llpointer.h" #include "message.h" diff --git a/indra/llmessage/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp index a00dbd1809..97134bd336 100644 --- a/indra/llmessage/lliohttpserver.cpp +++ b/indra/llmessage/lliohttpserver.cpp @@ -521,7 +521,7 @@ protected: * seek orfor string assignment. * @returns Returns true if a line was found. */ - bool readLine( + bool readHeaderLine( const LLChannelDescriptors& channels, buffer_ptr_t buffer, U8* dest, @@ -592,7 +592,7 @@ LLHTTPResponder::~LLHTTPResponder() //lldebugs << "destroying LLHTTPResponder" << llendl; } -bool LLHTTPResponder::readLine( +bool LLHTTPResponder::readHeaderLine( const LLChannelDescriptors& channels, buffer_ptr_t buffer, U8* dest, @@ -670,7 +670,7 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( #endif PUMP_DEBUG; - if(readLine(channels, buffer, (U8*)buf, len)) + if(readHeaderLine(channels, buffer, (U8*)buf, len)) { bool read_next_line = false; bool parse_all = true; @@ -734,7 +734,13 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( if(read_next_line) { len = HEADER_BUFFER_SIZE; - readLine(channels, buffer, (U8*)buf, len); + if (!readHeaderLine(channels, buffer, (U8*)buf, len)) + { + // Failed to read the header line, probably too long. + // readHeaderLine already marked the channel/buffer as bad. + keep_parsing = false; + break; + } } if(0 == len) { diff --git a/indra/llmessage/llmail.cpp b/indra/llmessage/llmail.cpp index d52ff6c7e8..ce206d8d7d 100644 --- a/indra/llmessage/llmail.cpp +++ b/indra/llmessage/llmail.cpp @@ -265,7 +265,7 @@ std::string LLMail::buildSMTPTransaction( // static bool LLMail::send( const std::string& header, - const std::string& message, + const std::string& raw_message, const char* from_address, const char* to_address) { @@ -276,8 +276,20 @@ bool LLMail::send( return false; } - // *FIX: this translation doesn't deal with a single period on a - // line by itself. + // remove any "." SMTP commands to prevent injection (DEV-35777) + // we don't need to worry about "\r\n.\r\n" because of the + // "\n" --> "\n\n" conversion going into rfc2822_msg below + std::string message = raw_message; + std::string bad_string = "\n.\n"; + std::string good_string = "\n..\n"; + while (1) + { + int index = message.find(bad_string); + if (index == std::string::npos) break; + message.replace(index, bad_string.size(), good_string); + } + + // convert all "\n" into "\r\n" std::ostringstream rfc2822_msg; for(U32 i = 0; i < message.size(); ++i) { diff --git a/indra/llmessage/llmessagesenderinterface.h b/indra/llmessage/llmessagesenderinterface.h index 119eb1d7f7..af6733fa05 100644 --- a/indra/llmessage/llmessagesenderinterface.h +++ b/indra/llmessage/llmessagesenderinterface.h @@ -1,5 +1,5 @@ /** - * @file + * @file llmessagesenderinterface.h * @brief * * $LicenseInfo:firstyear=2008&license=viewergpl$ diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp index 485bc6aa44..9376cde7b5 100644 --- a/indra/llmessage/llpartdata.cpp +++ b/indra/llmessage/llpartdata.cpp @@ -39,6 +39,8 @@ #include "v4coloru.h" #include "llsdutil.h" +#include "llsdutil_math.h" + const S32 PS_PART_DATA_BLOCK_SIZE = 4 + 2 + 4 + 4 + 2 + 2; // 18 diff --git a/indra/llmessage/llregionpresenceverifier.cpp b/indra/llmessage/llregionpresenceverifier.cpp index 0527d5cb8d..b1868e6a00 100644 --- a/indra/llmessage/llregionpresenceverifier.cpp +++ b/indra/llmessage/llregionpresenceverifier.cpp @@ -1,5 +1,5 @@ /** - * @file + * @file llregionpresenceverifier.cpp * @brief * * $LicenseInfo:firstyear=2008&license=viewergpl$ @@ -30,17 +30,48 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "llregionpresenceverifier.h" #include "llhttpclientinterface.h" #include <sstream> #include "net.h" #include "message.h" +namespace boost +{ + void intrusive_ptr_add_ref(LLRegionPresenceVerifier::Response* p) + { + ++p->mReferenceCount; + } + + void intrusive_ptr_release(LLRegionPresenceVerifier::Response* p) + { + if(p && 0 == --p->mReferenceCount) + { + delete p; + } + } +}; -LLRegionPresenceVerifier::RegionResponder::RegionResponder(ResponsePtr data) : mSharedData(data) +LLRegionPresenceVerifier::Response::~Response() { } +LLRegionPresenceVerifier::RegionResponder::RegionResponder(const std::string& + uri, + ResponsePtr data, + S32 retry_count) : + mUri(uri), + mSharedData(data), + mRetryCount(retry_count) +{ +} + +//virtual +LLRegionPresenceVerifier::RegionResponder::~RegionResponder() +{ +} void LLRegionPresenceVerifier::RegionResponder::result(const LLSD& content) { @@ -49,30 +80,36 @@ void LLRegionPresenceVerifier::RegionResponder::result(const LLSD& content) LLHost destination(host, port); LLUUID id = content["region_id"]; - llinfos << "Verifying " << destination.getString() << " is region " << id << llendl; + lldebugs << "Verifying " << destination.getString() << " is region " << id << llendl; std::stringstream uri; uri << "http://" << destination.getString() << "/state/basic/"; - mSharedData->getHttpClient().get(uri.str(), new VerifiedDestinationResponder(mSharedData, content)); + mSharedData->getHttpClient().get( + uri.str(), + new VerifiedDestinationResponder(mUri, mSharedData, content, mRetryCount)); } -void LLRegionPresenceVerifier::RegionResponder::completed( - U32 status, - const std::string& reason, - const LLSD& content) +void LLRegionPresenceVerifier::RegionResponder::error(U32 status, + const std::string& reason) { - LLHTTPClient::Responder::completed(status, reason, content); - - mSharedData->onCompletedRegionRequest(); + // TODO: babbage: distinguish between region presence service and + // region verification errors? + mSharedData->onRegionVerificationFailed(); } - -LLRegionPresenceVerifier::VerifiedDestinationResponder::VerifiedDestinationResponder(ResponsePtr data, const LLSD& content) : mSharedData(data), mContent(content) +LLRegionPresenceVerifier::VerifiedDestinationResponder::VerifiedDestinationResponder(const std::string& uri, ResponsePtr data, const LLSD& content, + S32 retry_count): + mUri(uri), + mSharedData(data), + mContent(content), + mRetryCount(retry_count) { } - - +//virtual +LLRegionPresenceVerifier::VerifiedDestinationResponder::~VerifiedDestinationResponder() +{ +} void LLRegionPresenceVerifier::VerifiedDestinationResponder::result(const LLSD& content) { @@ -87,13 +124,14 @@ void LLRegionPresenceVerifier::VerifiedDestinationResponder::result(const LLSD& { mSharedData->onRegionVerified(mContent); } - else if (mSharedData->shouldRetry()) + else if (mRetryCount > 0) { retry(); } else { - llwarns << "Could not correctly look up region from region presence service. Region: " << mSharedData->getRegionUri() << llendl; + llwarns << "Simulator verification failed. Region: " << mUri << llendl; + mSharedData->onRegionVerificationFailed(); } } @@ -101,13 +139,21 @@ void LLRegionPresenceVerifier::VerifiedDestinationResponder::retry() { LLSD headers; headers["Cache-Control"] = "no-cache, max-age=0"; - llinfos << "Requesting region information, get uncached for region " << mSharedData->getRegionUri() << llendl; - mSharedData->decrementRetries(); - mSharedData->getHttpClient().get(mSharedData->getRegionUri(), new RegionResponder(mSharedData), headers); + llinfos << "Requesting region information, get uncached for region " + << mUri << llendl; + --mRetryCount; + mSharedData->getHttpClient().get(mUri, new RegionResponder(mUri, mSharedData, mRetryCount), headers); } void LLRegionPresenceVerifier::VerifiedDestinationResponder::error(U32 status, const std::string& reason) { - retry(); + if(mRetryCount > 0) + { + retry(); + } + else + { + llwarns << "Failed to contact simulator for verification. Region: " << mUri << llendl; + mSharedData->onRegionVerificationFailed(); + } } - diff --git a/indra/llmessage/llregionpresenceverifier.h b/indra/llmessage/llregionpresenceverifier.h index 54ad6226d6..f57a62a731 100644 --- a/indra/llmessage/llregionpresenceverifier.h +++ b/indra/llmessage/llregionpresenceverifier.h @@ -1,5 +1,5 @@ /** - * @file + * @file llregionpresenceverifier.cpp * @brief * * $LicenseInfo:firstyear=2008&license=viewergpl$ @@ -37,7 +37,7 @@ #include "llhttpclient.h" #include <string> #include "llsd.h" -#include <boost/shared_ptr.hpp> +#include <boost/intrusive_ptr.hpp> class LLHTTPClientInterface; @@ -47,49 +47,58 @@ public: class Response { public: - virtual ~Response() {} + virtual ~Response() = 0; virtual bool checkValidity(const LLSD& content) const = 0; virtual void onRegionVerified(const LLSD& region_details) = 0; - - virtual void decrementRetries() = 0; + virtual void onRegionVerificationFailed() = 0; virtual LLHTTPClientInterface& getHttpClient() = 0; - virtual std::string getRegionUri() const = 0; - virtual bool shouldRetry() const = 0; - virtual void onCompletedRegionRequest() {} + public: /* but not really -- don't touch this */ + U32 mReferenceCount; }; - typedef boost::shared_ptr<Response> ResponsePtr; + typedef boost::intrusive_ptr<Response> ResponsePtr; class RegionResponder : public LLHTTPClient::Responder { public: - RegionResponder(ResponsePtr data); + RegionResponder(const std::string& uri, ResponsePtr data, + S32 retry_count); + virtual ~RegionResponder(); virtual void result(const LLSD& content); - virtual void completed( - U32 status, - const std::string& reason, - const LLSD& content); + virtual void error(U32 status, const std::string& reason); private: ResponsePtr mSharedData; + std::string mUri; + S32 mRetryCount; }; class VerifiedDestinationResponder : public LLHTTPClient::Responder { public: - VerifiedDestinationResponder(ResponsePtr data, const LLSD& content); + VerifiedDestinationResponder(const std::string& uri, ResponsePtr data, + const LLSD& content, S32 retry_count); + virtual ~VerifiedDestinationResponder(); virtual void result(const LLSD& content); virtual void error(U32 status, const std::string& reason); + private: void retry(); ResponsePtr mSharedData; LLSD mContent; + std::string mUri; + S32 mRetryCount; }; }; +namespace boost +{ + void intrusive_ptr_add_ref(LLRegionPresenceVerifier::Response* p); + void intrusive_ptr_release(LLRegionPresenceVerifier::Response* p); +}; #endif //LL_LLREGIONPRESENCEVERIFIER_H diff --git a/indra/llmessage/llsdmessage.cpp b/indra/llmessage/llsdmessage.cpp index 9967a6197f..2cb742e261 100644 --- a/indra/llmessage/llsdmessage.cpp +++ b/indra/llmessage/llsdmessage.cpp @@ -68,6 +68,7 @@ bool LLSDMessage::httpListener(const LLSD& request) } LLHTTPClient::post(url, payload, new LLSDMessage::EventResponder(LLEventPumps::instance(), + request, url, "POST", reply, error), LLSD(), // headers timeout); @@ -81,7 +82,9 @@ void LLSDMessage::EventResponder::result(const LLSD& data) // to the pump whose name is "". if (! mReplyPump.empty()) { - mPumps.obtain(mReplyPump).post(data); + LLSD response(data); + mReqID.stamp(response); + mPumps.obtain(mReplyPump).post(response); } else // default success handling { @@ -98,7 +101,7 @@ void LLSDMessage::EventResponder::errorWithContent(U32 status, const std::string // explicit pump name. if (! mErrorPump.empty()) { - LLSD info; + LLSD info(mReqID.makeResponse()); info["target"] = mTarget; info["message"] = mMessage; info["status"] = LLSD::Integer(status); diff --git a/indra/llmessage/llsdmessage.h b/indra/llmessage/llsdmessage.h index 65503756a8..6ee00fd41d 100644 --- a/indra/llmessage/llsdmessage.h +++ b/indra/llmessage/llsdmessage.h @@ -121,9 +121,11 @@ private: * (e.g. "POST") as @a message. */ EventResponder(LLEventPumps& pumps, + const LLSD& request, const std::string& target, const std::string& message, const std::string& replyPump, const std::string& errorPump): mPumps(pumps), + mReqID(request), mTarget(target), mMessage(message), mReplyPump(replyPump), @@ -135,6 +137,7 @@ private: private: LLEventPumps& mPumps; + LLReqID mReqID; const std::string mTarget, mMessage, mReplyPump, mErrorPump; }; diff --git a/indra/llmessage/llsdmessagebuilder.cpp b/indra/llmessage/llsdmessagebuilder.cpp index 21937f022f..6e41b03895 100755 --- a/indra/llmessage/llsdmessagebuilder.cpp +++ b/indra/llmessage/llsdmessagebuilder.cpp @@ -37,6 +37,7 @@ #include "llmessagetemplate.h" #include "llquaternion.h" #include "llsdutil.h" +#include "llsdutil_math.h" #include "llsdserialize.h" #include "u64.h" #include "v3dmath.h" diff --git a/indra/llmessage/llsdmessagereader.cpp b/indra/llmessage/llsdmessagereader.cpp index e699ec9e28..845a12d23b 100755 --- a/indra/llmessage/llsdmessagereader.cpp +++ b/indra/llmessage/llsdmessagereader.cpp @@ -38,6 +38,7 @@ #include "llsdmessagebuilder.h" #include "llsdutil.h" +#include "llsdutil_math.h" #include "v3math.h" #include "v4math.h" #include "v3dmath.h" diff --git a/indra/llmessage/llstoredmessage.cpp b/indra/llmessage/llstoredmessage.cpp index 32cbb15cb3..d6b2f45d04 100644 --- a/indra/llmessage/llstoredmessage.cpp +++ b/indra/llmessage/llstoredmessage.cpp @@ -1,5 +1,5 @@ /** - * @file + * @file llstoredmessage.cpp * @brief * * $LicenseInfo:firstyear=2009&license=viewergpl$ diff --git a/indra/llmessage/llstoredmessage.h b/indra/llmessage/llstoredmessage.h index 5069c2cb2e..359e4c5aea 100644 --- a/indra/llmessage/llstoredmessage.h +++ b/indra/llmessage/llstoredmessage.h @@ -1,5 +1,5 @@ /** - * @file + * @file llstoredmessage.h * @brief * * $LicenseInfo:firstyear=2009&license=viewergpl$ diff --git a/indra/llmessage/llthrottle.cpp b/indra/llmessage/llthrottle.cpp index 70279a3c62..0872efba50 100644 --- a/indra/llmessage/llthrottle.cpp +++ b/indra/llmessage/llthrottle.cpp @@ -265,6 +265,31 @@ BOOL LLThrottleGroup::setNominalBPS(F32* throttle_vec) return changed; } +// Return bits available in the channel +S32 LLThrottleGroup::getAvailable(S32 throttle_cat) +{ + S32 retval = 0; + + F32 category_bps = mCurrentBPS[throttle_cat]; + F32 lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME; + + // use a temporary bits_available + // since we don't want to change mBitsAvailable every time + F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat]); + F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time); + + if (bits_available >= lookahead_bits) + { + retval = (S32) gThrottleMaximumBPS[throttle_cat]; + } + else + { + retval = (S32) bits_available; + } + + return retval; +} + BOOL LLThrottleGroup::checkOverflow(S32 throttle_cat, F32 bits) { diff --git a/indra/llmessage/llthrottle.h b/indra/llmessage/llthrottle.h index 7d1679beb2..47a7c653b2 100644 --- a/indra/llmessage/llthrottle.h +++ b/indra/llmessage/llthrottle.h @@ -84,6 +84,8 @@ public: BOOL dynamicAdjust(); // Shift bandwidth from idle channels to busy channels, TRUE if adjustment occurred BOOL setNominalBPS(F32* throttle_vec); // TRUE if any value was different, resets adjustment system if was different + S32 getAvailable(S32 throttle_cat); // Return bits available in the channel + void packThrottle(LLDataPacker &dp) const; void unpackThrottle(LLDataPacker &dp); public: diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp index 5a1cd95ffc..41f3f3f607 100644 --- a/indra/llmessage/lltransfersourceasset.cpp +++ b/indra/llmessage/lltransfersourceasset.cpp @@ -270,7 +270,6 @@ bool is_asset_fetch_by_id_allowed(LLAssetType::EType type) case LLAssetType::AT_BODYPART: case LLAssetType::AT_ANIMATION: case LLAssetType::AT_GESTURE: - case LLAssetType::AT_FAVORITE: rv = true; break; default: diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 3ab8057abb..81b7761ed5 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -51,6 +51,7 @@ static const U32 HTTP_STATUS_PIPE_ERROR = 499; * String constants */ const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri"); +const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes"); static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); @@ -247,7 +248,29 @@ LLIOPipe::EStatus LLURLRequest::process_impl( PUMP_DEBUG; LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); //llinfos << "LLURLRequest::process_impl()" << llendl; - if(!buffer) return STATUS_ERROR; + if (!buffer) return STATUS_ERROR; + + // we're still waiting or prcessing, check how many + // bytes we have accumulated. + const S32 MIN_ACCUMULATION = 100000; + if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) + { + // This is a pretty sloppy calculation, but this + // tries to make the gross assumption that if data + // is coming in at 56kb/s, then this transfer will + // probably succeed. So, if we're accumlated + // 100,000 bytes (MIN_ACCUMULATION) then let's + // give this client another 2s to complete. + const F32 TIMEOUT_ADJUSTMENT = 2.0f; + mDetail->mByteAccumulator = 0; + pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT); + lldebugs << "LLURLRequest adjustTimeoutSeconds for request: " << mDetail->mURL << llendl; + if (mState == STATE_INITIALIZED) + { + llinfos << "LLURLRequest adjustTimeoutSeconds called during upload" << llendl; + } + } + switch(mState) { case STATE_INITIALIZED: @@ -286,27 +309,14 @@ LLIOPipe::EStatus LLURLRequest::process_impl( bool newmsg = mDetail->mCurlRequest->getResult(&result); if(!newmsg) { - // we're still waiting or prcessing, check how many - // bytes we have accumulated. - const S32 MIN_ACCUMULATION = 100000; - if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) - { - // This is a pretty sloppy calculation, but this - // tries to make the gross assumption that if data - // is coming in at 56kb/s, then this transfer will - // probably succeed. So, if we're accumlated - // 100,000 bytes (MIN_ACCUMULATION) then let's - // give this client another 2s to complete. - const F32 TIMEOUT_ADJUSTMENT = 2.0f; - mDetail->mByteAccumulator = 0; - pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT); - } - // keep processing break; } mState = STATE_HAVE_RESPONSE; + context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; + context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; + lldebugs << this << "Setting context to " << context << llendl; switch(result) { case CURLE_OK: @@ -353,10 +363,16 @@ LLIOPipe::EStatus LLURLRequest::process_impl( // we already stuffed everything into channel in in the curl // callback, so we are done. eos = true; + context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; + context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; + lldebugs << this << "Setting context to " << context << llendl; return STATUS_DONE; default: PUMP_DEBUG; + context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; + context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; + lldebugs << this << "Setting context to " << context << llendl; return STATUS_ERROR; } } @@ -369,6 +385,8 @@ void LLURLRequest::initialize() mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this); mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this); + mRequestTransferedBytes = 0; + mResponseTransferedBytes = 0; } bool LLURLRequest::configure() @@ -471,6 +489,7 @@ size_t LLURLRequest::downCallback( req->mDetail->mChannels.out(), (U8*)data, bytes); + req->mResponseTransferedBytes += bytes; req->mDetail->mByteAccumulator += bytes; return bytes; } @@ -494,6 +513,7 @@ size_t LLURLRequest::upCallback( req->mDetail->mLastRead, (U8*)data, bytes); + req->mRequestTransferedBytes += bytes; return bytes; } diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index 86ef71f085..cb3c466440 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -45,6 +45,12 @@ #include "llchainio.h" #include "llerror.h" + +extern const std::string CONTEXT_REQUEST; +extern const std::string CONTEXT_DEST_URI_SD_LABEL; +extern const std::string CONTEXT_RESPONSE; +extern const std::string CONTEXT_TRANSFERED_BYTES; + class LLURLRequestDetail; class LLURLRequestComplete; @@ -208,6 +214,8 @@ protected: ERequestAction mAction; LLURLRequestDetail* mDetail; LLIOPipe::ptr_t mCompletionCallback; + S32 mRequestTransferedBytes; + S32 mResponseTransferedBytes; private: /** diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp index 08c9192c9f..209bdb2249 100644 --- a/indra/llmessage/llxfermanager.cpp +++ b/indra/llmessage/llxfermanager.cpp @@ -760,30 +760,36 @@ static bool remove_prefix(std::string& filename, const std::string& prefix) static bool verify_cache_filename(const std::string& filename) { //NOTE: This routine is only used to check file names that our own - // code places in the cache directory. As such, it can be limited - // to this very restrictive file name pattern. It does not need to - // handle other characters. - + // code places in the cache directory. As such, it can be limited + // to this very restrictive file name pattern. It does not need to + // handle other characters. The only known uses of this are (with examples): + // sim to sim object pass: fc0b72d8-9456-63d9-a802-a557ef847313.tmp + // sim to viewer mute list: mute_b78eacd0-1244-448e-93ca-28ede242f647.tmp + // sim to viewer task inventory: inventory_d8ab59d2-baf0-0e79-c4c2-a3f99b9fcf45.tmp + + //IMPORTANT: Do not broaden the filenames accepted by this routine + // without careful analysis. Anything allowed by this function can + // be downloaded by the viewer. + size_t len = filename.size(); - //const boost::regex expr("[a-zA-Z0-9][-_.a-zA-Z0-9]<0,49>"); - if (len < 1 || len > 50) - { + //const boost::regex expr("[0-9a-zA-Z_-]<1,46>\.tmp"); + if (len < 5 || len > 50) + { return false; } - for(unsigned i=0; i<len; ++i) - { + for(size_t i=0; i<(len-4); ++i) + { char c = filename[i]; - bool ok = isalnum(c); - if (!ok && i > 0) - { - ok = '_'==c || '-'==c || '.'==c; - } + bool ok = isalnum(c) || '_'==c || '-'==c; if (!ok) { return false; } } - return true; + return filename[len-4] == '.' + && filename[len-3] == 't' + && filename[len-2] == 'm' + && filename[len-1] == 'p'; } void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user_data*/) diff --git a/indra/llmessage/tests/llareslistener_test.cpp b/indra/llmessage/tests/llareslistener_test.cpp new file mode 100644 index 0000000000..ac4886ccf4 --- /dev/null +++ b/indra/llmessage/tests/llareslistener_test.cpp @@ -0,0 +1,200 @@ +/** + * @file llareslistener_test.cpp + * @author Mark Palange + * @date 2009-02-26 + * @brief Tests of llareslistener.h. + * + * $LicenseInfo:firstyear=2009&license=internal$ + * Copyright (c) 2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + +// Precompiled header +#include "linden_common.h" +// associated header +#include "../llareslistener.h" +// STL headers +#include <iostream> +// std headers +// external library headers +#include <boost/bind.hpp> + +// other Linden headers +#include "llsd.h" +#include "llares.h" +#include "../test/lltut.h" +#include "llevents.h" +#include "tests/wrapllerrs.h" + +/***************************************************************************** +* Dummy stuff +*****************************************************************************/ +LLAres::LLAres(): + // Simulate this much of the real LLAres constructor: we need an + // LLAresListener instance. + mListener(new LLAresListener("LLAres", this)) +{} +LLAres::~LLAres() {} +void LLAres::rewriteURI(const std::string &uri, + LLAres::UriRewriteResponder *resp) +{ + // This is the only LLAres method I chose to implement. + // The effect is that LLAres returns immediately with + // a result that is equal to the input uri. + std::vector<std::string> result; + result.push_back(uri); + resp->rewriteResult(result); +} + +LLAres::QueryResponder::~QueryResponder() {} +void LLAres::QueryResponder::queryError(int) {} +void LLAres::QueryResponder::queryResult(char const*, size_t) {} +LLQueryResponder::LLQueryResponder() {} +void LLQueryResponder::queryResult(char const*, size_t) {} +void LLQueryResponder::querySuccess() {} +void LLAres::UriRewriteResponder::queryError(int) {} +void LLAres::UriRewriteResponder::querySuccess() {} +void LLAres::UriRewriteResponder::rewriteResult(const std::vector<std::string>& uris) {} + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct data + { + LLAres dummyAres; + }; + typedef test_group<data> llareslistener_group; + typedef llareslistener_group::object object; + llareslistener_group llareslistenergrp("llareslistener"); + + struct ResponseCallback + { + std::vector<std::string> mURIs; + bool operator()(const LLSD& response) + { + mURIs.clear(); + for (LLSD::array_const_iterator ri(response.beginArray()), rend(response.endArray()); + ri != rend; ++ri) + { + mURIs.push_back(*ri); + } + return false; + } + }; + + template<> template<> + void object::test<1>() + { + set_test_name("test event"); + // Tests the success and failure cases, since they both use + // the same code paths in the LLAres responder. + ResponseCallback response; + std::string pumpname("trigger"); + // Since we're asking LLEventPumps to obtain() the pump by the desired + // name, it will persist beyond the current scope, so ensure we + // disconnect from it when 'response' goes away. + LLTempBoundListener temp( + LLEventPumps::instance().obtain(pumpname).listen("rewriteURIresponse", + boost::bind(&ResponseCallback::operator(), &response, _1))); + // Now build an LLSD request that will direct its response events to + // that pump. + const std::string testURI("login.bar.com"); + LLSD request; + request["op"] = "rewriteURI"; + request["uri"] = testURI; + request["reply"] = pumpname; + LLEventPumps::instance().obtain("LLAres").post(request); + ensure_equals(response.mURIs.size(), 1); + ensure_equals(response.mURIs.front(), testURI); + } + + template<> template<> + void object::test<2>() + { + set_test_name("bad op"); + WrapLL_ERRS capture; + LLSD request; + request["op"] = "foo"; + std::string threw; + try + { + LLEventPumps::instance().obtain("LLAres").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("LLAresListener bad op", threw, "bad"); + } + + template<> template<> + void object::test<3>() + { + set_test_name("bad rewriteURI request"); + WrapLL_ERRS capture; + LLSD request; + request["op"] = "rewriteURI"; + std::string threw; + try + { + LLEventPumps::instance().obtain("LLAres").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("LLAresListener bad req", threw, "missing"); + ensure_contains("LLAresListener bad req", threw, "reply"); + ensure_contains("LLAresListener bad req", threw, "uri"); + } + + template<> template<> + void object::test<4>() + { + set_test_name("bad rewriteURI request"); + WrapLL_ERRS capture; + LLSD request; + request["op"] = "rewriteURI"; + request["reply"] = "nonexistent"; + std::string threw; + try + { + LLEventPumps::instance().obtain("LLAres").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("LLAresListener bad req", threw, "missing"); + ensure_contains("LLAresListener bad req", threw, "uri"); + ensure_does_not_contain("LLAresListener bad req", threw, "reply"); + } + + template<> template<> + void object::test<5>() + { + set_test_name("bad rewriteURI request"); + WrapLL_ERRS capture; + LLSD request; + request["op"] = "rewriteURI"; + request["uri"] = "foo.bar.com"; + std::string threw; + try + { + LLEventPumps::instance().obtain("LLAres").post(request); + } + catch (const WrapLL_ERRS::FatalException& e) + { + threw = e.what(); + } + ensure_contains("LLAresListener bad req", threw, "missing"); + ensure_contains("LLAresListener bad req", threw, "reply"); + ensure_does_not_contain("LLAresListener bad req", threw, "uri"); + } +} diff --git a/indra/llmessage/tests/llcurl_stub.cpp b/indra/llmessage/tests/llcurl_stub.cpp index 5dc5932fde..e6a5ad9946 100644 --- a/indra/llmessage/tests/llcurl_stub.cpp +++ b/indra/llmessage/tests/llcurl_stub.cpp @@ -22,6 +22,7 @@ #include "linden_common.h" LLCurl::Responder::Responder() + : mReferenceCount(0) { } diff --git a/indra/llmessage/tests/llmockhttpclient.h b/indra/llmessage/tests/llmockhttpclient.h new file mode 100644 index 0000000000..2f55e97fcc --- /dev/null +++ b/indra/llmessage/tests/llmockhttpclient.h @@ -0,0 +1,61 @@ +/** + * @file + * @brief + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008, Linden Research, Inc. + * + * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of + * this source code is governed by the Linden Lab Source Code Disclosure + * Agreement ("Agreement") previously entered between you and Linden + * Lab. By accessing, using, copying, modifying or distributing this + * software, you acknowledge that you have been informed of your + * obligations under the Agreement 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$ + */ + +/* Macro Definitions */ +#ifndef LL_LLMOCKHTTPCLIENT_H +#define LL_LLMOCKHTTPCLIENT_H + +#include "linden_common.h" +#include "llhttpclientinterface.h" + +#include <gmock/gmock.h> + +class LLMockHTTPClient : public LLHTTPClientInterface +{ +public: + MOCK_METHOD2(get, void(const std::string& url, LLCurl::ResponderPtr responder)); + MOCK_METHOD3(get, void(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers)); + MOCK_METHOD3(put, void(const std::string& url, const LLSD& body, LLCurl::ResponderPtr responder)); +}; + +// A helper to match responder types +template<typename T> +struct ResponderType +{ + bool operator()(LLCurl::ResponderPtr ptr) const + { + T* p = dynamic_cast<T*>(ptr.get()); + return p != NULL; + } +}; + +inline bool operator==(const LLSD& l, const LLSD& r) +{ + std::ostringstream ls, rs; + ls << l; + rs << r; + return ls.str() == rs.str(); + +} + + +#endif //LL_LLMOCKHTTPCLIENT_H + diff --git a/indra/llmessage/tests/llregionpresenceverifier_test.cpp b/indra/llmessage/tests/llregionpresenceverifier_test.cpp new file mode 100644 index 0000000000..c86126406e --- /dev/null +++ b/indra/llmessage/tests/llregionpresenceverifier_test.cpp @@ -0,0 +1,113 @@ +/** + * @file + * @brief + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2001-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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "../test/lltut.h" +#include "llregionpresenceverifier.h" +#include "llcurl_stub.cpp" +#include "llhost.cpp" +#include "net.cpp" +#include "lltesthttpclientadapter.cpp" + +class LLTestResponse : public LLRegionPresenceVerifier::Response +{ +public: + + virtual bool checkValidity(const LLSD& content) const + { + return true; + } + + virtual void onRegionVerified(const LLSD& region_details) + { + } + + virtual void onRegionVerificationFailed() + { + } + + virtual LLHTTPClientInterface& getHttpClient() + { + return mHttpInterface; + } + + LLTestHTTPClientAdapter mHttpInterface; +}; + +namespace tut +{ + struct LLRegionPresenceVerifierData + { + LLRegionPresenceVerifierData() : + mResponse(new LLTestResponse()), + mResponder("", LLRegionPresenceVerifier::ResponsePtr(mResponse), + LLSD(), 3) + { + } + + LLTestResponse* mResponse; + LLRegionPresenceVerifier::VerifiedDestinationResponder mResponder; + }; + + typedef test_group<LLRegionPresenceVerifierData> factory; + typedef factory::object object; +} + +namespace +{ + tut::factory tf("LLRegionPresenceVerifier test"); +} + +namespace tut +{ + // Test that VerifiedDestinationResponder does retry + // on error when shouldRetry returns true. + template<> template<> + void object::test<1>() + { + mResponder.error(500, "Internal server error"); + ensure_equals(mResponse->mHttpInterface.mGetUrl.size(), 1); + } + + // Test that VerifiedDestinationResponder only retries + // on error until shouldRetry returns false. + template<> template<> + void object::test<2>() + { + mResponder.error(500, "Internal server error"); + mResponder.error(500, "Internal server error"); + mResponder.error(500, "Internal server error"); + mResponder.error(500, "Internal server error"); + ensure_equals(mResponse->mHttpInterface.mGetUrl.size(), 3); + } +} + diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index e62f20912b..86d5761b1b 100644 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -16,16 +16,12 @@ import os import sys from threading import Thread from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler + mydir = os.path.dirname(__file__) # expected to be .../indra/llmessage/tests/ sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) from indra.util.fastest_elementtree import parse as xml_parse from indra.base import llsd - -def debug(*args): - sys.stdout.writelines(args) - sys.stdout.flush() -# comment out the line below to enable debug output -debug = lambda *args: None +from testrunner import run, debug class TestHTTPRequestHandler(BaseHTTPRequestHandler): """This subclass of BaseHTTPRequestHandler is to receive and echo @@ -106,25 +102,5 @@ class TestHTTPServer(Thread): debug("Starting HTTP server...\n") httpd.serve_forever() -def main(*args): - # Start HTTP server thread. Note that this and all other comm server - # threads should be daemon threads: we'll let them run "forever," - # confident that the whole process will terminate when the main thread - # terminates, which will be when the test executable child process - # terminates. - httpThread = TestHTTPServer(name="httpd") - httpThread.setDaemon(True) - httpThread.start() - # choice of os.spawnv(): - # - [v vs. l] pass a list of args vs. individual arguments, - # - [no p] don't use the PATH because we specifically want to invoke the - # executable passed as our first arg, - # - [no e] child should inherit this process's environment. - debug("Running %s...\n" % (" ".join(args))) - sys.stdout.flush() - rc = os.spawnv(os.P_WAIT, args[0], args) - debug("%s returned %s\n" % (args[0], rc)) - return rc - if __name__ == "__main__": - sys.exit(main(*sys.argv[1:])) + sys.exit(run(server=TestHTTPServer(name="httpd"), *sys.argv[1:])) diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py new file mode 100644 index 0000000000..3b9c3a7a19 --- /dev/null +++ b/indra/llmessage/tests/testrunner.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +"""\ +@file testrunner.py +@author Nat Goodspeed +@date 2009-03-20 +@brief Utilities for writing wrapper scripts for ADD_COMM_BUILD_TEST unit tests + +$LicenseInfo:firstyear=2009&license=viewergpl$ +Copyright (c) 2009, Linden Research, Inc. +$/LicenseInfo$ +""" + +import os +import sys + +def debug(*args): + sys.stdout.writelines(args) + sys.stdout.flush() +# comment out the line below to enable debug output +debug = lambda *args: None + +def run(*args, **kwds): + """All positional arguments collectively form a command line, executed as + a synchronous child process. + In addition, pass server=new_thread_instance as an explicit keyword (to + differentiate it from an additional command-line argument). + new_thread_instance should be an instantiated but not yet started Thread + subclass instance, e.g.: + run("python", "-c", 'print "Hello, world!"', server=TestHTTPServer(name="httpd")) + """ + # If there's no server= keyword arg, don't start a server thread: simply + # run a child process. + try: + thread = kwds.pop("server") + except KeyError: + pass + else: + # Start server thread. Note that this and all other comm server + # threads should be daemon threads: we'll let them run "forever," + # confident that the whole process will terminate when the main thread + # terminates, which will be when the child process terminates. + thread.setDaemon(True) + thread.start() + # choice of os.spawnv(): + # - [v vs. l] pass a list of args vs. individual arguments, + # - [no p] don't use the PATH because we specifically want to invoke the + # executable passed as our first arg, + # - [no e] child should inherit this process's environment. + debug("Running %s...\n" % (" ".join(args))) + sys.stdout.flush() + rc = os.spawnv(os.P_WAIT, args[0], args) + debug("%s returned %s\n" % (args[0], rc)) + return rc |